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

Commit ca36511

Browse files
munozemilioShuai Wangtsuwandy
authored
[Cherry-Pick] Orchestrator scenario for CI-CD (#1226)
* fix luis cross train unable to reference external files (#1181) * init * add path resovle * fix import path resolve issue * add tests * fix lint and unused imports * modify config name * retrigger * refactor Co-authored-by: Emilio Munoz <[email protected]> * fix luis:build write out version in luis setting when the directVersionPublish is not set (#1215) * init * fix tests * retrigger * remove redundent () * up versioned orchestrator-core to 4.13.1 (#1211) * Update package.json Use orchestrator-core v 4.13.1 * Update pnpm-lock.yaml * skip empty lu file, added empty lu file to test * Update orchestratorhelper.ts fixed lint issues * use forward slash for file path in orchestrator.settings.json to be compatible with non windows platform * addressed PR comments Co-authored-by: Emilio Munoz <[email protected]> * fix luis-cross train does not find imported files with locale (#1209) * fix import file with locale * simplify test * change folder structures * remove only * add cross train config * fix spelling * modify test * change naming * fix readme * fix typo Co-authored-by: Emilio Munoz <[email protected]> Co-authored-by: Shuai Wang <[email protected]> Co-authored-by: Tien Suwandy <[email protected]>
1 parent 71bced4 commit ca36511

35 files changed

+31404
-205
lines changed

common/config/rush/pnpm-lock.yaml

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/cli/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -891,6 +891,8 @@ OPTIONS
891891
--[no-]intra-dialog Only do intra dialog cross train
892892
893893
--log Writes out log messages to console
894+
895+
--exclude Excludes given folders under the input directory, for example, --exclude bin,obj,lib, this will ignore the /bin, /obj, /lib folders under the input path
894896
```
895897

896898
_See code: [@microsoft/bf-luis-cli](https://github.com/microsoft/botframework-cli/tree/master/packages/luis/src/commands/luis/cross-train.ts)_

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

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
*/
55

66
const path = require('path')
7-
const file = require('../../utils/filehelper')
7+
const fs = require('fs-extra')
8+
const filehelper = require('../../utils/filehelper')
89
const fileExtEnum = require('../utils/helpers').FileExtTypeEnum
910
const crossTrainer = require('./crossTrainer')
1011

@@ -18,28 +19,63 @@ module.exports = {
1819
* @param {inner: boolean, intra: boolean} trainingOpt trainingOpt indicates whether you want to control do the inner or intra dialog training seperately
1920
* @returns {luResult: any, qnaResult: any} trainedResult of luResult and qnaResult or undefined if no results.
2021
*/
21-
train: async function (input, intentName, config, verbose, trainingOpt) {
22+
train: async function (input, intentName, config, verbose, trainingOpt, exclude) {
23+
// get excluded foleders
24+
let excludedFolders = undefined
25+
if (exclude) {
26+
excludedFolders = exclude.split(',').map(e => e.trim())
27+
}
28+
2229
// Get all related file content.
23-
const luContents = await file.getFilesContent(input, fileExtEnum.LUFile)
24-
const qnaContents = await file.getFilesContent(input, fileExtEnum.QnAFile)
25-
const configContent = await file.getConfigContent(config)
30+
const luContents = await filehelper.getFilesContent(input, fileExtEnum.LUFile, excludedFolders)
31+
const qnaContents = await filehelper.getFilesContent(input, fileExtEnum.QnAFile, excludedFolders)
32+
const configContent = await filehelper.getConfigContent(config)
33+
const defaultLocale = 'en-us'
2634

27-
let importResolver = async function (_, idsToFind) {
35+
let importResolver = async function (id, idsToFind) {
2836
let importedContents = []
37+
const idWithoutExt = path.basename(id, path.extname(id))
38+
const locale = /\w\.\w/.test(idWithoutExt) ? idWithoutExt.split('.').pop() : defaultLocale;
2939
for (let idx = 0; idx < idsToFind.length; idx++) {
3040
let file = idsToFind[idx]
3141
if (path.isAbsolute(file.filePath)) {
3242
if (file.filePath.endsWith(fileExtEnum.LUFile)) {
33-
importedContents.push(...await file.getFilesContent(file.filePath, fileExtEnum.LUFile))
43+
importedContents.push(...await filehelper.getFilesContent(file.filePath, fileExtEnum.LUFile))
3444
} else if (file.filePath.endsWith(fileExtEnum.QnAFile)) {
35-
importedContents.push(...await file.getFilesContent(file.filePath, fileExtEnum.QnAFile))
45+
importedContents.push(...await filehelper.getFilesContent(file.filePath, fileExtEnum.QnAFile))
3646
}
3747
} else {
3848
const fileName = path.basename(file.filePath)
49+
const updateImportedContents = async function(typedContents, fileExt) {
50+
let found = []
51+
// import resolver should be capable to find implicit import files with locale, for example '[import](b.lu)' is defined in a.en-us.lu, the resolver should find b.en-us.lu
52+
const foundWithLocale = typedContents.filter(content => content.id === `${path.basename(fileName, fileExt)}.${locale}`)
53+
if (foundWithLocale.length > 0) {
54+
found = foundWithLocale
55+
} else {
56+
//if no locale specified file is found, just to check whether there is file without locale matched
57+
found = typedContents.filter(content => content.id === path.basename(fileName, fileExt))
58+
}
59+
60+
if(found.length > 0) {
61+
importedContents.push(...found)
62+
} else {
63+
const matchedLuisFiles = typedContents.filter(content => path.basename(content.fullPath) === id)
64+
for (const matchFile of matchedLuisFiles) {
65+
const sourceFileDir = path.dirname(matchFile.fullPath)
66+
const targetPath = path.resolve(sourceFileDir, file.filePath)
67+
if (fs.existsSync(targetPath)) {
68+
const importContent = await filehelper.getFilesContent(targetPath, fileExt)
69+
importedContents.push(...importContent)
70+
}
71+
}
72+
}
73+
}
74+
3975
if (fileName.endsWith(fileExtEnum.LUFile)) {
40-
importedContents.push(...luContents.filter(luContent => luContent.id === path.basename(fileName, fileExtEnum.LUFile)))
76+
await updateImportedContents(luContents, fileExtEnum.LUFile)
4177
} else if (fileName.endsWith(fileExtEnum.QnAFile)) {
42-
importedContents.push(...qnaContents.filter(qnaContent => qnaContent.id === path.basename(fileName, fileExtEnum.QnAFile)))
78+
await updateImportedContents(qnaContents, fileExtEnum.QnAFile)
4379
}
4480
}
4581
}
@@ -60,4 +96,4 @@ module.exports = {
6096

6197
return trainedResult
6298
}
63-
}
99+
}

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

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ export class Builder {
293293
return settingsContent
294294
}
295295

296-
async writeDialogAssets(contents: any[], options: any = {}) {
296+
async writeDialogAssets(contents: any[], options: any = {}, directVersionPublish?: boolean) {
297297
let force = options.force || false
298298
let out = options.out
299299
let luConfig = options.luConfig
@@ -311,7 +311,8 @@ export class Builder {
311311
} else {
312312
outPath = path.resolve(settingsContents[0].id)
313313
}
314-
writeContents.push(this.mergeSettingsContent(outPath, settingsContents))
314+
315+
writeContents.push(this.mergeSettingsContent(outPath, settingsContents, directVersionPublish))
315316
}
316317

317318
for (const content of writeContents) {
@@ -478,14 +479,16 @@ export class Builder {
478479
return contents
479480
}
480481

481-
mergeSettingsContent(settingsPath: string, contents: any[]) {
482+
mergeSettingsContent(settingsPath: string, contents: any[], directVersionPublish?: boolean) {
482483
let settings = new Settings(settingsPath, {})
483484
for (const content of contents) {
484485
const luisAppsMap = JSON.parse(content.content).luis
485486
for (const appName of Object.keys(luisAppsMap)) {
486-
settings.luis[appName] = {
487+
settings.luis[appName] = directVersionPublish ? {
487488
"appId": luisAppsMap[appName]["appId"],
488489
"version": luisAppsMap[appName]["version"]
490+
} : {
491+
"appId": luisAppsMap[appName]["appId"]
489492
}
490493
}
491494
}

packages/lu/src/utils/filehelper.ts

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -171,22 +171,35 @@ export async function detectLuContent(stdin: string, input: string) {
171171
return false
172172
}
173173

174-
export async function getFilesContent(input: string, extType: string) {
174+
export async function getFilesContent(input: string, extType: string, ignoredFolders?: string[]) {
175175
let fileStat = await fs.stat(input)
176176
if (fileStat.isFile()) {
177177
const filePath = path.resolve(input)
178178
const content = await getContentFromFile(input)
179-
return [{id: path.basename(filePath, extType), content}]
179+
return [{id: path.basename(filePath, extType), content, fullPath: filePath}]
180180
}
181181

182182
if (!fileStat.isDirectory()) {
183183
throw (new exception(retCode.errorCode.INVALID_INPUT_FILE, 'Sorry, ' + input + ' is not a folder or does not exist'))
184184
}
185-
const paths = await globby([`**/*${extType}`], {cwd: input, dot: true})
185+
186+
const allPaths = (await globby([`**/*${extType}` ], {cwd: input, dot: true}))
187+
let paths: string[] = []
188+
if (ignoredFolders) {
189+
for (const path of allPaths) {
190+
const isIgnored = ignoredFolders.filter(e => path.startsWith(e)).length > 0
191+
if (!isIgnored) {
192+
paths.push(path)
193+
}
194+
}
195+
} else {
196+
paths = allPaths
197+
}
198+
186199
return Promise.all(paths.map(async (item: string) => {
187200
const itemPath = path.resolve(path.join(input, item))
188201
const content = await getContentFromFile(itemPath)
189-
return {id: path.basename(itemPath, extType), content}
202+
return {id: path.basename(itemPath, extType), content, fullPath: itemPath}
190203
}))
191204
}
192205

packages/luis/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,8 @@ OPTIONS
431431
--[no-]intra-dialog Only do intra dialog cross train
432432
433433
--log Writes out log messages to console
434+
435+
--exclude Excludes given folders under the input directory, for example, --exclude bin,obj,lib, this will ignore the /bin, /obj, /lib folders under the input path
434436
```
435437

436438
_See code: [src/commands/luis/cross-train.ts](https://github.com/microsoft/botframework-cli/tree/master/packages/luis/src/commands/luis/cross-train.ts)_

packages/luis/src/commands/luis/build.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,13 +154,13 @@ export default class LuisBuild extends Command {
154154
fallbackLocale,
155155
schema,
156156
dialog
157-
})
157+
}, directVersionPublish)
158158

