Skip to content
This repository was archived by the owner on Jan 15, 2025. It is now read-only.

Commit fbe453f

Browse files
authored
fix multiturn id mismatching issue (#902)
* fix multiturn id mismatching issue * add unit tests
1 parent d3cbcf0 commit fbe453f

File tree

18 files changed

+92
-23
lines changed

18 files changed

+92
-23
lines changed

packages/lu/src/parser/cross-train/crossTrainer.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,7 @@ const qnaCrossTrainCore = function (luResource, qnaResource, fileName, interrupt
474474
let subDedupedUtterances = dedupedUtterances.splice(0, MAX_QUESTIONS_PER_ANSWER)
475475
// construct new question content for qna resource
476476
let utterancesContent = subDedupedUtterances.join(NEWLINE + '- ')
477-
let utterancesToQuestion = `${NEWLINE}${crossTrainingComments}${NEWLINE}# ? ${utterancesContent}${NEWLINE}${NEWLINE}**Filters:**${NEWLINE}- dialogName=${fileName}${NEWLINE}${NEWLINE}\`\`\`${NEWLINE}intent=DeferToRecognizer_LUIS_${fileName}${NEWLINE}\`\`\``
477+
let utterancesToQuestion = `${NEWLINE}${crossTrainingComments}${NEWLINE}> !# @qna.pair.source = crosstrained${NEWLINE}${NEWLINE}# ? ${utterancesContent}${NEWLINE}${NEWLINE}**Filters:**${NEWLINE}- dialogName=${fileName}${NEWLINE}${NEWLINE}\`\`\`${NEWLINE}intent=DeferToRecognizer_LUIS_${fileName}${NEWLINE}\`\`\``
478478
trainedQnaResource = new SectionOperator(trainedQnaResource).addSection(utterancesToQuestion)
479479
}
480480

packages/lu/src/parser/qna/qnamaker/kbCollate.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ const resolveMultiTurnReferences = function(qnaList) {
8585
let qnaId = qnaList.find(x => x.id === prompt.qnaId || x.id === parseInt(prompt.qnaId));
8686
if (!qnaId) {
8787
// find by question match
88-
qnaId = qnaList.find(x => x.questions.includes(prompt.qnaId) || x.questions.includes(prompt.qnaId.replace(/-/g, ' ').trim()))
88+
qnaId = qnaList.find(x => x.source.trim() !== 'crosstrained' && (x.questions.includes(prompt.qnaId) || x.questions.includes(prompt.qnaId.replace(/-/g, ' ').trim())))
8989
}
9090
if (qnaId === undefined) {
9191
throw (new exception(retCode.INVALID_INPUT, `[ERROR]: Cannot find follow up prompt definition for '- [${prompt.displayText}](#?${prompt.qnaId}).`));

packages/lu/src/parser/qnabuild/builder.ts

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ const qnaMakerBuilder = require('./../qna/qnamaker/qnaMakerBuilder')
2020
const qnaOptions = require('./../lu/qnaOptions')
2121
const Content = require('./../lu/qna')
2222
const KB = require('./../qna/qnamaker/kb')
23-
const NEWLINE = require('os').EOL
2423
const recognizerType = require('./../utils/enums/recognizertypes')
2524

2625
export class Builder {
@@ -40,7 +39,8 @@ export class Builder {
4039
let multiRecognizer: any
4140
let settings: any
4241
let recognizers = new Map<string, Recognizer>()
43-
let qnaContents = new Map<string, string>()
42+
let qnaContents = new Map<string, any>()
43+
let qnaObjects = new Map<string, any[]>()
4444

4545
for (const file of files) {
4646
let fileCulture: string
@@ -51,22 +51,7 @@ export class Builder {
5151
fileCulture = culture
5252
}
5353

54-
let fileContent = ''
55-
let result
5654
const qnaFiles = await fileHelper.getLuObjects(undefined, file, true, fileExtEnum.QnAFile)
57-
try {
58-
result = await qnaBuilderVerbose.build(qnaFiles, true)
59-
60-
// construct qna content without file and url references
61-
fileContent = result.parseToQnAContent()
62-
} catch (err) {
63-
if (err.source) {
64-
err.text = `Invalid QnA file ${err.source}: ${err.text}`
65-
} else {
66-
err.text = `Invalid QnA file ${file}: ${err.text}`
67-
}
68-
throw (new exception(retCode.errorCode.INVALID_INPUT_FILE, err.text))
69-
}
7055

7156
this.handler(`${file} loaded\n`)
7257

@@ -96,7 +81,7 @@ export class Builder {
9681
settings = new Settings(settingsPath, settingsContent)
9782
}
9883

99-
const content = new Content(fileContent, new qnaOptions(botName, true, fileCulture, file))
84+
const content = new Content('', new qnaOptions(botName, true, fileCulture, file))
10085

10186
if (!recognizers.has(content.name)) {
10287
const dialogFile = path.join(fileFolder, `${content.name}.dialog`)
@@ -113,14 +98,15 @@ export class Builder {
11398
let recognizer = Recognizer.load(content.path, content.name, dialogFile, settings, existingDialogObj, schema)
11499
recognizers.set(content.name, recognizer)
115100
qnaContents.set(content.name, content)
101+
qnaObjects.set(content.name, qnaFiles)
116102
} else {
117103
// merge contents of qna files with same name
118-
let existingContent: any = qnaContents.get(content.name)
119-
existingContent.content = `${existingContent.content}${NEWLINE}${NEWLINE}${content.content}`
120-
qnaContents.set(content.name, existingContent)
104+
qnaObjects.get(content.name)?.push(...qnaFiles)
121105
}
122106
}
123107

108+
await this.resolveMergedQnAContentIds(qnaContents, qnaObjects)
109+
124110
return {qnaContents: [...qnaContents.values()], recognizers, multiRecognizer, settings}
125111
}
126112

@@ -515,4 +501,23 @@ export class Builder {
515501
}
516502
}
517503
}
504+
505+
async resolveMergedQnAContentIds(contents: Map<string, any>, objects: Map<string, any[]>) {
506+
for (const [name, content] of contents) {
507+
let qnaObjects = objects.get(name)
508+
try {
509+
let result = await qnaBuilderVerbose.build(qnaObjects, true)
510+
let mergedContent = result.parseToQnAContent()
511+
content.content = mergedContent
512+
contents.set(name, content)
513+
} catch (err) {
514+
if (err.source) {
515+
err.text = `Invalid QnA file ${err.source}: ${err.text}`
516+
} else {
517+
err.text = `Invalid QnA file ${content.path}: ${err.text}`
518+
}
519+
throw (new exception(retCode.errorCode.INVALID_INPUT_FILE, err.text))
520+
}
521+
}
522+
}
518523
}

packages/lu/test/parser/qna/qnaMakerBuilder.test.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const QnAMakerBuilder = require('./../../../src/parser/qna/qnamaker/qnaMakerBuilder')
22
const QnA = require('./../../../src/parser/lu/qna')
3+
const qnaOptions = require('./../../../src/parser/lu/qnaOptions')
34
var chai = require('chai');
45
var assert = chai.assert;
56

@@ -25,4 +26,37 @@ describe('QnAMakerBuilder', function() {
2526
assert.equal(qnaMakerObject.kb.name, 'my test kb');
2627
});
2728

29+
it('Build QnaMaker app from multiple instances that have prompts', async () => {
30+
let qnaContent1 =
31+
`> !# @qna.pair.source = crosstrained
32+
# ? hi
33+
\`\`\`markdown
34+
hi from crosstrained
35+
\`\`\``;
36+
37+
let qnaContent2 =
38+
`# ? greeting
39+
\`\`\`markdown
40+
how to greeting
41+
\`\`\`
42+
43+
**Prompts:**
44+
- [hi greeting](#hi)`;
45+
46+
let qnaContent3 =
47+
`# ? hi
48+
\`\`\`markdown
49+
say hi
50+
\`\`\``;
51+
const qna1 = new QnA(qnaContent1, new qnaOptions())
52+
const qna2 = new QnA(qnaContent2, new qnaOptions())
53+
const qna3 = new QnA(qnaContent3, new qnaOptions())
54+
const qnaMakerObject = await QnAMakerBuilder.fromQna([qna1, qna2, qna3])
55+
56+
assert.equal(qnaMakerObject.kb.qnaList[0].id, 2);
57+
assert.equal(qnaMakerObject.kb.qnaList[1].id, 3);
58+
assert.equal(qnaMakerObject.kb.qnaList[2].id, 1);
59+
assert.equal(qnaMakerObject.kb.qnaList[1].context.prompts[0].qnaId, 1);
60+
});
61+
2862
});

