Skip to content
This repository was archived by the owner on Jan 15, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
126 commits
Select commit Hold shift + click to select a range
d1dc10d
add qna cross training
feich-ms Jan 6, 2020
3242373
Revert "remove cross-train cli related changes and only keep the api"
feich-ms Jan 6, 2020
721245f
call qnaCrossTrain
feich-ms Jan 7, 2020
dbc0ec2
Merge branch 'master' into feich/QnACrossTrain
feich-ms Jan 7, 2020
12a6c6a
add qna cross traini
feich-ms Jan 8, 2020
909a853
expose function
feich-ms Jan 8, 2020
306a557
optimize and add more corner test cases
feich-ms Jan 8, 2020
da83a88
Merge branch 'master' into feich/QnACrossTrain
feich-ms Jan 8, 2020
d23892f
rename crossTrainer class
feich-ms Jan 8, 2020
9aac639
remove cli code pieces and optimize tests
feich-ms Jan 8, 2020
1d04faf
move cross-train.ts to lu folder
feich-ms Jan 9, 2020
368be78
Merge branch 'master' into feich/QnACrossTrain
feich-ms Jan 13, 2020
063dabf
Merge branch 'master' into feich/QnACrossTrain
feich-ms Jan 13, 2020
9ce4473
Merge branch 'feich/QnACrossTrain' of https://github.com/microsoft/bo…
feich-ms Jan 13, 2020
2bb3df7
optimize function headers and naming
feich-ms Jan 13, 2020
3650b57
typo: dialogname to dialogName
feich-ms Jan 13, 2020
8e9ef5c
add more test cases
feich-ms Jan 13, 2020
9c7f472
Merge branch 'master' into feich/QnACrossTrain
feich-ms Jan 15, 2020
ec49691
remove unused '
feich-ms Jan 17, 2020
20acac9
add cross-train.ts cli file for test convenience and will remove it o…
feich-ms Jan 17, 2020
b8b4a6f
Merge branch 'master' into feich/QnACrossTrain
feich-ms Jan 17, 2020
ab8eaf7
fix bugs
feich-ms Jan 17, 2020
5746690
Merge branch 'master' into feich/QnACrossTrain
feich-ms Jan 20, 2020
5a9e559
trigger ci
feich-ms Jan 20, 2020
d27e7cb
Merge branch 'master' into feich/QnACrossTrain
feich-ms Feb 11, 2020
d975278
update qna cross train based on feedbacks
feich-ms Feb 12, 2020
b100a36
Merge branch 'master' into feich/QnACrossTrain
feich-ms Feb 12, 2020
7f4901a
add config path parameter to specify the config file
feich-ms Feb 12, 2020
3d7bb4b
fix posttest failure
feich-ms Feb 12, 2020
6e31cdd
remove dup utterances in interuption intents
feich-ms Feb 13, 2020
f2a4ff1
Merge branch 'master' into feich/QnACrossTrain
feich-ms Feb 13, 2020
df4fdd6
Merge branch 'master' into feich/QnACrossTrain
feich-ms Feb 14, 2020
3bcb68c
remove dup 1
feich-ms Feb 14, 2020
e7af357
Merge branch 'master' into feich/QnACrossTrain
feich-ms Feb 17, 2020
d2b0736
optimize all de-dup logic
feich-ms Feb 17, 2020
bb4160c
resolve all feedbacks
feich-ms Feb 18, 2020
125d01d
add test cases to test dedup functions
feich-ms Feb 18, 2020
ccdd91f
fix tslint errors
feich-ms Feb 18, 2020
52cbf82
add more friendly description for config CLI parameter
feich-ms Feb 18, 2020
857a114
fix config full path issue and remove uncessary loop validation
feich-ms Feb 19, 2020
01e592a
Merge branch 'master' into feich/QnACrossTrain
feich-ms Feb 20, 2020
64674c2
move cross train CLI to a new package named cross-train
feich-ms Feb 20, 2020
d3db087
fix build error in CI validation
feich-ms Feb 20, 2020
1a1ddb1
add missing package in package.json
feich-ms Feb 20, 2020
9fa064a
remove unused file
feich-ms Feb 20, 2020
59bb856
fix typo
feich-ms Feb 20, 2020
700195d
optimize dedep logic and resolve feedbacks
feich-ms Feb 21, 2020
8bd1442
adjust test cases
feich-ms Feb 21, 2020
9e83d00
Merge branch 'master' into feich/QnACrossTrain
feich-ms Feb 21, 2020
c7d54f2
support qna source, id, prompts reconstruction in cross training
feich-ms Feb 21, 2020
57c5a53
Merge branch 'master' into feich/QnACrossTrain
feich-ms Feb 24, 2020
19d29bb
typo: interuption to interruption
feich-ms Feb 24, 2020
c5637d9
Merge branch 'master' into feich/QnACrossTrain
feich-ms Feb 25, 2020
c055a39
move CLI test cases from lu to cross-train
feich-ms Feb 28, 2020
7087f12
Merge branch 'master' into cross-train
feich-ms Feb 28, 2020
a8f386f
adjust test cases of cross-train CLI to make it more readable
feich-ms Feb 28, 2020
a4b730e
merge master
feich-ms Mar 2, 2020
96d5e1e
Merge branch 'master' into cross-train
feich-ms Mar 3, 2020
76505ee
update some function to support load file content
lei9444 Mar 3, 2020
cf53d2a
support auto detect config based on root dialog and file system
feich-ms Mar 3, 2020
b56287d
update some code style
lei9444 Mar 3, 2020
9e45259
update some ref
lei9444 Mar 4, 2020
49eec25
optimize config parameter
feich-ms Mar 4, 2020
cebb32d
Merge branch 'leilzh/test' of https://github.com/microsoft/botframewo…
feich-ms Mar 4, 2020
c647f8c
add fileHelper.ts
feich-ms Mar 4, 2020
8bf0c44
fix typo
feich-ms Mar 4, 2020
d6c089c
Merge branch 'master' into leilzh/test
feich-ms Mar 4, 2020
744bdee
remove semicolon
feich-ms Mar 4, 2020
90c72e0
Merge branch 'leilzh/test' of https://github.com/microsoft/botframewo…
feich-ms Mar 4, 2020
a44db2d
add the object
lei9444 Mar 4, 2020
bbcafb4
change the value
lei9444 Mar 4, 2020
642d34d
make all the path the same parttern
lei9444 Mar 4, 2020
66ecf8d
optimize
feich-ms Mar 5, 2020
5779125
Merge branch 'master' into leilzh/test
feich-ms Mar 5, 2020
b7c0250
adjust test cases to use file name as id
feich-ms Mar 5, 2020
9ab76d4
fix typo
feich-ms Mar 5, 2020
c4db4c6
update fileHelper.ts
feich-ms Mar 5, 2020
f3f1a3a
Merge branch 'leilzh/test' into cross-train
feich-ms Mar 5, 2020
8346dd9
fix config id issue
feich-ms Mar 5, 2020
299a3ef
Merge branch 'master' into cross-train
feich-ms Mar 5, 2020
17df2e6
Merge branch 'master' into cross-train
feich-ms Mar 26, 2020
c372659
update pnpm lock
feich-ms Mar 26, 2020
6b2a4d4
Merge branch 'master' into cross-train
feich-ms Mar 26, 2020
4b0928b
update pnpm
feich-ms Mar 26, 2020
89f4a7e
fix test cases
feich-ms Mar 26, 2020
74e0d39
fix tslint
feich-ms Mar 26, 2020
121bbdc
Merge branch 'master' into cross-train
vishwacsena Mar 27, 2020
00ad88b
Merge branch 'master' into cross-train
feich-ms Apr 8, 2020
fdabc4d
optimzie cross train to care only trigger intents and allow empty tri…
feich-ms Apr 8, 2020
5f476b5
Merge branch 'feich/crosstrainOptimization' into cross-train
feich-ms Apr 8, 2020
939cb80
merge PR 706 and adjust test cases to cover more corner cases
feich-ms Apr 8, 2020
c2eb251
support multi trigger intents point to same lu file
feich-ms Apr 16, 2020
8738907
optimize config structure
feich-ms Apr 17, 2020
a31bf7e
optimize config object parser
feich-ms Apr 17, 2020
43aa78c
Merge branch 'feich/fixMultiTriggersToSingleLuFile' into cross-train
feich-ms Apr 17, 2020
72fcde0
fix bug
feich-ms Apr 17, 2020
8f6fd6c
Merge branch 'feich/fixMultiTriggersToSingleLuFile' into cross-train
feich-ms Apr 17, 2020
b7017b5
adjust pnpm
feich-ms Apr 17, 2020
91e098c
fix minor typo
feich-ms Apr 17, 2020
bf399f0
Merge branch 'feich/fixMultiTriggersToSingleLuFile' into cross-train
feich-ms Apr 17, 2020
ff62a0e
Merge branch 'master' into cross-train
vishwacsena Apr 23, 2020
a2b5816
Merge branch 'master' into cross-train
vishwacsena Apr 24, 2020
84409e6
Merge branch 'master' into cross-train
vishwacsena Apr 24, 2020
a98f029
docs.
Apr 24, 2020
635d36c
Merge branch 'cross-train' of https://github.com/microsoft/botframewo…
Apr 24, 2020
2a7bd4a
Merge branch 'master' into cross-train
vishwacsena Apr 24, 2020
4439ef6
Merge branch 'master' into cross-train
vishwacsena Apr 25, 2020
381ebf1
Merge branch 'master' into cross-train
feich-ms Apr 26, 2020
f130883
remove cross-train from top level command and add it to luis command
feich-ms Apr 27, 2020
3b6fd2a
fix test
feich-ms Apr 27, 2020
d7048a6
Merge branch 'master' into cross-train
feich-ms Apr 27, 2020
68ae4f1
add cross train in qna maker cli
feich-ms Apr 27, 2020
ecd79f0
Merge branch 'master' into cross-train
vishwacsena Apr 27, 2020
fd5719e
Merge branch 'master' into cross-train
vishwacsena Apr 27, 2020
80d22ae
Merge branch 'master' into cross-train
feich-ms Apr 28, 2020
4aa9b08
Merge branch 'cross-train' of https://github.com/microsoft/botframewo…
feich-ms Apr 28, 2020
99467fd
support to write corsstrained recognizer
feich-ms Apr 28, 2020
be35fad
Merge branch 'master' into cross-train
feich-ms Apr 28, 2020
e778b42
support crosstrained recognizer in qnamaker
feich-ms Apr 28, 2020
0ee4a1f
refine the write dialog logic to support crosstrained dialog
feich-ms Apr 29, 2020
660a3c5
update docs
feich-ms Apr 29, 2020
df6faab
fix crosstrained recognizer configuration issue
feich-ms Apr 30, 2020
a0b5b1e
remove patterns from lu in crosstrained qna queations
feich-ms Apr 30, 2020
7825540
fix test case
feich-ms Apr 30, 2020
e7b6bcc
fix tslint
feich-ms Apr 30, 2020
599796c
fix comments of reviewer
feich-ms May 1, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,749 changes: 1,004 additions & 745 deletions common/config/rush/pnpm-lock.yaml

