From b6428d038452619f1682c863892cd8f376efca84 Mon Sep 17 00:00:00 2001 From: louistiti Date: Mon, 10 Jan 2022 22:37:54 +0800 Subject: [PATCH] feat(server): expose queries over HTTP --- server/src/core/brain.js | 13 ++- server/src/core/nlu.js | 184 ++++++++++++++++++++++---------------- server/src/core/server.js | 31 +++++-- 3 files changed, 143 insertions(+), 85 deletions(-) diff --git a/server/src/core/brain.js b/server/src/core/brain.js index 8b2584e0..49750083 100644 --- a/server/src/core/brain.js +++ b/server/src/core/brain.js @@ -26,10 +26,18 @@ class Brain { log.success('New instance') } + get socket () { + return this._socket + } + set socket (newSocket) { this._socket = newSocket } + get tts () { + return this._tts + } + set tts (newTts) { this._tts = newTts } @@ -259,8 +267,11 @@ class Brain { const executionTime = executionTimeEnd - executionTimeStart resolve({ + queryId, + lang: langs[process.env.LEON_LANG].short, + ...obj, speeches, - executionTime + executionTime // In ms, module execution time only }) }) diff --git a/server/src/core/nlu.js b/server/src/core/nlu.js index bb3627ef..5adb8a6b 100644 --- a/server/src/core/nlu.js +++ b/server/src/core/nlu.js @@ -55,93 +55,125 @@ class Nlu { * pick-up the right classification * and extract entities */ - async process (query) { - log.title('NLU') - log.info('Processing...') + process (query, opts) { + const processingTimeStart = Date.now() - query = string.ucfirst(query) + return new Promise(async (resolve, reject) => { + log.title('NLU') + log.info('Processing...') - if (Object.keys(this.nlp).length === 0) { - 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') - - return false - } - - const lang = langs[process.env.LEON_LANG].short - const result = await this.nlp.process(lang, query) - - const { - domain, intent, score - } = result - const [moduleName, actionName] = intent.split('.') - let obj = { - query, - entities: [], - classification: { - package: domain, - module: moduleName, - action: actionName, - confidence: score + opts = opts || { + mute: false // Close Leon mouth e.g. over HTTP } - } + query = string.ucfirst(query) - /* istanbul ignore next */ - if (process.env.LEON_LOGGER === 'true' && process.env.LEON_NODE_ENV !== 'testing') { - this.request - .post('https://logger.getleon.ai/v1/expressions') - .set('X-Origin', 'leon-core') - .send({ - version, - query, + if (Object.keys(this.nlp).length === 0) { + if (!opts.mute) { + this.brain.talk(`${this.brain.wernicke('random_errors')}!`) + this.brain.socket.emit('is-typing', false) + } + + const msg = 'The NLP model is missing, please rebuild the project or if you are in dev run: npm run train' + log.error(msg) + return reject(msg) + } + + const lang = langs[process.env.LEON_LANG].short + const result = await this.nlp.process(lang, query) + + const { + domain, intent, score + } = result + const [moduleName, actionName] = intent.split('.') + let obj = { + query, + entities: [], + classification: { + package: domain, + module: moduleName, + action: actionName, + confidence: score + } + } + + /* istanbul ignore next */ + if (process.env.LEON_LOGGER === 'true' && process.env.LEON_NODE_ENV !== 'testing') { + this.request + .post('https://logger.getleon.ai/v1/expressions') + .set('X-Origin', 'leon-core') + .send({ + version, + query, + lang, + classification: obj.classification + }) + .then(() => { /* */ }) + .catch(() => { /* */ }) + } + + if (intent === 'None') { + const fallback = Nlu.fallback(obj, langs[process.env.LEON_LANG].fallbacks) + + if (fallback === false) { + if (!opts.mute) { + this.brain.talk(`${this.brain.wernicke('random_unknown_queries')}.`, true) + this.brain.socket.emit('is-typing', false) + } + + log.title('NLU') + const msg = 'Query not found' + log.warning(msg) + + const processingTimeEnd = Date.now() + const processingTime = processingTimeEnd - processingTimeStart + + return resolve({ + processingTime, + message: msg + }) + } + + obj = fallback + } + + log.title('NLU') + log.success('Query found') + + try { + obj.entities = await this.ner.extractEntities( lang, - classification: obj.classification - }) - .then(() => { /* */ }) - .catch(() => { /* */ }) - } + join(__dirname, '../../../packages', obj.classification.package, `data/expressions/${lang}.json`), + obj + ) + } catch (e) /* istanbul ignore next */ { + log[e.type](e.obj.message) - if (intent === 'None') { - const fallback = Nlu.fallback(obj, langs[process.env.LEON_LANG].fallbacks) - - if (fallback === false) { - this.brain.talk(`${this.brain.wernicke('random_unknown_queries')}.`, true) - this.brain.socket.emit('is-typing', false) - - log.title('NLU') - log.warning('Query not found') - - return false + if (!opts.mute) { + this.brain.talk(`${this.brain.wernicke(e.code, '', e.data)}!`) + } } - obj = fallback - } + try { + // Inject action entities with the others if there is + const data = await this.brain.execute(obj, { mute: opts.mute }) + const processingTimeEnd = Date.now() + const processingTime = processingTimeEnd - processingTimeStart - log.title('NLU') - log.success('Query found') + return resolve({ + processingTime, // In ms, total time + ...data, + nluProcessingTime: processingTime - data.executionTime // In ms, NLU processing time only + }) + } catch (e) /* istanbul ignore next */ { + log[e.type](e.obj.message) - try { - obj.entities = await this.ner.extractEntities( - lang, - join(__dirname, '../../../packages', obj.classification.package, `data/expressions/${lang}.json`), - obj - ) - } catch (e) /* istanbul ignore next */ { - log[e.type](e.obj.message) - this.brain.talk(`${this.brain.wernicke(e.code, '', e.data)}!`) - } + if (!opts.mute) { + this.brain.socket.emit('is-typing', false) + } - try { - // Inject action entities with the others if there is - await this.brain.execute(obj) - } catch (e) /* istanbul ignore next */ { - log[e.type](e.obj.message) - this.brain.socket.emit('is-typing', false) - } - - return true + return reject(e.obj) + } + }) } /** diff --git a/server/src/core/server.js b/server/src/core/server.js index be031bf8..0a7482d1 100644 --- a/server/src/core/server.js +++ b/server/src/core/server.js @@ -82,18 +82,14 @@ const generatePackagesRoutes = () => { package: pkg, module, action, - execution_time: 0, // ms speeches: [] } try { - const { speeches, executionTime } = await brain.execute(obj, { mute: true }) + const data = await brain.execute(obj, { mute: true }) reply.send({ - ...responseData, - entities, - speeches, - execution_time: executionTime, + ...data, success: true }) } catch (e) /* istanbul ignore next */ { @@ -102,7 +98,7 @@ const generatePackagesRoutes = () => { reply.send({ ...responseData, speeches: e.speeches, - execution_time: e.executionTime, + executionTime: e.executionTime, error: e.obj.message, success: false }) @@ -222,13 +218,32 @@ const bootstrap = async () => { root: join(__dirname, '..', '..', '..', 'app', 'dist'), prefix: '/' }) - fastify.get('/', (_request, reply) => { + fastify.get('/', (request, reply) => { reply.sendFile('index.html') }) fastify.register(infoPlugin, { apiVersion }) fastify.register(downloadsPlugin, { apiVersion }) + fastify.post('/core/query', async (request, reply) => { + const { query } = request.body + + try { + const data = await nlu.process(query, { mute: true }) + + reply.send({ + ...data, + success: true + }) + } catch (e) { + reply.statusCode = 500 + reply.send({ + error: e.message, + success: false + }) + } + }) + if (process.env.PACKAGES_OVER_HTTP === 'true') { generatePackagesRoutes() }