From 127cad91222dd05e0c08a9b6aec1714aa4d650d5 Mon Sep 17 00:00:00 2001 From: liweitian Date: Mon, 3 Aug 2020 23:58:31 +0800 Subject: [PATCH 1/2] update trigger actions --- .../ProjectTree/TriggerCreationModal.tsx | 95 ++++++++++++++++--- Composer/packages/client/src/constants.ts | 77 +++++++++++++++ .../client/src/pages/design/DesignPage.tsx | 27 ++++-- .../client/src/recoilModel/dispatchers/lg.ts | 15 +++ .../packages/client/src/utils/dialogUtil.ts | 7 +- .../packages/lib/shared/src/dialogFactory.ts | 78 ++++++++++++++- 6 files changed, 270 insertions(+), 29 deletions(-) diff --git a/Composer/packages/client/src/components/ProjectTree/TriggerCreationModal.tsx b/Composer/packages/client/src/components/ProjectTree/TriggerCreationModal.tsx index 218ac81b51..422991db05 100644 --- a/Composer/packages/client/src/components/ProjectTree/TriggerCreationModal.tsx +++ b/Composer/packages/client/src/components/ProjectTree/TriggerCreationModal.tsx @@ -38,9 +38,22 @@ import { qnaMatcherKey, onChooseIntentKey, } from '../../utils/dialogUtil'; -import { dialogsState, projectIdState, schemasState } from '../../recoilModel/atoms/botState'; +import { + dialogsState, + projectIdState, + schemasState, + localeState, + lgFilesState, +} from '../../recoilModel/atoms/botState'; import { userSettingsState } from '../../recoilModel'; -import { nameRegex } from '../../constants'; +import { + nameRegex, + adaptiveCardJsonBody, + whichOneDidYouMeanBody, + pickOne, + getAnswerReadBack, + getIntentReadBack, +} from '../../constants'; // -------------------- Styles -------------------- // @@ -201,7 +214,7 @@ export interface LuFilePayload { export interface LgFilePayload { id: string; - content: string; + lgTemplates: LgTemplate[]; } export interface QnAFilePayload { @@ -215,7 +228,7 @@ interface TriggerCreationModalProps { dialogId: string; isOpen: boolean; onDismiss: () => void; - onSubmit: (dialog: DialogInfo, intent?: LuIntentSection, lgTemplate?: LgTemplate[]) => void; + onSubmit: (dialog: DialogInfo, intent?: LuIntentSection, lgFilePayload?: { [key: string]: LgTemplate[] }) => void; } export const TriggerCreationModal: React.FC = (props) => { @@ -223,6 +236,9 @@ export const TriggerCreationModal: React.FC = (props) const dialogs = useRecoilValue(dialogsState); const projectId = useRecoilValue(projectIdState); const schemas = useRecoilValue(schemasState); + const locale = useRecoilValue(localeState); + const lgFiles = useRecoilValue(lgFilesState); + const commonlgFile = lgFiles.find(({ id }) => id === `common.${locale}`); const userSettings = useRecoilValue(userSettingsState); const dialogFile = dialogs.find((dialog) => dialog.id === dialogId); const isRegEx = (dialogFile?.content?.recognizer?.$kind ?? '') === regexRecognizerKey; @@ -290,25 +306,78 @@ export const TriggerCreationModal: React.FC = (props) const lgTemplateId1 = generateDesignerId(); const lgTemplateId2 = generateDesignerId(); const extraTriggerAttributes = {}; - let lgTemplates: LgTemplate[] = []; if (formData.$kind === onChooseIntentKey) { extraTriggerAttributes['actions[4].prompt'] = `\${TextInput_Prompt_${lgTemplateId1}()}`; extraTriggerAttributes['actions[5].elseActions[0].activity'] = `\${SendActivity_${lgTemplateId2}()}`; - lgTemplates = [ + const lgTemplates1: LgTemplate[] = [ { name: `TextInput_Prompt_${lgTemplateId1}`, - body: `[Activity\n - Attachments = \${json(AdaptiveCardJson())}\n - ]\n`, + body: '[Activity\n\ + Attachments = ${json(AdaptiveCardJson())}\n\ +]\n', } as LgTemplate, { name: `SendActivity_${lgTemplateId2}`, body: '- Sure, no worries.', } as LgTemplate, ]; + + let lgTemplates2: LgTemplate[] = [ + { + name: 'AdaptiveCardJson', + body: adaptiveCardJsonBody, + } as LgTemplate, + { + name: `whichOneDidYouMean`, + body: whichOneDidYouMeanBody, + } as LgTemplate, + { + name: 'pickOne', + body: pickOne, + } as LgTemplate, + { + name: 'getAnswerReadBack', + body: getAnswerReadBack, + } as LgTemplate, + { + name: 'getIntentReadBack', + body: getIntentReadBack, + } as LgTemplate, + ]; + + lgTemplates2 = lgTemplates2.filter( + (t) => commonlgFile?.templates.findIndex((clft) => clft.name === t.name) === -1 + ); + + const lgFilePayload = { + [`${dialogId}.${locale}`]: lgTemplates1, + [`common.${locale}`]: lgTemplates2, + }; + const newDialog = generateNewDialog(dialogs, dialogId, formData, schemas.sdk?.content, extraTriggerAttributes); + onSubmit(newDialog, undefined, lgFilePayload); + } else if (formData.$kind === qnaMatcherKey) { + extraTriggerAttributes['actions[0].actions[1].prompt'] = `\${TextInput_Prompt_${lgTemplateId1}()}`; + extraTriggerAttributes['actions[0].elseActions[0].activity'] = `\${SendActivity_${lgTemplateId2}()}`; + const lgTemplates: LgTemplate[] = [ + { + name: `TextInput_Prompt_${lgTemplateId1}`, + body: + '[Activity\n\ + Text = ${expandText(@answer)}\n\ + SuggestedActions = ${foreach(turn.recognized.answers[0].context.prompts, x, x.displayText)}\n\ +]', + } as LgTemplate, + { + name: `SendActivity_${lgTemplateId2}`, + body: '- ${expandText(@answer)}', + } as LgTemplate, + ]; + const lgFilePayload = { + [`${dialogId}.${locale}`]: lgTemplates, + }; + const newDialog = generateNewDialog(dialogs, dialogId, formData, schemas.sdk?.content, extraTriggerAttributes); + onSubmit(newDialog, undefined, lgFilePayload); } - const newDialog = generateNewDialog(dialogs, dialogId, formData, schemas.sdk?.content, extraTriggerAttributes); - onSubmit(newDialog, undefined, lgTemplates); } else { const newDialog = generateNewDialog(dialogs, dialogId, formData, schemas.sdk?.content, {}); onSubmit(newDialog); @@ -461,8 +530,8 @@ export const TriggerCreationModal: React.FC = (props) isRegEx ? formatMessage('What is the name of this trigger (RegEx)') : isLUISnQnA - ? formatMessage('What is the name of this trigger (LUIS)') - : formatMessage('What is the name of this trigger') + ? formatMessage('What is the name of this trigger (LUIS)') + : formatMessage('What is the name of this trigger') } styles={intent} onChange={onNameChange} diff --git a/Composer/packages/client/src/constants.ts b/Composer/packages/client/src/constants.ts index ae2f7de407..9db46a33d4 100644 --- a/Composer/packages/client/src/constants.ts +++ b/Composer/packages/client/src/constants.ts @@ -303,3 +303,80 @@ export const DefaultPublishConfig = { export const EmptyBotTemplateId = 'EmptyBot'; export const nameRegex = /^[a-zA-Z0-9-_]+$/; + +export const adaptiveCardJsonBody = + '-```\ +\n{\ + \n $schema: http://adaptivecards.io/schemas/adaptive-card.json,\ + \n version: 1.0,\ + \n type: AdaptiveCard,\ + \n speak: "",\ + \n body: [\ + \n {\ + \n type: TextBlock,\ + \n text: ${whichOneDidYouMean()},\ + \n weight: Bolder\ + \n },\ + \n {\ + \n type: TextBlock,\ + \n text: ${pickOne()},\ + \n separator: true\ + \n },\ + \n {\ + \n type: Input.ChoiceSet,\ + \n placeholder: Placeholder text,\ + \n id: userChosenIntent,\ + \n choices: [\ + \n {\ + \n title: ${getIntentReadBack()},\ + \n value: luisResult\ + \n },\ + \n {\ + \n title: ${getAnswerReadBack()},\ + \n value: qnaResult\ + \n },\ + \n {\ + \n title: None of the above,\ + \n value: none\ + \n }\ + \n ],\ + \n style: expanded,\ + \n value: luis\ + \n },\ + \n {\ + \n type: ActionSet,\ + \n actions: [\ + \n {\ + \n type: Action.Submit,\ + \n title: Submit,\ + \n data: {\ + \n intent: chooseIntentCardResponse\ + \n }\ + \n }\ + \n ]\ + \n }\ + \n ]\ +\n}\ +```'; + +export const whichOneDidYouMeanBody = "\ +- I'm not sure which one you mean.\ +- Hmmm, I find that to be ambiguous.\ +"; + +export const pickOne = ` +- Can you pick one ?\ +- Can you help clarify by choosing one ?\ +`; + +export const getIntentReadBack = ` +- SWITCH : \${toLower(dialog.luisResult.intent)}\n\ +- CASE : \${'GetUserProfile'}\n\ + - Start filling in your profile(GetUserProfile intent)\n\ +- DEFAULT :\n\ +- \${dialog.luisResult.intent}\n\ +`; + +export const getAnswerReadBack = ` +- See an answer from the Knowledge Base +`; diff --git a/Composer/packages/client/src/pages/design/DesignPage.tsx b/Composer/packages/client/src/pages/design/DesignPage.tsx index b0f43fc736..9173db0f7a 100644 --- a/Composer/packages/client/src/pages/design/DesignPage.tsx +++ b/Composer/packages/client/src/pages/design/DesignPage.tsx @@ -167,7 +167,7 @@ const DesignPage: React.FC { + const onTriggerCreationSubmit = async ( + dialog: DialogInfo, + intent?: LuIntentSection, + lgFilePayload?: { [key: string]: LgTemplate[] } + ) => { const dialogPayload = { id: dialog.id, projectId, @@ -288,14 +292,17 @@ const DesignPage: React.FC { - const lgPayload = { - id: lgFile.id, - template: t as LgTemplate, - }; - await createLgTemplate(lgPayload); - }); + if (lgFile && lgFilePayload) { + for (const key in lgFilePayload) { + await createLgTemplates({ id: key, templates: lgFilePayload[key] }); + } + // lgFilePayload.forEach(async (t) => { + // const lgPayload = { + // id: t.id, + // template: t.lgTemplate as LgTemplate, + // }; + // await createLgTemplate(lgPayload); + // }); } const index = get(dialog, 'content.triggers', []).length - 1; diff --git a/Composer/packages/client/src/recoilModel/dispatchers/lg.ts b/Composer/packages/client/src/recoilModel/dispatchers/lg.ts index 9c24d56c08..9802f461fd 100644 --- a/Composer/packages/client/src/recoilModel/dispatchers/lg.ts +++ b/Composer/packages/client/src/recoilModel/dispatchers/lg.ts @@ -178,6 +178,20 @@ export const lgDispatcher = () => { } ); + const createLgTemplates = useRecoilCallback( + (callbackHelpers: CallbackInterface) => async ({ id, templates }: { id: string; templates: LgTemplate[] }) => { + const { snapshot } = callbackHelpers; + const lgFiles = await snapshot.getPromise(lgFilesState); + const lgFile = lgFiles.find((file) => file.id === id); + if (!lgFile) { + throw new Error(formatMessage('lg file {id} does not exist.', { id })); + } + + const updatedFile = lgUtil.addTemplates(lgFile, templates, lgFileResolver(lgFiles)); + await updateLgFileState(callbackHelpers, { id, updatedFile, content: updatedFile.content }); + } + ); + const removeLgTemplate = useRecoilCallback( (callbackHelpers: CallbackInterface) => async ({ id, templateName }: { id: string; templateName: string }) => { const { snapshot } = callbackHelpers; @@ -232,6 +246,7 @@ export const lgDispatcher = () => { removeLgFile, updateLgTemplate, createLgTemplate, + createLgTemplates, removeLgTemplate, removeLgTemplates, copyLgTemplate, diff --git a/Composer/packages/client/src/utils/dialogUtil.ts b/Composer/packages/client/src/utils/dialogUtil.ts index 50c116dee7..e5a52b80aa 100644 --- a/Composer/packages/client/src/utils/dialogUtil.ts +++ b/Composer/packages/client/src/utils/dialogUtil.ts @@ -67,15 +67,14 @@ function insert(content, path: string, position: number | undefined, data: any) function updateTriggerActionsAttributes(trigger, extraTriggerAttributes) { const t = cloneDeep(trigger); - const { lgTemplateId } = extraTriggerAttributes; if (t.$kind === SDKKinds.OnQnAMatch) { - t.actions[0].activity = `$\{SendActivity_${lgTemplateId}()}`; + for (const key in extraTriggerAttributes) { + set(t, key, extraTriggerAttributes[key]); + } } if (t.$kind === SDKKinds.OnChooseIntent) { - //t.actions[1].prompt = `$\{ChoiceInput_Prompt_${lgTemplateId}}`; for (const key in extraTriggerAttributes) { set(t, key, extraTriggerAttributes[key]); - console.log(get(t, key, '')); } } return t; diff --git a/Composer/packages/lib/shared/src/dialogFactory.ts b/Composer/packages/lib/shared/src/dialogFactory.ts index 051f1ac99f..cf8a07241f 100644 --- a/Composer/packages/lib/shared/src/dialogFactory.ts +++ b/Composer/packages/lib/shared/src/dialogFactory.ts @@ -75,13 +75,87 @@ const initialDialogShape = () => ({ }, [SDKKinds.OnQnAMatch]: { $kind: SDKKinds.OnQnAMatch, + $designer: { + id: generateDesignerId(), + }, actions: [ { - $kind: SDKKinds.SendActivity, + $kind: SDKKinds.IfCondition, $designer: { id: generateDesignerId(), }, - activity: '', + condition: 'count(turn.recognized.answers[0].context.prompts) > 0', + actions: [ + { + $kind: SDKKinds.SetProperties, + $designer: { + id: generateDesignerId(), + }, + property: 'dialog.qnaContext', + value: '=turn.recognized.answers[0].context.prompts', + }, + { + $kind: SDKKinds.TextInput, + $designer: { + id: generateDesignerId(), + }, + disabled: false, + maxTurnCount: 3, + alwaysPrompt: true, + allowInterruptions: false, + prompt: '', + property: 'turn.qnaMultiTurnResponse', + }, + { + $kind: SDKKinds.SetProperties, + $designer: { + id: generateDesignerId(), + }, + property: 'turn.qnaMatchFromContext', + value: '=where(dialog.qnaContext, item, item.displayText == turn.qnaMultiTurnResponse)', + }, + { + $kind: SDKKinds.DeleteProperty, + $designer: { + id: generateDesignerId(), + }, + property: 'dialog.qnaContext', + }, + { + $kind: SDKKinds.IfCondition, + $designer: { + id: generateDesignerId(), + }, + condition: 'turn.qnaMatchFromContext && count(turn.qnaMatchFromContext) > 0', + actions: [ + { + $kind: SDKKinds.SetProperty, + $designer: { + id: generateDesignerId(), + }, + property: 'turn.qnaIdFromPrompt', + value: '=turn.qnaMatchFromContext[0].qnaId', + }, + ], + }, + { + $kind: SDKKinds.EmitEvent, + $designer: { + id: generateDesignerId(), + }, + eventName: 'activityReceived', + eventValue: '=turn.activity', + }, + ], + elseActions: [ + { + $kind: SDKKinds.SendActivity, + $designer: { + id: generateDesignerId(), + }, + activity: '', + }, + ], }, ], }, From c63cb8dd10ce81c82f2e6035ee3d3ab24f766504 Mon Sep 17 00:00:00 2001 From: liweitian Date: Tue, 4 Aug 2020 00:05:54 +0800 Subject: [PATCH 2/2] rebase --- .../src/components/ProjectTree/TriggerCreationModal.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Composer/packages/client/src/components/ProjectTree/TriggerCreationModal.tsx b/Composer/packages/client/src/components/ProjectTree/TriggerCreationModal.tsx index 422991db05..0c6740f737 100644 --- a/Composer/packages/client/src/components/ProjectTree/TriggerCreationModal.tsx +++ b/Composer/packages/client/src/components/ProjectTree/TriggerCreationModal.tsx @@ -530,8 +530,8 @@ export const TriggerCreationModal: React.FC = (props) isRegEx ? formatMessage('What is the name of this trigger (RegEx)') : isLUISnQnA - ? formatMessage('What is the name of this trigger (LUIS)') - : formatMessage('What is the name of this trigger') + ? formatMessage('What is the name of this trigger (LUIS)') + : formatMessage('What is the name of this trigger') } styles={intent} onChange={onNameChange}