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

Commit 4f7c793

Browse files
feich-msboydc2014
andauthored
[bf-lu] fix section operator issues and adjust file and url import interface (#965)
* fix some bugs and add tests * fix tsc issue * fix whitespace issue in import section * support multi turn extraction Co-authored-by: Dong Lei <[email protected]>
1 parent c8afdcc commit 4f7c793

File tree

9 files changed

+455
-285
lines changed

9 files changed

+455
-285
lines changed

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,8 @@ export class Builder {
229229
// otherwise create a new application
230230
if (recognizer.getAppId() && recognizer.getAppId() !== '') {
231231
// To see if need update the model
232-
needTrainAndPublish = await this.updateApplication(currentApp, luBuildCore, recognizer, timeBucket, keptVersionCount ?? maxVersionCount)
232+
const versionCount = keptVersionCount && keptVersionCount > 0 ? keptVersionCount : maxVersionCount
233+
needTrainAndPublish = await this.updateApplication(currentApp, luBuildCore, recognizer, timeBucket, versionCount)
233234
} else {
234235
// create a new application
235236
needTrainAndPublish = await this.createApplication(currentApp, luBuildCore, recognizer, timeBucket)

packages/lu/src/parser/lufile/LUFileLexer.g4

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,11 @@ AT
7878
;
7979

8080
IMPORT
81-
: '[' ~[\r\n[\]]*? ']' ('(' ~[\r\n()]*? ')' | '[' ~[\r\n[\]]*? ']')
81+
: WS* '[' ~[\r\n[\]]*? ']' WS* ('(' ~[\r\n()]*? ')' | '[' ~[\r\n[\]]*? ']')
8282
;
8383

8484
REFERENCE
85-
: '[' ~[\r\n[\]]*? ']' WS* ':' WS* ~[\r\n]*
85+
: WS* '[' ~[\r\n[\]]*? ']' WS* ':' WS* ~[\r\n]*
8686
;
8787

8888
FILTER_MARK

packages/lu/src/parser/lufile/generated/LUFileLexer.interp

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

packages/lu/src/parser/lufile/generated/LUFileLexer.js

Lines changed: 273 additions & 260 deletions
Large diffs are not rendered by default.

packages/lu/src/parser/lufile/importSection.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class ImportSection extends BaseSection {
2626
ExtractDescriptionAndPath(parseTree) {
2727
let importPathRegex = new RegExp(/\[([^\]]*)\]\(([^\)]*)\)/);
2828
let importReferenceRegex = new RegExp(/\[([^\]]*)\]\[([^\]]*)\]/);
29-
let importStr = parseTree.importDefinition().IMPORT().getText();
29+
let importStr = parseTree.importDefinition().IMPORT().getText().trim();
3030
let description;
3131
let path;
3232
let groups = importStr.match(importPathRegex);

packages/lu/src/parser/lufile/referenceSection.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class ReferenceSection extends BaseSection {
2525

2626
ExtractIdAndPath(parseTree) {
2727
let referenceRegex = new RegExp(/\[([^\]]*)\]\s*:\s*([^\)]*)/);
28-
let referenceStr = parseTree.referenceDefinition().REFERENCE().getText();
28+
let referenceStr = parseTree.referenceDefinition().REFERENCE().getText().trim();
2929

3030
let referenceId
3131
let path

packages/lu/src/parser/lufile/sectionOperator.js

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ class SectionOperator {
6666
this.adjustRangeForErrors(this.Luresource.Errors, newLineRange - originalRange, endLine);
6767

6868
// adjust updated sections' errors
69-
const offset = oldSection.Range.Start.Line - newResource.Sections[0].Range.Start.Line;
69+
const offset = oldSection.Range.Start.Line - 1;
7070
this.adjustRangeForErrors(newResource.Errors, offset);
7171
this.Luresource.Errors.push(...newResource.Errors);
7272

@@ -80,7 +80,7 @@ class SectionOperator {
8080
deleteSection(id) {
8181
const sectionIndex = this.Luresource.Sections.findIndex(u => u.Id === id);
8282
if (sectionIndex < 0) {
83-
return this;
83+
return this.Luresource;
8484
}
8585

8686
const oldSection = this.Luresource.Sections[sectionIndex];
@@ -122,7 +122,7 @@ class SectionOperator {
122122

123123
// adjust original errors
124124
const newLineRange = sectionContent.split(/\r?\n/).length;
125-
const startLine = sectionIndex <= 0 ? 1 : this.Luresource.Sections[sectionIndex].Range.Start.Line;
125+
const startLine = sectionIndex < 0 ? 1 : this.Luresource.Sections[sectionIndex].Range.Start.Line;
126126
this.adjustRangeForErrors(this.Luresource.Errors, newLineRange, startLine);
127127

128128
// adjust the insert errors of section
@@ -208,20 +208,19 @@ class SectionOperator {
208208
const sectionsSize = newSections.length;
209209
const oldStartLine = this.Luresource.Sections[oldIndex].Range.Start.Line;
210210
const oldEndLine = this.Luresource.Sections[oldIndex].Range.End.Line;
211-
const newStartLine = newSections[0].Range.Start.Line;
212211
const newEndLine = newSections[newSections.length - 1].Range.End.Line;
213212

214213
this.Luresource.Sections.splice(oldIndex, 1, ...newSections);
215214

216215
// adjust updated sections' range
217-
const updateOffset = oldStartLine - this.Luresource.Sections[oldIndex].Range.Start.Line;
216+
const updateOffset = oldStartLine - 1;
218217
for (let i = oldIndex; i < oldIndex + sectionsSize; i++) {
219218
const section = this.Luresource.Sections[i];
220219
this.adjustSectionRange(section, updateOffset);
221220
}
222221

223222
// adjust remaining sections' range
224-
const remainingOffset = (newEndLine - newStartLine) - (oldEndLine - oldStartLine);
223+
const remainingOffset = (newEndLine - 1) - (oldEndLine - oldStartLine);
225224
for (let i = oldIndex + sectionsSize; i < this.Luresource.Sections.length; i++) {
226225
const section = this.Luresource.Sections[i];
227226
this.adjustSectionRange(section, remainingOffset);
@@ -230,8 +229,7 @@ class SectionOperator {
230229

231230
adjustRangeForInsertSection(postIndex, newSections) {
232231
const sectionsSize = newSections.length;
233-
const insertOffset = postIndex <= 0 ? 0 : this.Luresource.Sections[postIndex].Range.Start.Line - 1;
234-
const newStartLine = newSections[0].Range.Start.Line;
232+
const insertOffset = postIndex < 0 ? 0 : this.Luresource.Sections[postIndex].Range.Start.Line - 1;
235233
const newEndLine = newSections[newSections.length - 1].Range.End.Line;
236234

237235
this.Luresource.Sections.splice(postIndex, 0, ...newSections);
@@ -243,7 +241,7 @@ class SectionOperator {
243241
}
244242

245243
// adjust remaining sections' range
246-
const remainingOffset = newEndLine - newStartLine + 1;
244+
const remainingOffset = newEndLine;
247245
for (let i = postIndex + sectionsSize; i < this.Luresource.Sections.length; i++) {
248246
const section = this.Luresource.Sections[i];
249247
this.adjustSectionRange(section, remainingOffset);

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

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,9 @@ export class Builder {
300300
url: string,
301301
subscriptionkey: string,
302302
endpoint: string,
303-
kbName: string) {
303+
kbName: string,
304+
enableHierarchicalExtraction: boolean = false,
305+
defaultAnswerUsedForExtraction: string = 'More Answers') {
304306
const qnaBuildCore = new QnaBuildCore(subscriptionkey, endpoint)
305307
const kbs = (await qnaBuildCore.getKBList()).knowledgebases
306308

@@ -319,7 +321,7 @@ export class Builder {
319321
}
320322

321323
// create a new kb
322-
kbId = await this.createUrlKB(qnaBuildCore, url, kbName)
324+
kbId = await this.createUrlKB(qnaBuildCore, url, kbName, enableHierarchicalExtraction, defaultAnswerUsedForExtraction)
323325

324326
const kbJson = await qnaBuildCore.exportKB(kbId, 'Test')
325327
const kb = new KB(kbJson)
@@ -334,7 +336,9 @@ export class Builder {
334336
fileUri: string,
335337
subscriptionkey: string,
336338
endpoint: string,
337-
kbName: string) {
339+
kbName: string,
340+
enableHierarchicalExtraction: boolean = false,
341+
defaultAnswerUsedForExtraction: string = 'More Answers') {
338342
const qnaBuildCore = new QnaBuildCore(subscriptionkey, endpoint)
339343
const kbs = (await qnaBuildCore.getKBList()).knowledgebases
340344

@@ -353,7 +357,7 @@ export class Builder {
353357
}
354358

355359
// create a new kb
356-
kbId = await this.createFileKB(qnaBuildCore, fileName, fileUri, kbName)
360+
kbId = await this.createFileKB(qnaBuildCore, fileName, fileUri, kbName, enableHierarchicalExtraction, defaultAnswerUsedForExtraction)
357361

358362
const kbJson = await qnaBuildCore.exportKB(kbId, 'Test')
359363
const kb = new KB(kbJson)
@@ -481,12 +485,17 @@ export class Builder {
481485
return true
482486
}
483487

484-
async createUrlKB(qnaBuildCore: QnaBuildCore, url: string, kbName: string) {
485-
const kbJson = {
488+
async createUrlKB(qnaBuildCore: QnaBuildCore, url: string, kbName: string, enableHierarchicalExtraction: boolean, defaultAnswerUsedForExtraction: string) {
489+
let kbJson: any = {
486490
name: kbName,
487491
qnaList: [],
488492
urls: [url],
489-
files: []
493+
files: [],
494+
}
495+
496+
if (enableHierarchicalExtraction) {
497+
kbJson.enableHierarchicalExtraction = true
498+
kbJson.defaultAnswerUsedForExtraction = defaultAnswerUsedForExtraction
490499
}
491500

492501
let response = await qnaBuildCore.importKB(kbJson)
@@ -497,8 +506,8 @@ export class Builder {
497506
return kbId
498507
}
499508

500-
async createFileKB(qnaBuildCore: QnaBuildCore, fileName: string, fileUri: string, kbName: string) {
501-
let kbJson = {
509+
async createFileKB(qnaBuildCore: QnaBuildCore, fileName: string, fileUri: string, kbName: string, enableHierarchicalExtraction: boolean, defaultAnswerUsedForExtraction: string) {
510+
let kbJson: any = {
502511
name: kbName,
503512
qnaList: [],
504513
urls: [],
@@ -508,6 +517,11 @@ export class Builder {
508517
}]
509518
}
510519

520+
if (enableHierarchicalExtraction) {
521+
kbJson.enableHierarchicalExtraction = true
522+
kbJson.defaultAnswerUsedForExtraction = defaultAnswerUsedForExtraction
523+
}
524+
511525
let response = await qnaBuildCore.importKB(kbJson)
512526
let operationId = response.operationId
513527
const opResult = await this.getKBOperationStatus(qnaBuildCore, operationId, 1000)

packages/lu/test/parser/lufile/sectionapi.test.js

Lines changed: 145 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -497,4 +497,148 @@ describe('Section range tests', () => {
497497
assert.equal(luresource.Sections[1].Range.End.Line, 11)
498498
assert.equal(luresource.Sections[1].Range.End.Character, 7)
499499
});
500-
})
500+
})
501+
502+
describe('Section CRUD tests for insert and update sections with newline', () => {
503+
let luresource = undefined;
504+
505+
let fileContent =
506+
`# ? who is CEO of Microsoft
507+
- Microsoft CEO
508+
509+
\`\`\`
510+
Satya Nadella
511+
\`\`\``;
512+
513+
let updatedQnAConent =
514+
`# ? who is CEO of Facebook
515+
- Facebook CEO
516+
517+
\`\`\`
518+
Mark Zuckerberg
519+
\`\`\``;
520+
521+
let insertQnAContent =
522+
`# ? how to greet
523+
524+
\`\`\`
525+
hello
526+
\`\`\``
527+
528+
let insertQnAContent2 =
529+
`# ? how to cancel
530+
531+
\`\`\`
532+
cancel that
533+
\`\`\``
534+
535+
let insertLuContent =
536+
`# welcome
537+
- welcome here`
538+
539+
let insertLuContent2 =
540+
`# stop
541+
- stop that`
542+
543+
it('update qna section test', () => {
544+
luresource = luparser.parse(fileContent);
545+
546+
luresource = new SectionOperator(luresource).updateSection(luresource.Sections[0].Id, `${NEWLINE}${updatedQnAConent}${NEWLINE}`);
547+
548+
assert.equal(luresource.Errors.length, 0);
549+
assert.equal(luresource.Sections.length, 1);
550+
assert.equal(luresource.Sections[0].SectionType, LUSectionTypes.QNASECTION);
551+
assert.equal(luresource.Sections[0].Body.replace(/\r\n/g, "\n"), `${updatedQnAConent}\n`);
552+
});
553+
554+
it('insert qna section at begining test', () => {
555+
luresource = new SectionOperator(luresource).insertSection(luresource.Sections[0].Id, `${NEWLINE}${insertQnAContent}`);
556+
557+
assert.equal(luresource.Errors.length, 0);
558+
assert.equal(luresource.Sections.length, 2);
559+
assert.equal(luresource.Sections[0].SectionType, LUSectionTypes.QNASECTION);
560+
assert.equal(luresource.Sections[1].SectionType, LUSectionTypes.QNASECTION);
561+
assert.equal(luresource.Sections[0].Body.replace(/\r\n/g, "\n"), `${insertQnAContent}`);
562+
assert.equal(luresource.Sections[1].Body.replace(/\r\n/g, "\n"), `${updatedQnAConent}\n`);
563+
});
564+
565+
it('insert qna section at middle test', () => {
566+
luresource = new SectionOperator(luresource).insertSection(luresource.Sections[1].Id, `${NEWLINE}${NEWLINE}${insertQnAContent2}${NEWLINE}`);
567+
568+
assert.equal(luresource.Errors.length, 0);
569+
assert.equal(luresource.Sections.length, 3);
570+
assert.equal(luresource.Sections[0].SectionType, LUSectionTypes.QNASECTION);
571+
assert.equal(luresource.Sections[1].SectionType, LUSectionTypes.QNASECTION);
572+
assert.equal(luresource.Sections[2].SectionType, LUSectionTypes.QNASECTION);
573+
assert.equal(luresource.Sections[0].Body.replace(/\r\n/g, "\n"), `${insertQnAContent}\n\n`);
574+
assert.equal(luresource.Sections[1].Body.replace(/\r\n/g, "\n"), `${insertQnAContent2}\n`);
575+
assert.equal(luresource.Sections[2].Body.replace(/\r\n/g, "\n"), `${updatedQnAConent}\n`);
576+
assert.equal(luresource.Content.replace(/\r\n/g, "\n"), `\n\n${insertQnAContent}\n\n\n${insertQnAContent2}\n\n${updatedQnAConent}\n`);
577+
});
578+
579+
it('insert lu section at begining test', () => {
580+
luresource = new SectionOperator(luresource).insertSection(luresource.Sections[0].Id, `${NEWLINE}${insertLuContent}${NEWLINE}${NEWLINE}`);
581+
582+
assert.equal(luresource.Errors.length, 0);
583+
assert.equal(luresource.Sections.length, 4);
584+
assert.equal(luresource.Sections[0].SectionType, LUSectionTypes.SIMPLEINTENTSECTION);
585+
assert.equal(`# welcome\n${luresource.Sections[0].Body.replace(/\r\n/g, "\n")}`, `${insertLuContent}\n\n`);
586+
});
587+
588+
it('update qna section with lu section test', () => {
589+
luresource = new SectionOperator(luresource).updateSection(luresource.Sections[3].Id, `${NEWLINE}${insertLuContent2}${NEWLINE}`);
590+
591+
assert.equal(luresource.Errors.length, 0);
592+
assert.equal(luresource.Sections.length, 4);
593+
assert.equal(luresource.Sections[0].SectionType, LUSectionTypes.SIMPLEINTENTSECTION);
594+
assert.equal(luresource.Sections[1].SectionType, LUSectionTypes.QNASECTION);
595+
assert.equal(luresource.Sections[2].SectionType, LUSectionTypes.QNASECTION);
596+
assert.equal(luresource.Sections[3].SectionType, LUSectionTypes.SIMPLEINTENTSECTION);
597+
assert.equal(`# welcome\n${luresource.Sections[0].Body.replace(/\r\n/g, "\n")}`, `${insertLuContent}\n\n`);
598+
assert.equal(luresource.Sections[1].Body.replace(/\r\n/g, "\n"), `${insertQnAContent}\n\n`);
599+
assert.equal(luresource.Sections[2].Body.replace(/\r\n/g, "\n"), `${insertQnAContent2}\n\n`);
600+
assert.equal(`# stop\n${luresource.Sections[3].Body.replace(/\r\n/g, "\n")}`, `${insertLuContent2}\n`);
601+
assert.equal(luresource.Content.replace(/\r\n/g, "\n"), `\n\n\n${insertLuContent}\n\n\n${insertQnAContent}\n\n\n${insertQnAContent2}\n\n\n${insertLuContent2}\n`);
602+
});
603+
});
604+
605+
describe('Section CRUD tests for import section', () => {
606+
let luresource = undefined;
607+
608+
let importContent1 = `[import](howto.source.qna)`;
609+
let importContent2 = ` [import](guide.source.qna)`;
610+
611+
let qnaContent =
612+
`# ? who is CEO of Microsoft
613+
- Microsoft CEO
614+
615+
\`\`\`
616+
Satya Nadella
617+
\`\`\``;
618+
619+
let insertImportConent = ` [import](windows.source.qna)`;
620+
621+
it('insert qna section test', () => {
622+
luresource = luparser.parse(`${importContent1}${NEWLINE}${importContent2}${NEWLINE}${NEWLINE}${qnaContent}`);
623+
luresource = new SectionOperator(luresource).insertSection(luresource.Sections[2].Id, `${NEWLINE}${insertImportConent}`);
624+
625+
assert.equal(luresource.Errors.length, 0);
626+
assert.equal(luresource.Sections.length, 4);
627+
assert.equal(luresource.Sections[0].SectionType, LUSectionTypes.IMPORTSECTION);
628+
assert.equal(luresource.Sections[1].SectionType, LUSectionTypes.IMPORTSECTION);
629+
assert.equal(luresource.Sections[2].SectionType, LUSectionTypes.IMPORTSECTION);
630+
assert.equal(luresource.Sections[3].SectionType, LUSectionTypes.QNASECTION);
631+
assert.equal(luresource.Content.replace(/\r\n/g, "\n"), `${importContent1}\n${importContent2}\n\n\n${insertImportConent}\n${qnaContent}`);
632+
});
633+
634+
it('delete qna section test', () => {
635+
luresource = new SectionOperator(luresource).deleteSection(luresource.Sections[1].Id);
636+
637+
assert.equal(luresource.Errors.length, 0);
638+
assert.equal(luresource.Sections.length, 3);
639+
assert.equal(luresource.Sections[0].SectionType, LUSectionTypes.IMPORTSECTION);
640+
assert.equal(luresource.Sections[1].SectionType, LUSectionTypes.IMPORTSECTION);
641+
assert.equal(luresource.Sections[2].SectionType, LUSectionTypes.QNASECTION);
642+
assert.equal(luresource.Content.replace(/\r\n/g, "\n"), `${importContent1}\n\n\n${insertImportConent}\n${qnaContent}`);
643+
});
644+
});

0 commit comments

Comments
 (0)