1
1
mirror of https://github.com/leon-ai/leon.git synced 2024-11-23 20:12:08 +03:00

Merge branch 'develop' into http-server-refactoring

This commit is contained in:
louistiti 2022-11-14 23:57:41 +08:00
commit b1739bbe6b
No known key found for this signature in database
GPG Key ID: 7ECA3DD523793FE6
15 changed files with 498 additions and 518 deletions

View File

@ -162,7 +162,8 @@ The TCP server is used to communicate between the core and third-party nodes, su
To contribute to these parts, you need to set up a Python environment running with a specific Python version and a specific Pipenv version.
It is recommended to use Pyenv to manage your Python versions.
If you are on Linux, you can run the following to install Pyenv, otherwise, please refer to the [Pyenv documentation to install it](https://github.com/pyenv/pyenv#installation):
If you are on GNU/Linux Ubuntu, you can run the following to install Pyenv, otherwise, please refer to the [Pyenv documentation to install it](https://github.com/pyenv/pyenv#installation):
```bash
# Update registry

View File

@ -185,13 +185,6 @@ You'll find a write-up on this [blog post](https://blog.getleon.ai/the-story-beh
<br>
<sub><sup>100 USD / month</sup></sub>
</td>
<td align="center" valign="middle" width="128">
<a href="https://github.com/phareal">
<img src="https://github.com/phareal.png?size=128" />
phareal
</a><br>
<sub><sup>30 USD / month</sup></sub>
</td>
<td align="center" valign="middle" width="128">
<a href="https://github.com/KeithIMyers">
<img src="https://github.com/KeithIMyers.png?size=128" />

View File

@ -1,12 +1,12 @@
{
"type": "",
"type": "service_account",
"project_id": "",
"private_key_id": "",
"private_key": "",
"client_email": "",
"client_email": "example@iam.gserviceaccount.com",
"client_id": "",
"auth_uri": "",
"token_uri": "",
"auth_provider_x509_cert_url": "",
"client_x509_cert_url": ""
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://accounts.google.com/o/oauth2/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/"
}

View File

@ -1,32 +0,0 @@
import type { Static } from '@sinclair/typebox'
import { Type } from '@sinclair/typebox'
const amazonVoiceConfiguration = Type.Object({
credentials: Type.Object({
accessKeyId: Type.String(),
secretAccessKey: Type.String()
}),
region: Type.String()
})
const googleCloudVoiceConfiguration = Type.Object({
type: Type.Literal('service_account'),
project_id: Type.String(),
private_key_id: Type.String(),
private_key: Type.String(),
client_email: Type.String({ format: 'email' }),
client_id: Type.String(),
auth_uri: Type.String({ format: 'uri' }),
token_uri: Type.String({ format: 'uri' }),
auth_provider_x509_cert_url: Type.String({ format: 'uri' }),
client_x509_cert_url: Type.String({ format: 'uri' })
})
const watsonVoiceConfiguration = Type.Object({
apikey: Type.String(),
url: Type.String({ format: 'uri' })
})
export type AmazonVoiceConfiguration = Static<typeof amazonVoiceConfiguration>
export type GoogleCloudVoiceConfiguration = Static<
typeof googleCloudVoiceConfiguration
>
export type WatsonVoiceConfiguration = Static<typeof watsonVoiceConfiguration>

View File

@ -1,4 +1,4 @@
{
"apikey": "",
"url": ""
"url": "https://stream.watsonplatform.net/speech-to-text/api"
}

View File

@ -1,4 +1,4 @@
{
"apikey": "",
"url": ""
"url": "https://stream.watsonplatform.net/text-to-speech/api"
}

View File

@ -1,45 +0,0 @@
import type { Static } from '@sinclair/typebox'
import { Type } from '@sinclair/typebox'
const globalEntity = {
options: Type.Record(
Type.String(),
Type.Object({
synonyms: Type.Array(Type.String()),
data: Type.Record(Type.String(), Type.Array(Type.String()))
})
)
}
const globalResolver = {
name: Type.String(),
intents: Type.Record(
Type.String(),
Type.Object({
utterance_samples: Type.Array(Type.String()),
value: Type.Unknown()
})
)
}
const answers = {
answers: Type.Record(
Type.String(),
Type.Union([
Type.Record(Type.String(), Type.String()),
Type.Array(Type.String())
])
)
}
const globalEntitySchemaObject = Type.Strict(
Type.Object(globalEntity, { additionalProperties: false })
)
const globalResolverSchemaObject = Type.Strict(
Type.Object(globalResolver, { additionalProperties: false })
)
const answersSchemaObject = Type.Strict(
Type.Object(answers, { additionalProperties: false })
)
export type GlobalEntity = Static<typeof globalEntitySchemaObject>
export type GlobalResolver = Static<typeof globalResolverSchemaObject>
export type Answers = Static<typeof answersSchemaObject>

264
package-lock.json generated
View File

@ -20,7 +20,10 @@
"@nlpjs/core-loader": "^4.22.7",
"@nlpjs/lang-all": "^4.22.12",
"@nlpjs/nlp": "^4.22.17",
"@sinclair/typebox": "0.24.49",
"@segment/ajv-human-errors": "2.1.2",
"@sinclair/typebox": "0.25.8",
"ajv": "8.11.0",
"ajv-formats": "2.1.1",
"archiver": "^5.3.1",
"async": "^3.2.0",
"axios": "1.1.2",
@ -1644,6 +1647,28 @@
"url": "https://opencollective.com/eslint"
}
},
"node_modules/@eslint/eslintrc/node_modules/ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
"uri-js": "^4.2.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true
},
"node_modules/@fast-csv/format": {
"version": "4.3.5",
"dev": true,
@ -1700,24 +1725,6 @@
"fast-uri": "^2.0.0"
}
},
"node_modules/@fastify/ajv-compiler/node_modules/ajv": {
"version": "8.11.0",
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2",
"uri-js": "^4.2.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/@fastify/ajv-compiler/node_modules/json-schema-traverse": {
"version": "1.0.0",
"license": "MIT"
},
"node_modules/@fastify/deepmerge": {
"version": "1.1.0",
"license": "MIT"
@ -3311,10 +3318,18 @@
"version": "1.1.0",
"license": "BSD-3-Clause"
},
"node_modules/@segment/ajv-human-errors": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/@segment/ajv-human-errors/-/ajv-human-errors-2.1.2.tgz",
"integrity": "sha512-d1uQndRFLRO01+xW1y5m+joxDgHf5SLJ70YCY2ArLoo2FJ50o6AoX2mEbuGvnKz/IdwnvDugm9Ti3mZQkW1OoA==",
"peerDependencies": {
"ajv": "^8.0.0"
}
},
"node_modules/@sinclair/typebox": {
"version": "0.24.49",
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.49.tgz",
"integrity": "sha512-qWgJVeCThMWTJSAZHyHDlHBkJY0LARFoq/96RH4oIFAwJptMwB3Isq62c4zRVRIAF2r4RMOc2WOhtOLj5C4InA=="
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.8.tgz",
"integrity": "sha512-/7GXgMOCfnxpIov52cPetqQ7bxRBaTBJAgp04Se2UQB1J0vUfEOIMpn63cLc3S5JXDUflCWxELKDV8eiPpmUTQ=="
},
"node_modules/@sinonjs/commons": {
"version": "1.8.5",
@ -4107,38 +4122,9 @@
}
},
"node_modules/ajv": {
"version": "6.12.6",
"dev": true,
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
"uri-js": "^4.2.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/ajv-formats": {
"version": "2.1.1",
"license": "MIT",
"dependencies": {
"ajv": "^8.0.0"
},
"peerDependencies": {
"ajv": "^8.0.0"
},
"peerDependenciesMeta": {
"ajv": {
"optional": true
}
}
},
"node_modules/ajv-formats/node_modules/ajv": {
"version": "8.11.0",
"license": "MIT",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz",
"integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==",
"dependencies": {
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",
@ -4150,9 +4136,21 @@
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/ajv-formats/node_modules/json-schema-traverse": {
"version": "1.0.0",
"license": "MIT"
"node_modules/ajv-formats": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
"integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
"dependencies": {
"ajv": "^8.0.0"
},
"peerDependencies": {
"ajv": "^8.0.0"
},
"peerDependenciesMeta": {
"ajv": {
"optional": true
}
}
},
"node_modules/ansi-colors": {
"version": "4.1.3",
@ -6607,6 +6605,22 @@
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
"node_modules/eslint/node_modules/ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
"uri-js": "^4.2.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/eslint/node_modules/eslint-utils": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz",
@ -6634,6 +6648,12 @@
"node": ">=10"
}
},
"node_modules/eslint/node_modules/json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true
},
"node_modules/espree": {
"version": "9.4.1",
"resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz",
@ -6952,24 +6972,6 @@
"rfdc": "^1.2.0"
}
},
"node_modules/fast-json-stringify/node_modules/ajv": {
"version": "8.11.0",
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2",
"uri-js": "^4.2.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/fast-json-stringify/node_modules/json-schema-traverse": {
"version": "1.0.0",
"license": "MIT"
},
"node_modules/fast-levenshtein": {
"version": "2.0.6",
"dev": true,
@ -10080,9 +10082,9 @@
"license": "MIT"
},
"node_modules/json-schema-traverse": {
"version": "0.4.1",
"dev": true,
"license": "MIT"
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
},
"node_modules/json-stable-stringify-without-jsonify": {
"version": "1.0.1",
@ -15544,6 +15546,26 @@
"js-yaml": "^4.1.0",
"minimatch": "^3.1.2",
"strip-json-comments": "^3.1.1"
},
"dependencies": {
"ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true,
"requires": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
"uri-js": "^4.2.2"
}
},
"json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true
}
}
},
"@fast-csv/format": {
@ -15598,20 +15620,6 @@
"ajv": "^8.11.0",
"ajv-formats": "^2.1.1",
"fast-uri": "^2.0.0"
},
"dependencies": {
"ajv": {
"version": "8.11.0",
"requires": {
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2",
"uri-js": "^4.2.2"
}
},
"json-schema-traverse": {
"version": "1.0.0"
}
}
},
"@fastify/deepmerge": {
@ -16839,10 +16847,16 @@
"@protobufjs/utf8": {
"version": "1.1.0"
},
"@segment/ajv-human-errors": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/@segment/ajv-human-errors/-/ajv-human-errors-2.1.2.tgz",
"integrity": "sha512-d1uQndRFLRO01+xW1y5m+joxDgHf5SLJ70YCY2ArLoo2FJ50o6AoX2mEbuGvnKz/IdwnvDugm9Ti3mZQkW1OoA==",
"requires": {}
},
"@sinclair/typebox": {
"version": "0.24.49",
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.49.tgz",
"integrity": "sha512-qWgJVeCThMWTJSAZHyHDlHBkJY0LARFoq/96RH4oIFAwJptMwB3Isq62c4zRVRIAF2r4RMOc2WOhtOLj5C4InA=="
"version": "0.25.8",
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.8.tgz",
"integrity": "sha512-/7GXgMOCfnxpIov52cPetqQ7bxRBaTBJAgp04Se2UQB1J0vUfEOIMpn63cLc3S5JXDUflCWxELKDV8eiPpmUTQ=="
},
"@sinonjs/commons": {
"version": "1.8.5",
@ -17338,33 +17352,22 @@
}
},
"ajv": {
"version": "6.12.6",
"dev": true,
"version": "8.11.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz",
"integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==",
"requires": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2",
"uri-js": "^4.2.2"
}
},
"ajv-formats": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
"integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
"requires": {
"ajv": "^8.0.0"
},
"dependencies": {
"ajv": {
"version": "8.11.0",
"requires": {
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2",
"uri-js": "^4.2.2"
}
},
"json-schema-traverse": {
"version": "1.0.0"
}
}
},
"ansi-colors": {
@ -18715,6 +18718,18 @@
"v8-compile-cache": "^2.0.3"
},
"dependencies": {
"ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true,
"requires": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
"uri-js": "^4.2.2"
}
},
"eslint-utils": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz",
@ -18731,6 +18746,12 @@
"dev": true
}
}
},
"json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true
}
}
},
@ -19139,20 +19160,6 @@
"fast-deep-equal": "^3.1.3",
"fast-uri": "^2.1.0",
"rfdc": "^1.2.0"
},
"dependencies": {
"ajv": {
"version": "8.11.0",
"requires": {
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2",
"uri-js": "^4.2.2"
}
},
"json-schema-traverse": {
"version": "1.0.0"
}
}
},
"fast-levenshtein": {
@ -21282,8 +21289,9 @@
"dev": true
},
"json-schema-traverse": {
"version": "0.4.1",
"dev": true
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
},
"json-stable-stringify-without-jsonify": {
"version": "1.0.1",

View File

@ -75,7 +75,10 @@
"@nlpjs/core-loader": "^4.22.7",
"@nlpjs/lang-all": "^4.22.12",
"@nlpjs/nlp": "^4.22.17",
"@sinclair/typebox": "0.24.49",
"@segment/ajv-human-errors": "2.1.2",
"@sinclair/typebox": "0.25.8",
"ajv": "8.11.0",
"ajv-formats": "2.1.1",
"archiver": "^5.3.1",
"async": "^3.2.0",
"axios": "1.1.2",

25
server/src/ajv.ts Normal file
View File

@ -0,0 +1,25 @@
import addFormats from 'ajv-formats'
import Ajv from 'ajv'
export const ajv = addFormats(
new Ajv({
allErrors: true,
verbose: true
}),
[
'date-time',
'time',
'date',
'email',
'hostname',
'ipv4',
'ipv6',
'uri',
'uri-reference',
'uuid',
'uri-template',
'json-pointer',
'relative-json-pointer',
'regex'
]
)

View File

@ -1,8 +1,9 @@
import fs from 'node:fs'
import path from 'node:path'
import { TypeCompiler } from '@sinclair/typebox/compiler'
import { AggregateAjvError } from '@segment/ajv-human-errors'
import { ajv } from '@/ajv'
import {
amazonVoiceConfiguration,
googleCloudVoiceConfiguration,
@ -31,6 +32,27 @@ import { SkillDomainHelper } from '@/helpers/skill-domain-helper'
import { VOICE_CONFIG_PATH, GLOBAL_DATA_PATH } from '@/constants'
import { getGlobalEntitiesPath, getGlobalResolversPath } from '@/utilities'
interface ObjectUnknown {
[key: string]: unknown
}
const validateSchema = (
schema: ObjectUnknown,
contentToValidate: ObjectUnknown,
customErrorMesage: string
): void => {
const validate = ajv.compile(schema)
const isValid = validate(contentToValidate)
if (!isValid) {
LogHelper.error(customErrorMesage)
const errors = new AggregateAjvError(validate.errors ?? [])
for (const error of errors) {
LogHelper.error(error.message)
}
process.exit(1)
}
}
/**
* Pre-checking
* Ensure JSON files are correctly formatted
@ -57,21 +79,19 @@ const GLOBAL_DATA_SCHEMAS = {
const voiceConfigFiles = (
await fs.promises.readdir(VOICE_CONFIG_PATH)
).filter((file) => file.endsWith('.json') && !file.includes('.sample.'))
).filter((file) => file.endsWith('.json'))
for (const file of voiceConfigFiles) {
const voiceConfigPath = path.join(VOICE_CONFIG_PATH, file)
const config: VoiceConfiguration = JSON.parse(
await fs.promises.readFile(path.join(VOICE_CONFIG_PATH, file), 'utf8')
await fs.promises.readFile(voiceConfigPath, 'utf8')
)
const [configName] = file.split('.') as [keyof typeof VOICE_CONFIG_SCHEMAS]
const result = TypeCompiler.Compile(VOICE_CONFIG_SCHEMAS[configName])
const errors = [...result.Errors(config)]
if (errors.length > 0) {
LogHelper.error(`The voice configuration schema "${file}" is not valid:`)
LogHelper.error(JSON.stringify(errors, null, 2))
process.exit(1)
}
validateSchema(
VOICE_CONFIG_SCHEMAS[configName],
config,
`The voice configuration schema "${voiceConfigPath}" is not valid:`
)
}
LogHelper.success('Voice configuration schemas checked')
@ -91,17 +111,15 @@ const GLOBAL_DATA_SCHEMAS = {
).filter((file) => file.endsWith('.json'))
for (const file of globalEntityFiles) {
const globalEntityPath = path.join(globalEntitiesPath, file)
const globalEntity: GlobalEntity = JSON.parse(
await fs.promises.readFile(path.join(globalEntitiesPath, file), 'utf8')
await fs.promises.readFile(globalEntityPath, 'utf8')
)
validateSchema(
globalEntitySchemaObject,
globalEntity,
`The global entity schema "${globalEntityPath}" is not valid:`
)
const result = TypeCompiler.Compile(globalEntitySchemaObject)
const errors = [...result.Errors(globalEntity)]
if (errors.length > 0) {
LogHelper.error(`The global entity schema "${file}" is not valid:`)
LogHelper.error(JSON.stringify(errors, null, 2))
process.exit(1)
}
}
/**
@ -113,37 +131,32 @@ const GLOBAL_DATA_SCHEMAS = {
).filter((file) => file.endsWith('.json'))
for (const file of globalResolverFiles) {
const globalResolverPath = path.join(globalResolversPath, file)
const globalResolver: GlobalResolver = JSON.parse(
await fs.promises.readFile(path.join(globalResolversPath, file), 'utf8')
await fs.promises.readFile(globalResolverPath, 'utf8')
)
validateSchema(
globalResolverSchemaObject,
globalResolver,
`The global resolver schema "${globalResolverPath}" is not valid:`
)
const result = TypeCompiler.Compile(globalResolverSchemaObject)
const errors = [...result.Errors(globalResolver)]
if (errors.length > 0) {
LogHelper.error(`The global resolver schema "${file}" is not valid:`)
LogHelper.error(JSON.stringify(errors, null, 2))
process.exit(1)
}
}
/**
* Global answers checking
*/
const globalAnswersPath = path.join(GLOBAL_DATA_PATH, lang, 'answers.json')
const answers: GlobalAnswers = JSON.parse(
await fs.promises.readFile(
path.join(GLOBAL_DATA_PATH, lang, 'answers.json'),
globalAnswersPath,
'utf8'
)
)
const result = TypeCompiler.Compile(GLOBAL_DATA_SCHEMAS.answers)
const errors = [...result.Errors(answers)]
if (errors.length > 0) {
LogHelper.error(`The global answers schema "answers.json" is not valid:`)
LogHelper.error(JSON.stringify(errors, null, 2))
process.exit(1)
}
validateSchema(
GLOBAL_DATA_SCHEMAS.answers,
answers,
`The global answers schema "${globalAnswersPath}" is not valid:`
)
}
LogHelper.success('Global data schemas checked')
@ -162,14 +175,11 @@ const GLOBAL_DATA_SCHEMAS = {
const domainObject: Domain = JSON.parse(
await fs.promises.readFile(pathToDomain, 'utf8')
)
const domainResult = TypeCompiler.Compile(domainSchemaObject)
const domainErrors = [...domainResult.Errors(domainObject)]
if (domainErrors.length > 0) {
LogHelper.error(`The domain schema "${pathToDomain}" is not valid:`)
LogHelper.error(JSON.stringify(domainErrors, null, 2))
process.exit(1)
}
validateSchema(
domainSchemaObject,
domainObject,
`The domain schema "${pathToDomain}" is not valid:`
)
const skillKeys = Object.keys(currentDomain.skills)
@ -184,14 +194,11 @@ const GLOBAL_DATA_SCHEMAS = {
const skillObject: Skill = JSON.parse(
await fs.promises.readFile(pathToSkill, 'utf8')
)
const skillResult = TypeCompiler.Compile(skillSchemaObject)
const skillErrors = [...skillResult.Errors(skillObject)]
if (skillErrors.length > 0) {
LogHelper.error(`The skill schema "${pathToSkill}" is not valid:`)
LogHelper.error(JSON.stringify(skillErrors, null, 2))
process.exit(1)
}
validateSchema(
skillSchemaObject,
skillObject,
`The skill schema "${pathToSkill}" is not valid:`
)
/**
* Skills config checking
@ -206,16 +213,11 @@ const GLOBAL_DATA_SCHEMAS = {
const skillConfig: SkillConfig = JSON.parse(
await fs.promises.readFile(skillConfigPath, 'utf8')
)
const result = TypeCompiler.Compile(skillConfigSchemaObject)
const errors = [...result.Errors(skillConfig)]
if (errors.length > 0) {
LogHelper.error(
`The skill config schema "${skillConfigPath}" is not valid:`
)
LogHelper.error(JSON.stringify(errors, null, 2))
process.exit(1)
}
validateSchema(
skillConfigSchemaObject,
skillConfig,
`The skill config schema "${skillConfigPath}" is not valid:`
)
}
}
}

View File

@ -1,34 +1,57 @@
import type { Static } from '@sinclair/typebox'
import { Type } from '@sinclair/typebox'
export const globalEntitySchemaObject = Type.Object({
options: Type.Record(
Type.String(),
Type.Object({
synonyms: Type.Array(Type.String()),
data: Type.Optional(Type.Record(Type.String(), Type.Array(Type.String())))
})
export const globalEntitySchemaObject = Type.Strict(
Type.Object(
{
options: Type.Record(
Type.String(),
Type.Object(
{
synonyms: Type.Array(Type.String()),
data: Type.Optional(
Type.Record(Type.String(), Type.Array(Type.String()))
)
},
{ additionalProperties: false }
)
)
},
{ additionalProperties: false }
)
})
export const globalResolverSchemaObject = Type.Object({
name: Type.String(),
intents: Type.Record(
Type.String(),
Type.Object({
utterance_samples: Type.Array(Type.String()),
value: Type.Unknown()
})
)
export const globalResolverSchemaObject = Type.Strict(
Type.Object(
{
name: Type.String(),
intents: Type.Record(
Type.String(),
Type.Object(
{
utterance_samples: Type.Array(Type.String()),
value: Type.Unknown()
},
{ additionalProperties: false }
)
)
},
{ additionalProperties: false }
)
})
export const globalAnswersSchemaObject = Type.Object({
answers: Type.Record(
Type.String(),
Type.Union([
Type.Record(Type.String(), Type.String()),
Type.Array(Type.String())
])
)
export const globalAnswersSchemaObject = Type.Strict(
Type.Object(
{
answers: Type.Record(
Type.String(),
Type.Union([
Type.Record(Type.String(), Type.String()),
Type.Array(Type.String())
])
)
},
{ additionalProperties: false }
)
})
)
export type GlobalEntity = Static<typeof globalEntitySchemaObject>
export type GlobalResolver = Static<typeof globalResolverSchemaObject>

View File

@ -1,6 +1,8 @@
import type { Static } from '@sinclair/typebox'
import { Type } from '@sinclair/typebox'
import { globalResolverSchemaObject } from '@/schemas/global-data-schemas'
const skillBridges = [Type.Literal('python'), Type.Null()]
const skillActionTypes = [Type.Literal('logic'), Type.Literal('dialog')]
const skillDataTypes = [
@ -10,109 +12,184 @@ const skillDataTypes = [
]
const skillCustomEntityTypes = [
Type.Array(
Type.Object({
type: Type.Literal('trim'),
name: Type.String({ minLength: 1 }),
conditions: Type.Array(
Type.Object({
type: Type.Union([
Type.Literal('between'),
Type.Literal('after'),
Type.Literal('after_first'),
Type.Literal('after_last'),
Type.Literal('before'),
Type.Literal('before_first'),
Type.Literal('before_last')
]),
from: Type.Optional(
Type.Union([
Type.Array(Type.String({ minLength: 1 })),
Type.String({ minLength: 1 })
])
),
to: Type.Optional(
Type.Union([
Type.Array(Type.String({ minLength: 1 })),
Type.String({ minLength: 1 })
])
Type.Object(
{
type: Type.Literal('trim'),
name: Type.String({ minLength: 1 }),
conditions: Type.Array(
Type.Object(
{
type: Type.Union([
Type.Literal('between'),
Type.Literal('after'),
Type.Literal('after_first'),
Type.Literal('after_last'),
Type.Literal('before'),
Type.Literal('before_first'),
Type.Literal('before_last')
]),
from: Type.Optional(
Type.Union([
Type.Array(Type.String({ minLength: 1 })),
Type.String({ minLength: 1 })
])
),
to: Type.Optional(
Type.Union([
Type.Array(Type.String({ minLength: 1 })),
Type.String({ minLength: 1 })
])
)
},
{ additionalProperties: false }
)
})
)
})
)
},
{ additionalProperties: false }
)
),
Type.Array(
Type.Object({
type: Type.Literal('regex'),
name: Type.String({ minLength: 1 }),
regex: Type.String({ minLength: 1 })
})
Type.Object(
{
type: Type.Literal('regex'),
name: Type.String({ minLength: 1 }),
regex: Type.String({ minLength: 1 })
},
{ additionalProperties: false }
)
),
Type.Array(
Type.Object({
type: Type.Literal('enum'),
name: Type.String(),
options: Type.Record(
Type.String({ minLength: 1 }),
Type.Object({
synonyms: Type.Array(Type.String({ minLength: 1 }))
})
)
})
Type.Object(
{
type: Type.Literal('enum'),
name: Type.String(),
options: Type.Record(
Type.String({ minLength: 1 }),
Type.Object({
synonyms: Type.Array(Type.String({ minLength: 1 }))
})
)
},
{ additionalProperties: false }
)
)
]
export const domainSchemaObject = Type.Object({
name: Type.String({ minLength: 1 })
})
export const skillSchemaObject = Type.Object({
name: Type.String({ minLength: 1 }),
bridge: Type.Union(skillBridges),
version: Type.String({ minLength: 1 }),
description: Type.String({ minLength: 1 }),
author: Type.Object({
name: Type.String({ minLength: 1 }),
email: Type.Optional(Type.String({ minLength: 1, maxLength: 254 })),
url: Type.Optional(Type.String({ minLength: 1, maxLength: 255 }))
})
})
export const skillConfigSchemaObject = Type.Object({
variables: Type.Optional(Type.Record(Type.String(), Type.String())),
actions: Type.Record(
Type.String(),
Type.Object({
type: Type.Union(skillActionTypes),
loop: Type.Optional(
Type.Object({
expected_item: Type.Object({
type: Type.Union(skillDataTypes),
name: Type.String()
})
})
),
utterance_samples: Type.Optional(Type.Array(Type.String())),
answers: Type.Optional(Type.Array(Type.String())),
unknown_answers: Type.Optional(Type.Array(Type.String())),
suggestions: Type.Optional(Type.Array(Type.String())),
slots: Type.Optional(
Type.Array(
Type.Object({
name: Type.String(),
item: Type.Object({
type: Type.Union(skillDataTypes),
name: Type.String()
}),
questions: Type.Array(Type.String()),
suggestions: Type.Optional(Type.Array(Type.String()))
})
export const domainSchemaObject = Type.Strict(
Type.Object(
{
name: Type.String({ minLength: 1 })
},
{ additionalProperties: false }
)
)
export const skillSchemaObject = Type.Strict(
Type.Object(
{
name: Type.String({ minLength: 1 }),
bridge: Type.Union(skillBridges),
version: Type.String({ minLength: 1 }),
description: Type.String({ minLength: 1 }),
author: Type.Object(
{
name: Type.String({ minLength: 1 }),
email: Type.Optional(
Type.String({ minLength: 1, maxLength: 254, format: 'email' })
),
url: Type.Optional(
Type.String({ minLength: 1, maxLength: 255, format: 'uri' })
)
},
{ additionalProperties: false }
)
},
{ additionalProperties: false }
)
)
export const skillConfigSchemaObject = Type.Strict(
Type.Object(
{
variables: Type.Optional(Type.Record(Type.String(), Type.String())),
actions: Type.Record(
Type.String(),
Type.Object(
{
type: Type.Union(skillActionTypes),
loop: Type.Optional(
Type.Object(
{
expected_item: Type.Object({
type: Type.Union(skillDataTypes),
name: Type.String()
})
},
{ additionalProperties: false }
)
),
http_api: Type.Optional(
Type.Object(
{
entities: Type.Array(
Type.Object(
{
entity: Type.String(),
resolution: Type.Array(Type.String())
},
{ additionalProperties: false }
)
)
},
{ additionalProperties: false }
)
),
utterance_samples: Type.Optional(Type.Array(Type.String())),
answers: Type.Optional(Type.Array(Type.String())),
unknown_answers: Type.Optional(Type.Array(Type.String())),
suggestions: Type.Optional(Type.Array(Type.String())),
slots: Type.Optional(
Type.Array(
Type.Object(
{
name: Type.String(),
item: Type.Object(
{
type: Type.Union(skillDataTypes),
name: Type.String()
},
{ additionalProperties: false }
),
questions: Type.Array(Type.String()),
suggestions: Type.Optional(Type.Array(Type.String()))
},
{ additionalProperties: false }
)
)
),
entities: Type.Optional(Type.Union(skillCustomEntityTypes)),
next_action: Type.Optional(Type.String())
},
{ additionalProperties: false }
)
),
entities: Type.Optional(Type.Union(skillCustomEntityTypes)),
next_action: Type.Optional(Type.String())
})
),
answers: Type.Optional(Type.Record(Type.String(), Type.Array(Type.String()))),
entities: Type.Optional(Type.Record(Type.String(), Type.String()))
})
answers: Type.Optional(
Type.Record(Type.String(), Type.Array(Type.String()))
),
entities: Type.Optional(Type.Record(Type.String(), Type.String())),
resolvers: Type.Optional(
Type.Record(
Type.String(),
Type.Object(
{
intents: globalResolverSchemaObject.properties.intents
},
{ additionalProperties: false }
)
)
)
},
{ additionalProperties: false }
)
)
export type Domain = Static<typeof domainSchemaObject>
export type Skill = Static<typeof skillSchemaObject>

View File

@ -1,29 +1,44 @@
import type { Static } from '@sinclair/typebox'
import { Type } from '@sinclair/typebox'
export const amazonVoiceConfiguration = Type.Object({
credentials: Type.Object({
accessKeyId: Type.String(),
secretAccessKey: Type.String()
}),
region: Type.String()
})
export const googleCloudVoiceConfiguration = Type.Object({
type: Type.Literal('service_account'),
project_id: Type.String(),
private_key_id: Type.String(),
private_key: Type.String(),
client_email: Type.String(),
client_id: Type.String(),
auth_uri: Type.String(),
token_uri: Type.String(),
auth_provider_x509_cert_url: Type.String(),
client_x509_cert_url: Type.String()
})
export const watsonVoiceConfiguration = Type.Object({
apikey: Type.String(),
url: Type.String()
})
export const amazonVoiceConfiguration = Type.Strict(
Type.Object(
{
credentials: Type.Object({
accessKeyId: Type.String(),
secretAccessKey: Type.String()
}),
region: Type.String()
},
{ additionalProperties: false }
)
)
export const googleCloudVoiceConfiguration = Type.Strict(
Type.Object(
{
type: Type.Literal('service_account'),
project_id: Type.String(),
private_key_id: Type.String(),
private_key: Type.String(),
client_email: Type.String({ format: 'email' }),
client_id: Type.String(),
auth_uri: Type.String({ format: 'uri' }),
token_uri: Type.String({ format: 'uri' }),
auth_provider_x509_cert_url: Type.String({ format: 'uri' }),
client_x509_cert_url: Type.String({ format: 'uri' })
},
{ additionalProperties: false }
)
)
export const watsonVoiceConfiguration = Type.Strict(
Type.Object(
{
apikey: Type.String(),
url: Type.String({ format: 'uri' })
},
{ additionalProperties: false }
)
)
export type AmazonVoiceConfiguration = Static<typeof amazonVoiceConfiguration>
export type GoogleCloudVoiceConfiguration = Static<

View File

@ -1,90 +0,0 @@
import type { Static } from '@sinclair/typebox'
import { Type } from '@sinclair/typebox'
const domainSchema = {
name: Type.String({ minLength: 1 })
}
const skillBridges = [Type.Literal('python')]
const skillSchema = {
name: Type.String({ minLength: 1 }),
bridge: Type.Union(skillBridges),
version: Type.String({ minLength: 1 }),
description: Type.String({ minLength: 1 }),
author: Type.Object({
name: Type.String({ minLength: 1 }),
email: Type.String({ minLength: 1, maxLength: 254, format: 'email' }),
url: Type.String({ minLength: 1, maxLength: 255, format: 'uri' })
})
}
const skillActionTypes = [Type.Literal('logic'), Type.Literal('dialog')]
const skillDataTypes = [
Type.Literal('skill_resolver'),
Type.Literal('global_resolver'),
Type.Literal('entity')
]
const skillConfigSchema = {
variables: Type.Optional(Type.Record(Type.String(), Type.String())),
actions: Type.Record(
Type.String(),
Type.Object({
type: Type.Union(skillActionTypes),
loop: Type.Optional(
Type.Object({
expected_item: Type.Object({
type: Type.Union(skillDataTypes),
name: Type.String()
})
})
),
utterance_samples: Type.Optional(Type.Array(Type.String())),
answers: Type.Optional(Type.Array(Type.String())),
unknown_answers: Type.Optional(Type.Array(Type.String())),
suggestions: Type.Optional(Type.Array(Type.String())),
slots: Type.Optional(
Type.Array(
Type.Object({
name: Type.String(),
item: Type.Object({
type: Type.Union(skillDataTypes),
name: Type.String()
}),
questions: Type.Array(Type.String()),
suggestions: Type.Optional(Type.Array(Type.String()))
})
)
),
entities: Type.Optional(
Type.Array(
Type.Object({
type: Type.Literal('enum'),
name: Type.String(),
options: Type.Record(
Type.String(),
Type.Object({
synonyms: Type.Array(Type.String())
})
)
})
)
),
next_action: Type.Optional(Type.String())
})
),
answers: Type.Optional(Type.Record(Type.String(), Type.Array(Type.String()))),
entities: Type.Optional(Type.Record(Type.String(), Type.String()))
}
const domainSchemaObject = Type.Strict(
Type.Object(domainSchema, { additionalProperties: false })
)
const skillSchemaObject = Type.Strict(
Type.Object(skillSchema, { additionalProperties: false })
)
const skillConfigSchemaObject = Type.Strict(
Type.Object(skillConfigSchema, { additionalProperties: false })
)
export type Domain = Static<typeof domainSchemaObject>
export type Skill = Static<typeof skillSchemaObject>
export type SkillConfig = Static<typeof skillConfigSchemaObject>
export type SkillBridge = Static<typeof skillSchema.bridge>