packages/luis/test/fixtures/verified/interruption/Main.qna

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ I can help book flight, book hotel and book train ticket
2828
```
2929

3030
> Source: cross training. Please do not edit these directly!
31+
> !# @qna.pair.source = crosstrained
32+
3133
# ? book a hotel for me
3234
- book a flight for me
3335
- book a train ticket for me

packages/luis/test/fixtures/verified/interruption/dia1.fr-fr.qna

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ ha ha ha
88
```
99

1010
> Source: cross training. Please do not edit these directly!
11+
> !# @qna.pair.source = crosstrained
12+
1113
# ? J'ai besoin d'un hôtel quatre étoiles
1214
- puis-je réserver un hôtel près de l'aiguille spatiale
1315
- guide de l'utilisateur

packages/luis/test/fixtures/verified/interruption/dia1.qna

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ I can help book flight, book hotel and book train ticket
5050
```
5151

5252
> Source: cross training. Please do not edit these directly!
53+
> !# @qna.pair.source = crosstrained
54+
5355
# ? I need a four star hotel
5456
- book a flight for me
5557
- book a train ticket for me

packages/luis/test/fixtures/verified/interruption/dia2.qna

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ ha ha ha
1717
```
1818

1919
> Source: cross training. Please do not edit these directly!
20+
> !# @qna.pair.source = crosstrained
21+
2022
# ? book a flight from Seattle to Beijing
2123
- book a train ticket from Seattle to Portland
2224
- book a hotel for me

packages/luis/test/fixtures/verified/interruption/dia3.qna

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11

22
> Source: cross training. Please do not edit these directly!
3+
> !# @qna.pair.source = crosstrained
4+
35
# ? book a flight to Beijing
46
- book a train ticket from Seattle to Portland
57
- book a hotel for me

packages/luis/test/fixtures/verified/interruption/main.fr-fr.qna

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ Voici le [guide de l'utilisateur] (http://contoso.com/userguide.pdf)
99
```
1010

1111
> Source: cross training. Please do not edit these directly!
12+
> !# @qna.pair.source = crosstrained
13+
1214
# ? réserver un hôtel
1315

1416
**Filters:**

0 commit comments

Comments
 (0)