Skip to content

Commit

Permalink
feat(server): clean up context if the action loop does not meet the e…
Browse files Browse the repository at this point in the history
…xpected items
  • Loading branch information
louistiti committed May 8, 2022
1 parent 1529c72 commit 035c9d5
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 39 deletions.
55 changes: 32 additions & 23 deletions server/src/core/brain.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,9 @@ class Brain {
console.log('brain obj', obj)
const { nluDataFilePath, classification: { action: actionName } } = obj
const { actions } = JSON.parse(fs.readFileSync(nluDataFilePath, 'utf8'))
const { type: actionType } = actions[actionName]
const action = actions[actionName]
const { type: actionType } = action
const nextAction = action.next_action ? actions[action.next_action] : null

if (actionType === 'logic') {
// Ensure the process is empty (to be able to execute other processes outside of Brain)
Expand Down Expand Up @@ -281,29 +283,32 @@ class Brain {

console.log('[BRAIN] this.finalOutput', this.finalOutput)

const speech = this.finalOutput.speech.toString()
if (!opts.mute) {
this.talk(speech, true)
}
speeches.push(speech)
let { speech } = this.finalOutput
if (speech) {
speech = speech.toString()
if (!opts.mute) {
this.talk(speech, true)
}
speeches.push(speech)

/* istanbul ignore next */
// Synchronize the downloaded content if enabled
if (this.finalOutput.type === 'end' && this.finalOutput.options.synchronization && this.finalOutput.options.synchronization.enabled
&& this.finalOutput.options.synchronization.enabled === true) {
const sync = new Synchronizer(
this,
obj.classification,
this.finalOutput.options.synchronization
)

// When the synchronization is finished
sync.synchronize((speech) => {
if (!opts.mute) {
this.talk(speech)
}
speeches.push(speech)
})
/* istanbul ignore next */
// Synchronize the downloaded content if enabled
if (this.finalOutput.type === 'end' && this.finalOutput.options.synchronization && this.finalOutput.options.synchronization.enabled
&& this.finalOutput.options.synchronization.enabled === true) {
const sync = new Synchronizer(
this,
obj.classification,
this.finalOutput.options.synchronization
)

// When the synchronization is finished
sync.synchronize((speech) => {
if (!opts.mute) {
this.talk(speech)
}
speeches.push(speech)
})
}
}
}

Expand All @@ -322,6 +327,8 @@ class Brain {
...obj,
speeches,
core: this.finalOutput.core,
action,
nextAction,
executionTime // In ms, skill execution time only
})
})
Expand Down Expand Up @@ -401,6 +408,8 @@ class Brain {
...obj,
speeches: [answer],
core: this.finalOutput.core,
action,
nextAction,
executionTime // In ms, skill execution time only
})
}
Expand Down
40 changes: 25 additions & 15 deletions server/src/core/nlu.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,18 @@ class Nlu {
)

const processedData = await this.brain.execute(this.nluResultObj, { mute: opts.mute })
const expectedItems = processedData.action.loop.expected_items.map(({ name }) => name)
// TODO: also check for resolvers, not only entities
const hasMatchingItem = processedData
.entities.filter(({ entity }) => expectedItems.includes(entity)).length > 0

// Ensure expected items are in the utterance, otherwise clean context and reprocess
if (!hasMatchingItem) {
this.brain.talk(`${this.brain.wernicke('random_context_out_of_topic')}.`)
this.conv.cleanActiveContext()
await this.process(utterance, opts)
return resolve(null)
}

// Break the action loop
if (processedData.core?.isInActionLoop === false) {
Expand Down Expand Up @@ -187,18 +199,17 @@ class Nlu {
}

if (Object.keys(processedData).length > 0) {
processedData.nextAction = 'guess'
// Set new context with the next action if there is one
if (processedData.nextAction) {
if (processedData.action.next_action) {
this.conv.activeContext = {
lang: this.brain.lang,
slots: { },
isInActionLoop: true, // TODO: dynamic value according to the skill output
isInActionLoop: !!processedData.nextAction.loop,
originalUtterance: processedData.utterance,
nluDataFilePath: processedData.nluDataFilePath,
actionName: processedData.nextAction,
actionName: processedData.action.next_action,
domain: processedData.classification.domain,
intent: `${processedData.classification.skill}.${processedData.nextAction}`,
intent: `${processedData.classification.skill}.${processedData.action.next_action}`,
entities: []
}

Expand Down Expand Up @@ -262,7 +273,7 @@ class Nlu {
// TODO: method
if (this.brain.lang !== locale) {
const connectedHandler = async () => {
await this.process(utterance)
await this.process(utterance, opts)
}

this.brain.lang = locale
Expand Down Expand Up @@ -358,18 +369,17 @@ class Nlu {
if (this.conv.hasActiveContext()) {
const processedData = await this.slotFill(utterance, opts)
if (processedData && Object.keys(processedData).length > 0) {
processedData.nextAction = 'guess'
// Set new context with the next action if there is one
if (processedData.nextAction) {
if (processedData.action.next_action) {
this.conv.activeContext = {
lang: this.brain.lang,
slots: { },
isInActionLoop: true, // TODO: dynamic value according to the skill output
isInActionLoop: !!processedData.nextAction.loop,
originalUtterance: processedData.utterance,
nluDataFilePath: processedData.nluDataFilePath,
actionName: processedData.nextAction,
actionName: processedData.action.next_action,
domain: processedData.classification.domain,
intent: `${processedData.classification.skill}.${processedData.nextAction}`,
intent: `${processedData.classification.skill}.${processedData.action.next_action}`,
entities: []
}

Expand Down Expand Up @@ -490,14 +500,14 @@ class Nlu {
* e.g. I wanna play with 2 players and [email protected]
* Need to refactor now (nluResultObj method to build it, etc.)
* 6. [OK] Handle a "loop" feature from action (guess the number)
* No need "loop" in the NLU skill config. Just add option in util output
* 7. While in an action loop, if something other than an expected entity is sent
* 7. [OK] While in an action loop, if something other than an expected entity is sent
* then break the loop. Need "loop" object in NLU skill config to describe
* 8. Make difference between actions to trigger immediately vs context to prepare
* 8.a. For the setup action, replace "next_action": "setup" by "action_to_trigger": "setup"
* 8.b. Keep next_action for the ones where context needs to be prepared ahead
* 9. Split this process() method into several ones + clean nlu.js and brain.js
* 10. Add logs in terminal about context switching, active context, etc.
* 9. Be able to use the loop without necessarily need slots
* 10. Split this process() method into several ones + clean nlu.js and brain.js
* 11. Add logs in terminal about context switching, active context, etc.
*/

this.nluResultObj = {
Expand Down
5 changes: 4 additions & 1 deletion skills/games/guess_the_number/src/actions/guess.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@ def guess(params):
nb_to_guess = 42 # TODO: pick up from DB

# Find entities
# TODO: if no number entity found, then break the action loop
for item in params['entities']:
if item['entity'] == 'number':
given_nb = item['resolution']['value']

# Return no speech if no number has been found
if given_nb == -1:
return utils.output('end', None, None, { 'isInActionLoop': False })

if given_nb == nb_to_guess:
return utils.output('end', 'guessed', '....CONGRATS....', { 'isInActionLoop': False })
if nb_to_guess < given_nb:
Expand Down

0 comments on commit 035c9d5

Please sign in to comment.