diff --git a/DSL/DMapper/hbs/get_response_by_intent_id.handlebars b/DSL/DMapper/hbs/get_response_by_intent_id.handlebars new file mode 100644 index 00000000..c8bc93cf --- /dev/null +++ b/DSL/DMapper/hbs/get_response_by_intent_id.handlebars @@ -0,0 +1,6 @@ +{ +"response": { + "name": "{{response.[0]._id}}", + "text": {{#if response.[0]._source.response.[0].condition}}"{{{response.[0]._source.response.[1].text}}}"{{else}}"{{{response.[0]._source.response.[0].text}}}"{{/if}} +} +} \ No newline at end of file diff --git a/DSL/OpenSearch/deploy-opensearch.sh b/DSL/OpenSearch/deploy-opensearch.sh index 93793f3e..30c0eee6 100755 --- a/DSL/OpenSearch/deploy-opensearch.sh +++ b/DSL/OpenSearch/deploy-opensearch.sh @@ -14,6 +14,7 @@ curl -XDELETE "$URL/responses?ignore_unavailable=true" -u "$AUTH" --insecure curl -H "Content-Type: application/x-ndjson" -X PUT "$URL/responses" -ku "$AUTH" --data-binary "@fieldMappings/responses.json" if $MOCK_ALLOWED; then curl -H "Content-Type: application/x-ndjson" -X PUT "$URL/responses/_bulk" -ku "$AUTH" --data-binary "@mock/responses.json"; fi curl -L -X POST "$URL/_scripts/response-with-name-and-text" -H 'Content-Type: application/json' -H 'Cookie: customJwtCookie=test' --data-binary "@templates/response-with-name-and-text.json" +curl -L -X POST "$URL/_scripts/response" -H 'Content-Type: application/json' -H 'Cookie: customJwtCookie=test' --data-binary "@templates/response.json" # intents curl -XDELETE "$URL/intents?ignore_unavailable=true" -u "$AUTH" --insecure diff --git a/DSL/OpenSearch/templates/response.json b/DSL/OpenSearch/templates/response.json new file mode 100644 index 00000000..bca378ee --- /dev/null +++ b/DSL/OpenSearch/templates/response.json @@ -0,0 +1,23 @@ +{ + "script": { + "lang": "mustache", + "source": { + "size": 10000, + "query": { + "bool": { + "must": [ + { + "query_string": { + "query": "{{response_name}}", + "default_field": "name" + } + } + ] + } + } + }, + "params": { + "response_name": "" + } + } +} diff --git a/DSL/Ruuter.private/GET/rasa/intents/by-id.yml b/DSL/Ruuter.private/GET/rasa/intents/by-id.yml index 77a31594..c6e0c2db 100644 --- a/DSL/Ruuter.private/GET/rasa/intents/by-id.yml +++ b/DSL/Ruuter.private/GET/rasa/intents/by-id.yml @@ -50,7 +50,6 @@ getServiceIntentConnections: url: "[#TRAINING_RESQL]/get-service-intent-connections" result: getServiceIntentConnectionsResult -# todo if not using full, maybe modify to return one value getIntentListLastChanged: call: http.post args: diff --git a/DSL/Ruuter.private/GET/rasa/response-by-intent-id.yml b/DSL/Ruuter.private/GET/rasa/response-by-intent-id.yml new file mode 100644 index 00000000..daea5487 --- /dev/null +++ b/DSL/Ruuter.private/GET/rasa/response-by-intent-id.yml @@ -0,0 +1,42 @@ +declaration: + call: declare + version: 0.1 + description: "Get response by intent name" + method: get + accepts: json + returns: json + namespace: training + allowlist: + params: + - field: intent + type: string + description: "Intent ID" + +assign_values: + assign: + response_name: ${"utter_" + incoming.params.intent} + +getResponsesWithNameAndText: + call: http.post + args: + url: "[#TRAINING_OPENSEARCH]/responses/_search/template" + body: + id: "response" + params: + response_name: ${response_name} + result: getResponsesResult + +mapResponsesData: + call: http.post + args: + url: "[#TRAINING_DMAPPER]/hbs/training/get_response_by_intent_id" + headers: + type: "json" + body: + response: ${getResponsesResult.response.body.hits.hits} + result: responsesData + next: returnSuccess + +returnSuccess: + return: ${responsesData.response.body.response} + next: end diff --git a/DSL/Ruuter.private/GET/rasa/responses-list.yml b/DSL/Ruuter.private/GET/rasa/responses-list.yml index a76201ac..0bb02ba5 100644 --- a/DSL/Ruuter.private/GET/rasa/responses-list.yml +++ b/DSL/Ruuter.private/GET/rasa/responses-list.yml @@ -22,7 +22,7 @@ mapResponsesData: args: url: "[#TRAINING_DMAPPER]/hbs/training/get_responses_list" headers: - type: 'json' + type: "json" body: response: ${getResponsesResult.response.body.hits.hits} result: responsesData diff --git a/GUI/src/pages/Training/Intents/IntentDetails.tsx b/GUI/src/pages/Training/Intents/IntentDetails.tsx index 962a351d..177effb7 100644 --- a/GUI/src/pages/Training/Intents/IntentDetails.tsx +++ b/GUI/src/pages/Training/Intents/IntentDetails.tsx @@ -28,14 +28,14 @@ import LoadingDialog from 'components/LoadingDialog'; import useDocumentEscapeListener from 'hooks/useDocumentEscapeListener'; import { IntentWithExamplesCount } from 'types/intentWithExampleCounts'; -interface ResponsesResponse - extends Array<{ - name: string; - response: { - name: string; - text: string; - }[]; - }> {} +interface Response { + name: string; + text: string; +} + +interface ResponseResponse { + response: Response; +} interface IntentResponse { response: Intent; @@ -58,8 +58,7 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li const [refreshing, setRefreshing] = useState(false); const [showConnectToServiceModal, setShowConnectToServiceModal] = useState(false); const [showDeleteModal, setShowDeleteModal] = useState(false); - const [intentResponseText, setIntentResponseText] = useState(''); - const [intentResponseName, setIntentResponseName] = useState(''); + const [response, setResponse] = useState(null); const [intentRule, setIntentRule] = useState(''); const queryClient = useQueryClient(); @@ -74,7 +73,6 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li setIntent(intentResponse.response); // Also reset form states on choosing another intent from list setEditingIntentTitle(null); - setIntentResponseText(''); } }, [intentResponse]); @@ -82,21 +80,6 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li queryKey: [`intents/is-marked-for-service?intent=${intentId}`], }); - const setIntentResponse = useCallback( - (responsesResponse: ResponsesResponse | undefined) => { - if (!responsesResponse) return; - - const intentExistingResponse = responsesResponse[0].response.find( - (response: any) => `utter_${intentId}` === response.name - ); - if (intentExistingResponse) { - setIntentResponseText(intentExistingResponse.text); - setIntentResponseName(intentExistingResponse.name); - } - }, - [intentId] - ); - const addIntentRule = useCallback( (rulesResponse: RulesResponse | undefined) => { if (!rulesResponse) return; @@ -117,23 +100,27 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li setIntent(intentsResponse.response); setSelectedIntent(intentsResponse.response); - const resonsesResponse = await queryClient.fetchQuery(['responses-list']); - setIntentResponse(resonsesResponse); + const responseResponse = await queryClient.fetchQuery([ + `response-by-intent-id?intent=${intentId}`, + ]); + setResponse(responseResponse.response); const rulesResponse = await queryClient.fetchQuery(['rules']); addIntentRule(rulesResponse); }, - [addIntentRule, intentId, queryClient, setIntentResponse, setSelectedIntent] + [addIntentRule, intentId, queryClient, setResponse, setSelectedIntent] ); - // TODO: need to fetch response for the selected intent only - const { data: responsesResponse } = useQuery({ - queryKey: ['responses-list'], + const { data: responseResponse } = useQuery({ + queryKey: [`response-by-intent-id?intent=${intentId}`], }); useEffect(() => { - setIntentResponse(responsesResponse); - }, [responsesResponse, setIntentResponse]); + if (responseResponse?.response) setResponse(responseResponse.response); + }, [responseResponse]); + + const responseText = response?.text ?? ''; + const responseName = response?.name ?? ''; // TODO: need to fetch rules for the selected intent only const { data: rulesResponse } = useQuery({ @@ -365,7 +352,10 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li setRefreshing(true); }, onSuccess: async () => { - await queryClient.invalidateQueries({ queryKey: ['response-list'], refetchType: 'all' }); + await queryClient.invalidateQueries({ + queryKey: [`intents/is-marked-for-service?intent=${intentId}`], + refetchType: 'all', + }); toast.open({ type: 'success', title: t('global.notification'), @@ -409,17 +399,17 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li }); const handleIntentResponseSubmit = async () => { - if (intentResponseText === '' || !intent) return; + if (responseText === '' || !intent) return; const intentId = intent.id; addOrEditResponseMutation.mutate({ id: `utter_${intentId}`, - responseText: intentResponseText, - update: !!intentResponseName, + responseText, + update: !!responseName, }); - if (!intentResponseName) { + if (!responseName) { addRuleMutation.mutate({ data: { rule: `rule_${intentId}`, @@ -471,7 +461,7 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li setRefreshing(true); }, onSuccess: async () => { - await queryClient.invalidateQueries(['response-list']); + await queryClient.invalidateQueries([`intents/is-marked-for-service?intent=${intentId}`]); await queryClient.invalidateQueries(['rules']); toast.open({ type: 'success', @@ -629,7 +619,7 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li

{t('training.intents.responseTitle')}

= ({ intentId, setSelectedIntent, li hideLabel maxLength={RESPONSE_TEXT_LENGTH} showMaxLength - onChange={(e) => setIntentResponseText(e.target.value)} + onChange={(e) => + setResponse({ + name: response?.name ?? '', + text: e.target.value ?? '', + }) + } disableHeightResize />