Large diffs are not rendered by default.

108 changes: 108 additions & 0 deletions packages/lu/src/parser/cross-train/confighelper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*!
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/

const fs = require('fs-extra')
const path = require('path')
const exception = require('../utils/exception')
const retCode = require('../utils/enums/CLI-errors')
const fileHelper = require('../../utils/filehelper')

const dialogExt = '.dialog'
const luExt = '.lu'

module.exports = {
generateConfig: async function (inputFolder, rootDialogFile) {
let dialogFiles = []
await getDialogFiles(inputFolder, dialogFiles)

let rootDialogObject = JSON.parse(await getInputFromFile(rootDialogFile))
rootDialogObject.path = rootDialogFile
rootDialogObject.isRoot = true

let dialogObjects = []
for (const dialogFile of dialogFiles) {
let dialogObject = JSON.parse(await getInputFromFile(dialogFile))
dialogObject.path = dialogFile
dialogObjects.push(dialogObject)
}

const configObject = createConfig(rootDialogObject, dialogObjects, inputFolder)

return JSON.stringify(configObject)
}
}

const getDialogFiles = async function (inputFolder, results) {
fs.readdirSync(inputFolder).forEach(async dirContent => {
dirContent = path.resolve(inputFolder, dirContent)
if (fs.statSync(dirContent).isDirectory()) {
await getDialogFiles(dirContent, results)
}

if (fs.statSync(dirContent).isFile()) {
if (dirContent.endsWith(dialogExt)) {
results.push(dirContent)
}
}
})
}

