Skip to content
This repository was archived by the owner on Jul 9, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
52df33b
feat: Taking control of recognizer dialog generation and make compose…
lei9444 Oct 12, 2020
c662d48
Merge branch 'main' of https://github.com/microsoft/BotFramework-Comp…
lei9444 Oct 13, 2020
51bd151
update the kind string
lei9444 Oct 13, 2020
ac92598
add some comments
lei9444 Oct 13, 2020
c5e37f5
add unit tests
lei9444 Oct 13, 2020
f830882
fix unit test
lei9444 Oct 13, 2020
63baf76
fix build file config
lei9444 Oct 13, 2020
cea8e8b
update the package
lei9444 Oct 13, 2020
b0ef32f
update the cross train config file name
lei9444 Oct 13, 2020
219b28b
Merge branch 'main' into build
cwhitten Oct 13, 2020
e648f87
fix test
lei9444 Oct 13, 2020
1879ddc
Merge branch 'build' of https://github.com/lei9444/BotFramework-Compo…
lei9444 Oct 14, 2020
1678353
Merge branch 'main' into build
cwhitten Oct 14, 2020
caaba73
Merge branch 'main' into build
cwhitten Oct 14, 2020
ce92460
Merge branch 'main' of https://github.com/microsoft/BotFramework-Comp…
lei9444 Oct 15, 2020
724f635
update the update function
lei9444 Oct 15, 2020
d4ef1e8
use cli replace the sampler
lei9444 Oct 15, 2020
66fc632
Merge branch 'main' into build
cwhitten Oct 15, 2020
298f138
Merge branch 'main' into build
cwhitten Oct 15, 2020
0b64197
Merge branch 'main' of https://github.com/microsoft/BotFramework-Comp…
lei9444 Oct 16, 2020
dbc26b5
update the bot test
lei9444 Oct 16, 2020
95e5e16
fix test
lei9444 Oct 16, 2020
72b58a8
Merge branch 'main' into build
cwhitten Oct 16, 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
15 changes: 6 additions & 9 deletions Composer/packages/client/__tests__/utils/buildUtil.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,11 @@ describe('createCrossTrainConfig', () => {
{ id: 'dia5.en-us' },
{ id: 'dia6.en-us' },
];
const config = createCrossTrainConfig(dialogs as DialogInfo[], luFiles as LuFile[]);
expect(config.rootIds.length).toEqual(1);
expect(config.rootIds[0]).toEqual('main.en-us.lu');
expect(config.triggerRules['main.en-us.lu'].dia1_trigger).toEqual('dia1.en-us.lu');
expect(config.triggerRules['main.en-us.lu'].no_dialog).toEqual('');
expect(config.triggerRules['main.en-us.lu'].dia1_trigger).toEqual('dia1.en-us.lu');
expect(config.triggerRules['main.en-us.lu'].dias_trigger.length).toBe(2);
expect(config.triggerRules['dia1.en-us.lu'].dia3_trigger).toEqual('dia3.en-us.lu');
expect(config.triggerRules['dia1.en-us.lu']['dia4.en-us.lu']).toBeUndefined();
const config = createCrossTrainConfig(dialogs as DialogInfo[], luFiles as LuFile[], ['en-us']);
expect(config['main.en-us.lu'].rootDialog).toBeTruthy();
expect(config['main.en-us.lu'].triggers.dia1_trigger[0]).toEqual('dia1.en-us.lu');
expect(config['main.en-us.lu'].triggers.no_dialog.length).toEqual(0);
expect(config['main.en-us.lu'].triggers.dia2_trigger[0]).toEqual('dia2.en-us.lu');
expect(config['main.en-us.lu'].triggers.dias_trigger.length).toBe(2);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { PrimaryButton } from 'office-ui-fabric-react/lib/Button';
import formatMessage from 'format-message';
import { useRecoilValue } from 'recoil';
import { IConfig, IPublishConfig, defaultPublishConfig } from '@bfc/shared';
import { useRecognizerConfig } from '@bfc/extension-client';

import {
botEndpointsState,
Expand Down Expand Up @@ -67,6 +68,7 @@ export const TestController: React.FC<{ projectId: string }> = (props) => {
const settings = useRecoilValue(settingsState(projectId));
const qnaFiles = useRecoilValue(qnaFilesState(projectId));
const botLoadErrorMsg = useRecoilValue(botLoadErrorState(projectId));
const { recognizers } = useRecognizerConfig();

const botEndpoints = useRecoilValue(botEndpointsState);
const {
Expand Down Expand Up @@ -156,12 +158,18 @@ export const TestController: React.FC<{ projectId: string }> = (props) => {
setBotStatus(BotStatus.publishing, projectId);
dismissDialog();
const { luis, qna } = config;
const recognizerTypes = dialogs.reduce((result, file) => {
const recognizer = recognizers.filter((r) => r.isSelected && r.isSelected(file.content.recognizer));
result[file.id] = recognizer[0]?.id || '';
return result;
}, {});

await setSettings(projectId, {
...settings,
luis: luis,
qna: Object.assign({}, settings.qna, qna),
});
await build(luis, qna, projectId);
await build(luis, qna, recognizerTypes, projectId);
}

async function handleLoadBot() {
Expand Down
27 changes: 22 additions & 5 deletions Composer/packages/client/src/components/Toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,17 @@

/** @jsx jsx */
import { jsx, css } from '@emotion/core';
import { Fragment } from 'react';
import { Fragment, useMemo } from 'react';
import formatMessage from 'format-message';
import { NeutralColors } from '@uifabric/fluent-theme';
import { ActionButton, CommandButton } from 'office-ui-fabric-react/lib/Button';
import { IContextualMenuProps, IIconProps } from 'office-ui-fabric-react/lib';
import { EditorExtension, mergePluginConfigs, PluginConfig } from '@bfc/extension-client';
import { useRecoilValue } from 'recoil';

import plugins from '../plugins';
import { currentProjectIdState, schemasState } from '../recoilModel';
import { useShell } from '../shell/useShell';

// -------------------- Styles -------------------- //

Expand Down Expand Up @@ -98,6 +104,15 @@ type ToolbarProps = {
// fabric-ui IButtonProps interface}
export const Toolbar = (props: ToolbarProps) => {
const { toolbarItems = [], ...rest } = props;
const projectId = useRecoilValue(currentProjectIdState);
const schemas = useRecoilValue(schemasState(projectId));
const shellForPropertyEditor = useShell('DesignPage', projectId);

const pluginConfig: PluginConfig = useMemo(() => {
const sdkUISchema = schemas?.ui?.content ?? {};
const userUISchema = schemas?.uiOverrides?.content ?? {};
return mergePluginConfigs({ uiSchema: sdkUISchema }, plugins, { uiSchema: userUISchema });
}, [schemas?.ui?.content, schemas?.uiOverrides?.content]);

const left: IToolbarItem[] = [];
const right: IToolbarItem[] = [];
Expand All @@ -113,9 +128,11 @@ export const Toolbar = (props: ToolbarProps) => {
}

return (
<div aria-label={formatMessage('toolbar')} css={headerSub} role="region" {...rest}>
<div css={leftActions}>{left.map(renderItemList)} </div>
<div css={rightActions}>{right.map(renderItemList)}</div>
</div>
<EditorExtension plugins={pluginConfig} projectId={projectId} shell={shellForPropertyEditor}>
<div aria-label={formatMessage('toolbar')} css={headerSub} role="region" {...rest}>
<div css={leftActions}>{left.map(renderItemList)} </div>
<div css={rightActions}>{right.map(renderItemList)}</div>
</div>
</EditorExtension>
);
};
12 changes: 7 additions & 5 deletions Composer/packages/client/src/recoilModel/dispatchers/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import httpClient from '../../utils/httpUtil';
import luFileStatusStorage from '../../utils/luFileStatusStorage';
import qnaFileStatusStorage from '../../utils/qnaFileStatusStorage';
import { luFilesState, qnaFilesState, dialogsState, botStatusState, botLoadErrorState } from '../atoms';
import { settingsState } from '../atoms/botState';

const checkEmptyQuestionOrAnswerInQnAFile = (sections) => {
return sections.some((s) => !s.Answer || s.Questions.some((q) => !q.content));
Expand All @@ -22,13 +23,14 @@ export const builderDispatcher = () => {
({ set, snapshot }: CallbackInterface) => async (
luisConfig: ILuisConfig,
qnaConfig: IQnAConfig,
recognizerTypes: { [fileName: string]: string },
projectId: string
) => {
const dialogs = await snapshot.getPromise(dialogsState(projectId));
const luFiles = await snapshot.getPromise(luFilesState(projectId));
const qnaFiles = await snapshot.getPromise(qnaFilesState(projectId));
const settings = await snapshot.getPromise(settingsState(projectId));
const referredLuFiles = luUtil.checkLuisBuild(luFiles, dialogs);

const errorMsg = qnaFiles.reduce(
(result, file) => {
if (
Expand All @@ -48,15 +50,15 @@ export const builderDispatcher = () => {
return;
}
try {
//TODO crosstrain should add locale
const crossTrainConfig = buildUtil.createCrossTrainConfig(dialogs, referredLuFiles);
const crossTrainConfig = buildUtil.createCrossTrainConfig(dialogs, referredLuFiles, settings.languages);
await httpClient.post(`/projects/${projectId}/build`, {
luisConfig,
qnaConfig,
projectId,
crossTrainConfig,
luFiles: referredLuFiles.map((file) => file.id),
qnaFiles: qnaFiles.map((file) => file.id),
recognizerTypes,
luFiles: referredLuFiles.map((file) => ({ id: file.id, isEmpty: file.empty })),
qnaFiles: qnaFiles.map((file) => ({ id: file.id, isEmpty: !file.qnaSections.length })),
});
luFileStatusStorage.publishAll(projectId);
qnaFileStatusStorage.publishAll(projectId);
Expand Down
144 changes: 27 additions & 117 deletions Composer/packages/client/src/utils/buildUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,137 +2,45 @@
// Licensed under the MIT License.

import { DialogInfo, LuFile } from '@bfc/shared';
import keys from 'lodash/keys';

import { LuisConfig, QnaConfig } from '../constants';

import { getReferredLuFiles } from './luUtil';
import { getReferredQnaFiles } from './qnaUtil';
import { getBaseName, getExtension } from './fileUtil';
import { getBaseName } from './fileUtil';

function createConfigId(fileId) {
return `${fileId}.lu`;
function createConfigId(fileId: string, language: string) {
return `${fileId}.${language}.lu`;
}

function getLuFilesByDialogId(dialogId: string, luFiles: LuFile[]) {
return luFiles.filter((lu) => getBaseName(lu.id) === dialogId).map((lu) => createConfigId(lu.id));
}

function getFileLocale(fileName: string) {
//file name = 'a.en-us.lu'
return getExtension(getBaseName(fileName));
}
//replace the dialogId with luFile's name
function addLocaleToConfig(config: ICrossTrainConfig, luFiles: LuFile[]) {
const { rootIds, triggerRules } = config;
config.rootIds = rootIds.reduce((result: string[], id: string) => {
return [...result, ...getLuFilesByDialogId(id, luFiles)];
}, []);
config.triggerRules = keys(triggerRules).reduce((result, key) => {
const fileNames = getLuFilesByDialogId(key, luFiles);
return {
...result,
...fileNames.reduce((result, name) => {
const locale = getFileLocale(name);
const triggers = triggerRules[key];
keys(triggers).forEach((trigger) => {
if (!result[name]) result[name] = {};
const ids = triggers[trigger];
if (Array.isArray(ids)) {
result[name][trigger] = ids.map((id) => (id ? `${id}.${locale}.lu` : id));
} else {
result[name][trigger] = ids ? `${ids}.${locale}.lu` : ids;
}
});
return result;
}, {}),
};
}, {});
return config;
}
interface ICrossTrainConfig {
rootIds: string[];
triggerRules: { [key: string]: any };
intentName: string;
verbose: boolean;
}
export function createCrossTrainConfig(dialogs: DialogInfo[], luFiles: LuFile[], languages: string[]) {
const config = dialogs.reduce((result, { isRoot: rootDialog, intentTriggers, id, luFile: luFileId }) => {
const luFile = luFiles.find((luFile) => getBaseName(luFile.id) === luFileId);

//generate the cross-train config without locale
/* the config is like
{
rootIds: [
'main.en-us.lu',
'main.fr-fr.lu'
],
triggerRules: {
'main.en-us.lu': {
'dia1_trigger': 'dia1.en-us.lu',
'dia2_trigger': 'dia2.en-us.lu'
},
'dia2.en-us.lu': {
'dia3_trigger': 'dia3.en-us.lu',
'dia4_trigger': 'dia4.en-us.lu'
},
'main.fr-fr.lu': {
'dia1_trigger': 'dia1.fr-fr.lu'
}
},
intentName: '_Interruption',
verbose: true
}
*/

export function createCrossTrainConfig(dialogs: DialogInfo[], luFiles: LuFile[]): ICrossTrainConfig {
const triggerRules = {};
const countMap = {};
if (!luFile) return result;

//map all referred lu files
luFiles.forEach((file) => {
countMap[getBaseName(file.id)] = 1;
});
const filtered = intentTriggers.filter((intentTrigger) =>
luFile.intents.find((intent) => intent.Name === intentTrigger.intent || intentTrigger.intent === '')
);

let rootId = '';
dialogs.forEach((dialog) => {
if (dialog.isRoot) rootId = dialog.id;
const luFile = luFiles.find((luFile) => getBaseName(luFile.id) === dialog.luFile);
if (luFile) {
const fileId = dialog.id;
const { intentTriggers } = dialog;
// filter intenttrigger which be involved in lu file
//find the trigger's dialog that use a recognizer
intentTriggers
.filter((intentTrigger) => luFile.intents.find((intent) => intent.Name === intentTrigger.intent))
.forEach((item) => {
//find all dialogs in trigger that has a luis recognizer
const used = item.dialogs.filter((dialog) => !!countMap[dialog]);
if (!filtered.length) return result;

const deduped = Array.from(new Set<string>(used));

const result = {};
if (deduped.length === 1) {
result[item.intent] = deduped[0];
} else if (deduped.length) {
result[item.intent] = deduped;
} else {
result[item.intent] = '';
}
languages.forEach((language) => {
const triggers = filtered.reduce((result, { intent, dialogs }) => {
const ids = dialogs
.map((dialog) => createConfigId(dialog, language))
.filter((id) => luFiles.some((file) => `${file.id}.lu` === id));
if (!ids.length && dialogs.length) return result;
result[intent] = ids;
return result;
}, {});
result[createConfigId(id, language)] = { rootDialog, triggers };
});

triggerRules[fileId] = { ...triggerRules[fileId], ...result };
});
}
});
return result;
}, {});

const crossTrainConfig: ICrossTrainConfig = {
rootIds: [],
triggerRules: {},
intentName: '_Interruption',
verbose: true,
};
crossTrainConfig.rootIds = keys(countMap).filter(
(key) => (countMap[key] === 0 || key === rootId) && triggerRules[key]
);
crossTrainConfig.triggerRules = triggerRules;
return addLocaleToConfig(crossTrainConfig, luFiles);
return config;
}

export function isBuildConfigComplete(config, dialogs, luFiles, qnaFiles) {
Expand All @@ -154,3 +62,5 @@ export function isBuildConfigComplete(config, dialogs, luFiles, qnaFiles) {
export function needsBuild(dialogs) {
return dialogs.some((dialog) => typeof dialog.content.recognizer === 'string');
}

export function createRecognizerTypeMap(dialogs: DialogInfo[]) {}
2 changes: 1 addition & 1 deletion Composer/packages/lib/indexers/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"rimraf": "^2.6.3"
},
"dependencies": {
"@microsoft/bf-lu": "^4.11.0-dev.20201005.7e5e1b8",
"@microsoft/bf-lu": "^4.11.0-dev.20201013.7ccb128",
"adaptive-expressions": "4.10.0-preview-147186",
"botbuilder-lg": "^4.10.0-preview-150886",
"lodash": "^4.17.19"
Expand Down
1 change: 1 addition & 0 deletions Composer/packages/lib/indexers/src/dialogIndexer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { JsonWalk, VisitorFunc } from './utils/jsonWalk';
import { getBaseName } from './utils/help';
import extractIntentTriggers from './dialogUtils/extractIntentTriggers';
import { createPath } from './validations/expressionValidation/utils';

// find out all lg templates given dialog
function extractLgTemplates(id, dialog): LgTemplateJsonPath[] {
const templates: LgTemplateJsonPath[] = [];
Expand Down
4 changes: 2 additions & 2 deletions Composer/packages/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,9 @@
"@bfc/lg-languageserver": "*",
"@bfc/lu-languageserver": "*",
"@bfc/shared": "*",
"@microsoft/bf-dispatcher": "^4.10.0-preview.141651",
"@microsoft/bf-dispatcher": "^4.11.0-beta.20201015.008a3a4",
"@microsoft/bf-generate-library": "^4.10.0-daily.20201008.172736",
"@microsoft/bf-lu": "^4.11.0-dev.20201005.7e5e1b8",
"@microsoft/bf-lu": "^4.11.0-dev.20201013.7ccb128",
"archiver": "^5.0.2",
"axios": "^0.19.2",
"azure-storage": "^2.10.3",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -367,17 +367,12 @@ describe('publish luis files', () => {
body: {
authoringKey: '0d4991873f334685a9686d1b48e0ff48',
projectId: projectId,
crossTrainConfig: {
rootIds: ['bot1.en-us.lu'],
triggerRules: { 'bot1.en-us.lu': {} },
intentName: '_Interruption',
verbose: true,
},
crossTrainConfig: {},
luFiles: [],
},
} as Request;
await ProjectController.build(mockReq, mockRes);
expect(mockRes.status).toHaveBeenCalledWith(200);
expect(mockRes.status).toHaveBeenCalled();
});
});

Expand Down
7 changes: 4 additions & 3 deletions Composer/packages/server/src/controllers/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -326,13 +326,14 @@ async function build(req: Request, res: Response) {
const currentProject = await BotProjectService.getProjectById(projectId, user);
if (currentProject !== undefined) {
try {
const { luisConfig, qnaConfig, luFiles, qnaFiles, crossTrainConfig } = req.body;
const { luisConfig, qnaConfig, luFiles, qnaFiles, crossTrainConfig, recognizerTypes } = req.body;
const files = await currentProject.buildFiles({
luisConfig,
qnaConfig,
luFileIds: luFiles,
qnaFileIds: qnaFiles,
luResource: luFiles,
qnaResource: qnaFiles,
crossTrainConfig,
recognizerTypes,
});
res.status(200).json(files);
} catch (error) {
Expand Down
Loading