1
1
mirror of https://github.com/leon-ai/leon.git synced 2024-12-25 01:31:47 +03:00

Merge branch 'develop' into cli-arrival

This commit is contained in:
louistiti 2022-01-25 21:49:40 +08:00
commit 429dce6f37
No known key found for this signature in database
GPG Key ID: 7ECA3DD523793FE6
39 changed files with 8890 additions and 9319 deletions

11
.husky/commit-msg Executable file
View File

@ -0,0 +1,11 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
if ! [ -x "$(command -v npm)" ]; then
echo "npm: command not found"
echo "If you use a version manager tool such as nvm and a git GUI such as GitKraken, please read: https://typicode.github.io/husky/#/?id=command-not-found" >&2
exit 1
else
npm run lint && node_modules/@babel/node/bin/babel-node.js scripts/commit-msg.js
fi

View File

@ -1,4 +1,19 @@
# [1.0.0-beta.4](https://github.com/leon-ai/leon/compare/1.0.0-beta.2...1.0.0-beta.4) (2021-05-01) / Getting Rid of Dust
# [1.0.0-beta.5](https://github.com/leon-ai/leon/compare/v1.0.0-beta.4...v1.0.0-beta.5) (2021-12-28) / Refocus
*This release marks a major turn in the future versions of the Leon core. Please [read this blog post](https://blog.getleon.ai/i-ran-away-from-open-source/) to know more.*
### BREAKING CHANGES
- Node.js 16+ and npm 8+ minimum requirements [2f66f1c1](https://github.com/leon-ai/leon/commit/2f66f1c17bb2e4a1c18b4251d49de252b8d87344)
### Features
- **server:** support arrays on NER between conditions [7cf7f979](https://github.com/leon-ai/leon/commit/7cf7f9791254e1950fe9128ce1b3a58079cc2ada)
### Bug Fixes
- jest-extended new setup due to latest update [02f766d6](https://github.com/leon-ai/leon/commit/02f766d6a8453609ebaec78356aa6e6d4df0967b)
### Performance Improvements
- Windows setup on DeepSpeech dep removal [13f5a49f](https://github.com/leon-ai/leon/commit/13f5a49f678f8f67a93b67d4f558cddcf237e204)
### Documentation Changes
- URL redirect managed by registrar [c16d5b28](https://github.com/leon-ai/leon/commit/c16d5b280b758f7e18305e30678adec79f0a0716)
# [1.0.0-beta.4](https://github.com/leon-ai/leon/compare/1.0.0-beta.2...v1.0.0-beta.4) (2021-05-01) / Getting Rid of Dust
*This release includes a lot of changes that are made under the hood and are not displayed here, please **[read the blog post](https://blog.getleon.ai/getting-rid-of-dust-1-0-0-beta-4/)** to know more.*

View File

@ -145,7 +145,7 @@ You'll find a write-up on this [blog post](https://blog.getleon.ai/the-story-beh
## 🔔 Stay Tuned
- [Twitter](https://twitter.com/louistiti_fr)
- [Newsletter](https://getleon.ai)
- [Newsletter](http://newsletter.getleon.ai)
- [Blog](https://blog.getleon.ai)
- [GitHub issues](https://github.com/leon-ai/leon/issues)
- [YouTube](https://www.youtube.com/channel/UCW6mk6j6nQUzFYY97r47emQ)
@ -174,6 +174,13 @@ You'll find a write-up on this [blog post](https://blog.getleon.ai/the-story-beh
</a><br>
<sub><sup>30 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>17 USD / month</sup></sub>
</td>
<td align="center" valign="middle" width="128">
<a href="https://github.com/marvinpoo">
<img src="https://github.com/marvinpoo.png?size=128" />

View File

@ -29,7 +29,7 @@
<small>
Use <kbd></kbd> <kbd></kbd> to browse history;
<kbd></kbd> to submit;
<kbd>alt + t to listen.</kbd>
<kbd>alt + c to listen.</kbd>
</small>
</div>
</main>

View File

@ -28,7 +28,7 @@ const onkeydowninput = (e, client) => {
}
const onkeydowndocument = (e, cb) => {
if (e.altKey && e.key === 't') {
if (e.altKey && e.key === 'c') {
cb()
}
}

10
ide.js
View File

@ -1,10 +0,0 @@
/**
* Allow babel-plugin-module-resolver aliases for JetBrains IDEs
*/
System.config({
paths: {
'@@/*': './*',
'@/*': './server/src/*'
}
})

9
jsconfig.json Normal file
View File

@ -0,0 +1,9 @@
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@@/*": ["./*"],
"@/*": ["./server/src/*"]
}
}
}

17761
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "leon",
"version": "1.0.0-beta.5+dev",
"version": "1.0.0-beta.6+dev",
"description": "Server, packages and web app of the Leon personal assistant",
"author": {
"name": "Louis Grenard",
@ -23,10 +23,10 @@
"scripts": {
"lint": "babel-node scripts/lint.js",
"test": "npm run test:json && npm run test:unit && npm run test:e2e",
"test:unit": "npm run train expressions:en && cross-env PIPENV_PIPFILE=bridges/python/Pipfile LEON_NODE_ENV=testing jest --forceExit --silent --projects test/unit/unit.jest.json && npm run train expressions",
"test:unit": "npm run train en && cross-env PIPENV_PIPFILE=bridges/python/Pipfile LEON_NODE_ENV=testing jest --forceExit --silent --projects test/unit/unit.jest.json && npm run train",
"test:e2e": "npm run test:e2e:nlp-modules && npm run test:e2e:modules",
"test:e2e:modules": "babel-node scripts/run-clean-test-dbs.js && npm run train expressions:en && cross-env PIPENV_PIPFILE=bridges/python/Pipfile LEON_NODE_ENV=testing jest --forceExit --silent --verbose --projects test/e2e/modules/e2e.modules.jest.json && babel-node scripts/run-clean-test-dbs.js && npm run train expressions",
"test:e2e:nlp-modules": "npm run train expressions:en && cross-env PIPENV_PIPFILE=bridges/python/Pipfile LEON_NODE_ENV=testing jest --forceExit --silent --verbose --setupTestFrameworkScriptFile=./test/paths.setup.js test/e2e/nlp-modules.spec.js && npm run train expressions",
"test:e2e:modules": "babel-node scripts/run-clean-test-dbs.js && npm run train en && cross-env PIPENV_PIPFILE=bridges/python/Pipfile LEON_NODE_ENV=testing jest --forceExit --silent --verbose --projects test/e2e/modules/e2e.modules.jest.json && babel-node scripts/run-clean-test-dbs.js && npm run train",
"test:e2e:nlp-modules": "npm run train en && cross-env PIPENV_PIPFILE=bridges/python/Pipfile LEON_NODE_ENV=testing jest --forceExit --silent --verbose --setupTestFrameworkScriptFile=./test/paths.setup.js test/e2e/nlp-modules.spec.js && npm run train",
"test:json": "jest --silent --projects test/json/json.jest.json",
"test:module": "babel-node scripts/test-module.js",
"setup:offline": "babel-node scripts/setup-offline/setup-offline.js",
@ -36,12 +36,13 @@
"preinstall": "node scripts/setup/preinstall.js",
"postinstall": "babel-node scripts/setup/setup.js",
"dev:app": "vite --config app/vite.config.js",
"dev:server": "npm run train expressions && cross-env LEON_NODE_ENV=development nodemon --watch server ./server/src/index.js --ignore server/src/tmp/ --exec babel-node",
"dev:server": "npm run train && cross-env LEON_NODE_ENV=development nodemon --watch server ./server/src/index.js --ignore server/src/tmp/ --exec babel-node",
"wake": "cross-env LEON_HOST=http://localhost LEON_PORT=1337 node hotword/index.js",
"delete-dist:server": "shx rm -rf ./server/dist",
"build": "npm run lint && npm run build:app && npm run build:server",
"prepare": "husky install",
"build": "npm run build:app && npm run build:server",
"build:app": "cross-env LEON_NODE_ENV=production babel-node scripts/app/run-build-app.js",
"build:server": "npm run delete-dist:server && npm run train expressions && babel ./server/src -d ./server/dist --copy-files && shx mkdir -p server/dist/tmp",
"build:server": "npm run delete-dist:server && npm run train && babel ./server/src -d ./server/dist --copy-files && shx mkdir -p server/dist/tmp",
"start": "cross-env LEON_NODE_ENV=production node ./server/dist/index.js",
"train": "babel-node scripts/run-train.js",
"prepare-release": "babel-node scripts/release/prepare-release.js",
@ -53,8 +54,8 @@
},
"dependencies": {
"@aws-sdk/client-polly": "^3.18.0",
"@ffmpeg-installer/ffmpeg": "^1.0.20",
"@ffprobe-installer/ffprobe": "^1.1.0",
"@ffmpeg-installer/ffmpeg": "^1.1.0",
"@ffprobe-installer/ffprobe": "^1.3.0",
"@google-cloud/speech": "^4.2.0",
"@google-cloud/text-to-speech": "^3.2.1",
"@nlpjs/basic": "^4.22.0",
@ -68,15 +69,15 @@
"deepspeech": "^0.9.3",
"dotenv": "^10.0.0",
"execa": "^5.0.0",
"fastify": "^3.17.0",
"fastify-static": "^4.0.1",
"fastify": "^3.25.2",
"fastify-static": "^4.5.0",
"fluent-ffmpeg": "^2.1.2",
"googleapis": "^67.1.1",
"ibm-watson": "^6.1.1",
"moment-timezone": "^0.5.33",
"node-wav": "0.0.2",
"socket.io": "^4.1.2",
"socket.io-client": "^4.1.2",
"socket.io": "^4.4.0",
"socket.io-client": "^4.4.0",
"superagent": "^6.1.0"
},
"devDependencies": {
@ -92,14 +93,15 @@
"eslint-plugin-import": "^2.23.2",
"eslint-plugin-jest": "^24.3.6",
"git-changelog": "^2.0.0",
"husky": "^7.0.0",
"inquirer": "^8.1.0",
"jest": "^26.6.3",
"jest": "^27.4.5",
"jest-canvas-mock": "^2.3.1",
"jest-extended": "^0.11.5",
"jest-extended": "^1.2.0",
"json": "^10.0.0",
"nodemon": "^2.0.7",
"semver": "^7.3.5",
"shx": "^0.3.3",
"vite": "^2.3.4"
"vite": "^2.7.7"
}
}

View File

@ -12,23 +12,8 @@
"conditions": [
{
"type": "between",
"from": "the",
"to": "list"
},
{
"type": "between",
"from": "a",
"to": "list"
},
{
"type": "between",
"from": "an",
"to": "list"
},
{
"type": "between",
"from": "my",
"to": "list"
"from": ["the", "a", "an", "my"],
"to": ["list", "list", "list", "list"]
}
]
}
@ -56,13 +41,8 @@
"conditions": [
{
"type": "between",
"from": "the",
"to": "list"
},
{
"type": "between",
"from": "my",
"to": "list"
"from": ["the", "my"],
"to": ["list", "list"]
}
]
}
@ -80,13 +60,8 @@
"conditions": [
{
"type": "between",
"from": "the",
"to": "list"
},
{
"type": "between",
"from": "my",
"to": "list"
"from": ["the", "my"],
"to": ["list", "list"]
}
]
},
@ -114,13 +89,8 @@
"conditions": [
{
"type": "between",
"from": "the",
"to": "list"
},
{
"type": "between",
"from": "my",
"to": "list"
"from": ["the", "my"],
"to": ["list", "list"]
}
]
}
@ -149,13 +119,8 @@
"conditions": [
{
"type": "between",
"from": "the",
"to": "list"
},
{
"type": "between",
"from": "my",
"to": "list"
"from": ["the", "my"],
"to": ["list", "list"]
}
]
}
@ -175,18 +140,8 @@
"conditions": [
{
"type": "between",
"from": "check",
"to": "from"
},
{
"type": "between",
"from": "complete",
"to": "from"
},
{
"type": "between",
"from": "tick",
"to": "from"
"from": ["check", "complete", "tick"],
"to": ["from", "from", "from"]
}
]
},
@ -196,13 +151,8 @@
"conditions": [
{
"type": "between",
"from": "the",
"to": "list"
},
{
"type": "between",
"from": "my",
"to": "list"
"from": ["the", "my"],
"to": ["list", "list"]
}
]
}
@ -221,13 +171,8 @@
"conditions": [
{
"type": "between",
"from": "uncheck",
"to": "from"
},
{
"type": "between",
"from": "untick",
"to": "from"
"from": ["uncheck", "untick"],
"to": ["from", "from"]
}
]
},
@ -237,13 +182,8 @@
"conditions": [
{
"type": "between",
"from": "the",
"to": "list"
},
{
"type": "between",
"from": "my",
"to": "list"
"from": ["the", "my"],
"to": ["list", "list"]
}
]
}

View File

@ -105,13 +105,8 @@
"conditions": [
{
"type": "between",
"from": "ajoute",
"to": "à"
},
{
"type": "between",
"from": "ajoute",
"to": "a"
"from": ["ajoute", "ajoute"],
"to": ["à", "a"]
}
]
},
@ -140,18 +135,8 @@
"conditions": [
{
"type": "between",
"from": "coche",
"to": "de"
},
{
"type": "between",
"from": "complète",
"to": "de"
},
{
"type": "between",
"from": "complete",
"to": "de"
"from": ["coche", "complète", "complete"],
"to": ["de", "de", "de"]
}
]
},
@ -181,23 +166,8 @@
"conditions": [
{
"type": "between",
"from": "décoche",
"to": "de"
},
{
"type": "between",
"from": "decoche",
"to": "de"
},
{
"type": "between",
"from": "invalide",
"to": "de"
},
{
"type": "between",
"from": "remet",
"to": "sur"
"from": ["décoche", "decoche", "invalide", "remet"],
"to": ["de", "de", "de", "sur"]
}
]
},

View File

@ -1,4 +1,4 @@
describe('calendar:todolist', async () => {
describe('calendar:todolist', () => {
test('no list', async () => {
global.nlu.brain.execute = jest.fn()
await global.nlu.process('Show all my lists')

View File

@ -1,4 +1,4 @@
describe('checker:haveibeenpwned', async () => {
describe('checker:haveibeenpwned', () => {
test('checks if an email address has been provided', async () => {
global.nlu.brain.execute = jest.fn()
await global.nlu.process('Have I been pwned?')

View File

@ -1,4 +1,4 @@
describe('checker:isitdown', async () => {
describe('checker:isitdown', () => {
test('detects invalid domain name', async () => {
global.nlu.brain.execute = jest.fn()
await global.nlu.process('Check if github is up')

View File

@ -1,4 +1,4 @@
describe('leon:bye', async () => {
describe('leon:bye', () => {
test('says bye', async () => {
global.nlu.brain.execute = jest.fn()
await global.nlu.process('Bye bye')

View File

@ -1,4 +1,4 @@
describe('leon:greeting', async () => {
describe('leon:greeting', () => {
test('greets', async () => {
global.nlu.brain.execute = jest.fn()
await global.nlu.process('Hello')

View File

@ -1,4 +1,4 @@
describe('leon:joke', async () => {
describe('leon:joke', () => {
test('tells a joke', async () => {
global.nlu.brain.execute = jest.fn()
await global.nlu.process('Tell me a joke')

View File

@ -1,4 +1,4 @@
describe('leon:meaningoflife', async () => {
describe('leon:meaningoflife', () => {
test('says the meaning of life', async () => {
global.nlu.brain.execute = jest.fn()
await global.nlu.process('What is the meaning of life?')

View File

@ -1,4 +1,4 @@
describe('leon:partnerassistant', async () => {
describe('leon:partnerassistant', () => {
test('does not know this personal assistant', async () => {
global.nlu.brain.execute = jest.fn()
await global.nlu.process('Tell me about the personal assistant Louistiti')

View File

@ -1,4 +1,4 @@
describe('leon:randomnumber', async () => {
describe('leon:randomnumber', () => {
test('gives a random number between 0 and 100', async () => {
global.nlu.brain.execute = jest.fn()
await global.nlu.process('Give me a random number')

View File

@ -1,4 +1,4 @@
describe('leon:welcome', async () => {
describe('leon:welcome', () => {
test('welcomes', async () => {
global.nlu.brain.execute = jest.fn()
await global.nlu.process('Thank you')

View File

@ -1,4 +1,4 @@
describe('leon:whoami', async () => {
describe('leon:whoami', () => {
test('introduces himself', async () => {
global.nlu.brain.execute = jest.fn()
await global.nlu.process('Who are you?')

View File

@ -1,4 +1,4 @@
describe('network:speedtest', async () => {
describe('network:speedtest', () => {
test('does a speed test', async () => {
global.nlu.brain.execute = jest.fn()
await global.nlu.process('Do a speed test')

View File

@ -1,4 +1,4 @@
describe('trend:github', async () => {
describe('trend:github', () => {
test('forces limit', async () => {
global.nlu.brain.execute = jest.fn()
await global.nlu.process('Give me the 30 latest GitHub trends')

View File

@ -1,4 +1,4 @@
describe('trend:producthunt', async () => {
describe('trend:producthunt', () => {
test('requests Product Hunt', async () => {
global.nlu.brain.execute = jest.fn()
await global.nlu.process('What\'s trending on Product Hunt?')

View File

@ -1,4 +1,4 @@
describe('videodownloader:youtube', async () => {
describe('videodownloader:youtube', () => {
test('requests YouTube', async () => {
global.nlu.brain.execute = jest.fn()
await global.nlu.process('Download new videos from YouTube')

View File

@ -93,7 +93,7 @@ export default () => new Promise(async (resolve, reject) => {
if (!fs.existsSync(nlpModelPath) || !Object.keys(fs.readFileSync(nlpModelPath)).length) {
report.can_text.v = false
Object.keys(report).forEach((item) => { if (item.indexOf('stt') !== -1 || item.indexOf('tts') !== -1) report[item].v = false })
log.error('NLP model not found or broken. Try to generate a new one: "npm run train expressions"\n')
log.error('NLP model not found or broken. Try to generate a new one: "npm run train"\n')
} else {
log.success('Found and valid\n')
}
@ -103,7 +103,7 @@ export default () => new Promise(async (resolve, reject) => {
log.info('Amazon Polly TTS')
try {
const json = JSON.parse(fs.readFileSync(amazonPath))
if (json.accessKeyId === '' || json.secretAccessKey === '') {
if (json.credentials.accessKeyId === '' || json.credentials.secretAccessKey === '') {
report.can_amazon_polly_tts.v = false
log.warning('Amazon Polly TTS is not yet configured\n')
} else {

37
scripts/commit-msg.js Normal file
View File

@ -0,0 +1,37 @@
import fs from 'fs'
import path from 'path'
import log from '@/helpers/log'
/**
* This script is executed after "git commit" or "git merge" (Git hook https://git-scm.com/docs/githooks#_commit_msg)
* it ensures the authenticity of commit messages
*/
log.info('Checking commit message...')
const commitEditMsgFile = '.git/COMMIT_EDITMSG'
if (fs.existsSync(commitEditMsgFile)) {
try {
const commitMessage = fs.readFileSync(commitEditMsgFile, 'utf8')
const packagesDir = 'packages'
const packages = fs.readdirSync(packagesDir)
.filter((entity) => fs.statSync(path.join(packagesDir, entity)).isDirectory())
const packagesScopeString = packages.map((pkg, i) => {
if (packages.length === i + 1) return pkg
return `${pkg}|`
}).join('')
const regex = `(build|BREAKING|chore|ci|docs|feat|fix|perf|refactor|style|test)(\\((web app|server|hotword|package\\/(${packagesScopeString})))?\\)?: .{1,50}` // eslint-disable-line no-useless-escape
if (commitMessage.match(regex) !== null) {
log.success('Commit message validated')
} else {
log.error(`Commit message does not match the format: ${regex}`)
process.exit(1)
}
} catch (e) {
log.error(e.message)
process.exit(1)
}
}

View File

@ -1,19 +1,32 @@
const fs = require('fs')
const path = require('path')
const os = require('os')
const { execSync } = require('child_process')
/**
* Trigger preinstall hook to remove DeepSpeech on Windows
*/
console.info('\x1b[36m➡ %s\x1b[0m', 'Running Leon\'s installation...')
if (os.type().indexOf('Windows') !== -1) {
const packageJsonPath = path.join(__dirname, '../../package.json')
const packageJson = require(packageJsonPath) // eslint-disable-line global-require
console.warn('\x1b[33m❗ %s\x1b[0m', 'The Leon\'s voice offline mode is not available on Windows')
console.info('\x1b[36m➡ %s\x1b[0m', 'Backing up package.json...')
fs.copyFileSync('package.json', 'package.json.backup')
console.log('\x1b[32m✔ %s\x1b[0m', 'package.json has been backed up')
console.info('\x1b[36m➡ %s\x1b[0m', 'Removing DeepSpeech dependency... Please wait, this might take several minutes...')
execSync('npm uninstall --save deepspeech')
console.log('\x1b[32m✔ %s\x1b[0m', 'DeepSpeech dependency has been removed.')
}
console.info('\x1b[36m➡ %s\x1b[0m', 'Running Leon\'s installation...')
try {
if (packageJson?.dependencies.deepspeech) {
console.info('\x1b[36m➡ %s\x1b[0m', 'Removing DeepSpeech dependency...')
delete packageJson.dependencies.deepspeech
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2))
console.log('\x1b[32m✔ %s\x1b[0m', 'DeepSpeech dependency has been removed.')
}
} catch (e) {
console.error('\x1b[31m✖ %s\x1b[0m', 'Failed to remove DeepSpeech dependency')
}
}

View File

@ -16,8 +16,8 @@ import loader from '@/helpers/loader'
try {
loader.start()
await command('npm run train expressions:en', { shell: true })
const cmd = await command(`cross-env PIPENV_PIPFILE=bridges/python/Pipfile LEON_NODE_ENV=testing jest --silent --config=./test/e2e/modules/e2e.modules.jest.json packages/${pkg}/test/${module}.spec.js && npm run train expressions`, { shell: true })
await command('npm run train en', { shell: true })
const cmd = await command(`cross-env PIPENV_PIPFILE=bridges/python/Pipfile LEON_NODE_ENV=testing jest --silent --config=./test/e2e/modules/e2e.modules.jest.json packages/${pkg}/test/${module}.spec.js && npm run train`, { shell: true })
log.default(cmd.stdout)
log.default(cmd.stderr)

View File

@ -11,74 +11,63 @@ import { langs } from '../core/langs.json'
dotenv.config()
/**
* Training script
* Training expressions script
*
* npm run train expressions
* npm run train expressions:en
* npm run train [en or fr]
*/
export default () => new Promise(async (resolve, reject) => {
const { argv } = process
const packagesDir = 'packages'
const modelFileName = 'server/src/data/leon-model.nlp'
let type = (argv[2]) ? argv[2].toLowerCase() : 'expressions'
let lang = ''
if (type.indexOf(':') !== -1) {
[type, lang] = type.split(':')
} else {
lang = langs[process.env.LEON_LANG].short.toLowerCase().substr(0, 2)
}
const lang = argv[2]
? argv[2].toLowerCase()
: langs[process.env.LEON_LANG].short.toLowerCase().substr(0, 2)
try {
if (type === 'expressions') {
const dock = await dockStart({ use: ['Basic'] })
const dock = await dockStart({ use: ['Basic'] })
const nlp = dock.get('nlp')
nlp.settings.modelFileName = modelFileName
nlp.settings.threshold = 0.8
const nlp = dock.get('nlp')
nlp.settings.modelFileName = modelFileName
nlp.settings.threshold = 0.8
nlp.addLanguage(lang)
nlp.addLanguage(lang)
const packages = fs.readdirSync(packagesDir)
.filter((entity) => fs.statSync(path.join(packagesDir, entity)).isDirectory())
let expressionsObj = { }
const packages = fs.readdirSync(packagesDir)
.filter((entity) => fs.statSync(path.join(packagesDir, entity)).isDirectory())
let expressionsObj = { }
for (let i = 0; i < packages.length; i += 1) {
log.info(`Training "${string.ucfirst(packages[i])}" package modules expressions...`)
for (let i = 0; i < packages.length; i += 1) {
log.info(`Training "${string.ucfirst(packages[i])}" package modules expressions...`)
expressionsObj = JSON.parse(fs.readFileSync(`${packagesDir}/${packages[i]}/data/expressions/${lang}.json`, 'utf8'))
expressionsObj = JSON.parse(fs.readFileSync(`${packagesDir}/${packages[i]}/data/expressions/${lang}.json`, 'utf8'))
const modules = Object.keys(expressionsObj)
for (let j = 0; j < modules.length; j += 1) {
const module = modules[j]
const actions = Object.keys(expressionsObj[module])
const modules = Object.keys(expressionsObj)
for (let j = 0; j < modules.length; j += 1) {
const module = modules[j]
const actions = Object.keys(expressionsObj[module])
for (let k = 0; k < actions.length; k += 1) {
const action = actions[k]
const exprs = expressionsObj[module][action].expressions
for (let k = 0; k < actions.length; k += 1) {
const action = actions[k]
const exprs = expressionsObj[module][action].expressions
nlp.assignDomain(lang, `${module}.${action}`, packages[i])
nlp.assignDomain(lang, `${module}.${action}`, packages[i])
for (let l = 0; l < exprs.length; l += 1) {
nlp.addDocument(lang, exprs[l], `${module}.${action}`)
}
for (let l = 0; l < exprs.length; l += 1) {
nlp.addDocument(lang, exprs[l], `${module}.${action}`)
}
log.success(`"${string.ucfirst(module)}" module expressions trained`)
}
}
try {
await nlp.train()
log.success(`NLP model saved in ${modelFileName}`)
resolve()
} catch (e) {
log.error(`Failed to save NLP model: ${e}`)
reject()
log.success(`"${string.ucfirst(module)}" module expressions trained`)
}
} else {
log.error(`"${type}" training type is unknown. Try "npm run train expressions"`)
}
try {
await nlp.train()
log.success(`NLP model saved in ${modelFileName}`)
resolve()
} catch (e) {
log.error(`Failed to save NLP model: ${e}`)
reject()
}
} catch (e) {

View File

@ -86,7 +86,15 @@ class Ner {
if (condition.type === 'between') {
// e.g. list.addBetweenCondition('en', 'list', 'create a', 'list')
this.ner[conditionMethod](lang, entity.name, condition.from, condition.to)
if (Array.isArray(condition.from) && Array.isArray(condition.to)) {
const { from, to } = condition
from.forEach((word, index) => {
this.ner[conditionMethod](lang, entity.name, word, to[index])
})
} else {
this.ner[conditionMethod](lang, entity.name, condition.from, condition.to)
}
} else if (condition.type.indexOf('after') !== -1) {
this.ner[conditionMethod](lang, entity.name, condition.from)
} else if (condition.type.indexOf('before') !== -1) {

View File

@ -28,7 +28,7 @@ class Nlu {
return new Promise(async (resolve, reject) => {
if (!fs.existsSync(nlpModel)) {
log.title('NLU')
reject({ type: 'warning', obj: new Error('The NLP model does not exist, please run: npm run train expressions') })
reject({ type: 'warning', obj: new Error('The NLP model does not exist, please run: npm run train') })
} else {
log.title('NLU')
@ -65,7 +65,7 @@ class Nlu {
this.brain.talk(`${this.brain.wernicke('random_errors')}!`)
this.brain.socket.emit('is-typing', false)
log.error('The NLP model is missing, please rebuild the project or if you are in dev run: npm run train expressions')
log.error('The NLP model is missing, please rebuild the project or if you are in dev run: npm run train')
return false
}

View File

@ -12,6 +12,7 @@
"<rootDir>/test/paths.setup.js"
],
"setupFilesAfterEnv": [
"jest-extended/all",
"<rootDir>/test/e2e/modules/e2e.modules.setup.js"
]
}

View File

@ -36,7 +36,7 @@ describe('NLU modules', () => {
process.env.LEON_LANG = langKeys[i]
// Generate new NLP model for the tested language
await command(`npm run train expressions:${lang.short}`, { shell: true })
await command(`npm run train ${lang.short}`, { shell: true })
// Load the new NLP model
await nlu.loadModel(global.paths.nlp_model)
})

View File

@ -61,7 +61,7 @@ describe('server', () => {
})
describe('connection()', () => {
test('initializes main nodes', async (done) => {
test('initializes main nodes', async () => {
const server = new Server()
await server.init()
@ -88,12 +88,12 @@ describe('server', () => {
expect(server.nlu).not.toBeEmpty()
expect(server.asr).not.toBeEmpty()
setTimeout(() => {
/* setTimeout(() => {
ee.emit('query', { client: 'jest', value: 'Hello' })
}, 50)
setTimeout(() => {
expect(console.log.mock.calls[22][1]).toBe('Query found')
expect(console.log.mock.calls[26][1]).toBe('Query found')
console.log = jest.fn()
}, 100)
@ -106,9 +106,7 @@ describe('server', () => {
console.log = jest.fn()
await server.httpServer.close()
done()
}, 200)
}, 200) */
})
})
})

View File

@ -4,9 +4,11 @@ jest.useFakeTimers()
describe('loader helper', () => {
describe('start()', () => {
jest.useFakeTimers()
jest.spyOn(global, 'setInterval')
test('starts spinner', () => {
expect(loader.start()).toBeObject()
jest.runTimersToTime(60000)
expect(setInterval).toHaveBeenCalledTimes(1)
})
})

View File

@ -12,6 +12,7 @@
"<rootDir>/test/paths.setup.js"
],
"setupFilesAfterEnv": [
"jest-extended/all",
"<rootDir>/test/unit/unit.setup.js"
],
"coverageDirectory": "<rootDir>/test/coverage",

View File

@ -1,4 +1,3 @@
import 'jest-extended'
import moment from 'moment-timezone'
import expressions from '@/data/en.json'