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

Commit accb073

Browse files
liweitianalanlong9278luhan2017VanyLawlei9444
authored
feat: Qna recognizer (#3704)
* rebuild qna CRUD and qna page UI (#3692) * rebase * Rebuild qna (#3701) * add unique key to qna property * import online file * debug * add qna trigger type (#3703) * resolve conflict * bug fix * refine qna crud (#3711) * Update Program.cs * fix bug (#3715) * bugfix: start bot pop error message for new echo bot * revert the change on common.lg * send qna endpointKey to runtime * publish pop * eslint * Regression on publish page after recoil * clean some code * recognizer dropdown & custom default content * migration * fix files structure change * fix object can only read * add botProject interface and move some interface into shared folder * fix lint * update trigger action (#3720) * merge publish error fixed * Update package to include the resourc id fix * add id for dialog & migrate it with old bot * crosstrain config * eslint & qna jump * migration old bot for recognizer * migration old bot for recognizer * revert rootId in luUtil * change qna location * refactor publish logic * cross train when default recognizer * sync cross train config logic with azure deploy * fix bug (#3748) * Update the sdk package to include expandText fix * solve conflict * resolve conflict * cross train config filter no body trigger intent * deploy logic change * azure deploy bugfix * yarn * typecheck error * Qna recognizer tmp (#3765) * fix: LG/LU editing performance optimize (#3738) * update luUtil interface * update * use structure luFile do CRUD * refine lgUtile interface * update worker * update lu package * update lg package * update parse line number * log * parse still use worker * lint fix * clean up * Update Composer/packages/client/src/recoilModel/dispatchers/lg.ts Co-authored-by: Andy Brown <[email protected]> * Update Composer/packages/client/src/recoilModel/dispatchers/lg.ts Co-authored-by: Andy Brown <[email protected]> * Update Composer/packages/client/src/recoilModel/dispatchers/lg.ts Co-authored-by: Andy Brown <[email protected]> * Update Composer/packages/client/src/recoilModel/dispatchers/lg.ts Co-authored-by: Andy Brown <[email protected]> * Update Composer/packages/client/src/recoilModel/dispatchers/lg.ts Co-authored-by: Andy Brown <[email protected]> * wrap with formatMessage Co-authored-by: Chris Whitten <[email protected]> Co-authored-by: Andy Brown <[email protected]> * feat: control Flow Editor via the Electron app menu (#3660) * draft: consume the EditorAPI in electron menu * register global EditorAPI * apply EditorAPI in ToolBar * apply EditorAPI in Electron app menu * add todos in undo/redo * emit Electron menu events to renderer process * use ipc channel to subscribe Electron menu events * override default menu event * add shortcuts * remove duplicated usage of getEditorApi() * fix UT * update 'Redo' shortcut in Electron menu * disable cut/copy/del menu when no action selected * extract Electron logic to a hook * check ipcMain in jest env Co-authored-by: Chris Whitten <[email protected]> * deploy logic change * azure deploy bugfix * yarn * typecheck error * fix: Luis publish missing parameter (#3764) * fix luis publish * remove empty space in setting page * resolve conflict Co-authored-by: Zhixiang Zhan <[email protected]> Co-authored-by: Chris Whitten <[email protected]> Co-authored-by: Andy Brown <[email protected]> Co-authored-by: zeye <[email protected]> Co-authored-by: Long Alan <[email protected]> Co-authored-by: Long Jun <[email protected]> * set max memory for build * refactor qna popup logic && form editor ux * Update trigger (#3768) * update trigger actions * rebase * update trigger (#3772) * fix export bug (#3776) * separates Form / Flow's ExtensionContext * test fixed * fix trigger actions bug (#3787) * fix trigger bug (#3788) * fix trigger bug * update trigger text * conflict err * update bf-lu package & fix conflict err * Creating qn a sample (#3819) * create qnaSample bot flow * update create qna bot flow * fix bug * fix bug (#3822) * test for build qna & lu in server * Update qn a flow (#3831) * fix bug * update import qna flow * Fix ui bug (#3833) * fix bug * update import QNA UI * Update autoEndDialog to false * Navigate to qna page (#3834) * fix bug * navigate to qna page * Updates * Fix * refine code * refactor & add comment * refine code" * handle comments * popup desc * hidden the subsription key * recognizer * replace the default name with unique id * use common component to show error * comments * update UI * fix the qna icon reload issue * fix UI bug * change hover color * remove toggle on row * qna endpointKey * popup * add try catch for get qna endpointKey * update UI * remove the scroll bar and update the error display * update qna UI * input luis hoisting * conflict * comments * test fixed * test fixed * handle comments * separated QnAEditor & rename to knowledge-base (#3857) * separated QnAEditor & rename to knowledge-base * rename * rename helper to utilities * fix test case * fix unit tests * test * qna-recognizer add tests (#3859) * add triggerCreattionModal test * triggerApi test * add test for import modal * add test * dialog id * fix bug * fix bug * wrong url * conflict * Update ComposerSettingsExtensions * update the error message * conflict fix * Update ComposerSettingsExtensions.cs * azure deploy * lu build * qna build bugfix * code refine * lint fix * qna name * code refine * Update package to daily to fix the CrossTrainRecognizer issue * concurrent import qna from urls and set call limits * error message & e2e test * e2e test * code scan error * fix code scanner error * fix code scanner error * update the get params * fix UI * update qna error logic * Update package to 4.10.1 * encode qna urls * refactor * fix e2e todobot Co-authored-by: Long Jun <[email protected]> Co-authored-by: Long Alan <[email protected]> Co-authored-by: Lu Han <[email protected]> Co-authored-by: Wenyi Luo <[email protected]> Co-authored-by: leilzh <[email protected]> Co-authored-by: Zhixiang Zhan <[email protected]> Co-authored-by: Chris Whitten <[email protected]> Co-authored-by: Andy Brown <[email protected]> Co-authored-by: zeye <[email protected]> Co-authored-by: zeye <[email protected]>
1 parent 9a11603 commit accb073

File tree

170 files changed

+5675
-806
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

170 files changed

+5675
-806
lines changed

Composer/cypress/integration/LuisDeploy.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ context('Luis Deploy', () => {
1616
cy.url().should('contain', 'language-understanding/all');
1717
cy.route({
1818
method: 'POST',
19-
url: 'api/projects/*/luFiles/publish',
19+
url: 'api/projects/*/build',
2020
status: 200,
2121
response: 'fixture:luPublish/success',
2222
});
@@ -33,7 +33,7 @@ context('Luis Deploy', () => {
3333

3434
cy.route({
3535
method: 'POST',
36-
url: 'api/projects/*/luFiles/publish',
36+
url: 'api/projects/*/build',
3737
status: 400,
3838
response: 'fixture:luPublish/error',
3939
});

Composer/cypress/integration/ToDoBot.spec.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
// Licensed under the MIT License.
33

44
context('ToDo Bot', () => {
5-
beforeEach(() => {
5+
before(() => {
66
cy.visit('/home');
77
cy.createBot('TodoSample');
8+
cy.findByTestId('WelcomeModalCloseIcon').click();
9+
cy.findByText('Yes').click();
810
});
911

1012
it('can open the main dialog', () => {
@@ -19,7 +21,6 @@ context('ToDo Bot', () => {
1921
it('can open the AddToDo dialog', () => {
2022
cy.findByTestId('ProjectTree').within(() => {
2123
cy.findByText('addtodo').click();
22-
cy.findByText('addtodo').click();
2324
});
2425

2526
cy.url().should('contain', 'addtodo');
@@ -28,7 +29,6 @@ context('ToDo Bot', () => {
2829
it('can open the ClearToDos dialog', () => {
2930
cy.findByTestId('ProjectTree').within(() => {
3031
cy.findByText('cleartodos').click();
31-
cy.findByText('cleartodos').click();
3232
});
3333

3434
cy.url().should('contain', 'cleartodos');
@@ -37,7 +37,6 @@ context('ToDo Bot', () => {
3737
it('can open the DeleteToDo dialog', () => {
3838
cy.findByTestId('ProjectTree').within(() => {
3939
cy.findByText('deletetodo').click();
40-
cy.findByText('deletetodo').click();
4140
});
4241

4342
cy.url().should('contain', 'deletetodo');
@@ -46,7 +45,6 @@ context('ToDo Bot', () => {
4645
it('can open the ShowToDos dialog', () => {
4746
cy.findByTestId('ProjectTree').within(() => {
4847
cy.findByText('showtodos').click();
49-
cy.findByText('showtodos').click();
5048
});
5149

5250
cy.url().should('contain', 'showtodos');

Composer/packages/client/__tests__/components/TestController/publish-luis-modal.test.tsx

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import * as React from 'react';
44
import { fireEvent } from '@bfc/test-utils';
55

6-
import { PublishLuisDialog } from '../../../src/components/TestController/publishDialog';
6+
import { PublishDialog } from '../../../src/components/TestController/publishDialog';
77
import { projectIdState, botNameState, settingsState, dispatcherState } from '../../../src/recoilModel';
88
import { renderWithRecoil } from '../../testUtils';
99
jest.useFakeTimers();
@@ -18,8 +18,10 @@ const luisConfig = {
1818
defaultLanguage: 'en-us',
1919
environment: 'composer',
2020
};
21-
describe('<PublishLuisDialog />', () => {
22-
it('should render the <PublishLuisDialog />', () => {
21+
const config = { subscriptionKey: '12345', qnaRegion: 'westus', ...luisConfig };
22+
const qnaConfig = { subscriptionKey: '12345', endpointKey: '12345', qnaRegion: 'westus' };
23+
describe('<PublishDialog />', () => {
24+
it('should render the <PublishDialog />', () => {
2325
const onDismiss = jest.fn(() => {});
2426
const onPublish = jest.fn(() => {});
2527
const setSettingsMock = jest.fn(() => {});
@@ -31,16 +33,11 @@ describe('<PublishLuisDialog />', () => {
3133
set(botNameState, 'sampleBot0');
3234
set(settingsState, {
3335
luis: luisConfig,
36+
qna: qnaConfig,
3437
});
3538
};
3639
const { getByText } = renderWithRecoil(
37-
<PublishLuisDialog
38-
isOpen
39-
botName={'sampleBot0'}
40-
config={luisConfig}
41-
onDismiss={onDismiss}
42-
onPublish={onPublish}
43-
/>,
40+
<PublishDialog isOpen botName={'sampleBot0'} config={config} onDismiss={onDismiss} onPublish={onPublish} />,
4441
recoilInitState
4542
);
4643

@@ -50,14 +47,21 @@ describe('<PublishLuisDialog />', () => {
5047
fireEvent.click(publishButton);
5148
expect(onPublish).toBeCalled();
5249
expect(onPublish).toBeCalledWith({
53-
name: 'sampleBot0',
54-
authoringKey: '12345',
55-
authoringEndpoint: 'testAuthoringEndpoint',
56-
endpointKey: '12345',
57-
endpoint: 'testEndpoint',
58-
authoringRegion: 'westus',
59-
defaultLanguage: 'en-us',
60-
environment: 'composer',
50+
luis: {
51+
name: 'sampleBot0',
52+
authoringKey: '12345',
53+
authoringEndpoint: 'testAuthoringEndpoint',
54+
endpointKey: '12345',
55+
endpoint: 'testEndpoint',
56+
authoringRegion: 'westus',
57+
defaultLanguage: 'en-us',
58+
environment: 'composer',
59+
},
60+
qna: {
61+
subscriptionKey: '12345',
62+
endpointKey: '',
63+
qnaRegion: 'westus',
64+
},
6165
});
6266
});
6367
});

Composer/packages/client/__tests__/components/skill.test.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ const recoilInitState = ({ set }) => {
5151
defaultLanguage: 'en-us',
5252
environment: 'composer',
5353
},
54+
qna: {
55+
subscriptionKey: '12345',
56+
qnaRegion: 'westus',
57+
endpointKey: '',
58+
},
5459
});
5560
};
5661

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
import * as React from 'react';
5+
import { fireEvent, waitFor } from '@bfc/test-utils';
6+
7+
import { TriggerCreationModal } from '../../src/components/ProjectTree/TriggerCreationModal';
8+
import { renderWithRecoil } from '../testUtils';
9+
10+
describe('<TriggerCreationModal/>', () => {
11+
const onSubmitMock = jest.fn();
12+
const onDismissMock = jest.fn();
13+
14+
function renderComponent() {
15+
return renderWithRecoil(
16+
<TriggerCreationModal isOpen dialogId={'todobot'} onDismiss={onDismissMock} onSubmit={onSubmitMock} />
17+
);
18+
}
19+
20+
it('should render the component', () => {
21+
const component = renderComponent();
22+
expect(component.container).toBeDefined();
23+
});
24+
25+
it('hould create a Luis Intent recognized', async () => {
26+
const component = renderComponent();
27+
const triggerType = component.getByTestId('triggerTypeDropDown');
28+
fireEvent.click(triggerType);
29+
30+
const luisOption = component.getByTitle('Intent recognized');
31+
fireEvent.click(luisOption);
32+
const node = await waitFor(() => component.getByTestId('triggerFormSubmit'));
33+
expect(node).toBeDisabled();
34+
});
35+
36+
it('should create a QnA Intent recognized', async () => {
37+
const component = renderComponent();
38+
const triggerType = component.getByTestId('triggerTypeDropDown');
39+
fireEvent.click(triggerType);
40+
41+
const qnaOption = component.getByTitle('QnA Intent recognized');
42+
fireEvent.click(qnaOption);
43+
44+
const node = await waitFor(() => component.getByTestId('triggerFormSubmit'));
45+
expect(node).toBeEnabled();
46+
});
47+
});
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
/* eslint-disable react-hooks/rules-of-hooks */
4+
import React from 'react';
5+
6+
import TableView from '../../../src/pages/knowledge-base/table-view';
7+
import CodeEditor from '../../../src/pages/knowledge-base/code-editor';
8+
import { renderWithRecoil } from '../../testUtils';
9+
import {
10+
projectIdState,
11+
localeState,
12+
dialogsState,
13+
qnaFilesState,
14+
settingsState,
15+
schemasState,
16+
dispatcherState,
17+
} from '../../../src/recoilModel';
18+
import mockProjectResponse from '../../../src/recoilModel/dispatchers/__tests__/mocks/mockProjectResponse.json';
19+
20+
const initialContent = `
21+
# ?question
22+
\`\`\`
23+
answer
24+
\`\`\`
25+
`;
26+
27+
const state = {
28+
projectId: 'test',
29+
dialogs: [{ id: '1' }, { id: '2' }],
30+
locale: 'en-us',
31+
qnaFiles: [
32+
{
33+
id: 'a.en-us',
34+
content: initialContent,
35+
qnaSections: [
36+
{
37+
Questions: [{ content: 'question', id: 1 }],
38+
Answer: 'answer',
39+
uuid: 1,
40+
},
41+
],
42+
},
43+
],
44+
settings: {
45+
defaultLanguage: 'en-us',
46+
languages: ['en-us', 'fr-fr'],
47+
},
48+
};
49+
50+
const updateQnAFileMock = jest.fn();
51+
52+
const initRecoilState = ({ set }) => {
53+
set(projectIdState, state.projectId);
54+
set(localeState, state.locale);
55+
set(dialogsState, state.dialogs);
56+
set(qnaFilesState, state.qnaFiles);
57+
set(settingsState, state.settings);
58+
set(schemasState, mockProjectResponse.schemas);
59+
set(dispatcherState, {
60+
updateQnAFile: updateQnAFileMock,
61+
});
62+
};
63+
64+
describe('QnA page all up view', () => {
65+
it('should render QnA page table view', () => {
66+
const { getByText, getByTestId } = renderWithRecoil(<TableView dialogId={'a'} />, initRecoilState);
67+
getByTestId('table-view');
68+
getByText('question (1)');
69+
getByText('answer');
70+
});
71+
72+
it('should render QnA page code editor', () => {
73+
renderWithRecoil(<CodeEditor dialogId={'a'} />, initRecoilState);
74+
});
75+
});

Composer/packages/client/__tests__/plugins.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ describe('mergePluginConfigs', () => {
5858
};
5959

6060
// @ts-expect-error
61-
expect(mergePluginConfigs(config1, config2).recognizers).toEqual(['recognizer 1', 'recognizer 2']);
61+
expect(mergePluginConfigs(config1, config2).recognizers).toEqual(['recognizer 2', 'recognizer 1']);
6262
});
6363

6464
it('replaces other arrays', () => {
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
import { renderHook } from '@bfc/test-utils/lib/hooks';
5+
import * as React from 'react';
6+
import { RecoilRoot } from 'recoil';
7+
8+
import { useTriggerApi } from '../../src/shell/triggerApi';
9+
import {
10+
projectIdState,
11+
localeState,
12+
luFilesState,
13+
lgFilesState,
14+
dialogsState,
15+
schemasState,
16+
dispatcherState,
17+
} from '../../src/recoilModel';
18+
import { Dispatcher } from '../../src/recoilModel/dispatchers';
19+
20+
const state = {
21+
dialogs: [
22+
{
23+
id: 'test',
24+
content: {},
25+
},
26+
],
27+
luFiles: [
28+
{
29+
content: 'test',
30+
id: 'test.en-us',
31+
intents: [],
32+
},
33+
],
34+
lgFiles: [
35+
{
36+
content: 'test',
37+
id: 'test.en-us',
38+
templates: [],
39+
},
40+
],
41+
schemas: { sdk: { content: {} } },
42+
focusPath: '',
43+
locale: 'en-us',
44+
projectId: 'test',
45+
};
46+
47+
describe('use triggerApi hooks', () => {
48+
let selectToMock, updateDialogMock, createLgTemplatesMock, createLuIntentMock, result;
49+
beforeEach(() => {
50+
selectToMock = jest.fn();
51+
updateDialogMock = jest.fn();
52+
createLgTemplatesMock = jest.fn();
53+
createLuIntentMock = jest.fn();
54+
55+
const initRecoilState = ({ set }) => {
56+
set(projectIdState, state.projectId);
57+
set(localeState, 'en-us');
58+
set(luFilesState, state.luFiles);
59+
set(lgFilesState, state.lgFiles);
60+
set(dialogsState, state.dialogs);
61+
set(schemasState, state.schemas);
62+
set(dispatcherState, (current: Dispatcher) => ({
63+
...current,
64+
selectTo: selectToMock,
65+
updateDialog: updateDialogMock,
66+
createLgTemplates: createLgTemplatesMock,
67+
createLuIntent: createLuIntentMock,
68+
}));
69+
};
70+
71+
const wrapper = (props: { children?: React.ReactNode }) => {
72+
const { children } = props;
73+
return <RecoilRoot initializeState={initRecoilState}>{children}</RecoilRoot>;
74+
};
75+
const rendered = renderHook(() => useTriggerApi(), {
76+
wrapper,
77+
});
78+
result = rendered.result;
79+
});
80+
81+
it('should create QnA trigger', async () => {
82+
const dialogId = 'test';
83+
const formData = {
84+
$kind: 'Microsoft.OnQnAMatch',
85+
errors: { $kind: '', intent: '', event: '', triggerPhrases: '', regEx: '', activity: '' },
86+
event: '',
87+
intent: '',
88+
regEx: '',
89+
triggerPhrases: '',
90+
};
91+
await result.current.createTrigger(dialogId, formData);
92+
expect(createLgTemplatesMock).toBeCalledTimes(1);
93+
expect(updateDialogMock).toBeCalledTimes(1);
94+
expect(createLgTemplatesMock).toBeCalledTimes(1);
95+
expect(updateDialogMock).toBeCalledTimes(1);
96+
});
97+
});

Composer/packages/client/__tests__/utils/dialogUtil.test.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,9 +186,11 @@ describe('getTriggerTypes', () => {
186186
const triggerTypes = getTriggerTypes();
187187
expect(triggerTypes).toEqual([
188188
{ key: 'Microsoft.OnIntent', text: 'Intent recognized' },
189+
{ key: 'Microsoft.OnQnAMatch', text: 'QnA Intent recognized' },
189190
{ key: 'Microsoft.OnUnknownIntent', text: 'Unknown intent' },
190191
{ key: 'Microsoft.OnDialogEvent', text: 'Dialog events' },
191192
{ key: 'Microsoft.OnActivity', text: 'Activities' },
193+
{ key: 'Microsoft.OnChooseIntent', text: 'Duplicated intents recognized' },
192194
{ key: 'OnCustomEvent', text: 'Custom events' },
193195
]);
194196
});

0 commit comments

Comments
 (0)