const getInputFromFile = async function (path) {
if (path) {
try {
return await fileHelper.getContentFromFile(path)
} catch (error) {
throw (new exception(retCode.errorCode.INVALID_INPUT, `Failed to read file: ${error}`))
}
}
return ''
}

const createConfig = function (rootDialog, dialogs, configPath) {
let result = {}

const key = createPath(rootDialog.path, configPath)
const rootLuPath = rootDialog.path.replace(dialogExt, luExt)

if (!fs.existsSync(rootLuPath)) {
throw (new exception(retCode.errorCode.INVALID_INPUT, `Failed to parse mapping rules config from file system: ${rootLuPath} does not exist. Please provide config file by --config`))
}

rootDialog.triggers.forEach(trigger => {
if (trigger.$type && trigger.$type === 'Microsoft.OnIntent') {
const actions = trigger.actions || []
for (const action of actions) {
if (action.$type !== 'Microsoft.BeginDialog') continue

const dialogName = action.dialog
const target = dialogs.find(dialog => path.basename(dialog.path, dialogExt) === dialogName)

if (!target) continue

const relativePath = createPath(target.path, configPath)
if (!result[key]) result[key] = { triggers: {} }
if (!result[key].triggers[trigger.intent]) {
result[key].triggers[trigger.intent] = relativePath
} else if (typeof result[key].triggers[trigger.intent] === 'string') {
result[key].triggers[trigger.intent] = [result[key].triggers[trigger.intent], relativePath]
} else {
result[key].triggers[trigger.intent].push(relativePath)
}

result = { ...result, ...createConfig(target, dialogs, configPath) }
}
}
})

if (rootDialog.isRoot && result[key]) result[key].rootDialog = true

return result
}

