From 014798dbffd4d09cebe7bf9e26303e49697a347f Mon Sep 17 00:00:00 2001 From: Ingo Fischer Date: Sun, 8 Jan 2023 14:43:35 +0100 Subject: [PATCH] fix: Fix crash on initial slot fill without a former question asked fixes https://github.com/axa-group/nlp.js/issues/1252 --- packages/nlp/test/nlp.test.js | 71 ++++++++++++++++++++++ packages/slot/src/slot-manager.js | 4 +- packages/slot/test/slot-manager.test.js | 78 +++++++++++++++++++++++++ 3 files changed, 152 insertions(+), 1 deletion(-) diff --git a/packages/nlp/test/nlp.test.js b/packages/nlp/test/nlp.test.js index 35e3e0f5c..2fb8ce4b3 100644 --- a/packages/nlp/test/nlp.test.js +++ b/packages/nlp/test/nlp.test.js @@ -1165,6 +1165,77 @@ describe('NLP', () => { 'When do you want to travel from {{fromCity}} to {{toCity}}?' ); }); + test('On initial processing slotFill.latestSlot is not set in response (because no asked slot filled now)', async () => { + const corpus = { + name: 'basic conversations', + locale: 'en-us', + entities: { + clientName: { + trim: [ + { + position: 'betweenLast', + leftWords: ['is', 'am'], + rightWords: ['.'], + }, + { + position: 'afterLast', + words: ['is', 'am'], + }, + ], + }, + location: { + trim: [ + { + position: 'betweenLast', + leftWords: ['in', 'around'], + rightWords: ['today', 'currently', 'at'], + }, + { + position: 'afterLast', + words: ['in', 'around', 'to', 'at', 'from'], + }, + ], + }, + }, + data: [ + { + intent: 'user.introduce', + utterances: ['i am @clientName', 'my name is @clientName'], + answer: [ + 'Nice to meet you @clientName.', + "It's a pleasure to meet you @clientName.", + ], + slotFilling: { + clientName: "I'm sorry but i didn't get your name", + location: 'Where are you from @clientName?', + }, + }, + ], + }; + const nlp = new Nlp({ + languages: ['en'], + autoSave: false, + }); + await nlp.addCorpus(corpus); + expect(nlp.ner.rules.en).toBeDefined(); + expect(nlp.ner.rules.en.clientName).toBeDefined(); + expect(nlp.ner.rules.en.location).toBeDefined(); + expect(nlp.slotManager.intents['user.introduce']).toBeDefined(); + + await nlp.train(); + const input = { + locale: 'en', + text: 'my name is John', + }; + const actual = await nlp.process(input); + expect(actual.intent).toEqual('user.introduce'); + expect(actual.entities).toBeDefined(); + expect(actual.entities[0].entity).toEqual('clientName'); + expect(actual.entities[0].sourceText).toEqual('John'); + expect(actual.slotFill).toBeDefined(); + expect(actual.slotFill.currentSlot).toEqual('location'); + expect(actual.slotFill.latestSlot).toBeUndefined(); + }); test('The corpus can contain entities with action details', async () => { const corpus = { name: 'Slot Filling Corpus', diff --git a/packages/slot/src/slot-manager.js b/packages/slot/src/slot-manager.js index f2cd747e0..6b8280321 100644 --- a/packages/slot/src/slot-manager.js +++ b/packages/slot/src/slot-manager.js @@ -292,8 +292,10 @@ class SlotManager { entities: result.entities, answer: result.answer, srcAnswer: result.srcAnswer, - latestSlot: context.slotFill.latestSlot, }; + if (context.slotFill && context.slotFill.latestSlot) { + result.slotFill.latestSlot = context.slotFill.latestSlot; + } const currentSlot = mandatorySlots[keys[0]]; result.slotFill.currentSlot = currentSlot.entity; result.srcAnswer = currentSlot.locales[result.localeIso2]; diff --git a/packages/slot/test/slot-manager.test.js b/packages/slot/test/slot-manager.test.js index 1b2d9cb58..304bee546 100644 --- a/packages/slot/test/slot-manager.test.js +++ b/packages/slot/test/slot-manager.test.js @@ -410,6 +410,84 @@ describe('Slot Manager', () => { entities: [], }); }); + test('On initial slotfill, fill in but leave latestFilled empty', () => { + const manager = new SlotManager(); + manager.addSlot('intent', 'entity1', true); + const result = { + intent: 'intent', + utterance: 'hello John', + score: 1, + entities: [ + { + sourceText: 'John', + utteranceText: 'John', + entity: 'entity1', + }, + ], + }; + const context = {}; + const actual = manager.process(result, context); + expect(actual).toBeTruthy(); + expect(result).toEqual({ + intent: 'intent', + utterance: 'hello John', + score: 1, + entities: [ + { + entity: 'entity1', + utteranceText: 'John', + sourceText: 'John', + }, + ], + }); + }); + test('On initial slotfill, fill in but leave latestFilled empty, and ask for another entity', () => { + const manager = new SlotManager(); + manager.addSlot('intent', 'entity1', true); + manager.addSlot('intent', 'entity2', true, { en: 'answer' }); + const result = { + intent: 'intent', + utterance: 'hello John', + score: 1, + localeIso2: 'en', + entities: [ + { + sourceText: 'John', + utteranceText: 'John', + entity: 'entity1', + }, + ], + }; + const context = {}; + const actual = manager.process(result, context); + expect(actual).toBeTruthy(); + expect(result).toEqual({ + intent: 'intent', + utterance: 'hello John', + score: 1, + localeIso2: 'en', + entities: [ + { + entity: 'entity1', + utteranceText: 'John', + sourceText: 'John', + }, + ], + slotFill: { + currentSlot: 'entity2', + entities: [ + { + entity: 'entity1', + utteranceText: 'John', + sourceText: 'John', + }, + ], + intent: 'intent', + localeIso2: 'en', + }, + srcAnswer: 'answer', + }); + }); test('If slot fill is waiting for an entity, fill the entity', () => { const manager = new SlotManager(); manager.addSlot('intent', 'entity1', true);