159159
let writeDone = await builder.writeDialogAssets(dialogContents, {
160160
force,
161161
out: outputFolder,
162162
luConfig
163-
})
163+
}, directVersionPublish)
164164

165165
if (writeDone) {
166166
this.log(`Successfully wrote .dialog files to ${outputFolder}\n`)
@@ -173,7 +173,7 @@ export default class LuisBuild extends Command {
173173
force,
174174
out: outputFolder,
175175
luConfig
176-
})
176+
}, directVersionPublish)
177177

178178
if (writeDone) {
179179
this.log(`Successfully wrote settings file to ${outputFolder}\n`)

packages/luis/src/commands/luis/cross-train.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ export default class LuisCrossTrain extends Command {
2222
force: flags.boolean({char: 'f', description: 'If --out flag is provided with the path to an existing file, overwrites that file', default: false}),
2323
log: flags.boolean({description: 'Writes out log messages to console', default: false}),
2424
'inner-dialog': flags.boolean({description: 'Only do inner dialog cross train', default: true, allowNo: true}),
25-
'intra-dialog': flags.boolean({description: 'Only do intra dialog cross train', default: true, allowNo: true})
25+
'intra-dialog': flags.boolean({description: 'Only do intra dialog cross train', default: true, allowNo: true}),
26+
exclude: flags.string({description: 'Excludes folders under the input directory, separated by ",". If not specified, all luis and qna files will be included in the cross-train'})
2627
}
2728

2829
async run() {
@@ -46,7 +47,7 @@ export default class LuisCrossTrain extends Command {
4647
intra: flags['intra-dialog']
4748
}
4849

49-
const trainedResult = await crossTrain.train(flags.in, flags.intentName, flags.config, flags.log, trainingOpt)
50+
const trainedResult = await crossTrain.train(flags.in, flags.intentName, flags.config, flags.log, trainingOpt, flags.exclude)
5051

5152
if (flags.out === undefined) {
5253
flags.out = path.join(process.cwd(), 'cross-trained')

0 commit comments

Comments
 (0)