const createPath = function (dialogPath, configPath) {
const luFilePath = dialogPath.replace('.dialog', '.lu')
const relativePath = path.relative(configPath, luFilePath)
return relativePath
}
15 changes: 14 additions & 1 deletion packages/lu/src/parser/cross-train/cross-train.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,24 @@ const fileExtEnum = require('../utils/helpers').FileExtTypeEnum
const exception = require('../utils/exception')
const retCode = require('../utils/enums/CLI-errors')
const crossTrainer = require('./crossTrainer')
const confighelper = require('./confighelper')

module.exports = {
/**
* Generate cross train config based on input folder and root dialog file.
* @param {string} inputFolder full path of input lu and qna files folder.
* @param {string} rootDialogFile full path of root dialog file.
* @returns {string} config object json string.
*/
generateConfig: async function (inputFolder, rootDialogFile) {
const configStr = await confighelper.generateConfig(inputFolder, rootDialogFile)

return configStr
},

/**
* Cross train lu and qna files.
* @param {string} input input lu and qna files folder.
* @param {string} input full path of input lu and qna files folder.
* @param {string} intentName interruption intent name. Default value is _Interruption.
* @param {string} config path to config of mapping rules or mapping rules json content itself. If undefined, it will read config.json from input folder.
* @returns {luResult: any, qnaResult: any} trainedResult of luResult and qnaResult or undefined if no results.
Expand Down
13 changes: 8 additions & 5 deletions packages/lu/src/parser/cross-train/crossTrainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -300,20 +300,21 @@ const removeDupUtterances = function (resource) {

const extractIntentUtterances = function(resource, intentName) {
const intentSections = resource.Sections.filter(s => s.SectionType === LUSectionTypes.SIMPLEINTENTSECTION || s.SectionType === LUSectionTypes.NESTEDINTENTSECTION)
const curlyRe = /.*\{.*\}.*/

let intentUtterances = []
if (intentName && intentName !== '') {
const specificSections = intentSections.filter(s => s.Name === intentName)
if (specificSections.length > 0) {
intentUtterances = intentUtterances.concat(specificSections[0].UtteranceAndEntitiesMap.map(u => u.utterance))
intentUtterances = intentUtterances.concat(specificSections[0].UtteranceAndEntitiesMap.map(u => u.utterance).filter(i => curlyRe.exec(i) === null))
}
} else {
intentSections.forEach(s => {
if (s.SectionType === LUSectionTypes.SIMPLEINTENTSECTION) {
intentUtterances = intentUtterances.concat(s.UtteranceAndEntitiesMap.map(u => u.utterance))
intentUtterances = intentUtterances.concat(s.UtteranceAndEntitiesMap.map(u => u.utterance).filter(i => curlyRe.exec(i) === null))
} else {
s.SimpleIntentSections.forEach(section => {
intentUtterances = intentUtterances.concat(section.UtteranceAndEntitiesMap.map(u => u.utterance))
intentUtterances = intentUtterances.concat(section.UtteranceAndEntitiesMap.map(u => u.utterance).filter(i => curlyRe.exec(i) === null))
})
}
})}
Expand Down Expand Up @@ -430,11 +431,13 @@ const qnaCrossTrainCore = function (luResource, qnaResource, fileName, interrupt
qnaSectionContents.push(qnaSectionContent)
}

const qnaContents = qnaSectionContents.join(NEWLINE + NEWLINE)
let qnaContents = qnaSectionContents.join(NEWLINE + NEWLINE)
if (qnaContents && qnaContents !== '') {
const modelInfoSections = qnaResource.Sections.filter(s => s.SectionType === LUSectionTypes.MODELINFOSECTION)
const modelInforContent = modelInfoSections.map(m => m.ModelInfo).join(NEWLINE)
trainedQnaResource = new SectionOperator(new LUResource([], modelInforContent, [])).addSection(NEWLINE + qnaContents)
if (modelInforContent && modelInforContent !== '') qnaContents = NEWLINE + qnaContents

trainedQnaResource = new SectionOperator(new LUResource([], modelInforContent, [])).addSection(qnaContents)
}

// remove utterances which are duplicated with local qna questions
Expand Down
31 changes: 28 additions & 3 deletions packages/lu/src/parser/lubuild/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {LuBuildCore} from './core'
import {Settings} from './settings'
import {MultiLanguageRecognizer} from './multi-language-recognizer'
import {Recognizer} from './recognizer'
import {CrossTrainedRecognizer} from './cross-trained-recognizer'
const path = require('path')
const fs = require('fs-extra')
const delay = require('delay')
Expand All @@ -18,6 +19,7 @@ const LuisBuilderVerbose = require('./../luis/luisCollate')
const LuisBuilder = require('./../luis/luisBuilder')
const LUOptions = require('./../lu/luOptions')
const Content = require('./../lu/lu')
const recognizerType = require('./../utils/enums/recognizertypes')

export class Builder {
private readonly handler: (input: string) => any
Expand Down Expand Up @@ -215,7 +217,7 @@ export class Builder {
return dialogContents
}

async writeDialogAssets(contents: any[], force: boolean, out: string, luconfig: string) {
async writeDialogAssets(contents: any[], force: boolean, out: string, dialogType: string, luconfig: string) {
let writeDone = false

let writeContents = contents.filter(c => c.id.endsWith('.dialog'))
Expand All @@ -242,7 +244,7 @@ export class Builder {
}

this.handler(`Writing to ${outFilePath}\n`)
await fs.writeFile(outFilePath, content.content, 'utf-8')
await this.writeDialog(content.content, outFilePath, dialogType)
writeDone = true
}
}
Expand All @@ -254,7 +256,7 @@ export class Builder {
}

this.handler(`Writing to ${content.path}\n`)
await fs.writeFile(content.path, content.content, 'utf-8')
await this.writeDialog(content.content, content.path, dialogType)
writeDone = true
}
}
Expand Down Expand Up @@ -404,4 +406,27 @@ export class Builder {
app.intents = filteredIntents
}
}

