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

Commit 041a67c

Browse files
Shuai Wangmunozemilio
andcommitted
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 <emmunozp@microsoft.com>
1 parent f13f418 commit 041a67c

File tree

15 files changed

+31206
-15
lines changed

15 files changed

+31206
-15
lines changed

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/utils/filehelper.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ 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)
@@ -182,7 +182,20 @@ export async function getFilesContent(input: string, extType: string) {
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)

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/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')

packages/luis/test/commands/luis/crossTrain.test.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,4 +148,30 @@ describe('luis:cross-train tests for lu and qna contents', () => {
148148
expect(await compareLuFiles('./../../../interruptionGen/dia1.lu', './../../fixtures/verified/interruption7/dia1.lu')).to.be.true
149149
expect(await compareLuFiles('./../../../interruptionGen/dia1.qna', './../../fixtures/verified/interruption7/dia1.qna')).to.be.true
150150
})
151+
152+
test
153+
.stdout()
154+
.command(['luis:cross-train',
155+
'--in', `${path.join(__dirname, './../../fixtures/testcases/application')}`,
156+
'--config', `${path.join(__dirname, './../../fixtures/testcases/application/cross-train.config')}`,
157+
'--out', './interruptionGen',
158+
'--force'])
159+
.it('luis:cross training should able to import files out of current directory', async () => {
160+
expect(await compareLuFiles('./../../../interruptionGen/application.lu', './../../fixtures/verified/interruption8/application.lu')).to.be.true
161+
expect(await compareLuFiles('./../../../interruptionGen/application.qna', './../../fixtures/verified/interruption8/application.qna')).to.be.true
162+
})
163+
164+
test
165+
.stdout()
166+
.command(['luis:cross-train',
167+
'--in', `${path.join(__dirname, './../../fixtures/testcases/testImportWithLocale')}`,
168+
'--config', `${path.join(__dirname, './../../fixtures/testcases/testImportWithLocale/cross-train.config')}`,
169+
'--out', './interruptionGen',
170+
'--exclude', 'bin',
171+
'--force'])
172+
.it('luis:cross training should able to import files with locale and ignore files under the directory specified', async () => {
173+
expect(await compareLuFiles('./../../../interruptionGen/ChitchatDialog.en-us.lu', './../../fixtures/verified/interruption9/ChitchatDialog.en-us.lu')).to.be.true
174+
expect(await compareLuFiles('./../../../interruptionGen/ChitchatDialog.en-us.qna', './../../fixtures/verified/interruption9/ChitchatDialog.en-us.qna')).to.be.true
175+
expect(fs.existsSync('./../../../interruptionGen/extra.en-us.lu')).to.be.false
176+
})
151177
})

packages/luis/test/fixtures/testcases/testImportWithLocale/ChitchatDialog.en-us.lu

Whitespace-only changes.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[import](chitchat_professional.source.qna)
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# BotTour
2+
- bot tour
3+
- onboarding
4+
- get started
5+
- start the tour
6+
- what can you do
7+
- what can i ask you
8+
9+
# Cancel
10+
- cancel
11+
- quit
12+
- abort
13+
- exit
14+
- never mind
15+
- forget about it
16+
- just stop already
17+
- end this now
18+
19+
# Help
20+
- help
21+
- im stuck
22+
- how do you work
23+
- what can you do
24+
- what can you help me with
25+
- i need help
26+
- i need some assistance
27+
28+
# Feedback
29+
- i want to give feedback
30+
- i have a problem
31+
- something is broken
32+
- this is a bad experience
33+
- fill out survey
34+
35+
# None
36+
- where is my car?
37+
- I want to order a pizza
38+
- place an item on hold
39+
- Is it going to rain?
40+
- turn on the lights
41+
- check my account balance
42+
- do unicorns really exist
43+
- duck

packages/luis/test/fixtures/testcases/testImportWithLocale/assistant_core.en-us.qna

Whitespace-only changes.

0 commit comments

Comments
 (0)