async writeDialog(content: string, filePath: string, dialogType: string) {
await fs.writeFile(filePath, content, 'utf-8')
const contentObj = JSON.parse(content)
if (dialogType === recognizerType.CROSSTRAINED && contentObj.$kind === 'Microsoft.MultiLanguageRecognizer') {
const fileName = path.basename(filePath, '.lu.dialog')
const crossTrainedFileName = fileName + '.lu.qna.dialog'
const crossTrainedFilePath = path.join(path.dirname(filePath), crossTrainedFileName)
if (fs.existsSync(crossTrainedFilePath)) {
const existingCRDialog = JSON.parse(await fileHelper.getContentFromFile(crossTrainedFilePath))
if (!existingCRDialog.recognizers.includes(fileName + '.lu')) {
existingCRDialog.recognizers.push(fileName + '.lu')
}

content = JSON.stringify(existingCRDialog, null, 4)
} else {
const recognizers = [fileName + '.lu']
content = new CrossTrainedRecognizer(crossTrainedFilePath, recognizers).save()
}

await fs.writeFile(crossTrainedFilePath, content, 'utf-8')
}
}
}
27 changes: 27 additions & 0 deletions packages/lu/src/parser/lubuild/cross-trained-recognizer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*!
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/

export class CrossTrainedRecognizer {
public recognizers: any
private readonly dialogPath: string

constructor(dialogPath: string, recognizers: any) {
this.dialogPath = dialogPath
this.recognizers = recognizers
}

save(): string {
let output = {
$kind: 'Microsoft.CrossTrainedRecognizerSet',
recognizers: this.recognizers
}

return JSON.stringify(output, null, 4)
}

getDialogPath(): string {
return this.dialogPath
}
}
65 changes: 49 additions & 16 deletions packages/lu/src/parser/qnabuild/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {QnaBuildCore} from './core'
import {Settings} from './settings'
import {MultiLanguageRecognizer} from './multi-language-recognizer'
import {Recognizer} from './recognizer'
import {CrossTrainedRecognizer} from './cross-trained-recognizer'
const path = require('path')
const fs = require('fs-extra')
const delay = require('delay')
Expand All @@ -20,6 +21,7 @@ const qnaOptions = require('./../lu/qnaOptions')
const Content = require('./../lu/qna')
const KB = require('./../qna/qnamaker/kb')
const NEWLINE = require('os').EOL
const recognizerType = require('./../utils/enums/recognizertypes')

export class Builder {
private readonly handler: (input: string) => any
Expand Down Expand Up @@ -285,25 +287,21 @@ export class Builder {
return kbToLuContent
}

async writeDialogAssets(contents: any[], force: boolean, out: string) {
async writeDialogAssets(contents: any[], force: boolean, out: string, dialogType: string, files: string[]) {
let writeDone = false

if (out) {
for (const content of contents) {
const outFilePath = path.join(path.resolve(out), path.basename(content.path))
if (force || !fs.existsSync(outFilePath)) {
this.handler(`Writing to ${outFilePath}\n`)
await fs.writeFile(outFilePath, content.content, 'utf-8')
writeDone = true
}
for (const content of contents) {
let outFilePath
if (out) {
outFilePath = path.join(path.resolve(out), path.basename(content.path))
} else {
outFilePath = content.path
}
} else {
for (const content of contents) {
if (force || !fs.existsSync(content.path)) {
this.handler(`Writing to ${content.path}\n`)
await fs.writeFile(content.path, content.content, 'utf-8')
writeDone = true
}

if (force || !fs.existsSync(outFilePath)) {
this.handler(`Writing to ${outFilePath}\n`)
await this.writeDialog(content.content, outFilePath, dialogType, files)
writeDone = true
}
}

Expand Down Expand Up @@ -467,4 +465,39 @@ export class Builder {
await qnaBuildCore.publishKB(recognizer.getKBId())
this.handler(`Publishing finished for kb ${kbName}\n`)
}

async writeDialog(content: string, filePath: string, dialogType: string, files: string[]) {
await fs.writeFile(filePath, content, 'utf-8')
const contentObj = JSON.parse(content)
if (dialogType === recognizerType.CROSSTRAINED && contentObj.$kind === 'Microsoft.MultiLanguageRecognizer') {
const fileName = path.basename(filePath, '.dialog')

for (const file of files) {
let qnafileName
let cultureFromPath = fileHelper.getCultureFromPath(file)
if (cultureFromPath) {
let fileNameWithCulture = path.basename(file, path.extname(file))
qnafileName = fileNameWithCulture.substring(0, fileNameWithCulture.length - cultureFromPath.length - 1)
} else {
qnafileName = path.basename(file, path.extname(file))
}

let crossTrainedFileName = `${qnafileName}.lu.qna.dialog`
let crossTrainedFilePath = path.join(path.dirname(filePath), crossTrainedFileName)
if (fs.existsSync(crossTrainedFilePath)) {
let existingCRDialog = JSON.parse(await fileHelper.getContentFromFile(crossTrainedFilePath))
if (!existingCRDialog.recognizers.includes(fileName)) {
existingCRDialog.recognizers.push(fileName)
}

content = JSON.stringify(existingCRDialog, null, 4)
} else {
let recognizers = [fileName]
content = new CrossTrainedRecognizer(crossTrainedFilePath, recognizers).save()
}

await fs.writeFile(crossTrainedFilePath, content, 'utf-8')
}
}
}
}
27 changes: 27 additions & 0 deletions packages/lu/src/parser/qnabuild/cross-trained-recognizer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*!
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/

export class CrossTrainedRecognizer {
public recognizers: any
private readonly dialogPath: string

constructor(dialogPath: string, recognizers: any) {
this.dialogPath = dialogPath
this.recognizers = recognizers
}

save(): string {
let output = {
$kind: 'Microsoft.CrossTrainedRecognizerSet',
recognizers: this.recognizers
}

return JSON.stringify(output, null, 4)
}

getDialogPath(): string {
return this.dialogPath
}
}
Loading