From 6316b3befbd2e91f618de1c492c4d6f07e2de13f Mon Sep 17 00:00:00 2001 From: Igor Krupenja Date: Tue, 3 Dec 2024 11:29:52 +0200 Subject: [PATCH 01/44] Merge relevant from old branch --- .vscode/settings.json | 5 +- DSL/DMapper/hbs/get_intent_by_id.handlebars | 12 + ...ntent_names_from_example_counts.handlebars | 7 + .../hbs/get_intents_with_examples.handlebars | 2 +- ...get_intents_with_examples_count.handlebars | 6 +- DSL/OpenSearch/deploy-opensearch.sh | 1 + .../intents-with-examples-count.json | 23 +- DSL/Ruuter.private/GET/rasa/intents/by-id.yml | 90 ++ .../GET/rasa/intents/examples/count.yml | 33 - .../GET/rasa/intents/with-examples-count.yml | 65 ++ .../POST/rasa/intents/examples/count.yml | 36 - .../pages/Training/Intents/IntentDetails.tsx | 814 +++++++++++++++ GUI/src/pages/Training/Intents/IntentList.tsx | 45 +- .../pages/Training/Intents/IntentTabList.tsx | 39 +- GUI/src/pages/Training/Intents/index.tsx | 954 ++---------------- GUI/src/types/intentWithExampleCounts.ts | 3 + GUI/src/utils/compare.ts | 14 +- docker-compose.yml | 13 + 18 files changed, 1134 insertions(+), 1028 deletions(-) create mode 100644 DSL/DMapper/hbs/get_intent_by_id.handlebars create mode 100644 DSL/DMapper/hbs/get_intent_names_from_example_counts.handlebars create mode 100644 DSL/Ruuter.private/GET/rasa/intents/by-id.yml delete mode 100644 DSL/Ruuter.private/GET/rasa/intents/examples/count.yml create mode 100644 DSL/Ruuter.private/GET/rasa/intents/with-examples-count.yml delete mode 100644 DSL/Ruuter.private/POST/rasa/intents/examples/count.yml create mode 100644 GUI/src/pages/Training/Intents/IntentDetails.tsx create mode 100644 GUI/src/types/intentWithExampleCounts.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 7a73a41b..738ac3f1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,2 +1,5 @@ { -} \ No newline at end of file + "[typescript][typescriptreact][json]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + } +} diff --git a/DSL/DMapper/hbs/get_intent_by_id.handlebars b/DSL/DMapper/hbs/get_intent_by_id.handlebars new file mode 100644 index 00000000..bdfd5bf7 --- /dev/null +++ b/DSL/DMapper/hbs/get_intent_by_id.handlebars @@ -0,0 +1,12 @@ +{ + "id": "{{intents.intents.0._source.id}}", + "examples": [ + {{#each intents.intents.0._source.examples}} + "{{{this}}}"{{#unless @last}},{{/unless}} + {{/each}} + ], + "inModel": {{isInModel intents.intents.0._source.intent intents}}, + "serviceId": "{{findConnectedServiceId intents.intents.0._source.intent intents}}", + "isForService": "{{getObjectKeyFromObjectArray intents/intentsModifiedAt 'intent' intents.intents.0._source.intent 'isforservice'}}", + "modifiedAt": "{{intents.intentsModifiedAt.0.created}}" +} diff --git a/DSL/DMapper/hbs/get_intent_names_from_example_counts.handlebars b/DSL/DMapper/hbs/get_intent_names_from_example_counts.handlebars new file mode 100644 index 00000000..4469bde2 --- /dev/null +++ b/DSL/DMapper/hbs/get_intent_names_from_example_counts.handlebars @@ -0,0 +1,7 @@ +{ +"intents": [ +{{#each hits}} + "{{key}}"{{#unless @last}},{{/unless}} +{{/each}} +] +} diff --git a/DSL/DMapper/hbs/get_intents_with_examples.handlebars b/DSL/DMapper/hbs/get_intents_with_examples.handlebars index d9396188..f44a888d 100644 --- a/DSL/DMapper/hbs/get_intents_with_examples.handlebars +++ b/DSL/DMapper/hbs/get_intents_with_examples.handlebars @@ -2,7 +2,7 @@ "intents": [ {{#each hits}} { - "id": "{{_source._id}}", + "id": "{{_id}}", "title": "{{_source.intent}}", "examples": [ {{#each _source.examples}} diff --git a/DSL/DMapper/hbs/get_intents_with_examples_count.handlebars b/DSL/DMapper/hbs/get_intents_with_examples_count.handlebars index f6f1316d..fc0e9a62 100644 --- a/DSL/DMapper/hbs/get_intents_with_examples_count.handlebars +++ b/DSL/DMapper/hbs/get_intents_with_examples_count.handlebars @@ -2,8 +2,10 @@ "intents": [ {{#each buckets}} { - "intent": "{{key}}", - "examples_count": {{examples_counts.value}} + "id": "{{key}}", + "examples_count": {{examples_counts.value}}, + "inModel": {{isInModel key ../intents}}, + "modifiedAt": "{{findModifiedAt key ../intents/intentsModifiedAt}}" }{{#unless @last}},{{/unless}} {{/each}} ] diff --git a/DSL/OpenSearch/deploy-opensearch.sh b/DSL/OpenSearch/deploy-opensearch.sh index 93793f3e..f56f4688 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-by-intent-name" -H 'Content-Type: application/json' --data-binary "@templates/response-by-intent-name.json" # intents curl -XDELETE "$URL/intents?ignore_unavailable=true" -u "$AUTH" --insecure diff --git a/DSL/OpenSearch/templates/intents-with-examples-count.json b/DSL/OpenSearch/templates/intents-with-examples-count.json index ebef7f19..b05cb200 100644 --- a/DSL/OpenSearch/templates/intents-with-examples-count.json +++ b/DSL/OpenSearch/templates/intents-with-examples-count.json @@ -6,32 +6,31 @@ "aggs": { "hot": { "terms": { - "field": "intent.keyword" + "field": "intent", + "size": 10000 }, "aggs": { "examples_counts": { "value_count": { - "field": "examples.raw" + "field": "examples" } } } } }, "query": { - "bool": { - "must": [ - { - "query_string": { - "query": "*{{intent}}*", - "default_field": "intent" - } - } - ] + {{#intent}} + "wildcard": { + "intent": "*{{intent}}*" } + {{/intent}} + {{^intent}} + "match_all": {} + {{/intent}} } }, "params": { "intent": "" } } -} +} \ No newline at end of file diff --git a/DSL/Ruuter.private/GET/rasa/intents/by-id.yml b/DSL/Ruuter.private/GET/rasa/intents/by-id.yml new file mode 100644 index 00000000..77a31594 --- /dev/null +++ b/DSL/Ruuter.private/GET/rasa/intents/by-id.yml @@ -0,0 +1,90 @@ +declaration: + call: declare + version: 0.1 + description: "Get intent with details by ID" + method: get + accepts: json + returns: json + namespace: training + allowlist: + headers: + - field: cookie + type: string + description: "Cookie field" + params: + - field: intent + type: string + description: "Intent ID" + +assignValues: + assign: + intent: ${incoming.params.intent} + +getIntent: + call: http.post + args: + url: "[#TRAINING_OPENSEARCH]/intents/_search/template" + body: + id: "intent-with-name" + params: + intent: ${intent} + result: getIntentResult + +getDomainFile: + call: http.get + args: + url: "[#TRAINING_PUBLIC_RUUTER]/internal/domain-file" + headers: + cookie: ${incoming.headers.cookie} + result: getDomainDataResult + +checkIfIntentExists: + switch: + - condition: ${getIntentResult.response.body.hits.hits != null} + next: getServiceIntentConnections + next: returnNoIntentFound + +getServiceIntentConnections: + call: http.post + args: + 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: + url: "[#TRAINING_RESQL]/get-intents-list-last-changed" + body: + intentsList: ${getIntentResult.response.body.hits.hits[0]._id} + result: getIntentListLastChangedResult + +assignResults: + assign: + # TODO: Ideally, should use a single object, not array + intents: + intents: ${getIntentResult.response.body.hits.hits} + inmodel: ${getDomainDataResult.response.body.response.intents} + connections: ${getServiceIntentConnectionsResult.response.body} + intentsModifiedAt: ${getIntentListLastChangedResult.response.body} + +mapIntentData: + call: http.post + args: + url: "[#TRAINING_DMAPPER]/hbs/training/get_intent_by_id" + headers: + type: "json" + body: + intents: ${intents} + result: getIntentDataResult + next: returnSuccess + +returnSuccess: + return: ${getIntentDataResult.response.body} + next: end + +returnNoIntentFound: + return: "Error: no intent found" + wrapper: false + status: 404 + next: end diff --git a/DSL/Ruuter.private/GET/rasa/intents/examples/count.yml b/DSL/Ruuter.private/GET/rasa/intents/examples/count.yml deleted file mode 100644 index 6ac2e1da..00000000 --- a/DSL/Ruuter.private/GET/rasa/intents/examples/count.yml +++ /dev/null @@ -1,33 +0,0 @@ -declaration: - call: declare - version: 0.1 - description: "Decription placeholder for 'COUNT'" - method: get - accepts: json - returns: json - namespace: training - -getIntentsExampleCount: - call: http.post - args: - url: "[#TRAINING_OPENSEARCH]/intents/_search/template" - body: - id: "intents-with-examples-count" - params: - intent: '' - result: getIntentsResult - -mapIntentsData: - call: http.post - args: - url: "[#TRAINING_DMAPPER]/hbs/training/get_intents_with_examples_count" - headers: - type: 'json' - body: - buckets: ${getIntentsResult.response.body.aggregations.hot.buckets} - result: intentsData - next: returnSuccess - -returnSuccess: - return: ${intentsData.response.body} - next: end diff --git a/DSL/Ruuter.private/GET/rasa/intents/with-examples-count.yml b/DSL/Ruuter.private/GET/rasa/intents/with-examples-count.yml new file mode 100644 index 00000000..4cb27c3f --- /dev/null +++ b/DSL/Ruuter.private/GET/rasa/intents/with-examples-count.yml @@ -0,0 +1,65 @@ +declaration: + call: declare + version: 0.1 + description: "Decription placeholder for 'COUNT'" + method: get + accepts: json + returns: json + namespace: training + allowlist: + headers: + - field: cookie + type: string + description: "Cookie field" + +getIntentsExampleCount: + call: http.post + args: + url: "[#TRAINING_OPENSEARCH]/intents/_search/template" + body: + id: "intents-with-examples-count" + result: getIntentsResult + +getDomainFile: + call: http.get + args: + url: "[#TRAINING_PUBLIC_RUUTER]/internal/domain-file" + headers: + cookie: ${incoming.headers.cookie} + result: getDomainDataResult + +getIntentsNames: + call: http.post + args: + url: "[#TRAINING_DMAPPER]/hbs/training/get_intent_names_from_example_counts" + headers: + type: "json" + body: + hits: ${getIntentsResult.response.body.aggregations.hot.buckets} + result: getIntentsNamesResult + +getIntentListLastChanged: + call: http.post + args: + url: "[#TRAINING_RESQL]/get-intents-list-last-changed" + body: + intentsList: ${getIntentsNamesResult.response.body.intents} + result: getIntentsListLastChangedResult + +mapIntentsData: + call: http.post + args: + url: "[#TRAINING_DMAPPER]/hbs/training/get_intents_with_examples_count" + headers: + type: "json" + body: + buckets: ${getIntentsResult.response.body.aggregations.hot.buckets} + intents: + inmodel: ${getDomainDataResult.response.body.response.intents} + intentsModifiedAt: ${getIntentsListLastChangedResult.response.body} + result: intentsData + next: returnSuccess + +returnSuccess: + return: ${intentsData.response.body} + next: end diff --git a/DSL/Ruuter.private/POST/rasa/intents/examples/count.yml b/DSL/Ruuter.private/POST/rasa/intents/examples/count.yml deleted file mode 100644 index 2b99f01b..00000000 --- a/DSL/Ruuter.private/POST/rasa/intents/examples/count.yml +++ /dev/null @@ -1,36 +0,0 @@ -declaration: - call: declare - version: 0.1 - description: "Decription placeholder for 'COUNT'" - method: post - accepts: json - returns: json - namespace: training - -assign_values: - assign: - params: ${incoming.params} - -getIntentsExampleCount: - call: http.post - args: - url: "[#TRAINING_OPENSEARCH]/intents/_search/template" - body: - id: "intents-with-examples-count" - params: ${params} - result: getIntentsResult - -mapIntentsData: - call: http.post - args: - url: "[#TRAINING_DMAPPER]/hbs/training/get_intents_with_examples_count" - headers: - type: 'json' - body: - buckets: ${getIntentsResult.response.body.aggregations.hot.buckets} - result: intentsData - next: returnSuccess - -returnSuccess: - return: ${intentsData.response.body} - next: end diff --git a/GUI/src/pages/Training/Intents/IntentDetails.tsx b/GUI/src/pages/Training/Intents/IntentDetails.tsx new file mode 100644 index 00000000..60546119 --- /dev/null +++ b/GUI/src/pages/Training/Intents/IntentDetails.tsx @@ -0,0 +1,814 @@ +import { Track, FormInput, Button, Icon, Switch, Tooltip, FormTextarea, Dialog } from 'components'; +import { isHiddenFeaturesEnabled, RESPONSE_TEXT_LENGTH } from 'constants/config'; +import { format } from 'date-fns'; +import { t } from 'i18next'; +import { Dispatch, FC, SetStateAction, useCallback, useEffect, useMemo, useState } from 'react'; +import { MdOutlineSave, MdOutlineModeEditOutline } from 'react-icons/md'; +import { Intent } from 'types/intent'; +import IntentExamplesTable from './IntentExamplesTable'; +import * as Tabs from '@radix-ui/react-tabs'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import { AxiosError } from 'axios'; +import { + addExample, + addRemoveIntentModel, + deleteIntent, + downloadExamples, + editIntent, + markForService, + uploadExamples, +} from 'services/intents'; +import { useToast } from 'hooks/useToast'; +import { ROLES } from 'hoc/with-authorization'; +import useStore from '../../../store/store'; +import { editResponse } from 'services/responses'; +import { addStoryOrRule, deleteStoryOrRule } from 'services/stories'; +import { RuleDTO } from 'types/rule'; +import ConnectServiceToIntentModal from 'pages/ConnectServiceToIntentModal'; +import LoadingDialog from 'components/LoadingDialog'; +import { Entity } from 'types/entity'; +import useDocumentEscapeListener from 'hooks/useDocumentEscapeListener'; +import { IntentWithExamplesCount } from 'types/intentWithExampleCounts'; + +interface Response { + name: string; + text: string; +} + +interface IntentResponse { + response: Intent; +} + +interface IntentDetailsProps { + intentId: string; + entities: Entity[]; + setSelectedIntent: Dispatch>; +} + +const IntentDetails: FC = ({ intentId, setSelectedIntent, entities }) => { + const [intent, setIntent] = useState(null); + + const [editingIntentTitle, setEditingIntentTitle] = useState(null); + const [refreshing, setRefreshing] = useState(false); + const [isMarkedForService, setIsMarkedForService] = useState(false); + const [connectableIntent, setConnectableIntent] = useState(null); + const [deletableIntent, setDeletableIntent] = useState(null); + const [intentResponseText, setIntentResponseText] = useState(''); + const [intentResponseName, setIntentResponseName] = useState(''); + const [intentRule, setIntentRule] = useState(''); + + const queryClient = useQueryClient(); + const toast = useToast(); + + const { data: intentResponse } = useQuery({ + queryKey: [`intents/by-id?intent=${intentId}`], + }); + + // todo check IntentExamplesTable for /full query - and if not needed there, remove all related stuff + + useEffect(() => { + if (intentResponse) { + setIntent(intentResponse.response); + // Also reset form states on choosing another intent from list + setEditingIntentTitle(null); + setIntentResponseText(''); + } + }, [intentResponse]); + + const { data: isPossibleToUpdateMark, refetch } = useQuery({ + queryKey: [`intents/is-marked-for-service?intent=${intentId}`], + }); + + const queryRefresh = useCallback( + async (intent?: string) => { + const response = await queryClient.fetchQuery([`intents/by-id?intent=${intent ?? intentId}`]); + if (response) { + setIntent(response.response); + setSelectedIntent(response.response); + } + // todo setIsMarkedForService? + // todo also set intentResponseName and intentResponseText? + + // setIntentResponseName(null); + // setIntentResponseText(null); + // setIntentRule(null); + // queryClient.fetchQuery(['intents/full']).then((res: any) => { + // setRefreshing(false); + // if (intents.length > 0) { + // const newSelectedIntent = res.response.intents.find((intent: any) => intent.title === selectIntent) || null; + // if (newSelectedIntent) { + // setSelectedIntent({ + // id: newSelectedIntent.title, + // description: null, + // inModel: newSelectedIntent.inmodel, + // modifiedAt: newSelectedIntent.modifiedAt, + // examplesCount: newSelectedIntent.examples.length, + // examples: newSelectedIntent.examples, + // serviceId: newSelectedIntent.serviceId, + // isForService: newSelectedIntent.isForService, + // }); + // setIsMarkedForService(newSelectedIntent.isForService ? newSelectedIntent.isForService : false); + // // queryClient.fetchQuery(['responses-list']).then((res: any) => { + // // if (intentResponses.length > 0) { + // // const intentExistingResponse = res[0].response.find((response: any) => `utter_${newSelectedIntent.title}` === response.name); + // // if (intentExistingResponse) { + // // setIntentResponseText(intentExistingResponse.text); + // // setIntentResponseName(intentExistingResponse.name); + // // } + // // } + // // }) + // // queryClient.fetchQuery(['rules']).then((res: any) => { + // // if (rules.length > 0) { + // // const intentExistingRule = res.response.find((rule: any) => rule.id === `rule_${newSelectedIntent.title}`) + // // if (intentExistingRule) { + // // setIntentRule(intentExistingRule.id); + // // } + // // } + // // }) + // } + // } + // }); + }, + [queryClient] + ); + + // todo not yet fully working + const { data: responsesFullResponse } = useQuery({ + queryKey: ['responses-list'], + }); + + useEffect(() => { + if (responsesFullResponse) { + // @ts-ignore + const intentExistingResponse = responsesFullResponse[0].response.find( + (response: any) => `utter_${intent?.id}` === response.name + ); + console.log('intentExistingResponse', intentExistingResponse); + if (intentExistingResponse) { + setIntentResponseText(intentExistingResponse.text); + setIntentResponseName(intentExistingResponse.name); + } + } + }, [intent?.id, responsesFullResponse]); + + // todo not implemented, need to get rules for one intent only + const { data: rulesFullResponse } = useQuery({ + queryKey: ['rules'], + }); + + // let rulesFullList = rulesFullResponse?.response; + // let rules: Rule[] = []; + + // if (rulesFullList) { + // rulesFullList.forEach((rule: any) => { + // rules.push(rule); + // }); + // } + + // const queryRefreshOld = useCallback( + // function queryRefresh(selectIntent?: string) { + // setSelectedIntent(null); + // setIntentResponseName(null); + // setIntentResponseText(null); + // setIntentRule(null); + + // queryClient.fetchQuery(['intents/with-examples-count']).then((res: any) => { + // setRefreshing(false); + + // if (intents.length > 0) { + // const newSelectedIntent = res.response.intents.find((intent: any) => intent.title === selectIntent) || null; + // if (newSelectedIntent) { + // setSelectedIntent({ + // id: newSelectedIntent.title, + // description: null, + // inModel: newSelectedIntent.inmodel, + // modifiedAt: newSelectedIntent.modifiedAt, + // examplesCount: newSelectedIntent.examples.length, + // examples: newSelectedIntent.examples, + // serviceId: newSelectedIntent.serviceId, + // isForService: newSelectedIntent.isForService, + // }); + // setIsMarkedForService(newSelectedIntent.isForService ? newSelectedIntent.isForService : false); + + // queryClient.fetchQuery(['responses-list']).then((res: any) => { + // if (intentResponses.length > 0) { + // const intentExistingResponse = res[0].response.find( + // (response: any) => `utter_${newSelectedIntent.title}` === response.name + // ); + // if (intentExistingResponse) { + // setIntentResponseText(intentExistingResponse.text); + // setIntentResponseName(intentExistingResponse.name); + // } + // } + // }); + + // // queryClient.fetchQuery(['rules']).then((res: any) => { + // // if (rules.length > 0) { + // // const intentExistingRule = res.response.find((rule: any) => rule.id === `rule_${newSelectedIntent.title}`) + // // if (intentExistingRule) { + // // setIntentRule(intentExistingRule.id); + // // } + // // } + // // }) + // } + // } + // }); + // }, + // [queryClient, setSelectedIntent] + // ); + + const markIntentServiceMutation = useMutation({ + mutationFn: (data: { name: string; isForService: boolean }) => markForService(data), + onMutate: () => { + setRefreshing(true); + }, + onSuccess: () => { + toast.open({ + type: 'success', + title: t('global.notification'), + message: t('toast.intentUpdated'), + }); + setIsMarkedForService(!isMarkedForService); + }, + onError: (error: AxiosError) => { + toast.open({ + type: 'error', + title: t('global.notificationError'), + message: error.message, + }); + }, + onSettled: () => { + setRefreshing(false); + }, + }); + + const updateMarkForService = (value: boolean) => { + refetch().then((r) => { + if (!r.data) { + markIntentServiceMutation.mutate({ name: intent?.id ?? '', isForService: value }); + } + }); + }; + + const intentEditMutation = useMutation({ + mutationFn: (editIntentData: { oldName: string; newName: string }) => editIntent(editIntentData), + onMutate: () => { + setRefreshing(true); + }, + onSuccess: async () => { + await queryClient.invalidateQueries(['intents/with-examples-count']); + toast.open({ + type: 'success', + title: t('global.notification'), + message: t('toast.intentTitleSaved'), + }); + }, + onError: (error: AxiosError) => { + toast.open({ + type: 'error', + title: t('global.notificationError'), + message: error.message, + }); + }, + onSettled: () => { + setEditingIntentTitle(null); + setRefreshing(false); + }, + }); + + const editIntentName = async () => { + if (!intent || !editingIntentTitle) return; + + const newName = editingIntentTitle.replace(/\s+/g, '_'); + + await intentEditMutation.mutateAsync({ + oldName: intent.id, + newName, + }); + + queryRefresh(newName); + }; + + const isValidDate = (dateString: string | number | Date) => { + const date = new Date(dateString); + return !isNaN(date.getTime()); + }; + + const serviceEligible = () => { + const roles = useStore.getState().userInfo?.authorities; + if (roles && roles.length > 0) { + return ( + roles?.includes(ROLES.ROLE_ADMINISTRATOR) || + (roles?.includes(ROLES.ROLE_SERVICE_MANAGER) && roles?.includes(ROLES.ROLE_CHATBOT_TRAINER)) + ); + } + return false; + }; + + const intentUploadMutation = useMutation({ + mutationFn: ({ intentName, formData }: { intentName: string; formData: File }) => + uploadExamples(intentName, formData), + onMutate: () => { + setRefreshing(true); + }, + onSuccess: () => { + toast.open({ + type: 'success', + title: t('global.notification'), + message: t('toast.fileUploadedSuccessfully'), + }); + }, + onError: (error: AxiosError) => { + toast.open({ + type: 'error', + title: t('global.notificationError'), + message: error.message, + }); + }, + onSettled: () => { + setRefreshing(false); + queryRefresh(); + }, + }); + + const handleIntentExamplesUpload = () => { + const input = document.createElement('input'); + input.type = 'file'; + input.accept = '.csv'; + + input.addEventListener('change', async (event) => { + const fileInput = event.target as HTMLInputElement; + const files = fileInput.files; + + if (!files || files.length === 0) { + return; + } + + const file = files[0]; + + try { + await intentUploadMutation.mutateAsync({ + intentName: intent?.id || '', + formData: file, + }); + } catch (error) {} + }); + + input.click(); + }; + + const intentDownloadMutation = useMutation({ + mutationFn: (intentModelData: { intentName: string }) => downloadExamples(intentModelData), + onSuccess: async (data) => { + // @ts-ignore + const blob = new Blob([data], { type: 'text/csv' }); + const fileName = intent?.id + '.csv'; + + if (window.showSaveFilePicker) { + const handle = await window.showSaveFilePicker({ suggestedName: fileName }); + const writable = await handle.createWritable(); + await writable.write(blob); + writable.close(); + } else { + const url = window.URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = fileName; + a.click(); + window.URL.revokeObjectURL(url); + } + + toast.open({ + type: 'success', + title: t('global.notification'), + message: t('toast.examplesSentForDownloading'), + }); + }, + onError: (error: AxiosError) => { + if (error.name !== 'AbortError') { + toast.open({ + type: 'error', + title: t('global.notificationError'), + message: error.message, + }); + } + }, + }); + + const intentModelMutation = useMutation({ + mutationFn: (intentModelData: { name: string; inModel: boolean }) => addRemoveIntentModel(intentModelData), + onMutate: () => { + setRefreshing(true); + }, + onSuccess: () => { + if (intent?.inModel === true) { + toast.open({ + type: 'success', + title: t('global.notification'), + message: t('toast.intentRemovedFromModel'), + }); + } else { + toast.open({ + type: 'success', + title: t('global.notification'), + message: t('toast.intentAddedToModel'), + }); + } + }, + onError: (error: AxiosError) => { + toast.open({ + type: 'error', + title: t('global.notificationError'), + message: error.message, + }); + }, + onSettled: () => { + setEditingIntentTitle(null); + setRefreshing(false); + queryRefresh(); + }, + }); + + const addOrEditResponseMutation = useMutation({ + mutationFn: (intentResponseData: { id: string; responseText: string; update: boolean }) => + editResponse(intentResponseData.id, intentResponseData.responseText, intentResponseData.update), + onMutate: () => { + setRefreshing(true); + }, + onSuccess: async () => { + // todo invalidate + await queryClient.invalidateQueries(['response-list']); + toast.open({ + type: 'success', + title: t('global.notification'), + message: t('toast.newResponseAdded'), + }); + }, + onError: (error: AxiosError) => { + toast.open({ + type: 'error', + title: t('global.notificationError'), + message: error.message, + }); + }, + onSettled: () => { + setRefreshing(false); + }, + }); + + const addRuleMutation = useMutation({ + mutationFn: ({ data }: { data: RuleDTO }) => addStoryOrRule(data as RuleDTO, 'rules'), + onMutate: () => { + setRefreshing(true); + }, + onSuccess: () => { + toast.open({ + type: 'success', + title: t('global.notification'), + message: t('toast.storyAdded'), + }); + }, + onError: (error: AxiosError) => { + toast.open({ + type: 'error', + title: t('global.notificationError'), + message: error.message, + }); + }, + onSettled: () => { + setRefreshing(false); + }, + }); + + // todo clean up and fix errors + const handleIntentResponseSubmit = async (newId?: string) => { + if (!intentResponseText || intentResponseText == '' || !intent) return; + + const intentId = newId || intent.id; + + await addOrEditResponseMutation.mutate({ + id: `utter_${intentId}`, + responseText: intentResponseText, + update: !!intentResponseName, + }); + + if (!intentResponseName) { + await addRuleMutation.mutate({ + data: { + rule: `rule_${intentId}`, + steps: [ + { + intent: intentId, + }, + { + action: `utter_${intentId}`, + }, + ], + }, + }); + } + + if (editingIntentTitle) { + await intentEditMutation.mutateAsync({ + oldName: intent.id, + newName: newId, + }); + queryRefresh(); + } + }; + + const examplesData = useMemo( + () => intent?.examples.map((example, index) => ({ id: index, value: example })), + [intent?.examples] + ); + + const addExamplesMutation = useMutation({ + mutationFn: (addExamplesData: { intentName: string; intentExamples: string[]; newExamples: string }) => + addExample(addExamplesData), + onMutate: () => { + setRefreshing(true); + }, + onSuccess: () => { + toast.open({ + type: 'success', + title: t('global.notification'), + message: t('toast.newExampleAdded'), + }); + }, + onError: (error: AxiosError) => { + toast.open({ + type: 'error', + title: t('global.notificationError'), + message: error.message, + }); + }, + onSettled: () => { + queryRefresh(); + }, + }); + + const handleNewExample = (example: string) => { + if (!intent) return; + addExamplesMutation.mutate({ + intentName: intent.id, + intentExamples: intent.examples, + newExamples: example.replace(/(\t|\n)+/g, ' ').trim(), + }); + }; + + const deleteIntentMutation = useMutation({ + mutationFn: (name: string) => deleteIntent({ name }), + onMutate: () => { + setRefreshing(true); + setDeletableIntent(null); + setConnectableIntent(null); + setSelectedIntent(null); + }, + onSuccess: async () => { + await queryClient.invalidateQueries(['intents/with-examples-count']); + toast.open({ + type: 'success', + title: t('global.notification'), + message: t('toast.intentDeleted'), + }); + }, + onError: (error: AxiosError) => { + toast.open({ + type: 'error', + title: t('global.notificationError'), + message: error.message, + }); + }, + onSettled: () => { + queryRefresh(); + }, + }); + + const deleteRuleWithIntentMutation = useMutation({ + mutationFn: (id: string | number) => deleteStoryOrRule(id, 'rules'), + onMutate: () => { + setRefreshing(true); + }, + onSuccess: async () => { + // todo invalidate + await queryClient.invalidateQueries(['response-list']); + await queryClient.invalidateQueries(['rules']); + toast.open({ + type: 'success', + title: t('global.notification'), + message: t('toast.storyDeleted'), + }); + }, + onError: (error: AxiosError) => { + toast.open({ + type: 'error', + title: t('global.notificationError'), + message: error.message, + }); + }, + onSettled: () => { + deleteIntentMutation.mutate(deletableIntent!.id); + setRefreshing(false); + }, + }); + + const handleDeleteIntent = async () => { + if (intentRule) { + await deleteRuleWithIntentMutation.mutateAsync(intentRule); + } else { + await deleteIntentMutation.mutateAsync(deletableIntent!.id); + } + }; + + const updateSelectedIntent = (updatedIntent: Intent) => { + setSelectedIntent(null); + setTimeout(() => setSelectedIntent(updatedIntent), 20); + }; + + useDocumentEscapeListener(() => setEditingIntentTitle(null)); + + if (!intent) return <>Loading...; + + return ( + +
+ + + + {editingIntentTitle ? ( + setEditingIntentTitle(e.target.value)} + hideLabel + /> + ) : ( +

{intent.id.replace(/_/g, ' ')}

+ )} + {editingIntentTitle ? ( + + ) : ( + + )} + +

+ {t('global.modifiedAt')}: + {isValidDate(intent.modifiedAt) + ? ` ${format(new Date(intent.modifiedAt), 'dd.MM.yyyy')}` + : ` ${t('global.missing')}`} +

+ + {serviceEligible() && ( + + updateMarkForService(value)} + checked={isMarkedForService} + disabled={isPossibleToUpdateMark} + /> + + )} + + + + {intent.inModel ? ( + + ) : ( + + )} + {isHiddenFeaturesEnabled && serviceEligible() && ( + + + + + + )} + + + +
+ +
+ {intent?.examples && ( + +
+ {/* todo missing/broken props */} + +
+
+ + +

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

+ setIntentResponseText(e.target.value)} + disableHeightResize + /> + + + +
+ + )} +
+ + {deletableIntent !== null && ( + setDeletableIntent(null)} + footer={ + <> + + + + } + > +

{t('global.removeValidation')}

+
+ )} + + {connectableIntent !== null && ( + setConnectableIntent(null)} /> + )} + + {refreshing && ( + +

{t('global.updatingDataBody')}

+
+ )} +
+ ); +}; + +export default IntentDetails; diff --git a/GUI/src/pages/Training/Intents/IntentList.tsx b/GUI/src/pages/Training/Intents/IntentList.tsx index 2d624504..5d85cde0 100644 --- a/GUI/src/pages/Training/Intents/IntentList.tsx +++ b/GUI/src/pages/Training/Intents/IntentList.tsx @@ -1,13 +1,13 @@ -import { FC } from "react"; +import { FC } from 'react'; import * as Tabs from '@radix-ui/react-tabs'; import { Icon, Tooltip, Track } from 'components'; import { MdCheckCircleOutline } from 'react-icons/md'; -import { useTranslation } from "react-i18next"; -import { Intent } from "types/intent"; -import "./IntentTabList.scss"; +import { useTranslation } from 'react-i18next'; +import './IntentTabList.scss'; +import { IntentWithExamplesCount } from 'types/intentWithExampleCounts'; interface IntentListProps { - intents: Intent[]; + intents: IntentWithExamplesCount[]; } const IntentList: FC = ({ intents }) => { @@ -16,41 +16,28 @@ const IntentList: FC = ({ intents }) => { return ( <> {intents.map((intent, index) => ( - + - - {intent.id.replace(/_/g, ' ')} - + {intent.id.replace(/_/g, ' ')} - - {intent.examplesCount} - + {intent.examplesCount} - {!intent.inModel - ? - : ( - + {!intent.inModel ? ( + + ) : ( + - } + icon={} /> - - )} + + )} ))} ); -} +}; export default IntentList; diff --git a/GUI/src/pages/Training/Intents/IntentTabList.tsx b/GUI/src/pages/Training/Intents/IntentTabList.tsx index 137494d2..bdbbd3d4 100644 --- a/GUI/src/pages/Training/Intents/IntentTabList.tsx +++ b/GUI/src/pages/Training/Intents/IntentTabList.tsx @@ -1,14 +1,14 @@ -import { FC, useMemo, useState } from "react"; +import { FC, useMemo, useState } from 'react'; import { SwitchBox } from 'components'; -import { useTranslation } from "react-i18next"; -import { Intent } from "types/intent"; -import { compareInModel, compareInModelReversed } from "utils/compare"; -import IntentList from "./IntentList"; -import "./IntentTabList.scss"; +import { useTranslation } from 'react-i18next'; +import { compareInModel, compareInModelReversed } from 'utils/compare'; +import IntentList from './IntentList'; +import './IntentTabList.scss'; +import { IntentWithExamplesCount } from 'types/intentWithExampleCounts'; interface IntentTabListProps { filter: string; - intents: Intent[]; + intents: IntentWithExamplesCount[]; onDismiss: () => void; } @@ -45,26 +45,23 @@ const IntentTabList: FC = ({ filter, intents, onDismiss }) = return (
-
-
- +
+
+ { onDismiss(); setShowCommons(!showCommons); - }} + }} />
-
- - setOrder(e.target.value)}> @@ -79,7 +76,7 @@ const IntentTabList: FC = ({ filter, intents, onDismiss }) = {showCommons &&
} {showCommons && }
- ) -} + ); +}; export default IntentTabList; diff --git a/GUI/src/pages/Training/Intents/index.tsx b/GUI/src/pages/Training/Intents/index.tsx index e2b20415..eac65768 100644 --- a/GUI/src/pages/Training/Intents/index.tsx +++ b/GUI/src/pages/Training/Intents/index.tsx @@ -1,286 +1,98 @@ -import { FC, useCallback, useEffect, useMemo, useState } from 'react'; +import { FC, useCallback, useEffect, useState } from 'react'; import { useSearchParams } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import * as Tabs from '@radix-ui/react-tabs'; -import { format } from 'date-fns'; import { AxiosError } from 'axios'; -import {MdOutlineModeEditOutline, MdOutlineSave,} from 'react-icons/md'; -import {Button, Dialog, FormInput, FormTextarea, Icon, Switch, Tooltip, Track} from 'components'; -import useDocumentEscapeListener from 'hooks/useDocumentEscapeListener'; +import { Button, Dialog, FormInput, Track } from 'components'; import { useToast } from 'hooks/useToast'; import { Intent } from 'types/intent'; import { Entity } from 'types/entity'; -import { - addExample, - addIntent, - addRemoveIntentModel, - deleteIntent, - downloadExamples, - editIntent, - getLastModified, markForService, - turnIntentIntoService, - uploadExamples, -} from 'services/intents'; -import IntentExamplesTable from './IntentExamplesTable'; +import { addIntent, turnIntentIntoService } from 'services/intents'; import LoadingDialog from '../../../components/LoadingDialog'; -import ConnectServiceToIntentModal from 'pages/ConnectServiceToIntentModal'; import withAuthorization, { ROLES } from 'hoc/with-authorization'; -import { isHiddenFeaturesEnabled, RESPONSE_TEXT_LENGTH } from 'constants/config'; -import { deleteResponse, editResponse } from '../../../services/responses'; -import { Rule, RuleDTO } from '../../../types/rule'; -import { addStoryOrRule, deleteStoryOrRule } from '../../../services/stories'; import IntentTabList from './IntentTabList'; -import useStore from "../../../store/store"; +import IntentDetails from './IntentDetails'; +import { IntentWithExamplesCount } from 'types/intentWithExampleCounts'; -type Response = { - name: string; - text: string; -} +// TODO: change examples_count to examplesCount when possible with changes in CommonIntents +type IntentWithExamplesCountResponse = Pick & { examples_count: number }; + +type IntentsWithExamplesCountResponse = { + response: { + intents: IntentWithExamplesCountResponse[]; + }; +}; + +const intentResponseToIntent = (intent: IntentWithExamplesCountResponse): IntentWithExamplesCount => ({ + ...intent, + examplesCount: intent.examples_count, + isCommon: intent.id.startsWith('common_'), +}); const Intents: FC = () => { const { t } = useTranslation(); const queryClient = useQueryClient(); const toast = useToast(); - - const [intentResponseName, setIntentResponseName] = useState(''); - const [intentResponseText, setIntentResponseText] = useState(''); - const [searchParams] = useSearchParams(); - const [filter, setFilter] = useState(''); - const [isMarkedForService, setIsMarkedForService] = useState(false); + const [intents, setIntents] = useState([]); + const [selectedIntent, setSelectedIntent] = useState(null); const [refreshing, setRefreshing] = useState(false); + const [filter, setFilter] = useState(''); + const [turnIntentToServiceIntent, setTurnIntentToServiceIntent] = useState(null); - const [editingIntentTitle, setEditingIntentTitle] = useState(null); - const [selectedIntent, setSelectedIntent] = useState(null); - const [intentRule, setIntentRule] = useState(''); - - const [deletableIntent, setDeletableIntent] = useState(null); - const [connectableIntent, setConnectableIntent] = useState( - null - ); - const [turnIntentToServiceIntent, setTurnIntentToServiceIntent] = - useState(null); - - let intentParam; - - const updateMarkForService = (value: boolean) => { - refetch().then(r => { - if(!r.data) { - markIntentServiceMutation.mutate({ name: selectedIntent?.id ?? '', isForService: value }) - } - }); - } - - const { data: isPossibleToUpdateMark, refetch } = useQuery({ queryKey: [`intents/is-marked-for-service?intent=${selectedIntent?.id}`]}) + const { data: intentsFullResponse, isLoading } = useQuery({ + queryKey: ['intents/with-examples-count'], + }); - const serviceEligable = () => { - const roles = useStore.getState().userInfo?.authorities; - if(roles && roles.length > 0) { - return roles?.includes(ROLES.ROLE_ADMINISTRATOR) || (roles?.includes(ROLES.ROLE_SERVICE_MANAGER) && roles?.includes(ROLES.ROLE_CHATBOT_TRAINER)) + useEffect(() => { + if (intentsFullResponse) { + setIntents(intentsFullResponse.response.intents.map((intent) => intentResponseToIntent(intent))); } - return false; - } + }, [intentsFullResponse]); - const { - data: intentsFullResponse, - isLoading, - } = useQuery({ - queryKey: ['intents/full'], - }); - - const { data: entities } = useQuery({ + const { data: entitiesResponse } = useQuery<{ response: Entity[] }>({ queryKey: ['entities'], }); - const { data: responsesFullResponse } = useQuery({ - queryKey: ['responses-list'], - }); - - const { data: rulesFullResponse } = useQuery({ - queryKey: ['rules'], - }) - - let intentsFullList = intentsFullResponse?.response?.intents; - let intents: Intent[] = []; + const queryRefresh = useCallback( + async (selectIntent?: string) => { + // todo this is necessary to reset - but maybe in child component IntentDetails.tsx with responses-list query? + // setIntentResponseText(null); - let intentResponsesFullList = responsesFullResponse ? responsesFullResponse[0].response : null; - let intentResponses: Response[] = []; + const response = await queryClient.fetchQuery(['intents/with-examples-count']); - let rulesFullList = rulesFullResponse?.response; - let rules: Rule[] = []; + if (response) { + setIntents(response.response.intents.map((intent) => intentResponseToIntent(intent))); - if (intentsFullList) { - intentsFullList.forEach((intent: any) => { - const countExamples = intent.examples.length; - const newIntent: Intent = { - id: intent.title, - description: null, - inModel: intent.inmodel, - modifiedAt: intent.modifiedAt, - examplesCount: countExamples, - examples: intent.examples, - serviceId: intent.serviceId, - isCommon: intent.title.startsWith('common_'), - }; - intents.push(newIntent); - }); - intentParam = searchParams.get('intent'); - } + const selectedIntent = response.response.intents.find( + (intent) => intent.id === selectIntent + ) as IntentWithExamplesCountResponse; - if (intentResponsesFullList) { - intentResponsesFullList?.forEach((response: any) => { - const newIntentResponse: Response = { - name: response.name, - text: response.text, + setSelectedIntent(intentResponseToIntent(selectedIntent)); } - intentResponses.push(newIntentResponse); - }); - } - - if (rulesFullList) { - rulesFullList.forEach((rule: any) => { - rules.push(rule); - }); - } + }, + [queryClient] + ); useEffect(() => { - if (!intentParam || intentsFullList?.length !== intents?.length) return; + let intentParam = searchParams.get('intent'); + if (!intentParam) return; - const queryIntent = intents.find( - (intent) => intent.id === intentParam - ); + const queryIntent = intents.find((intent) => intent.id === intentParam); if (queryIntent) { setSelectedIntent(queryIntent); } - }, [intentParam]); - - const queryRefresh = useCallback( - function queryRefresh(selectIntent: string | null) { - setSelectedIntent(null); - setIntentResponseName(null); - setIntentResponseText(null); - setIntentRule(null); - - queryClient.fetchQuery(['intents/full']).then((res: any) => { - setRefreshing(false); - - if (intents.length > 0) { - const newSelectedIntent = res.response.intents.find((intent: any) => intent.title === selectIntent) || null; - if (newSelectedIntent) { - setSelectedIntent({ - id: newSelectedIntent.title, - description: null, - inModel: newSelectedIntent.inmodel, - modifiedAt: newSelectedIntent.modifiedAt, - examplesCount: newSelectedIntent.examples.length, - examples: newSelectedIntent.examples, - serviceId: newSelectedIntent.serviceId, - isForService: newSelectedIntent.isForService - }); - setIsMarkedForService(newSelectedIntent.isForService ? newSelectedIntent.isForService : false); - - queryClient.fetchQuery(['responses-list']).then((res: any) => { - if (intentResponses.length > 0) { - const intentExistingResponse = res[0].response.find((response: any) => `utter_${newSelectedIntent.title}` === response.name); - if (intentExistingResponse) { - setIntentResponseText(intentExistingResponse.text); - setIntentResponseName(intentExistingResponse.name); - } - } - }) - - queryClient.fetchQuery(['rules']).then((res: any) => { - if (rules.length > 0) { - const intentExistingRule = res.response.find((rule: any) => rule.id === `rule_${newSelectedIntent.title}`) - if (intentExistingRule) { - setIntentRule(intentExistingRule.id); - } - } - }) - } - } - }); - }, - [intents, intentResponses, rules] - ); - - function isValidDate(dateString: string | number | Date) { - const date = new Date(dateString); - return !isNaN(date.getTime()); - } - - const updateSelectedIntent = (updatedIntent: Intent) => { - setSelectedIntent(null); - setTimeout(() => setSelectedIntent(updatedIntent), 20); - }; - - const addExamplesMutation = useMutation({ - mutationFn: (addExamplesData: { - intentName: string; - intentExamples: string[]; - newExamples: string; - }) => addExample(addExamplesData), - onMutate: () => { - setRefreshing(true); - }, - onSuccess: () => { - toast.open({ - type: 'success', - title: t('global.notification'), - message: t('toast.newExampleAdded'), - }); - }, - onError: (error: AxiosError) => { - toast.open({ - type: 'error', - title: t('global.notificationError'), - message: error.message, - }); - }, - onSettled: () => { - queryRefresh(selectedIntent!.id); - }, - }); - - const deleteIntentMutation = useMutation({ - mutationFn: (name: string) => deleteIntent({ name }), - onMutate: () => { - setRefreshing(true); - setDeletableIntent(null); - setConnectableIntent(null); - setSelectedIntent(null); - }, - onSuccess: async () => { - await queryClient.invalidateQueries(['intents/full']); - toast.open({ - type: 'success', - title: t('global.notification'), - message: t('toast.intentDeleted'), - }); - }, - onError: (error: AxiosError) => { - toast.open({ - type: 'error', - title: t('global.notificationError'), - message: error.message, - }); - }, - onSettled: () => { - queryRefresh(''); - }, - }); - - const intentModifiedMutation = useMutation({ - mutationFn: (data: { intentName: string }) => getLastModified(data), - }); + }, [intents, searchParams]); + // TODO: This is not used at all at the moment + // TODO: If this is needed at some point, errors should be fixed + // todo check first point here: https://github.com/buerokratt/Training-Module/pull/663 const turnIntentIntoServiceMutation = useMutation({ - mutationFn: ({ intent }: { intent: Intent }) => - turnIntentIntoService(intent), + mutationFn: ({ intent }: { intent: Intent }) => turnIntentIntoService(intent), onMutate: () => { setRefreshing(true); }, @@ -305,41 +117,12 @@ const Intents: FC = () => { }, }); - useDocumentEscapeListener(() => setEditingIntentTitle(null)); - - - const examplesData = useMemo( - () => selectedIntent?.examples.map((example, index) => ({ id: index, value: example })), - [selectedIntent?.examples] - ); - const handleTabsValueChange = useCallback( (value: string) => { - setEditingIntentTitle(null); - setSelectedIntent(null); - setIntentResponseName(null); - setIntentResponseText(null); - - if (!intents) return; const selectedIntent = intents.find((intent) => intent.id === value); - if (selectedIntent) { - queryRefresh(selectedIntent?.id || ''); - intentModifiedMutation.mutate( - { intentName: selectedIntent.id }, - { - onSuccess: (data) => { - selectedIntent.modifiedAt = data.response; - setSelectedIntent(selectedIntent); - }, - onError: (error) => { - selectedIntent.modifiedAt = ''; - setSelectedIntent(selectedIntent); - }, - } - ); - } + setSelectedIntent(selectedIntent!); }, - [intentModifiedMutation, intents, queryRefresh] + [intents] ); const newIntentMutation = useMutation({ @@ -363,389 +146,11 @@ const Intents: FC = () => { }, onSettled: () => { setRefreshing(false); - queryRefresh(filter.trim().replace(/\s+/g, '_')) + queryRefresh(filter.trim().replace(/\s+/g, '_')); setFilter(''); }, }); - const intentEditMutation = useMutation({ - mutationFn: (editIntentData: { oldName: string; newName: string }) => - editIntent(editIntentData), - onMutate: () => { - setRefreshing(true); - }, - onSuccess: async () => { - await queryClient.invalidateQueries(['intents/full']); - toast.open({ - type: 'success', - title: t('global.notification'), - message: t('toast.intentTitleSaved'), - }); - }, - onError: (error: AxiosError) => { - toast.open({ - type: 'error', - title: t('global.notificationError'), - message: error.message, - }); - }, - onSettled: () => { - setEditingIntentTitle(null); - setRefreshing(false); - }, - }); - - const markIntentServiceMutation = useMutation({ - mutationFn: (data: { name: string, isForService: boolean }) => markForService(data), - onMutate: () => { - setRefreshing(true); - }, - onSuccess: () => { - toast.open({ - type: 'success', - title: t('global.notification'), - message: t('toast.intentUpdated'), - }); - setIsMarkedForService(!isMarkedForService); - }, - onError: (error: AxiosError) => { - toast.open({ - type: 'error', - title: t('global.notificationError'), - message: error.message, - }); - }, - onSettled: () => { - setRefreshing(false); - }, - }); - - const intentModelMutation = useMutation({ - mutationFn: (intentModelData: { name: string; inModel: boolean }) => - addRemoveIntentModel(intentModelData), - onMutate: () => { - setRefreshing(true); - }, - onSuccess: () => { - if (selectedIntent?.inModel === true) { - toast.open({ - type: 'success', - title: t('global.notification'), - message: t('toast.intentRemovedFromModel'), - }); - } else { - toast.open({ - type: 'success', - title: t('global.notification'), - message: t('toast.intentAddedToModel'), - }); - } - }, - onError: (error: AxiosError) => { - toast.open({ - type: 'error', - title: t('global.notificationError'), - message: error.message, - }); - }, - onSettled: () => { - setEditingIntentTitle(null); - setRefreshing(false); - queryRefresh(selectedIntent?.id || ''); - }, - }); - - const intentDownloadMutation = useMutation({ - mutationFn: (intentModelData: { intentName: string }) => - downloadExamples(intentModelData), - onSuccess: async (data) => { - // @ts-ignore - const blob = new Blob([data], { type: 'text/csv' }); - const fileName = selectedIntent?.id + '.csv'; - - if (window.showSaveFilePicker) { - const handle = await window.showSaveFilePicker({ suggestedName: fileName }); - const writable = await handle.createWritable(); - await writable.write(blob); - writable.close(); - } else { - const url = window.URL.createObjectURL(blob); - const a = document.createElement('a'); - a.href = url; - a.download = fileName; - a.click(); - window.URL.revokeObjectURL(url); - } - - toast.open({ - type: 'success', - title: t('global.notification'), - message: t('toast.examplesSentForDownloading'), - }); - }, - onError: (error: AxiosError) => { - if (error.name !== 'AbortError') { - toast.open({ - type: 'error', - title: t('global.notificationError'), - message: error.message, - }); - } - }, - }); - - const intentUploadMutation = useMutation({ - mutationFn: ({ - intentName, - formData, - }: { - intentName: string; - formData: File; - }) => uploadExamples(intentName, formData), - onMutate: () => { - setRefreshing(true); - }, - onSuccess: () => { - toast.open({ - type: 'success', - title: t('global.notification'), - message: t('toast.fileUploadedSuccessfully'), - }); - }, - onError: (error: AxiosError) => { - toast.open({ - type: 'error', - title: t('global.notificationError'), - message: error.message, - }); - }, - onSettled: () => { - setRefreshing(false); - queryRefresh(selectedIntent?.id || ''); - }, - }); - - const handleNewExample = (example: string) => { - if (!selectedIntent) return; - addExamplesMutation.mutate({ - intentName: selectedIntent.id, - intentExamples: selectedIntent.examples, - newExamples: example.replace(/(\t|\n)+/g, ' ').trim(), - }); - }; - - const handleIntentExamplesUpload = () => { - const input = document.createElement('input'); - input.type = 'file'; - input.accept = '.csv'; - - input.addEventListener('change', async (event) => { - const fileInput = event.target as HTMLInputElement; - const files = fileInput.files; - - if (!files || files.length === 0) { - return; - } - - const file = files[0]; - - try { - await intentUploadMutation.mutateAsync({ - intentName: selectedIntent?.id || '', - formData: file, - }); - } catch (error) {} - }); - - input.click(); - }; - - const addOrEditResponseMutation = useMutation({ - mutationFn: (intentResponseData: { - id: string, - responseText: string, - update: boolean - }) => editResponse(intentResponseData.id, intentResponseData.responseText, intentResponseData.update), - onMutate: () => { - setRefreshing(true); - }, - onSuccess: async () => { - await queryClient.invalidateQueries(['response-list']); - toast.open({ - type: 'success', - title: t('global.notification'), - message: t('toast.newResponseAdded'), - }); - }, - onError: (error: AxiosError) => { - toast.open({ - type: 'error', - title: t('global.notificationError'), - message: error.message, - }); - }, - onSettled: () => { - setRefreshing(false); - }, - }); - - const deleteResponseMutation = useMutation({ - mutationFn: (response: string) => deleteResponse({ response }), - onMutate: () => { - setRefreshing(true); - }, - onSuccess: async () => { - await queryClient.invalidateQueries(['response-list']); - toast.open({ - type: 'success', - title: t('global.notification'), - message: t('toast.responseDeleted'), - }); - }, - onError: (error: AxiosError) => { - toast.open({ - type: 'error', - title: t('global.notificationError'), - message: error.message, - }); - }, - onSettled: () => { - setRefreshing(false); - } - }) - - const addRuleMutation = useMutation({ - mutationFn: ({ data }: {data: RuleDTO}) => addStoryOrRule(data as RuleDTO, "rules"), - onMutate: () => { - setRefreshing(true); - }, - onSuccess: () => { - toast.open({ - type: 'success', - title: t('global.notification'), - message: t('toast.storyAdded'), - }); - }, - onError: (error: AxiosError) => { - toast.open({ - type: 'error', - title: t('global.notificationError'), - message: error.message, - }); - }, - onSettled: () => { - setRefreshing(false); - }, - }); - - const deleteRuleMutation = useMutation({ - mutationFn: (id: string | number) => deleteStoryOrRule(id, 'rules'), - onMutate: () => { - setRefreshing(true); - }, - onSuccess: async () => { - await queryClient.invalidateQueries(['rules']); - toast.open({ - type: 'success', - title: t('global.notification'), - message: t('toast.storyDeleted'), - }); - }, - onError: (error: AxiosError) => { - toast.open({ - type: 'error', - title: t('global.notificationError'), - message: error.message, - }); - }, - onSettled: () => { - setRefreshing(false); - }, - }); - - const deleteRuleWithIntentMutation = useMutation({ - mutationFn: (id: string | number) => deleteStoryOrRule(id, 'rules'), - onMutate: () => { - setRefreshing(true); - }, - onSuccess: async () => { - await queryClient.invalidateQueries(['response-list']); - await queryClient.invalidateQueries(['rules']); - toast.open({ - type: 'success', - title: t('global.notification'), - message: t('toast.storyDeleted'), - }); - }, - onError: (error: AxiosError) => { - toast.open({ - type: 'error', - title: t('global.notificationError'), - message: error.message, - }); - }, - onSettled: () => { - deleteIntentMutation.mutate(deletableIntent!.id); - setRefreshing(false); - }, - }); - - const editIntentName = async () => { - if (!selectedIntent || !editingIntentTitle) return; - - const newName = editingIntentTitle.replace(/\s+/g, '_'); - - await intentEditMutation.mutateAsync({ - oldName: selectedIntent.id, - newName, - }); - queryRefresh(newName); - } - - const handleDeleteIntent = async () => { - if (intentRule) { - await deleteRuleWithIntentMutation.mutateAsync(intentRule); - } else { - await deleteIntentMutation.mutateAsync(deletableIntent!.id) - } - } - - const handleIntentResponseSubmit = async (newId?: string) => { - if (!intentResponseText || intentResponseText == '' || !selectedIntent) return; - - const intentId = newId || selectedIntent.id; - - await addOrEditResponseMutation.mutate({ - id: `utter_${intentId}`, - responseText: intentResponseText, - update: !!intentResponseName - }); - - if (!intentResponseName) { - await addRuleMutation.mutate({ - data: { - rule: `rule_${intentId}`, - steps: [ - { - intent: intentId, - }, - { - action: `utter_${intentId}`, - } - ] - } - }); - } - - if (editingIntentTitle) { - await intentEditMutation.mutateAsync({ - oldName: selectedIntent.id, - newName: newId, - }); - queryRefresh(intentId); - } - } - if (isLoading) return <>Loading...; return ( @@ -759,28 +164,18 @@ const Intents: FC = () => { value={selectedIntent?.id || undefined} onValueChange={handleTabsValueChange} > - +
setFilter(e.target.value)} hideLabel /> - @@ -790,243 +185,30 @@ const Intents: FC = () => { intents={intents} filter={filter} onDismiss={() => { - if(!selectedIntent?.isCommon) return; + if (!selectedIntent?.isCommon) return; setSelectedIntent(null); - setEditingIntentTitle(null); - setIntentResponseName(null); - setIntentResponseText(null); }} /> {selectedIntent && ( - -
- - - - {editingIntentTitle ? ( - - setEditingIntentTitle(e.target.value) - } - hideLabel - /> - ) : ( -

{selectedIntent.id.replace(/_/g, ' ')}

- )} - {editingIntentTitle ? ( - - ) : ( - - )} - -

- {t('global.modifiedAt')}: - {isValidDate(selectedIntent.modifiedAt) - ? ` ${format( - new Date(selectedIntent.modifiedAt), - 'dd.MM.yyyy' - )}` - : ` ${t('global.missing')}`} -

- - {serviceEligable() && ( - updateMarkForService(value)} - checked={isMarkedForService} - disabled={isPossibleToUpdateMark} - /> - - )} - - - - {selectedIntent.inModel ? ( - - ) : ( - - )} - { - isHiddenFeaturesEnabled && serviceEligable() && ( - - - - - - ) - } - - - -
-
- {selectedIntent?.examples && ( - -
- -
-
- - -

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

- setIntentResponseText(e.target.value)} - disableHeightResize - /> - - - -
- - )} -
-
+ )} )} - {deletableIntent !== null && ( - setDeletableIntent(null)} - footer={ - <> - - - - } - > -

{t('global.removeValidation')}

-
- )} - - {connectableIntent !== null && ( - setConnectableIntent(null)} - /> - )} {turnIntentToServiceIntent !== null && ( setTurnIntentToServiceIntent(null)} footer={ <> - )} - {(refreshing) && ( + {refreshing && (

{t('global.updatingDataBody')}

diff --git a/GUI/src/types/intentWithExampleCounts.ts b/GUI/src/types/intentWithExampleCounts.ts new file mode 100644 index 00000000..bf181bfb --- /dev/null +++ b/GUI/src/types/intentWithExampleCounts.ts @@ -0,0 +1,3 @@ +import { Intent } from './intent'; + +export type IntentWithExamplesCount = Pick; diff --git a/GUI/src/utils/compare.ts b/GUI/src/utils/compare.ts index 1e049812..0c877b79 100644 --- a/GUI/src/utils/compare.ts +++ b/GUI/src/utils/compare.ts @@ -1,11 +1,11 @@ -import { Intent } from "types/intent"; +import { IntentWithExamplesCount } from 'types/intentWithExampleCounts'; -export const compareInModel= (a: Intent, b: Intent) => { - if(a.inModel === b.inModel) return 0; - if(a.inModel) return -1; +export const compareInModel = (a: IntentWithExamplesCount, b: IntentWithExamplesCount) => { + if (a.inModel === b.inModel) return 0; + if (a.inModel) return -1; return 1; -} +}; -export const compareInModelReversed= (a: Intent, b: Intent) => { +export const compareInModelReversed = (a: IntentWithExamplesCount, b: IntentWithExamplesCount) => { return -1 * compareInModel(a, b); -} +}; diff --git a/docker-compose.yml b/docker-compose.yml index 2a889af3..913e4d99 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -249,6 +249,19 @@ services: networks: - bykstack + opensearch-dashboards: + image: opensearchproject/opensearch-dashboards:2.11.1 # Make sure the version of opensearch-dashboards matches the version of opensearch installed on other nodes + container_name: opensearch-dashboards + ports: + - 5601:5601 # Map host port 5601 to container port 5601 + expose: + - "5601" # Expose port 5601 for web access to OpenSearch Dashboards + environment: + OPENSEARCH_HOSTS: '["http://opensearch:9200"]' # Define the OpenSearch nodes that OpenSearch Dashboards will query + DISABLE_SECURITY_DASHBOARDS_PLUGIN: true + networks: + - bykstack + rasa: image: rasa container_name: rasa From 47d942cebb136aa5a40c75c4e89faa15c64aa72e Mon Sep 17 00:00:00 2001 From: Igor Krupenja Date: Tue, 3 Dec 2024 11:30:57 +0200 Subject: [PATCH 02/44] Fix guard --- DSL/Ruuter.private/GET/.guard | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DSL/Ruuter.private/GET/.guard b/DSL/Ruuter.private/GET/.guard index 18f2da91..b1bde4ee 100644 --- a/DSL/Ruuter.private/GET/.guard +++ b/DSL/Ruuter.private/GET/.guard @@ -9,7 +9,7 @@ process_request: verify_header_nonce: call: http.post args: - url: [#TRAINING_RESQL]/use-nonce + url: "[#TRAINING_RESQL]/use-nonce" body: updated_nonce: ${incoming.headers['x-ruuter-nonce']} result: nonce_response @@ -18,7 +18,7 @@ verify_header_nonce: verify_param_nonce: call: http.post args: - url: [#TRAINING_RESQL]/use-nonce + url: "[#TRAINING_RESQL]/use-nonce" body: updated_nonce: ${incoming.params['ruuter-nonce']} result: nonce_response From b10dc8d8abd895d8b8a3151ed7ce74542649d3c4 Mon Sep 17 00:00:00 2001 From: Igor Krupenja Date: Tue, 3 Dec 2024 13:57:25 +0200 Subject: [PATCH 03/44] Fix response logic --- .../pages/Training/Intents/IntentDetails.tsx | 69 +++++++++++++------ GUI/src/pages/Training/Intents/index.tsx | 6 +- 2 files changed, 49 insertions(+), 26 deletions(-) diff --git a/GUI/src/pages/Training/Intents/IntentDetails.tsx b/GUI/src/pages/Training/Intents/IntentDetails.tsx index 60546119..ef482029 100644 --- a/GUI/src/pages/Training/Intents/IntentDetails.tsx +++ b/GUI/src/pages/Training/Intents/IntentDetails.tsx @@ -35,6 +35,13 @@ interface Response { text: string; } +// TODO: back-end should return data in better format +interface ResponsesResponse + extends Array<{ + name: string; + response: Response[]; + }> {} + interface IntentResponse { response: Intent; } @@ -82,15 +89,21 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, en const queryRefresh = useCallback( async (intent?: string) => { const response = await queryClient.fetchQuery([`intents/by-id?intent=${intent ?? intentId}`]); - if (response) { - setIntent(response.response); - setSelectedIntent(response.response); - } + setIntent(response.response); + setSelectedIntent(response.response); // todo setIsMarkedForService? - // todo also set intentResponseName and intentResponseText? + // todo also rule + + // todo dupe code + const res = await queryClient.fetchQuery(['responses-list']); + const intentExistingResponse = res[0].response.find((response: any) => `utter_${intentId}` === response.name); + if (intentExistingResponse) { + setIntentResponseText(intentExistingResponse.text); + setIntentResponseName(intentExistingResponse.name); + } - // setIntentResponseName(null); - // setIntentResponseText(null); + // setIntentResponseName(''); + // setIntentResponseText(''); // setIntentRule(null); // queryClient.fetchQuery(['intents/full']).then((res: any) => { // setRefreshing(false); @@ -132,24 +145,37 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, en [queryClient] ); - // todo not yet fully working - const { data: responsesFullResponse } = useQuery({ + // TODO: need to fetch only the response for the selected intent + const { data: responsesResponse } = useQuery({ queryKey: ['responses-list'], }); + 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] + ); + useEffect(() => { - if (responsesFullResponse) { - // @ts-ignore - const intentExistingResponse = responsesFullResponse[0].response.find( + if (responsesResponse) { + const intentExistingResponse = responsesResponse[0].response.find( (response: any) => `utter_${intent?.id}` === response.name ); - console.log('intentExistingResponse', intentExistingResponse); if (intentExistingResponse) { setIntentResponseText(intentExistingResponse.text); setIntentResponseName(intentExistingResponse.name); } } - }, [intent?.id, responsesFullResponse]); + }, [intent?.id, responsesResponse]); // todo not implemented, need to get rules for one intent only const { data: rulesFullResponse } = useQuery({ @@ -436,8 +462,9 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, en setRefreshing(true); }, onSuccess: async () => { - // todo invalidate - await queryClient.invalidateQueries(['response-list']); + // todo does not work for some reason? + console.log('invalidating response-list'); + await queryClient.invalidateQueries({ queryKey: ['response-list'], refetchType: 'all' }); toast.open({ type: 'success', title: t('global.notification'), @@ -481,8 +508,8 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, en }); // todo clean up and fix errors - const handleIntentResponseSubmit = async (newId?: string) => { - if (!intentResponseText || intentResponseText == '' || !intent) return; + const handleIntentResponseSubmit = async (newId: string) => { + if (!intentResponseText || intentResponseText === '' || !intent) return; const intentId = newId || intent.id; @@ -513,8 +540,9 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, en oldName: intent.id, newName: newId, }); - queryRefresh(); } + + queryRefresh(); }; const examplesData = useMemo( @@ -590,7 +618,6 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, en setRefreshing(true); }, onSuccess: async () => { - // todo invalidate await queryClient.invalidateQueries(['response-list']); await queryClient.invalidateQueries(['rules']); toast.open({ @@ -747,7 +774,7 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, en onAddNewExample={handleNewExample} entities={entities} selectedIntent={intent} - // todo necessary, + // todo necessary // queryRefresh={queryRefresh} updateSelectedIntent={updateSelectedIntent} /> diff --git a/GUI/src/pages/Training/Intents/index.tsx b/GUI/src/pages/Training/Intents/index.tsx index eac65768..7f789f0a 100644 --- a/GUI/src/pages/Training/Intents/index.tsx +++ b/GUI/src/pages/Training/Intents/index.tsx @@ -16,7 +16,7 @@ import IntentTabList from './IntentTabList'; import IntentDetails from './IntentDetails'; import { IntentWithExamplesCount } from 'types/intentWithExampleCounts'; -// TODO: change examples_count to examplesCount when possible with changes in CommonIntents +// TODO: rename examples_count to examplesCount when possible with changes in CommonIntents type IntentWithExamplesCountResponse = Pick & { examples_count: number }; type IntentsWithExamplesCountResponse = { @@ -59,9 +59,6 @@ const Intents: FC = () => { const queryRefresh = useCallback( async (selectIntent?: string) => { - // todo this is necessary to reset - but maybe in child component IntentDetails.tsx with responses-list query? - // setIntentResponseText(null); - const response = await queryClient.fetchQuery(['intents/with-examples-count']); if (response) { @@ -196,7 +193,6 @@ const Intents: FC = () => { intentId={selectedIntent.id} setSelectedIntent={setSelectedIntent} entities={entitiesResponse?.response ?? []} - listRefresh={queryRefresh} /> )} From 37dfc75d95fdd69ee6909a46980971a66336c262 Mon Sep 17 00:00:00 2001 From: Igor Krupenja Date: Tue, 3 Dec 2024 13:58:01 +0200 Subject: [PATCH 04/44] Fix deps --- GUI/src/pages/Training/Intents/IntentDetails.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GUI/src/pages/Training/Intents/IntentDetails.tsx b/GUI/src/pages/Training/Intents/IntentDetails.tsx index ef482029..9cff9ee3 100644 --- a/GUI/src/pages/Training/Intents/IntentDetails.tsx +++ b/GUI/src/pages/Training/Intents/IntentDetails.tsx @@ -142,7 +142,7 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, en // } // }); }, - [queryClient] + [intentId, queryClient, setSelectedIntent] ); // TODO: need to fetch only the response for the selected intent From 06ecd8514bdc8b251cee1f83b7b5bc94b4dca731 Mon Sep 17 00:00:00 2001 From: Igor Krupenja Date: Tue, 3 Dec 2024 14:03:33 +0200 Subject: [PATCH 05/44] Remove deupe code --- .../pages/Training/Intents/IntentDetails.tsx | 62 ++++++++----------- 1 file changed, 26 insertions(+), 36 deletions(-) diff --git a/GUI/src/pages/Training/Intents/IntentDetails.tsx b/GUI/src/pages/Training/Intents/IntentDetails.tsx index 9cff9ee3..d8490f39 100644 --- a/GUI/src/pages/Training/Intents/IntentDetails.tsx +++ b/GUI/src/pages/Training/Intents/IntentDetails.tsx @@ -72,6 +72,7 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, en }); // todo check IntentExamplesTable for /full query - and if not needed there, remove all related stuff + // todo check deleting intent - I think selected logic breaks useEffect(() => { if (intentResponse) { @@ -86,21 +87,33 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, en queryKey: [`intents/is-marked-for-service?intent=${intentId}`], }); - const queryRefresh = useCallback( - async (intent?: string) => { - const response = await queryClient.fetchQuery([`intents/by-id?intent=${intent ?? intentId}`]); - setIntent(response.response); - setSelectedIntent(response.response); - // todo setIsMarkedForService? - // todo also rule + const setIntentResponse = useCallback( + (responsesResponse: ResponsesResponse | undefined) => { + if (!responsesResponse) return; - // todo dupe code - const res = await queryClient.fetchQuery(['responses-list']); - const intentExistingResponse = res[0].response.find((response: any) => `utter_${intentId}` === response.name); + const intentExistingResponse = responsesResponse[0].response.find( + (response: any) => `utter_${intentId}` === response.name + ); if (intentExistingResponse) { setIntentResponseText(intentExistingResponse.text); setIntentResponseName(intentExistingResponse.name); } + }, + [intentId] + ); + + const queryRefresh = useCallback( + async (intent?: string) => { + const intentsResponse = await queryClient.fetchQuery([ + `intents/by-id?intent=${intent ?? intentId}`, + ]); + setIntent(intentsResponse.response); + setSelectedIntent(intentsResponse.response); + // todo setIsMarkedForService? + // todo also rule + + const resonsesResponse = await queryClient.fetchQuery(['responses-list']); + setIntentResponse(resonsesResponse); // setIntentResponseName(''); // setIntentResponseText(''); @@ -142,7 +155,7 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, en // } // }); }, - [intentId, queryClient, setSelectedIntent] + [intentId, queryClient, setIntentResponse, setSelectedIntent] ); // TODO: need to fetch only the response for the selected intent @@ -150,32 +163,9 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, en queryKey: ['responses-list'], }); - 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] - ); - useEffect(() => { - if (responsesResponse) { - const intentExistingResponse = responsesResponse[0].response.find( - (response: any) => `utter_${intent?.id}` === response.name - ); - if (intentExistingResponse) { - setIntentResponseText(intentExistingResponse.text); - setIntentResponseName(intentExistingResponse.name); - } - } - }, [intent?.id, responsesResponse]); + setIntentResponse(responsesResponse); + }, [intent?.id, responsesResponse, setIntentResponse]); // todo not implemented, need to get rules for one intent only const { data: rulesFullResponse } = useQuery({ From 5f6230e082801f4bf188d76851a557cd40f38207 Mon Sep 17 00:00:00 2001 From: Igor Krupenja Date: Tue, 3 Dec 2024 14:11:54 +0200 Subject: [PATCH 06/44] TODOs --- GUI/src/pages/Training/Intents/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GUI/src/pages/Training/Intents/index.tsx b/GUI/src/pages/Training/Intents/index.tsx index 7f789f0a..1bcb75d9 100644 --- a/GUI/src/pages/Training/Intents/index.tsx +++ b/GUI/src/pages/Training/Intents/index.tsx @@ -87,7 +87,7 @@ const Intents: FC = () => { // TODO: This is not used at all at the moment // TODO: If this is needed at some point, errors should be fixed - // todo check first point here: https://github.com/buerokratt/Training-Module/pull/663 + // TODO: Possibly relevant https://github.com/buerokratt/Training-Module/pull/663 const turnIntentIntoServiceMutation = useMutation({ mutationFn: ({ intent }: { intent: Intent }) => turnIntentIntoService(intent), onMutate: () => { From 2340c4c7d2e1d44e70eca9ca8bfd4b7e26089867 Mon Sep 17 00:00:00 2001 From: Igor Krupenja Date: Tue, 3 Dec 2024 14:22:10 +0200 Subject: [PATCH 07/44] Revert unrelated changes --- DSL/OpenSearch/deploy-opensearch.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/DSL/OpenSearch/deploy-opensearch.sh b/DSL/OpenSearch/deploy-opensearch.sh index f56f4688..93793f3e 100755 --- a/DSL/OpenSearch/deploy-opensearch.sh +++ b/DSL/OpenSearch/deploy-opensearch.sh @@ -14,7 +14,6 @@ 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-by-intent-name" -H 'Content-Type: application/json' --data-binary "@templates/response-by-intent-name.json" # intents curl -XDELETE "$URL/intents?ignore_unavailable=true" -u "$AUTH" --insecure From 26f57fc21567154b9dad7c95be8f4fa88f72a84b Mon Sep 17 00:00:00 2001 From: Igor Krupenja Date: Tue, 3 Dec 2024 14:43:27 +0200 Subject: [PATCH 08/44] Clean up --- .../pages/Training/Intents/IntentDetails.tsx | 30 ++++++++----------- 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/GUI/src/pages/Training/Intents/IntentDetails.tsx b/GUI/src/pages/Training/Intents/IntentDetails.tsx index d8490f39..72f9d91a 100644 --- a/GUI/src/pages/Training/Intents/IntentDetails.tsx +++ b/GUI/src/pages/Training/Intents/IntentDetails.tsx @@ -35,7 +35,7 @@ interface Response { text: string; } -// TODO: back-end should return data in better format +// TODO: back-end should return data in a better format interface ResponsesResponse extends Array<{ name: string; @@ -57,7 +57,6 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, en const [editingIntentTitle, setEditingIntentTitle] = useState(null); const [refreshing, setRefreshing] = useState(false); - const [isMarkedForService, setIsMarkedForService] = useState(false); const [connectableIntent, setConnectableIntent] = useState(null); const [deletableIntent, setDeletableIntent] = useState(null); const [intentResponseText, setIntentResponseText] = useState(''); @@ -109,12 +108,12 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, en ]); setIntent(intentsResponse.response); setSelectedIntent(intentsResponse.response); - // todo setIsMarkedForService? - // todo also rule const resonsesResponse = await queryClient.fetchQuery(['responses-list']); setIntentResponse(resonsesResponse); + // todo also rule + // setIntentResponseName(''); // setIntentResponseText(''); // setIntentRule(null); @@ -244,7 +243,10 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, en title: t('global.notification'), message: t('toast.intentUpdated'), }); - setIsMarkedForService(!isMarkedForService); + setIntent((prev) => { + if (!prev) return null; + return { ...prev, isForService: !prev.isForService }; + }); }, onError: (error: AxiosError) => { toast.open({ @@ -452,8 +454,6 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, en setRefreshing(true); }, onSuccess: async () => { - // todo does not work for some reason? - console.log('invalidating response-list'); await queryClient.invalidateQueries({ queryKey: ['response-list'], refetchType: 'all' }); toast.open({ type: 'success', @@ -498,10 +498,10 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, en }); // todo clean up and fix errors - const handleIntentResponseSubmit = async (newId: string) => { - if (!intentResponseText || intentResponseText === '' || !intent) return; + const handleIntentResponseSubmit = async () => { + if (intentResponseText === '' || !intent) return; - const intentId = newId || intent.id; + const intentId = intent.id; await addOrEditResponseMutation.mutate({ id: `utter_${intentId}`, @@ -525,13 +525,6 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, en }); } - if (editingIntentTitle) { - await intentEditMutation.mutateAsync({ - oldName: intent.id, - newName: newId, - }); - } - queryRefresh(); }; @@ -689,7 +682,7 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, en onLabel={t('global.yes') ?? 'yes'} offLabel={t('global.no') ?? 'no'} onCheckedChange={(value) => updateMarkForService(value)} - checked={isMarkedForService} + checked={intent.isForService} disabled={isPossibleToUpdateMark} /> @@ -787,6 +780,7 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, en disableHeightResize /> + {/* todo ???? */} From c9750b98f8d22676dcccdd2ee19d60b5c7244940 Mon Sep 17 00:00:00 2001 From: Igor Krupenja Date: Tue, 3 Dec 2024 14:49:13 +0200 Subject: [PATCH 09/44] Clean up --- .../pages/Training/Intents/IntentDetails.tsx | 1 - GUI/src/services/intents.ts | 57 ++++++++++--------- 2 files changed, 30 insertions(+), 28 deletions(-) diff --git a/GUI/src/pages/Training/Intents/IntentDetails.tsx b/GUI/src/pages/Training/Intents/IntentDetails.tsx index 72f9d91a..95129c03 100644 --- a/GUI/src/pages/Training/Intents/IntentDetails.tsx +++ b/GUI/src/pages/Training/Intents/IntentDetails.tsx @@ -378,7 +378,6 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, en const intentDownloadMutation = useMutation({ mutationFn: (intentModelData: { intentName: string }) => downloadExamples(intentModelData), onSuccess: async (data) => { - // @ts-ignore const blob = new Blob([data], { type: 'text/csv' }); const fileName = intent?.id + '.csv'; diff --git a/GUI/src/services/intents.ts b/GUI/src/services/intents.ts index b439457d..43e5eb1e 100644 --- a/GUI/src/services/intents.ts +++ b/GUI/src/services/intents.ts @@ -6,17 +6,19 @@ export async function addIntent(newIntentData: { name: string }) { return data; } -export async function markForService(markData: { name: string, isForService: boolean }) { - const { data } = await rasaApi.get(`/intents/mark-for-service?name=${markData.name}&isForService=${markData.isForService}`); +export async function markForService(markData: { name: string; isForService: boolean }) { + const { data } = await rasaApi.get( + `/intents/mark-for-service?name=${markData.name}&isForService=${markData.isForService}` + ); return data; } -export async function addIntentWithExample(newIntentExample: { intentName: string,newExamples: string }) { +export async function addIntentWithExample(newIntentExample: { intentName: string; newExamples: string }) { const { data } = await rasaApi.post('/intents/add-with-example', newIntentExample); return data; } -export async function editIntent(editIntentData: {oldName: string, newName: string}) { +export async function editIntent(editIntentData: { oldName: string; newName: string }) { const { data } = await rasaApi.post(`intents/update`, editIntentData); return data; } @@ -26,39 +28,49 @@ export async function deleteIntent(deleteIntentData: { name: string }) { return data; } -export async function addRemoveIntentModel(intentModelData: {name: string, inModel: boolean}) { +export async function addRemoveIntentModel(intentModelData: { name: string; inModel: boolean }) { const { data } = await rasaApi.post(`intents/add-remove-from-model`, intentModelData); return data; } -export async function getLastModified(intentModifiedData: {intentName: string}) { +export async function getLastModified(intentModifiedData: { intentName: string }) { const { data } = await rasaApi.post(`intents/last-modified`, intentModifiedData); return data; } -export async function addExample(addExampleData: { intentName: string, intentExamples: string[], newExamples: string }) { - const { data } = await rasaApi.post<{ intentName: string; example: string; }>(`intents/examples/add`, addExampleData); +export async function addExample(addExampleData: { + intentName: string; + intentExamples: string[]; + newExamples: string; +}) { + const { data } = await rasaApi.post<{ intentName: string; example: string }>(`intents/examples/add`, addExampleData); return data; } export async function addExampleFromHistory(intentName: string, exampleData: { example: string }) { const request = { intentName: intentName, intentExamples: [], newExamples: exampleData.example }; - const {data} = await rasaApi.post<{ intentName: string; example: string; }>(`intents/examples/add`, request); + const { data } = await rasaApi.post<{ intentName: string; example: string }>(`intents/examples/add`, request); return data; } -export async function editExample(editExampleData: { intentName: string, oldExample: string, newExample: string }) { - const { data } = await rasaApi.post<{ intentName: string; example: string; }>(`intents/examples/update`, editExampleData); +export async function editExample(editExampleData: { intentName: string; oldExample: string; newExample: string }) { + const { data } = await rasaApi.post<{ intentName: string; example: string }>( + `intents/examples/update`, + editExampleData + ); return data; } -export async function deleteExample(deleteExampleData: { intentName: string, example: string }) { - const { data } = await rasaApi.post<{ intentName: string; example: string; }>(`intents/examples/delete`, deleteExampleData); +export async function deleteExample(deleteExampleData: { intentName: string; example: string }) { + const { data } = await rasaApi.post<{ intentName: string; example: string }>( + `intents/examples/delete`, + deleteExampleData + ); return data; } export async function downloadExamples(downloadExampleData: { intentName: string }) { - const { data } = await rasaApi.post<{ intentName: string; }>(`intents/download`, downloadExampleData); + const { data } = await rasaApi.post(`intents/download`, downloadExampleData); return data; } @@ -70,24 +82,15 @@ export async function uploadExamples(intentName: string, formData: File) { return data; } - -export async function turnExampleIntoIntent(data: { - exampleName: string; - intentName: string; -}): Promise { +export async function turnExampleIntoIntent(data: { exampleName: string; intentName: string }): Promise { await rasaApi.post('intents/add', { intent: data.exampleName, }); - await rasaApi.post( - 'intents/examples/delete', - { intent: data.intentName, example: data.exampleName } - ); + await rasaApi.post('intents/examples/delete', { intent: data.intentName, example: data.exampleName }); } -export async function turnIntentIntoService( - intent: Intent -): Promise { +export async function turnIntentIntoService(intent: Intent): Promise { await rasaApi.post('intents/turn-into-service', { - intentName: intent.id + intentName: intent.id, }); } From 9f7306678f69ee14ef29ffe9f25dfccb10256975 Mon Sep 17 00:00:00 2001 From: Igor Krupenja Date: Tue, 3 Dec 2024 14:49:53 +0200 Subject: [PATCH 10/44] Clean up --- GUI/src/pages/Training/Intents/IntentDetails.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/GUI/src/pages/Training/Intents/IntentDetails.tsx b/GUI/src/pages/Training/Intents/IntentDetails.tsx index 95129c03..619a3fc5 100644 --- a/GUI/src/pages/Training/Intents/IntentDetails.tsx +++ b/GUI/src/pages/Training/Intents/IntentDetails.tsx @@ -779,7 +779,6 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, en disableHeightResize /> - {/* todo ???? */} From e916819fe3187e68195d5f4ae52c60eb2898d471 Mon Sep 17 00:00:00 2001 From: Igor Krupenja Date: Tue, 3 Dec 2024 15:47:07 +0200 Subject: [PATCH 11/44] Fix deletion --- .../pages/Training/Intents/IntentDetails.tsx | 31 ++++++++++--------- GUI/src/pages/Training/Intents/index.tsx | 5 +-- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/GUI/src/pages/Training/Intents/IntentDetails.tsx b/GUI/src/pages/Training/Intents/IntentDetails.tsx index 619a3fc5..9fd78f8a 100644 --- a/GUI/src/pages/Training/Intents/IntentDetails.tsx +++ b/GUI/src/pages/Training/Intents/IntentDetails.tsx @@ -48,17 +48,20 @@ interface IntentResponse { interface IntentDetailsProps { intentId: string; + // todo maybe fetch in IntentExamplesTable entities: Entity[]; setSelectedIntent: Dispatch>; + listRefresh: (newIntent?: string) => Promise; } -const IntentDetails: FC = ({ intentId, setSelectedIntent, entities }) => { +const IntentDetails: FC = ({ intentId, setSelectedIntent, entities, listRefresh }) => { const [intent, setIntent] = useState(null); const [editingIntentTitle, setEditingIntentTitle] = useState(null); const [refreshing, setRefreshing] = useState(false); + // todo also boolean const [connectableIntent, setConnectableIntent] = useState(null); - const [deletableIntent, setDeletableIntent] = useState(null); + const [showDeleteDialog, setShowDeleteDialog] = useState(false); const [intentResponseText, setIntentResponseText] = useState(''); const [intentResponseName, setIntentResponseName] = useState(''); const [intentRule, setIntentRule] = useState(''); @@ -157,7 +160,7 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, en [intentId, queryClient, setIntentResponse, setSelectedIntent] ); - // TODO: need to fetch only the response for the selected intent + // TODO: need to fetch response for the selected intent only const { data: responsesResponse } = useQuery({ queryKey: ['responses-list'], }); @@ -166,6 +169,7 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, en setIntentResponse(responsesResponse); }, [intent?.id, responsesResponse, setIntentResponse]); + // TODO: need to fetch rules for the selected intent only // todo not implemented, need to get rules for one intent only const { data: rulesFullResponse } = useQuery({ queryKey: ['rules'], @@ -570,12 +574,14 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, en mutationFn: (name: string) => deleteIntent({ name }), onMutate: () => { setRefreshing(true); - setDeletableIntent(null); + setShowDeleteDialog(false); setConnectableIntent(null); - setSelectedIntent(null); }, onSuccess: async () => { + setSelectedIntent(null); await queryClient.invalidateQueries(['intents/with-examples-count']); + // Without the delay, back end still returns the deleted intent. Perhaps BE is deleting asynchronously? + setTimeout(() => listRefresh(), 300); toast.open({ type: 'success', title: t('global.notification'), @@ -589,9 +595,6 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, en message: error.message, }); }, - onSettled: () => { - queryRefresh(); - }, }); const deleteRuleWithIntentMutation = useMutation({ @@ -616,7 +619,7 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, en }); }, onSettled: () => { - deleteIntentMutation.mutate(deletableIntent!.id); + deleteIntentMutation.mutate(intentId); setRefreshing(false); }, }); @@ -625,7 +628,7 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, en if (intentRule) { await deleteRuleWithIntentMutation.mutateAsync(intentRule); } else { - await deleteIntentMutation.mutateAsync(deletableIntent!.id); + await deleteIntentMutation.mutateAsync(intentId); } }; @@ -737,7 +740,7 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, en )}
- {deletableIntent !== null && ( + {showDeleteDialog && ( setDeletableIntent(null)} + onClose={() => setShowDeleteDialog(false)} footer={ <> - @@ -752,11 +750,9 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, en {intent?.examples && (
- {/* todo missing/broken props */} void; - entities: Entity[]; + // entities: Entity[]; selectedIntent: Intent; queryRefresh: (selectIntent: string) => void; updateSelectedIntent: (intent: Intent) => void; @@ -33,10 +28,10 @@ type IntentExamplesTableProps = { const IntentExamplesTable: FC = ({ examples, onAddNewExample, - entities, + // entities, selectedIntent, queryRefresh, - updateSelectedIntent + updateSelectedIntent, }) => { let updatedExampleTitle = ''; const { t } = useTranslation(); @@ -52,16 +47,18 @@ const IntentExamplesTable: FC = ({ const [deletableRow, setDeletableRow] = useState<{ intentName: string; value: string; - } | null>( - null - ); + } | null>(null); const [exampleToIntent, setExampleToIntent] = useState<{ - intentName: string, + intentName: string; value: string; } | null>(null); const columnHelper = createColumnHelper<{ id: string; value: string }>(); const queryClient = useQueryClient(); + const { data: entitiesResponse } = useQuery<{ response: Entity[] }>({ + queryKey: ['entities'], + }); + const handleRefresh = (selectIntent: string) => { queryRefresh(selectIntent); }; @@ -79,19 +76,19 @@ const IntentExamplesTable: FC = ({ const updatedIntent = selectedIntent; updatedIntent.examples[updatedIntent.examples.indexOf(oldExample)] = newExample; updateSelectedIntent(updatedIntent); - } + }; const deleteExampleFromList = (example: string): void => { const updatedIntent = selectedIntent; const examplesArray = updatedIntent.examples; - const index = examplesArray.findIndex(item => item === example); + const index = examplesArray.findIndex((item) => item === example); examplesArray.splice(index, 1); updatedIntent.examples = examplesArray; updateSelectedIntent(updatedIntent); - } + }; const exampleToIntentMutation = useMutation({ - mutationFn: ({ exampleName }: {intentName: string, exampleName: string} ) => + mutationFn: ({ exampleName }: { intentName: string; exampleName: string }) => turnExampleIntoIntent({ intentName: selectedIntent.id, exampleName: exampleName, @@ -117,12 +114,10 @@ const IntentExamplesTable: FC = ({ }); const exampleEditMutation = useMutation({ - mutationFn: (addExamplesData: { - intentName: string, - oldExample: string, - newExample: string}) => editExample(addExamplesData), + mutationFn: (addExamplesData: { intentName: string; oldExample: string; newExample: string }) => + editExample(addExamplesData), onMutate: async () => { - setRefreshing(true) + setRefreshing(true); await queryClient.invalidateQueries(['intents/full']); }, onSuccess: () => { @@ -143,11 +138,11 @@ const IntentExamplesTable: FC = ({ onSettled: () => { setEditableRow(null); setRefreshing(false); - } + }, }); const exampleDeleteMutation = useMutation({ - mutationFn: (deleteExampleData: { intentName: string, example: string}) => deleteExample(deleteExampleData), + mutationFn: (deleteExampleData: { intentName: string; example: string }) => deleteExample(deleteExampleData), onMutate: () => setRefreshing(true), onSuccess: () => { toast.open({ @@ -167,8 +162,8 @@ const IntentExamplesTable: FC = ({ }, onSettled: () => { setDeletableRow(null); - setRefreshing(false) - } + setRefreshing(false); + }, }); const handleNewExampleSubmit = () => { @@ -180,13 +175,20 @@ const IntentExamplesTable: FC = ({ const updateEditingExampleTitle = (newName: string) => { updatedExampleTitle = newName; - } + }; const examplesColumns = useMemo( () => [ columnHelper.accessor('value', { header: t('training.intents.examples') || '', - cell: (props) => buildValueCell(editableRow, updateEditingExampleTitle, entities, props.row.original.id, props.getValue()), + cell: (props) => + buildValueCell( + editableRow, + updateEditingExampleTitle, + entitiesResponse?.response ?? [], + props.row.original.id, + props.getValue() + ), }), columnHelper.display({ header: '', @@ -194,7 +196,7 @@ const IntentExamplesTable: FC = ({ row: { original: { id, value: name }, }, - }) => buildTurnExampleToIntentCell(() => setExampleToIntent({ intentName: id, value: name})), + }) => buildTurnExampleToIntentCell(() => setExampleToIntent({ intentName: id, value: name })), id: 'turnExampleIntoIntent', meta: { size: '1%', @@ -202,24 +204,25 @@ const IntentExamplesTable: FC = ({ }), columnHelper.display({ header: '', - cell: (props) => buildEditCell( - editableRow?.intentName === props.row.original.id, - () => { - if(!editableRow) - return; - setOldExampleText(editableRow.value); - setExampleText(updatedExampleTitle.trim()); + cell: (props) => + buildEditCell( + editableRow?.intentName === props.row.original.id, + () => { + if (!editableRow) return; + setOldExampleText(editableRow.value); + setExampleText(updatedExampleTitle.trim()); exampleEditMutation.mutate({ intentName: selectedIntent.id, oldExample: editableRow.value, newExample: updatedExampleTitle.trim(), + }); + }, + () => + handleEditableRow({ + intentName: props.row.original.id, + value: props.row.original.value, }) - }, - () => handleEditableRow({ - intentName: props.row.original.id, - value: props.row.original.value - }), - ), + ), id: 'edit', meta: { size: '1%', @@ -227,18 +230,29 @@ const IntentExamplesTable: FC = ({ }), columnHelper.display({ header: '', - cell: (props) => buildDeleteCell(() => setDeletableRow({ - intentName: props.row.original.id, - value: props.row.original.value - })), + cell: (props) => + buildDeleteCell(() => + setDeletableRow({ + intentName: props.row.original.id, + value: props.row.original.value, + }) + ), id: 'delete', meta: { size: '1%', }, }), ], - [columnHelper, t, editableRow, entities, updateEditingExampleTitle, - exampleEditMutation, selectedIntent.id, updatedExampleTitle] + [ + columnHelper, + t, + editableRow, + updateEditingExampleTitle, + entitiesResponse?.response, + updatedExampleTitle, + exampleEditMutation, + selectedIntent.id, + ] ); return ( @@ -263,12 +277,8 @@ const IntentExamplesTable: FC = ({ /> - @@ -282,21 +292,18 @@ const IntentExamplesTable: FC = ({ onClose={() => setDeletableRow(null)} footer={ <> - @@ -313,21 +320,17 @@ const IntentExamplesTable: FC = ({ onClose={() => setExampleToIntent(null)} footer={ <> - @@ -338,22 +341,22 @@ const IntentExamplesTable: FC = ({
)} {refreshing && ( - -

{t('global.updatingDataBody')}

-
+ +

{t('global.updatingDataBody')}

+
)} ); }; const buildValueCell = ( - editableRow: { intentName: string; value: string; } | null, + editableRow: { intentName: string; value: string } | null, updateEditingExampleTitle: (newName: string) => void, entities: Entity[], id: string, - value: string, + value: string ): any => { - if(editableRow?.intentName === id) { + if (editableRow?.intentName === id) { return ( updateEditingExampleTitle(e.target.value)} - showMaxLength /> + showMaxLength + /> ); } - return ( - - ); -} + return ; +}; const buildTurnExampleToIntentCell = (onClick: () => void) => { return ( - - ) -} + ); +}; -const buildEditCell = ( - isSave: boolean, - onSaveClick: () => void, - onEditClick: () => void, -) => { - if(isSave) { +const buildEditCell = (isSave: boolean, onSaveClick: () => void, onEditClick: () => void) => { + if (isSave) { return ( - - ) + ); } return ( - - ) -} + + ); +}; const buildDeleteCell = (onClick: () => void) => { return ( - - ) -} + ); +}; export default IntentExamplesTable; diff --git a/GUI/src/pages/Training/Intents/index.tsx b/GUI/src/pages/Training/Intents/index.tsx index 1ad699a5..38338fe8 100644 --- a/GUI/src/pages/Training/Intents/index.tsx +++ b/GUI/src/pages/Training/Intents/index.tsx @@ -43,19 +43,15 @@ const Intents: FC = () => { const [filter, setFilter] = useState(''); const [turnIntentToServiceIntent, setTurnIntentToServiceIntent] = useState(null); - const { data: intentsFullResponse, isLoading } = useQuery({ + const { data: intentsResponse, isLoading } = useQuery({ queryKey: ['intents/with-examples-count'], }); useEffect(() => { - if (intentsFullResponse) { - setIntents(intentsFullResponse.response.intents.map((intent) => intentResponseToIntent(intent))); + if (intentsResponse) { + setIntents(intentsResponse.response.intents.map((intent) => intentResponseToIntent(intent))); } - }, [intentsFullResponse]); - - const { data: entitiesResponse } = useQuery<{ response: Entity[] }>({ - queryKey: ['entities'], - }); + }, [intentsResponse]); const queryRefresh = useCallback( async (newIntent?: string) => { @@ -192,7 +188,6 @@ const Intents: FC = () => { )} From 4faea191925312a349ce3ee7e59e791b517f2838 Mon Sep 17 00:00:00 2001 From: Igor Krupenja Date: Tue, 3 Dec 2024 16:01:04 +0200 Subject: [PATCH 14/44] Clean up --- GUI/src/pages/Training/Intents/index.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/GUI/src/pages/Training/Intents/index.tsx b/GUI/src/pages/Training/Intents/index.tsx index 38338fe8..8653c5a5 100644 --- a/GUI/src/pages/Training/Intents/index.tsx +++ b/GUI/src/pages/Training/Intents/index.tsx @@ -3,12 +3,11 @@ import { useSearchParams } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import * as Tabs from '@radix-ui/react-tabs'; -import { AxiosError } from 'axios'; +import { AxiosError, AxiosError } from 'axios'; import { Button, Dialog, FormInput, Track } from 'components'; import { useToast } from 'hooks/useToast'; import { Intent } from 'types/intent'; -import { Entity } from 'types/entity'; import { addIntent, turnIntentIntoService } from 'services/intents'; import LoadingDialog from '../../../components/LoadingDialog'; import withAuthorization, { ROLES } from 'hoc/with-authorization'; From 3ebaaf850894b0c4ec7304e48934540956a15a1c Mon Sep 17 00:00:00 2001 From: Igor Krupenja Date: Tue, 3 Dec 2024 16:03:46 +0200 Subject: [PATCH 15/44] Clean up --- .../pages/Training/Intents/IntentDetails.tsx | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/GUI/src/pages/Training/Intents/IntentDetails.tsx b/GUI/src/pages/Training/Intents/IntentDetails.tsx index e8880c0e..1f0a8c84 100644 --- a/GUI/src/pages/Training/Intents/IntentDetails.tsx +++ b/GUI/src/pages/Training/Intents/IntentDetails.tsx @@ -56,9 +56,8 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li const [editingIntentTitle, setEditingIntentTitle] = useState(null); const [refreshing, setRefreshing] = useState(false); - // todo also boolean - const [connectableIntent, setConnectableIntent] = useState(null); - const [showDeleteDialog, setShowDeleteDialog] = useState(false); + const [showConnectToServiceModal, setShowConnectToServiceModal] = useState(false); + const [showDeleteModal, setShowDeleteModal] = useState(false); const [intentResponseText, setIntentResponseText] = useState(''); const [intentResponseName, setIntentResponseName] = useState(''); const [intentRule, setIntentRule] = useState(''); @@ -571,8 +570,8 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li mutationFn: (name: string) => deleteIntent({ name }), onMutate: () => { setRefreshing(true); - setShowDeleteDialog(false); - setConnectableIntent(null); + setShowDeleteModal(false); + setShowConnectToServiceModal(false); }, onSuccess: async () => { setSelectedIntent(null); @@ -727,7 +726,7 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li {isHiddenFeaturesEnabled && serviceEligible() && ( - @@ -786,13 +785,13 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li )}
- {showDeleteDialog && ( + {showDeleteModal && ( setShowDeleteDialog(false)} + onClose={() => setShowDeleteModal(false)} footer={ <> - )} - {connectableIntent !== null && ( - setConnectableIntent(null)} /> + {showConnectToServiceModal && ( + setShowConnectToServiceModal(false)} /> )} {refreshing && ( From f1fefc286ffa2f4abe413184ff9fa6d8ff59a1e8 Mon Sep 17 00:00:00 2001 From: Igor Krupenja Date: Tue, 3 Dec 2024 16:14:09 +0200 Subject: [PATCH 16/44] Fix adding example --- GUI/src/pages/Training/Intents/IntentDetails.tsx | 1 + GUI/src/pages/Training/Intents/IntentExamplesTable.tsx | 2 -- GUI/src/pages/Training/Intents/index.tsx | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/GUI/src/pages/Training/Intents/IntentDetails.tsx b/GUI/src/pages/Training/Intents/IntentDetails.tsx index 1f0a8c84..7d3bb3d2 100644 --- a/GUI/src/pages/Training/Intents/IntentDetails.tsx +++ b/GUI/src/pages/Training/Intents/IntentDetails.tsx @@ -553,6 +553,7 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li }); }, onSettled: () => { + setRefreshing(false); queryRefresh(); }, }); diff --git a/GUI/src/pages/Training/Intents/IntentExamplesTable.tsx b/GUI/src/pages/Training/Intents/IntentExamplesTable.tsx index 7aede26e..5ffa08ef 100644 --- a/GUI/src/pages/Training/Intents/IntentExamplesTable.tsx +++ b/GUI/src/pages/Training/Intents/IntentExamplesTable.tsx @@ -19,7 +19,6 @@ import i18n from '../../../../i18n'; type IntentExamplesTableProps = { examples: { id: number; value: string }[]; onAddNewExample: (example: string) => void; - // entities: Entity[]; selectedIntent: Intent; queryRefresh: (selectIntent: string) => void; updateSelectedIntent: (intent: Intent) => void; @@ -28,7 +27,6 @@ type IntentExamplesTableProps = { const IntentExamplesTable: FC = ({ examples, onAddNewExample, - // entities, selectedIntent, queryRefresh, updateSelectedIntent, diff --git a/GUI/src/pages/Training/Intents/index.tsx b/GUI/src/pages/Training/Intents/index.tsx index 8653c5a5..40d7976b 100644 --- a/GUI/src/pages/Training/Intents/index.tsx +++ b/GUI/src/pages/Training/Intents/index.tsx @@ -3,7 +3,7 @@ import { useSearchParams } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import * as Tabs from '@radix-ui/react-tabs'; -import { AxiosError, AxiosError } from 'axios'; +import { AxiosError } from 'axios'; import { Button, Dialog, FormInput, Track } from 'components'; import { useToast } from 'hooks/useToast'; From df075da006e7afb2b0ca59332c6f1fbd117ac143 Mon Sep 17 00:00:00 2001 From: Igor Krupenja Date: Tue, 3 Dec 2024 16:25:38 +0200 Subject: [PATCH 17/44] Fix examples --- .../pages/Training/Intents/CommonIntents.tsx | 184 ++++-------------- .../pages/Training/Intents/IntentDetails.tsx | 40 +--- .../Training/Intents/IntentExamplesTable.tsx | 54 +++-- 3 files changed, 77 insertions(+), 201 deletions(-) diff --git a/GUI/src/pages/Training/Intents/CommonIntents.tsx b/GUI/src/pages/Training/Intents/CommonIntents.tsx index 6ee956a4..093d1a20 100644 --- a/GUI/src/pages/Training/Intents/CommonIntents.tsx +++ b/GUI/src/pages/Training/Intents/CommonIntents.tsx @@ -7,16 +7,7 @@ import { format } from 'date-fns'; import { AxiosError } from 'axios'; import { MdCheckCircleOutline } from 'react-icons/md'; -import { - Button, - Card, - Dialog, - FormInput, - Icon, - Switch, - Tooltip, - Track, -} from 'components'; +import { Button, Card, Dialog, FormInput, Icon, Switch, Tooltip, Track } from 'components'; import { useToast } from 'hooks/useToast'; import { Intent } from 'types/intent'; import { Entity } from 'types/entity'; @@ -40,12 +31,8 @@ const CommonIntents: FC = () => { const [searchParams] = useSearchParams(); const [commonIntentsEnabled, setCommonIntentsEnabled] = useState(true); const [selectedIntent, setSelectedIntent] = useState(null); - const [deletableIntent, setDeletableIntent] = useState< - string | number | null - >(null); - const [connectableIntent, setConnectableIntent] = useState( - null - ); + const [deletableIntent, setDeletableIntent] = useState(null); + const [connectableIntent, setConnectableIntent] = useState(null); const [filter, setFilter] = useState(''); const [refreshing, setRefreshing] = useState(false); @@ -82,9 +69,7 @@ const CommonIntents: FC = () => { useEffect(() => { if (!intentParam || intentsFullList?.length !== commonIntents?.length) return; - const queryIntent = commonIntents.find( - (intent) => intent.id === intentParam - ); + const queryIntent = commonIntents.find((intent) => intent.id === intentParam); if (queryIntent) { setSelectedIntent(queryIntent); @@ -126,37 +111,8 @@ const CommonIntents: FC = () => { [commonIntents, queryClient] ); - const addExamplesMutation = useMutation({ - mutationFn: (addExamplesData: { - intentName: string; - intentExamples: string[]; - newExamples: string; - }) => addExample(addExamplesData), - onMutate: () => { - setRefreshing(true); - }, - onSuccess: () => { - toast.open({ - type: 'success', - title: t('global.notification'), - message: t('toast.newExampleAdded'), - }); - }, - onError: (error: AxiosError) => { - toast.open({ - type: 'error', - title: t('global.notificationError'), - message: error.message, - }); - }, - onSettled: () => { - queryRefresh(selectedIntent?.id || ''); - }, - }); - const intentModelMutation = useMutation({ - mutationFn: (intentModelData: { name: string; inModel: boolean }) => - addRemoveIntentModel(intentModelData), + mutationFn: (intentModelData: { name: string; inModel: boolean }) => addRemoveIntentModel(intentModelData), onMutate: () => { setRefreshing(true); }, @@ -213,9 +169,7 @@ const CommonIntents: FC = () => { }); }, onSettled: () => { - commonIntents = commonIntents.filter( - (intent) => intent.id !== selectedIntent?.id - ); + commonIntents = commonIntents.filter((intent) => intent.id !== selectedIntent?.id); setRefreshing(false); }, }); @@ -223,7 +177,8 @@ const CommonIntents: FC = () => { const filteredIntents = useMemo(() => { if (!commonIntents) return []; const formattedFilter = filter.trim().replace(/\s+/g, '_'); - return commonIntents.filter((intent) => intent.id?.includes(formattedFilter)) + return commonIntents + .filter((intent) => intent.id?.includes(formattedFilter)) .sort((a, b) => a.id.localeCompare(b.id)); }, [commonIntents, filter]); @@ -232,7 +187,7 @@ const CommonIntents: FC = () => { }); const examplesData = useMemo( - () => selectedIntent?.examples.map((example, index) => ({id: index, value: example})), + () => selectedIntent?.examples.map((example, index) => ({ id: index, value: example })), [selectedIntent?.examples] ); @@ -240,9 +195,7 @@ const CommonIntents: FC = () => { (value: string) => { setSelectedIntent(null); if (!commonIntents) return; - const selectedIntent = commonIntents.find( - (intent) => intent.id === value - ); + const selectedIntent = commonIntents.find((intent) => intent.id === value); if (selectedIntent) { queryRefresh(selectedIntent.id || ''); intentModifiedMutation.mutate( @@ -263,18 +216,8 @@ const CommonIntents: FC = () => { [intentModifiedMutation, commonIntents, queryRefresh] ); - const handleNewExample = (example: string) => { - if (!selectedIntent) return; - addExamplesMutation.mutate({ - intentName: selectedIntent.id, - intentExamples: selectedIntent.examples, - newExamples: example, - }); - }; - const intentDownloadMutation = useMutation({ - mutationFn: (intentModelData: { intentName: string }) => - downloadExamples(intentModelData), + mutationFn: (intentModelData: { intentName: string }) => downloadExamples(intentModelData), onSuccess: async (data) => { // @ts-ignore const blob = new Blob([data], { type: 'text/csv' }); @@ -306,7 +249,7 @@ const CommonIntents: FC = () => { type: 'error', title: t('global.notificationError'), message: error.message, - }); + }); } }, }); @@ -338,13 +281,8 @@ const CommonIntents: FC = () => { }; const intentUploadMutation = useMutation({ - mutationFn: ({ - intentName, - formData, - }: { - intentName: string; - formData: File; - }) => uploadExamples(intentName, formData), + mutationFn: ({ intentName, formData }: { intentName: string; formData: File }) => + uploadExamples(intentName, formData), onSuccess: () => { toast.open({ type: 'success', @@ -399,18 +337,13 @@ const CommonIntents: FC = () => { value={selectedIntent?.id ?? undefined} onValueChange={handleTabsValueChange} > - +
setFilter(e.target.value)} hideLabel @@ -420,39 +353,29 @@ const CommonIntents: FC = () => {
{filteredIntents.map((intent, index) => ( - + - - {intent.id.replace(/^common_/, '').replace(/_/g, ' ')} - + {intent.id.replace(/^common_/, '').replace(/_/g, ' ')} - - {intent.examplesCount} - + {intent.examplesCount} {intent.inModel ? ( - - - } - /> - + + + } + /> + ) : ( )} - ))}
+ ))} +
{selectedIntent && ( @@ -471,18 +394,12 @@ const CommonIntents: FC = () => {

{t('global.modifiedAt')}: {isValidDate(selectedIntent.modifiedAt) - ? ` ${format( - new Date(selectedIntent.modifiedAt), - 'dd.MM.yyyy' - )}` + ? ` ${format(new Date(selectedIntent.modifiedAt), 'dd.MM.yyyy')}` : ` ${t('global.missing')}`}

- )} - + - @@ -551,13 +457,10 @@ const CommonIntents: FC = () => {
- {selectedIntent?.examples && examplesData && ( + {selectedIntent?.examples && examplesData && ( )} @@ -568,10 +471,7 @@ const CommonIntents: FC = () => { )} {connectableIntent !== null && ( - setConnectableIntent(null)} - /> + setConnectableIntent(null)} /> )} {deletableIntent !== null && ( @@ -580,18 +480,10 @@ const CommonIntents: FC = () => { onClose={() => setDeletableIntent(null)} footer={ <> - - diff --git a/GUI/src/pages/Training/Intents/IntentDetails.tsx b/GUI/src/pages/Training/Intents/IntentDetails.tsx index 7d3bb3d2..520c794d 100644 --- a/GUI/src/pages/Training/Intents/IntentDetails.tsx +++ b/GUI/src/pages/Training/Intents/IntentDetails.tsx @@ -70,7 +70,6 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li }); // todo check IntentExamplesTable for /full query - and if not needed there, remove all related stuff - // todo IntentExamplesTable setRefreshing does not false on adding new one Lisa useEffect(() => { if (intentResponse) { @@ -527,46 +526,12 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li queryRefresh(); }; + // todo move to child - also fix common intents const examplesData = useMemo( () => intent?.examples.map((example, index) => ({ id: index, value: example })) ?? [], [intent?.examples] ); - const addExamplesMutation = useMutation({ - mutationFn: (addExamplesData: { intentName: string; intentExamples: string[]; newExamples: string }) => - addExample(addExamplesData), - onMutate: () => { - setRefreshing(true); - }, - onSuccess: () => { - toast.open({ - type: 'success', - title: t('global.notification'), - message: t('toast.newExampleAdded'), - }); - }, - onError: (error: AxiosError) => { - toast.open({ - type: 'error', - title: t('global.notificationError'), - message: error.message, - }); - }, - onSettled: () => { - setRefreshing(false); - queryRefresh(); - }, - }); - - const handleNewExample = (example: string) => { - if (!intent) return; - addExamplesMutation.mutate({ - intentName: intent.id, - intentExamples: intent.examples, - newExamples: example.replace(/(\t|\n)+/g, ' ').trim(), - }); - }; - const deleteIntentMutation = useMutation({ mutationFn: (name: string) => deleteIntent({ name }), onMutate: () => { @@ -752,10 +717,7 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li
diff --git a/GUI/src/pages/Training/Intents/IntentExamplesTable.tsx b/GUI/src/pages/Training/Intents/IntentExamplesTable.tsx index 5ffa08ef..c189dcf5 100644 --- a/GUI/src/pages/Training/Intents/IntentExamplesTable.tsx +++ b/GUI/src/pages/Training/Intents/IntentExamplesTable.tsx @@ -9,7 +9,7 @@ import { Button, DataTable, Dialog, FormTextarea, Icon } from 'components'; import useDocumentEscapeListener from 'hooks/useDocumentEscapeListener'; import { INTENT_EXAMPLE_LENGTH } from 'constants/config'; import type { Entity } from 'types/entity'; -import { turnExampleIntoIntent, deleteExample, editExample } from 'services/intents'; +import { turnExampleIntoIntent, deleteExample, editExample, addExample } from 'services/intents'; import { useToast } from 'hooks/useToast'; import IntentExamplesEntry from './IntentExamplesEntry'; import { Intent } from '../../../types/intent'; @@ -18,19 +18,11 @@ import i18n from '../../../../i18n'; type IntentExamplesTableProps = { examples: { id: number; value: string }[]; - onAddNewExample: (example: string) => void; selectedIntent: Intent; - queryRefresh: (selectIntent: string) => void; updateSelectedIntent: (intent: Intent) => void; }; -const IntentExamplesTable: FC = ({ - examples, - onAddNewExample, - selectedIntent, - queryRefresh, - updateSelectedIntent, -}) => { +const IntentExamplesTable: FC = ({ examples, selectedIntent, updateSelectedIntent }) => { let updatedExampleTitle = ''; const { t } = useTranslation(); const toast = useToast(); @@ -57,10 +49,6 @@ const IntentExamplesTable: FC = ({ queryKey: ['entities'], }); - const handleRefresh = (selectIntent: string) => { - queryRefresh(selectIntent); - }; - useDocumentEscapeListener(() => { updatedExampleTitle = ''; setEditableRow(null); @@ -148,7 +136,7 @@ const IntentExamplesTable: FC = ({ title: t('global.notification'), message: t('toast.exampleDeleted'), }); - handleRefresh(selectedIntent.id); + updateSelectedIntent(selectedIntent); deleteExampleFromList(oldExampleText); }, onError: (error: AxiosError) => { @@ -164,9 +152,43 @@ const IntentExamplesTable: FC = ({ }, }); + const addExamplesMutation = useMutation({ + mutationFn: (addExamplesData: { intentName: string; intentExamples: string[]; newExamples: string }) => + addExample(addExamplesData), + onMutate: () => { + setRefreshing(true); + }, + onSuccess: () => { + toast.open({ + type: 'success', + title: t('global.notification'), + message: t('toast.newExampleAdded'), + }); + }, + onError: (error: AxiosError) => { + toast.open({ + type: 'error', + title: t('global.notificationError'), + message: error.message, + }); + }, + onSettled: () => { + setRefreshing(false); + updateSelectedIntent(selectedIntent); + }, + }); + + const handleNewExample = (example: string) => { + addExamplesMutation.mutate({ + intentName: selectedIntent.id, + intentExamples: selectedIntent.examples, + newExamples: example.replace(/(\t|\n)+/g, ' ').trim(), + }); + }; + const handleNewExampleSubmit = () => { if (!newExampleRef.current) return; - onAddNewExample(newExampleRef.current.value || ''); + handleNewExample(newExampleRef.current.value || ''); newExampleRef.current.value = ''; setExampleText(''); }; From ec5213156c12a034f87eb9618bc805f9fbbac331 Mon Sep 17 00:00:00 2001 From: Igor Krupenja Date: Tue, 3 Dec 2024 16:32:43 +0200 Subject: [PATCH 18/44] Clean up --- GUI/src/pages/Training/Intents/CommonIntents.tsx | 1 - GUI/src/pages/Training/Intents/IntentDetails.tsx | 3 --- GUI/src/pages/Training/Intents/IntentExamplesTable.tsx | 1 - 3 files changed, 5 deletions(-) diff --git a/GUI/src/pages/Training/Intents/CommonIntents.tsx b/GUI/src/pages/Training/Intents/CommonIntents.tsx index 093d1a20..7f72365b 100644 --- a/GUI/src/pages/Training/Intents/CommonIntents.tsx +++ b/GUI/src/pages/Training/Intents/CommonIntents.tsx @@ -12,7 +12,6 @@ import { useToast } from 'hooks/useToast'; import { Intent } from 'types/intent'; import { Entity } from 'types/entity'; import { - addExample, addRemoveIntentModel, deleteIntent, downloadExamples, diff --git a/GUI/src/pages/Training/Intents/IntentDetails.tsx b/GUI/src/pages/Training/Intents/IntentDetails.tsx index 520c794d..f0c1a5e8 100644 --- a/GUI/src/pages/Training/Intents/IntentDetails.tsx +++ b/GUI/src/pages/Training/Intents/IntentDetails.tsx @@ -10,7 +10,6 @@ import * as Tabs from '@radix-ui/react-tabs'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { AxiosError } from 'axios'; import { - addExample, addRemoveIntentModel, deleteIntent, downloadExamples, @@ -69,8 +68,6 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li queryKey: [`intents/by-id?intent=${intentId}`], }); - // todo check IntentExamplesTable for /full query - and if not needed there, remove all related stuff - useEffect(() => { if (intentResponse) { setIntent(intentResponse.response); diff --git a/GUI/src/pages/Training/Intents/IntentExamplesTable.tsx b/GUI/src/pages/Training/Intents/IntentExamplesTable.tsx index c189dcf5..801db5d0 100644 --- a/GUI/src/pages/Training/Intents/IntentExamplesTable.tsx +++ b/GUI/src/pages/Training/Intents/IntentExamplesTable.tsx @@ -104,7 +104,6 @@ const IntentExamplesTable: FC = ({ examples, selectedI editExample(addExamplesData), onMutate: async () => { setRefreshing(true); - await queryClient.invalidateQueries(['intents/full']); }, onSuccess: () => { toast.open({ From 048941eadeed5011cc2ebcdc80fbfba7193d0fa3 Mon Sep 17 00:00:00 2001 From: Igor Krupenja Date: Tue, 3 Dec 2024 16:38:34 +0200 Subject: [PATCH 19/44] Clean up --- GUI/src/pages/Training/Intents/IntentExamplesTable.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/GUI/src/pages/Training/Intents/IntentExamplesTable.tsx b/GUI/src/pages/Training/Intents/IntentExamplesTable.tsx index 801db5d0..5e23d8e3 100644 --- a/GUI/src/pages/Training/Intents/IntentExamplesTable.tsx +++ b/GUI/src/pages/Training/Intents/IntentExamplesTable.tsx @@ -43,7 +43,6 @@ const IntentExamplesTable: FC = ({ examples, selectedI value: string; } | null>(null); const columnHelper = createColumnHelper<{ id: string; value: string }>(); - const queryClient = useQueryClient(); const { data: entitiesResponse } = useQuery<{ response: Entity[] }>({ queryKey: ['entities'], From 4596ee35d8d245fa2556ca6613b7925e5d2636a0 Mon Sep 17 00:00:00 2001 From: Igor Krupenja Date: Tue, 3 Dec 2024 16:44:24 +0200 Subject: [PATCH 20/44] Clean up --- .../pages/Training/Intents/CommonIntents.tsx | 13 ++------ .../pages/Training/Intents/IntentDetails.tsx | 12 +------- .../Training/Intents/IntentExamplesTable.tsx | 30 +++++++++++-------- 3 files changed, 20 insertions(+), 35 deletions(-) diff --git a/GUI/src/pages/Training/Intents/CommonIntents.tsx b/GUI/src/pages/Training/Intents/CommonIntents.tsx index 7f72365b..3fcabc15 100644 --- a/GUI/src/pages/Training/Intents/CommonIntents.tsx +++ b/GUI/src/pages/Training/Intents/CommonIntents.tsx @@ -185,11 +185,6 @@ const CommonIntents: FC = () => { mutationFn: (data: { intentName: string }) => getLastModified(data), }); - const examplesData = useMemo( - () => selectedIntent?.examples.map((example, index) => ({ id: index, value: example })), - [selectedIntent?.examples] - ); - const handleTabsValueChange = useCallback( (value: string) => { setSelectedIntent(null); @@ -456,12 +451,8 @@ const CommonIntents: FC = () => {
- {selectedIntent?.examples && examplesData && ( - + {selectedIntent?.examples && ( + )}
diff --git a/GUI/src/pages/Training/Intents/IntentDetails.tsx b/GUI/src/pages/Training/Intents/IntentDetails.tsx index f0c1a5e8..55b24e49 100644 --- a/GUI/src/pages/Training/Intents/IntentDetails.tsx +++ b/GUI/src/pages/Training/Intents/IntentDetails.tsx @@ -523,12 +523,6 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li queryRefresh(); }; - // todo move to child - also fix common intents - const examplesData = useMemo( - () => intent?.examples.map((example, index) => ({ id: index, value: example })) ?? [], - [intent?.examples] - ); - const deleteIntentMutation = useMutation({ mutationFn: (name: string) => deleteIntent({ name }), onMutate: () => { @@ -712,11 +706,7 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li {intent?.examples && (
- +
diff --git a/GUI/src/pages/Training/Intents/IntentExamplesTable.tsx b/GUI/src/pages/Training/Intents/IntentExamplesTable.tsx index 5e23d8e3..acab77aa 100644 --- a/GUI/src/pages/Training/Intents/IntentExamplesTable.tsx +++ b/GUI/src/pages/Training/Intents/IntentExamplesTable.tsx @@ -17,12 +17,11 @@ import LoadingDialog from '../../../components/LoadingDialog'; import i18n from '../../../../i18n'; type IntentExamplesTableProps = { - examples: { id: number; value: string }[]; - selectedIntent: Intent; + intent: Intent; updateSelectedIntent: (intent: Intent) => void; }; -const IntentExamplesTable: FC = ({ examples, selectedIntent, updateSelectedIntent }) => { +const IntentExamplesTable: FC = ({ intent, updateSelectedIntent }) => { let updatedExampleTitle = ''; const { t } = useTranslation(); const toast = useToast(); @@ -48,6 +47,11 @@ const IntentExamplesTable: FC = ({ examples, selectedI queryKey: ['entities'], }); + const examples = useMemo( + () => intent?.examples.map((example, index) => ({ id: index, value: example })) ?? [], + [intent?.examples] + ); + useDocumentEscapeListener(() => { updatedExampleTitle = ''; setEditableRow(null); @@ -58,13 +62,13 @@ const IntentExamplesTable: FC = ({ examples, selectedI }; const updateExampleOnList = (oldExample: string, newExample: string): void => { - const updatedIntent = selectedIntent; + const updatedIntent = intent; updatedIntent.examples[updatedIntent.examples.indexOf(oldExample)] = newExample; updateSelectedIntent(updatedIntent); }; const deleteExampleFromList = (example: string): void => { - const updatedIntent = selectedIntent; + const updatedIntent = intent; const examplesArray = updatedIntent.examples; const index = examplesArray.findIndex((item) => item === example); examplesArray.splice(index, 1); @@ -75,7 +79,7 @@ const IntentExamplesTable: FC = ({ examples, selectedI const exampleToIntentMutation = useMutation({ mutationFn: ({ exampleName }: { intentName: string; exampleName: string }) => turnExampleIntoIntent({ - intentName: selectedIntent.id, + intentName: intent.id, exampleName: exampleName, }), onSuccess: () => { @@ -134,7 +138,7 @@ const IntentExamplesTable: FC = ({ examples, selectedI title: t('global.notification'), message: t('toast.exampleDeleted'), }); - updateSelectedIntent(selectedIntent); + updateSelectedIntent(intent); deleteExampleFromList(oldExampleText); }, onError: (error: AxiosError) => { @@ -172,14 +176,14 @@ const IntentExamplesTable: FC = ({ examples, selectedI }, onSettled: () => { setRefreshing(false); - updateSelectedIntent(selectedIntent); + updateSelectedIntent(intent); }, }); const handleNewExample = (example: string) => { addExamplesMutation.mutate({ - intentName: selectedIntent.id, - intentExamples: selectedIntent.examples, + intentName: intent.id, + intentExamples: intent.examples, newExamples: example.replace(/(\t|\n)+/g, ' ').trim(), }); }; @@ -230,7 +234,7 @@ const IntentExamplesTable: FC = ({ examples, selectedI setOldExampleText(editableRow.value); setExampleText(updatedExampleTitle.trim()); exampleEditMutation.mutate({ - intentName: selectedIntent.id, + intentName: intent.id, oldExample: editableRow.value, newExample: updatedExampleTitle.trim(), }); @@ -269,7 +273,7 @@ const IntentExamplesTable: FC = ({ examples, selectedI entitiesResponse?.response, updatedExampleTitle, exampleEditMutation, - selectedIntent.id, + intent.id, ] ); @@ -318,7 +322,7 @@ const IntentExamplesTable: FC = ({ examples, selectedI onClick={() => { setOldExampleText(deletableRow!.value); exampleDeleteMutation.mutate({ - intentName: selectedIntent.id, + intentName: intent.id, example: deletableRow!.value, }); }} From 802eed247c20a5916ea533a189dec0dabb6937f8 Mon Sep 17 00:00:00 2001 From: Igor Krupenja Date: Wed, 4 Dec 2024 12:04:19 +0200 Subject: [PATCH 21/44] Fix rule --- .../pages/Training/Intents/IntentDetails.tsx | 147 ++++-------------- 1 file changed, 32 insertions(+), 115 deletions(-) diff --git a/GUI/src/pages/Training/Intents/IntentDetails.tsx b/GUI/src/pages/Training/Intents/IntentDetails.tsx index 55b24e49..88902ced 100644 --- a/GUI/src/pages/Training/Intents/IntentDetails.tsx +++ b/GUI/src/pages/Training/Intents/IntentDetails.tsx @@ -22,28 +22,29 @@ import { ROLES } from 'hoc/with-authorization'; import useStore from '../../../store/store'; import { editResponse } from 'services/responses'; import { addStoryOrRule, deleteStoryOrRule } from 'services/stories'; -import { RuleDTO } from 'types/rule'; +import { Rule, RuleDTO } from 'types/rule'; import ConnectServiceToIntentModal from 'pages/ConnectServiceToIntentModal'; import LoadingDialog from 'components/LoadingDialog'; import useDocumentEscapeListener from 'hooks/useDocumentEscapeListener'; import { IntentWithExamplesCount } from 'types/intentWithExampleCounts'; -interface Response { - name: string; - text: string; -} - -// TODO: back-end should return data in a better format interface ResponsesResponse extends Array<{ name: string; - response: Response[]; + response: { + name: string; + text: string; + }[]; }> {} interface IntentResponse { response: Intent; } +interface RulesResponse { + response: Rule[]; +} + interface IntentDetailsProps { intentId: string; setSelectedIntent: Dispatch>; @@ -96,6 +97,18 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li [intentId] ); + const addIntentRule = useCallback( + (rulesResponse: RulesResponse | undefined) => { + if (!rulesResponse) return; + + const intentRule = rulesResponse.response.find((rule: Rule) => rule.id === `rule_${intentId}`); + if (intentRule) { + setIntentRule(intentRule.id); + } + }, + [intentId] + ); + const queryRefresh = useCallback( async (intent?: string) => { const intentsResponse = await queryClient.fetchQuery([ @@ -107,49 +120,10 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li const resonsesResponse = await queryClient.fetchQuery(['responses-list']); setIntentResponse(resonsesResponse); - // todo also rule - - // setIntentResponseName(''); - // setIntentResponseText(''); - // setIntentRule(null); - // queryClient.fetchQuery(['intents/full']).then((res: any) => { - // setRefreshing(false); - // if (intents.length > 0) { - // const newSelectedIntent = res.response.intents.find((intent: any) => intent.title === selectIntent) || null; - // if (newSelectedIntent) { - // setSelectedIntent({ - // id: newSelectedIntent.title, - // description: null, - // inModel: newSelectedIntent.inmodel, - // modifiedAt: newSelectedIntent.modifiedAt, - // examplesCount: newSelectedIntent.examples.length, - // examples: newSelectedIntent.examples, - // serviceId: newSelectedIntent.serviceId, - // isForService: newSelectedIntent.isForService, - // }); - // setIsMarkedForService(newSelectedIntent.isForService ? newSelectedIntent.isForService : false); - // // queryClient.fetchQuery(['responses-list']).then((res: any) => { - // // if (intentResponses.length > 0) { - // // const intentExistingResponse = res[0].response.find((response: any) => `utter_${newSelectedIntent.title}` === response.name); - // // if (intentExistingResponse) { - // // setIntentResponseText(intentExistingResponse.text); - // // setIntentResponseName(intentExistingResponse.name); - // // } - // // } - // // }) - // // queryClient.fetchQuery(['rules']).then((res: any) => { - // // if (rules.length > 0) { - // // const intentExistingRule = res.response.find((rule: any) => rule.id === `rule_${newSelectedIntent.title}`) - // // if (intentExistingRule) { - // // setIntentRule(intentExistingRule.id); - // // } - // // } - // // }) - // } - // } - // }); - }, - [intentId, queryClient, setIntentResponse, setSelectedIntent] + const rulesResponse = await queryClient.fetchQuery(['rules']); + addIntentRule(rulesResponse); + }, + [addIntentRule, intentId, queryClient, setIntentResponse, setSelectedIntent] ); // TODO: need to fetch response for the selected intent only @@ -159,74 +133,16 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li useEffect(() => { setIntentResponse(responsesResponse); - }, [intent?.id, responsesResponse, setIntentResponse]); + }, [responsesResponse, setIntentResponse]); // TODO: need to fetch rules for the selected intent only - // todo not implemented, need to get rules for one intent only - const { data: rulesFullResponse } = useQuery({ + const { data: rulesResponse } = useQuery({ queryKey: ['rules'], }); - // let rulesFullList = rulesFullResponse?.response; - // let rules: Rule[] = []; - - // if (rulesFullList) { - // rulesFullList.forEach((rule: any) => { - // rules.push(rule); - // }); - // } - - // const queryRefreshOld = useCallback( - // function queryRefresh(selectIntent?: string) { - // setSelectedIntent(null); - // setIntentResponseName(null); - // setIntentResponseText(null); - // setIntentRule(null); - - // queryClient.fetchQuery(['intents/with-examples-count']).then((res: any) => { - // setRefreshing(false); - - // if (intents.length > 0) { - // const newSelectedIntent = res.response.intents.find((intent: any) => intent.title === selectIntent) || null; - // if (newSelectedIntent) { - // setSelectedIntent({ - // id: newSelectedIntent.title, - // description: null, - // inModel: newSelectedIntent.inmodel, - // modifiedAt: newSelectedIntent.modifiedAt, - // examplesCount: newSelectedIntent.examples.length, - // examples: newSelectedIntent.examples, - // serviceId: newSelectedIntent.serviceId, - // isForService: newSelectedIntent.isForService, - // }); - // setIsMarkedForService(newSelectedIntent.isForService ? newSelectedIntent.isForService : false); - - // queryClient.fetchQuery(['responses-list']).then((res: any) => { - // if (intentResponses.length > 0) { - // const intentExistingResponse = res[0].response.find( - // (response: any) => `utter_${newSelectedIntent.title}` === response.name - // ); - // if (intentExistingResponse) { - // setIntentResponseText(intentExistingResponse.text); - // setIntentResponseName(intentExistingResponse.name); - // } - // } - // }); - - // // queryClient.fetchQuery(['rules']).then((res: any) => { - // // if (rules.length > 0) { - // // const intentExistingRule = res.response.find((rule: any) => rule.id === `rule_${newSelectedIntent.title}`) - // // if (intentExistingRule) { - // // setIntentRule(intentExistingRule.id); - // // } - // // } - // // }) - // } - // } - // }); - // }, - // [queryClient, setSelectedIntent] - // ); + useEffect(() => { + addIntentRule(rulesResponse); + }, [rulesResponse, addIntentRule]); const markIntentServiceMutation = useMutation({ mutationFn: (data: { name: string; isForService: boolean }) => markForService(data), @@ -492,7 +408,8 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li }, }); - // todo clean up and fix errors + // todo clean up and fix errors - LAST ONE + // todo change example broken const handleIntentResponseSubmit = async () => { if (intentResponseText === '' || !intent) return; From efad763f3117555f5ecabee82581a0e08e842095 Mon Sep 17 00:00:00 2001 From: Igor Krupenja Date: Wed, 4 Dec 2024 12:19:57 +0200 Subject: [PATCH 22/44] Fix editing example --- .../pages/Training/Intents/IntentDetails.tsx | 1 - .../Training/Intents/IntentExamplesTable.tsx | 13 +++-- ....timestamp-1733303777321-e05aef2aeb96e.mjs | 48 +++++++++++++++++++ 3 files changed, 58 insertions(+), 4 deletions(-) create mode 100644 GUI/vite.config.ts.timestamp-1733303777321-e05aef2aeb96e.mjs diff --git a/GUI/src/pages/Training/Intents/IntentDetails.tsx b/GUI/src/pages/Training/Intents/IntentDetails.tsx index 88902ced..84ff6907 100644 --- a/GUI/src/pages/Training/Intents/IntentDetails.tsx +++ b/GUI/src/pages/Training/Intents/IntentDetails.tsx @@ -409,7 +409,6 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li }); // todo clean up and fix errors - LAST ONE - // todo change example broken const handleIntentResponseSubmit = async () => { if (intentResponseText === '' || !intent) return; diff --git a/GUI/src/pages/Training/Intents/IntentExamplesTable.tsx b/GUI/src/pages/Training/Intents/IntentExamplesTable.tsx index acab77aa..c7c98ae4 100644 --- a/GUI/src/pages/Training/Intents/IntentExamplesTable.tsx +++ b/GUI/src/pages/Training/Intents/IntentExamplesTable.tsx @@ -15,6 +15,7 @@ import IntentExamplesEntry from './IntentExamplesEntry'; import { Intent } from '../../../types/intent'; import LoadingDialog from '../../../components/LoadingDialog'; import i18n from '../../../../i18n'; +import { t } from 'i18next'; type IntentExamplesTableProps = { intent: Intent; @@ -103,8 +104,8 @@ const IntentExamplesTable: FC = ({ intent, updateSelec }); const exampleEditMutation = useMutation({ - mutationFn: (addExamplesData: { intentName: string; oldExample: string; newExample: string }) => - editExample(addExamplesData), + mutationFn: (editExampleData: { intentName: string; oldExample: string; newExample: string }) => + editExample(editExampleData), onMutate: async () => { setRefreshing(true); }, @@ -233,10 +234,16 @@ const IntentExamplesTable: FC = ({ intent, updateSelec if (!editableRow) return; setOldExampleText(editableRow.value); setExampleText(updatedExampleTitle.trim()); + + const updatedTrimmedExample = updatedExampleTitle.trim(); + if (updatedTrimmedExample === '') { + setEditableRow(null); + return; + } exampleEditMutation.mutate({ intentName: intent.id, oldExample: editableRow.value, - newExample: updatedExampleTitle.trim(), + newExample: updatedTrimmedExample, }); }, () => diff --git a/GUI/vite.config.ts.timestamp-1733303777321-e05aef2aeb96e.mjs b/GUI/vite.config.ts.timestamp-1733303777321-e05aef2aeb96e.mjs new file mode 100644 index 00000000..e029de06 --- /dev/null +++ b/GUI/vite.config.ts.timestamp-1733303777321-e05aef2aeb96e.mjs @@ -0,0 +1,48 @@ +// vite.config.ts +import { defineConfig, loadEnv } from "file:///app/node_modules/vite/dist/node/index.js"; +import react from "file:///app/node_modules/@vitejs/plugin-react/dist/index.mjs"; +import tsconfigPaths from "file:///app/node_modules/vite-tsconfig-paths/dist/index.mjs"; +import svgr from "file:///app/node_modules/vite-plugin-svgr/dist/index.mjs"; +import path from "path"; +var __vite_injected_original_dirname = "/app"; +var vite_config_default = ({ mode }) => { + process.env = Object.assign(process.env, loadEnv(mode, process.cwd(), "")); + return defineConfig({ + envPrefix: "REACT_APP_", + plugins: [ + react(), + tsconfigPaths(), + svgr() + ], + base: "/training", + //Change this according to your reverse proxy subpath + optimizeDeps: { + include: ["howler"] + }, + define: { + "process.env": process.env + }, + server: { + headers: { + ...process.env.REACT_APP_CSP && { + "Content-Security-Policy": process.env.REACT_APP_CSP + } + } + }, + build: { + outDir: "./build", + target: "es2015", + emptyOutDir: true + }, + resolve: { + alias: { + "~@fontsource": path.resolve(__vite_injected_original_dirname, "node_modules/@fontsource"), + "@": `${path.resolve(__vite_injected_original_dirname, "./src")}` + } + } + }); +}; +export { + vite_config_default as default +}; +//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCIvYXBwXCI7Y29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2ZpbGVuYW1lID0gXCIvYXBwL3ZpdGUuY29uZmlnLnRzXCI7Y29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2ltcG9ydF9tZXRhX3VybCA9IFwiZmlsZTovLy9hcHAvdml0ZS5jb25maWcudHNcIjtpbXBvcnQgeyBkZWZpbmVDb25maWcsIGxvYWRFbnYgfSBmcm9tICd2aXRlJztcbmltcG9ydCByZWFjdCBmcm9tICdAdml0ZWpzL3BsdWdpbi1yZWFjdCc7XG5pbXBvcnQgdHNjb25maWdQYXRocyBmcm9tICd2aXRlLXRzY29uZmlnLXBhdGhzJztcbmltcG9ydCBzdmdyIGZyb20gJ3ZpdGUtcGx1Z2luLXN2Z3InO1xuaW1wb3J0IHBhdGggZnJvbSAncGF0aCc7XG5cbi8vIGh0dHBzOi8vdml0ZWpzLmRldi9jb25maWcvXG5leHBvcnQgZGVmYXVsdCAoeyBtb2RlIH0pID0+IHtcbiAgcHJvY2Vzcy5lbnYgPSBPYmplY3QuYXNzaWduKHByb2Nlc3MuZW52LCBsb2FkRW52KG1vZGUsIHByb2Nlc3MuY3dkKCksICcnKSk7XG5cbiAgcmV0dXJuIGRlZmluZUNvbmZpZyh7XG4gICAgZW52UHJlZml4OiAnUkVBQ1RfQVBQXycsXG4gICAgcGx1Z2luczogW1xuICAgICAgcmVhY3QoKSxcbiAgICAgIHRzY29uZmlnUGF0aHMoKSxcbiAgICAgIHN2Z3IoKSxcbiAgICBdLFxuICAgIGJhc2U6ICcvdHJhaW5pbmcnLCAvL0NoYW5nZSB0aGlzIGFjY29yZGluZyB0byB5b3VyIHJldmVyc2UgcHJveHkgc3VicGF0aFxuICAgIG9wdGltaXplRGVwczoge1xuICAgICAgaW5jbHVkZTogWydob3dsZXInXSxcbiAgICB9LFxuICAgIGRlZmluZToge1xuICAgICAgJ3Byb2Nlc3MuZW52JzogcHJvY2Vzcy5lbnYsXG4gICAgfSxcbiAgICBzZXJ2ZXI6IHtcbiAgICAgIGhlYWRlcnM6IHtcbiAgICAgICAgLi4uKHByb2Nlc3MuZW52LlJFQUNUX0FQUF9DU1AgJiYge1xuICAgICAgICAgICdDb250ZW50LVNlY3VyaXR5LVBvbGljeSc6IHByb2Nlc3MuZW52LlJFQUNUX0FQUF9DU1AsXG4gICAgICAgIH0pLFxuICAgICAgfSxcbiAgICB9LFxuICAgIGJ1aWxkOiB7XG4gICAgICBvdXREaXI6ICcuL2J1aWxkJyxcbiAgICAgIHRhcmdldDogJ2VzMjAxNScsXG4gICAgICBlbXB0eU91dERpcjogdHJ1ZSxcbiAgICB9LFxuICAgIHJlc29sdmU6IHtcbiAgICAgIGFsaWFzOiB7XG4gICAgICAgICd+QGZvbnRzb3VyY2UnOiBwYXRoLnJlc29sdmUoX19kaXJuYW1lLCAnbm9kZV9tb2R1bGVzL0Bmb250c291cmNlJyksXG4gICAgICAgICdAJzogYCR7cGF0aC5yZXNvbHZlKF9fZGlybmFtZSwgJy4vc3JjJyl9YCxcbiAgICAgIH0sXG4gICAgfSxcbiAgfSk7XG59O1xuIl0sCiAgIm1hcHBpbmdzIjogIjtBQUE4TCxTQUFTLGNBQWMsZUFBZTtBQUNwTyxPQUFPLFdBQVc7QUFDbEIsT0FBTyxtQkFBbUI7QUFDMUIsT0FBTyxVQUFVO0FBQ2pCLE9BQU8sVUFBVTtBQUpqQixJQUFNLG1DQUFtQztBQU96QyxJQUFPLHNCQUFRLENBQUMsRUFBRSxLQUFLLE1BQU07QUFDM0IsVUFBUSxNQUFNLE9BQU8sT0FBTyxRQUFRLEtBQUssUUFBUSxNQUFNLFFBQVEsSUFBSSxHQUFHLEVBQUUsQ0FBQztBQUV6RSxTQUFPLGFBQWE7QUFBQSxJQUNsQixXQUFXO0FBQUEsSUFDWCxTQUFTO0FBQUEsTUFDUCxNQUFNO0FBQUEsTUFDTixjQUFjO0FBQUEsTUFDZCxLQUFLO0FBQUEsSUFDUDtBQUFBLElBQ0EsTUFBTTtBQUFBO0FBQUEsSUFDTixjQUFjO0FBQUEsTUFDWixTQUFTLENBQUMsUUFBUTtBQUFBLElBQ3BCO0FBQUEsSUFDQSxRQUFRO0FBQUEsTUFDTixlQUFlLFFBQVE7QUFBQSxJQUN6QjtBQUFBLElBQ0EsUUFBUTtBQUFBLE1BQ04sU0FBUztBQUFBLFFBQ1AsR0FBSSxRQUFRLElBQUksaUJBQWlCO0FBQUEsVUFDL0IsMkJBQTJCLFFBQVEsSUFBSTtBQUFBLFFBQ3pDO0FBQUEsTUFDRjtBQUFBLElBQ0Y7QUFBQSxJQUNBLE9BQU87QUFBQSxNQUNMLFFBQVE7QUFBQSxNQUNSLFFBQVE7QUFBQSxNQUNSLGFBQWE7QUFBQSxJQUNmO0FBQUEsSUFDQSxTQUFTO0FBQUEsTUFDUCxPQUFPO0FBQUEsUUFDTCxnQkFBZ0IsS0FBSyxRQUFRLGtDQUFXLDBCQUEwQjtBQUFBLFFBQ2xFLEtBQUssR0FBRyxLQUFLLFFBQVEsa0NBQVcsT0FBTyxDQUFDO0FBQUEsTUFDMUM7QUFBQSxJQUNGO0FBQUEsRUFDRixDQUFDO0FBQ0g7IiwKICAibmFtZXMiOiBbXQp9Cg== From 4b4bd079866bfe6cf27dc3f05eabb04fef620150 Mon Sep 17 00:00:00 2001 From: Igor Krupenja Date: Wed, 4 Dec 2024 13:01:59 +0200 Subject: [PATCH 23/44] gsa --- .../pages/Training/Intents/IntentDetails.tsx | 2 +- GUI/src/types/rule.ts | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/GUI/src/pages/Training/Intents/IntentDetails.tsx b/GUI/src/pages/Training/Intents/IntentDetails.tsx index 84ff6907..a58f8a63 100644 --- a/GUI/src/pages/Training/Intents/IntentDetails.tsx +++ b/GUI/src/pages/Training/Intents/IntentDetails.tsx @@ -421,7 +421,7 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li }); if (!intentResponseName) { - await addRuleMutation.mutate({ + addRuleMutation.mutate({ data: { rule: `rule_${intentId}`, steps: [ diff --git a/GUI/src/types/rule.ts b/GUI/src/types/rule.ts index 84881188..896a9270 100644 --- a/GUI/src/types/rule.ts +++ b/GUI/src/types/rule.ts @@ -1,6 +1,21 @@ +type RuleStep = { + intent?: string; + action?: string; + active_loop?: string | null; + slot_was_set?: + | Array> + | { + requested_slot: string | null; + slot?: string; + }; + entities?: Array<{ + [key: string]: string; + }>; +}; + export interface Rule { id: string; - steps: string | string[]; + steps: RuleStep | RuleStep[]; conversation_start?: string; wait_for_user_input?: string; } From 8786af776f4a2eb98449e1bc3d45e9cd0a40f032 Mon Sep 17 00:00:00 2001 From: Igor Krupenja Date: Wed, 4 Dec 2024 13:02:39 +0200 Subject: [PATCH 24/44] Clean up --- GUI/src/pages/Training/Intents/IntentDetails.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/GUI/src/pages/Training/Intents/IntentDetails.tsx b/GUI/src/pages/Training/Intents/IntentDetails.tsx index a58f8a63..962a351d 100644 --- a/GUI/src/pages/Training/Intents/IntentDetails.tsx +++ b/GUI/src/pages/Training/Intents/IntentDetails.tsx @@ -2,7 +2,7 @@ import { Track, FormInput, Button, Icon, Switch, Tooltip, FormTextarea, Dialog } import { isHiddenFeaturesEnabled, RESPONSE_TEXT_LENGTH } from 'constants/config'; import { format } from 'date-fns'; import { t } from 'i18next'; -import { Dispatch, FC, SetStateAction, useCallback, useEffect, useMemo, useState } from 'react'; +import { Dispatch, FC, SetStateAction, useCallback, useEffect, useState } from 'react'; import { MdOutlineSave, MdOutlineModeEditOutline } from 'react-icons/md'; import { Intent } from 'types/intent'; import IntentExamplesTable from './IntentExamplesTable'; @@ -408,13 +408,12 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li }, }); - // todo clean up and fix errors - LAST ONE const handleIntentResponseSubmit = async () => { if (intentResponseText === '' || !intent) return; const intentId = intent.id; - await addOrEditResponseMutation.mutate({ + addOrEditResponseMutation.mutate({ id: `utter_${intentId}`, responseText: intentResponseText, update: !!intentResponseName, From f785f2f9baa67b29ff773d4e8591a67f661eebb4 Mon Sep 17 00:00:00 2001 From: Igor Krupenja Date: Wed, 4 Dec 2024 13:02:55 +0200 Subject: [PATCH 25/44] Clean up --- ....timestamp-1733303777321-e05aef2aeb96e.mjs | 48 ------------------- 1 file changed, 48 deletions(-) delete mode 100644 GUI/vite.config.ts.timestamp-1733303777321-e05aef2aeb96e.mjs diff --git a/GUI/vite.config.ts.timestamp-1733303777321-e05aef2aeb96e.mjs b/GUI/vite.config.ts.timestamp-1733303777321-e05aef2aeb96e.mjs deleted file mode 100644 index e029de06..00000000 --- a/GUI/vite.config.ts.timestamp-1733303777321-e05aef2aeb96e.mjs +++ /dev/null @@ -1,48 +0,0 @@ -// vite.config.ts -import { defineConfig, loadEnv } from "file:///app/node_modules/vite/dist/node/index.js"; -import react from "file:///app/node_modules/@vitejs/plugin-react/dist/index.mjs"; -import tsconfigPaths from "file:///app/node_modules/vite-tsconfig-paths/dist/index.mjs"; -import svgr from "file:///app/node_modules/vite-plugin-svgr/dist/index.mjs"; -import path from "path"; -var __vite_injected_original_dirname = "/app"; -var vite_config_default = ({ mode }) => { - process.env = Object.assign(process.env, loadEnv(mode, process.cwd(), "")); - return defineConfig({ - envPrefix: "REACT_APP_", - plugins: [ - react(), - tsconfigPaths(), - svgr() - ], - base: "/training", - //Change this according to your reverse proxy subpath - optimizeDeps: { - include: ["howler"] - }, - define: { - "process.env": process.env - }, - server: { - headers: { - ...process.env.REACT_APP_CSP && { - "Content-Security-Policy": process.env.REACT_APP_CSP - } - } - }, - build: { - outDir: "./build", - target: "es2015", - emptyOutDir: true - }, - resolve: { - alias: { - "~@fontsource": path.resolve(__vite_injected_original_dirname, "node_modules/@fontsource"), - "@": `${path.resolve(__vite_injected_original_dirname, "./src")}` - } - } - }); -}; -export { - vite_config_default as default -}; -//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCIvYXBwXCI7Y29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2ZpbGVuYW1lID0gXCIvYXBwL3ZpdGUuY29uZmlnLnRzXCI7Y29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2ltcG9ydF9tZXRhX3VybCA9IFwiZmlsZTovLy9hcHAvdml0ZS5jb25maWcudHNcIjtpbXBvcnQgeyBkZWZpbmVDb25maWcsIGxvYWRFbnYgfSBmcm9tICd2aXRlJztcbmltcG9ydCByZWFjdCBmcm9tICdAdml0ZWpzL3BsdWdpbi1yZWFjdCc7XG5pbXBvcnQgdHNjb25maWdQYXRocyBmcm9tICd2aXRlLXRzY29uZmlnLXBhdGhzJztcbmltcG9ydCBzdmdyIGZyb20gJ3ZpdGUtcGx1Z2luLXN2Z3InO1xuaW1wb3J0IHBhdGggZnJvbSAncGF0aCc7XG5cbi8vIGh0dHBzOi8vdml0ZWpzLmRldi9jb25maWcvXG5leHBvcnQgZGVmYXVsdCAoeyBtb2RlIH0pID0+IHtcbiAgcHJvY2Vzcy5lbnYgPSBPYmplY3QuYXNzaWduKHByb2Nlc3MuZW52LCBsb2FkRW52KG1vZGUsIHByb2Nlc3MuY3dkKCksICcnKSk7XG5cbiAgcmV0dXJuIGRlZmluZUNvbmZpZyh7XG4gICAgZW52UHJlZml4OiAnUkVBQ1RfQVBQXycsXG4gICAgcGx1Z2luczogW1xuICAgICAgcmVhY3QoKSxcbiAgICAgIHRzY29uZmlnUGF0aHMoKSxcbiAgICAgIHN2Z3IoKSxcbiAgICBdLFxuICAgIGJhc2U6ICcvdHJhaW5pbmcnLCAvL0NoYW5nZSB0aGlzIGFjY29yZGluZyB0byB5b3VyIHJldmVyc2UgcHJveHkgc3VicGF0aFxuICAgIG9wdGltaXplRGVwczoge1xuICAgICAgaW5jbHVkZTogWydob3dsZXInXSxcbiAgICB9LFxuICAgIGRlZmluZToge1xuICAgICAgJ3Byb2Nlc3MuZW52JzogcHJvY2Vzcy5lbnYsXG4gICAgfSxcbiAgICBzZXJ2ZXI6IHtcbiAgICAgIGhlYWRlcnM6IHtcbiAgICAgICAgLi4uKHByb2Nlc3MuZW52LlJFQUNUX0FQUF9DU1AgJiYge1xuICAgICAgICAgICdDb250ZW50LVNlY3VyaXR5LVBvbGljeSc6IHByb2Nlc3MuZW52LlJFQUNUX0FQUF9DU1AsXG4gICAgICAgIH0pLFxuICAgICAgfSxcbiAgICB9LFxuICAgIGJ1aWxkOiB7XG4gICAgICBvdXREaXI6ICcuL2J1aWxkJyxcbiAgICAgIHRhcmdldDogJ2VzMjAxNScsXG4gICAgICBlbXB0eU91dERpcjogdHJ1ZSxcbiAgICB9LFxuICAgIHJlc29sdmU6IHtcbiAgICAgIGFsaWFzOiB7XG4gICAgICAgICd+QGZvbnRzb3VyY2UnOiBwYXRoLnJlc29sdmUoX19kaXJuYW1lLCAnbm9kZV9tb2R1bGVzL0Bmb250c291cmNlJyksXG4gICAgICAgICdAJzogYCR7cGF0aC5yZXNvbHZlKF9fZGlybmFtZSwgJy4vc3JjJyl9YCxcbiAgICAgIH0sXG4gICAgfSxcbiAgfSk7XG59O1xuIl0sCiAgIm1hcHBpbmdzIjogIjtBQUE4TCxTQUFTLGNBQWMsZUFBZTtBQUNwTyxPQUFPLFdBQVc7QUFDbEIsT0FBTyxtQkFBbUI7QUFDMUIsT0FBTyxVQUFVO0FBQ2pCLE9BQU8sVUFBVTtBQUpqQixJQUFNLG1DQUFtQztBQU96QyxJQUFPLHNCQUFRLENBQUMsRUFBRSxLQUFLLE1BQU07QUFDM0IsVUFBUSxNQUFNLE9BQU8sT0FBTyxRQUFRLEtBQUssUUFBUSxNQUFNLFFBQVEsSUFBSSxHQUFHLEVBQUUsQ0FBQztBQUV6RSxTQUFPLGFBQWE7QUFBQSxJQUNsQixXQUFXO0FBQUEsSUFDWCxTQUFTO0FBQUEsTUFDUCxNQUFNO0FBQUEsTUFDTixjQUFjO0FBQUEsTUFDZCxLQUFLO0FBQUEsSUFDUDtBQUFBLElBQ0EsTUFBTTtBQUFBO0FBQUEsSUFDTixjQUFjO0FBQUEsTUFDWixTQUFTLENBQUMsUUFBUTtBQUFBLElBQ3BCO0FBQUEsSUFDQSxRQUFRO0FBQUEsTUFDTixlQUFlLFFBQVE7QUFBQSxJQUN6QjtBQUFBLElBQ0EsUUFBUTtBQUFBLE1BQ04sU0FBUztBQUFBLFFBQ1AsR0FBSSxRQUFRLElBQUksaUJBQWlCO0FBQUEsVUFDL0IsMkJBQTJCLFFBQVEsSUFBSTtBQUFBLFFBQ3pDO0FBQUEsTUFDRjtBQUFBLElBQ0Y7QUFBQSxJQUNBLE9BQU87QUFBQSxNQUNMLFFBQVE7QUFBQSxNQUNSLFFBQVE7QUFBQSxNQUNSLGFBQWE7QUFBQSxJQUNmO0FBQUEsSUFDQSxTQUFTO0FBQUEsTUFDUCxPQUFPO0FBQUEsUUFDTCxnQkFBZ0IsS0FBSyxRQUFRLGtDQUFXLDBCQUEwQjtBQUFBLFFBQ2xFLEtBQUssR0FBRyxLQUFLLFFBQVEsa0NBQVcsT0FBTyxDQUFDO0FBQUEsTUFDMUM7QUFBQSxJQUNGO0FBQUEsRUFDRixDQUFDO0FBQ0g7IiwKICAibmFtZXMiOiBbXQp9Cg== From 5a11419434d3b9f4c7727239cbaa7f7810f401b4 Mon Sep 17 00:00:00 2001 From: Igor Krupenja Date: Wed, 4 Dec 2024 13:49:54 +0200 Subject: [PATCH 26/44] Clean up --- docker-compose.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 913e4d99..bc87a5b6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -250,14 +250,14 @@ services: - bykstack opensearch-dashboards: - image: opensearchproject/opensearch-dashboards:2.11.1 # Make sure the version of opensearch-dashboards matches the version of opensearch installed on other nodes + image: opensearchproject/opensearch-dashboards:2.11.1 container_name: opensearch-dashboards ports: - - 5601:5601 # Map host port 5601 to container port 5601 + - 5601:5601 expose: - - "5601" # Expose port 5601 for web access to OpenSearch Dashboards + - "5601" environment: - OPENSEARCH_HOSTS: '["http://opensearch:9200"]' # Define the OpenSearch nodes that OpenSearch Dashboards will query + OPENSEARCH_HOSTS: '["http://opensearch:9200"]' DISABLE_SECURITY_DASHBOARDS_PLUGIN: true networks: - bykstack From 871fc7da9698432cf67c35ab3e930f8793e21eb7 Mon Sep 17 00:00:00 2001 From: Igor Krupenja Date: Thu, 5 Dec 2024 15:59:16 +0200 Subject: [PATCH 27/44] Fix the issue --- .../POST/rasa/intents/turn-into-service.yml | 236 ------------------ GUI/src/pages/Training/Intents/index.tsx | 60 +---- GUI/src/services/intents.ts | 6 - 3 files changed, 2 insertions(+), 300 deletions(-) delete mode 100644 DSL/Ruuter.private/POST/rasa/intents/turn-into-service.yml diff --git a/DSL/Ruuter.private/POST/rasa/intents/turn-into-service.yml b/DSL/Ruuter.private/POST/rasa/intents/turn-into-service.yml deleted file mode 100644 index f1829f56..00000000 --- a/DSL/Ruuter.private/POST/rasa/intents/turn-into-service.yml +++ /dev/null @@ -1,236 +0,0 @@ -declaration: - call: declare - version: 0.1 - description: "Decription placeholder for 'TURN-INTO-SERVICE'" - method: post - accepts: json - returns: json - namespace: training - allowlist: - body: - - field: intentName - type: string - description: "Body field 'intentName'" - headers: - - field: cookie - type: string - description: "Cookie field" - -assign_values: - assign: - intentName: ${incoming.body.intentName.replaceAll(/\s+/g, "_")} - -extract_token_data: - call: http.post - args: - url: "[#TRAINING_PUBLIC_RUUTER]/internal/mock-tim-custom-jwt-userinfo" - headers: - cookie: ${incoming.headers.cookie} - body: - cookieName: "customJwtCookie" - result: jwtResult - -validate_administrator: - switch: - - condition: ${jwtResult.response.body.response.authorities.includes("ROLE_ADMINISTRATOR")} - next: get_domain_file - next: return_unauthorized - -get_domain_file: - call: http.get - args: - url: "[#TRAINING_PUBLIC_RUUTER]/internal/domain-file" - headers: - cookie: ${incoming.headers.cookie} - result: domainData - -validate_intent_exists: - switch: - - condition: ${domainData.response.body.response.intents.includes(intentName) === false} - next: return_intent_not_found - next: get_file_locations - -get_file_locations: - call: http.get - args: - url: "[#TRAINING_PUBLIC_RUUTER]/internal/return-file-locations" - headers: - cookie: ${incoming.headers.cookie} - result: fileLocations - -get_existing_intent_file: - call: http.post - args: - url: "[#TRAINING_DMAPPER]/file-manager/read-file" - body: - file_path: ${fileLocations.response.body.response.intents_location + intentName + "_nlu.yml"} - result: intentFile - -convert_yaml_to_json: - call: http.post - args: - url: "[#TRAINING_DMAPPER]/conversion/yaml_to_json" - body: - file: ${intentFile.response.body.file} - result: intentData - -manipulate_existing_examples: - call: http.post - args: - url: "[#TRAINING_DMAPPER]/conversion/string-replace" - body: - data: ${intentData.response.body.nlu[0].examples} - search: "- " - replace: "" - result: manipulateExistingExamplesResult - -split_existing_examles: - call: http.post - args: - url: "[#TRAINING_DMAPPER]/conversion/string-split" - body: - data: ${manipulateExistingExamplesResult.response.body} - separator: "\n" - result: splitedExamples - -get_new_service_intent_file: - call: http.post - args: - url: "[#TRAINING_DMAPPER]/hbs/training/get_intent_file" - headers: - type: "json" - body: - intent: ${"service_" + intentName} - examples: ${splitedExamples.response.body} - result: intentFileJson - -convert_intent_json_to_yaml: - call: http.post - args: - url: "[#TRAINING_DMAPPER]/conversion/json_to_yaml" - body: - version: "3.0" - nlu: ${intentFileJson.response.body} - result: intentYaml - -save_intent_file: - call: http.post - args: - url: "[#TRAINING_DMAPPER]/file-manager/create" - body: - file_path: ${fileLocations.response.body.response.intents_location + "service_" + intentName + "_nlu.tmp"} - content: ${intentYaml.response.body.json} - result: fileResult - -delete_old_intent_file: - call: http.post - args: - url: "[#TRAINING_DMAPPER]/file-manager/delete" - body: - file_path: ${fileLocations.response.body.response.intents_location + intentName + "_nlu.yml"} - result: deleteResult - -merge_new_intent_into_existing_intents: - call: http.post - args: - url: "[#TRAINING_DMAPPER]/merge/replace-array-element" - body: - array: ${domainData.response.body.response.intents} - element: ${intentName} - newValue: ${"service_" + intentName} - result: mergedIntents - -update_existing_domain_response: - call: http.post - args: - url: "[#TRAINING_DMAPPER]/domain/update-existing-response" - body: - json: ${domainData.response.body.response.responses} - searchKey: ${intentName} - newKey: ${"utter_service_" + intentName} - newKeyValue: ${"#service_" + intentName} - result: updatedResponses - -convert_domain_json_to_yaml: - call: http.post - args: - url: "[#TRAINING_DMAPPER]/conversion/json_to_yaml" - body: - version: ${domainData.response.body.response.version} - intents: ${mergedIntents.response.body.array} - entities: ${domainData.response.body.response.entities} - slots: ${domainData.response.body.response.slots} - forms: ${domainData.response.body.response.forms} - actions: ${domainData.response.body.response.actions} - responses: ${updatedResponses.response.body} - session_config: ${domainData.response.body.response.session_config} - result: domainYaml - -resave_domain_file: - call: http.post - args: - url: "[#TRAINING_DMAPPER]/file-manager/create" - body: - file_path: ${fileLocations.response.body.response.domain_location} - content: ${domainYaml.response.body.json} - result: fileResult - -# remove rule from rules -get_rules_file: - call: http.post - args: - url: "[#TRAINING_DMAPPER]/file-manager/read-file" - body: - file_path: ${fileLocations.response.body.response.rules_location} - result: ruleFile - -convert_rules_yaml_to_json: - call: http.post - args: - url: "[#TRAINING_DMAPPER]/conversion/yaml_to_json" - body: - file: ${ruleFile.response.body.file} - result: rulesData - -find_rule_by_intent_name: - call: http.post - args: - url: "[#TRAINING_DMAPPER]/object/rules/remove-by-intent-name" - body: - rulesJson: ${rulesData.response.body.rules} - searchIntentName: ${intentName} - result: rulesResult - -rules_json_to_yaml: - call: http.post - args: - url: "[#TRAINING_DMAPPER]/conversion/json_to_yaml" - body: - version: "3.0" - rules: ${rulesResult.response.body} - result: rulesYaml - -resave_rules_file: - call: http.post - args: - url: "[#TRAINING_DMAPPER]/file-manager/create" - body: - file_path: ${fileLocations.response.body.response.rules_location} - content: ${rulesYaml.response.body.json} - result: fileResult - -return_value: - wrapper: false - status: 201 - return: "" - next: end - -return_intent_not_found: - status: 404 - return: "Intent with that name not found" - next: end - -return_unauthorized: - status: 401 - return: "Unauthorized" - next: end diff --git a/GUI/src/pages/Training/Intents/index.tsx b/GUI/src/pages/Training/Intents/index.tsx index 40d7976b..23376345 100644 --- a/GUI/src/pages/Training/Intents/index.tsx +++ b/GUI/src/pages/Training/Intents/index.tsx @@ -5,10 +5,10 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import * as Tabs from '@radix-ui/react-tabs'; import { AxiosError } from 'axios'; -import { Button, Dialog, FormInput, Track } from 'components'; +import { Button, FormInput, Track } from 'components'; import { useToast } from 'hooks/useToast'; import { Intent } from 'types/intent'; -import { addIntent, turnIntentIntoService } from 'services/intents'; +import { addIntent } from 'services/intents'; import LoadingDialog from '../../../components/LoadingDialog'; import withAuthorization, { ROLES } from 'hoc/with-authorization'; import IntentTabList from './IntentTabList'; @@ -40,7 +40,6 @@ const Intents: FC = () => { const [selectedIntent, setSelectedIntent] = useState(null); const [refreshing, setRefreshing] = useState(false); const [filter, setFilter] = useState(''); - const [turnIntentToServiceIntent, setTurnIntentToServiceIntent] = useState(null); const { data: intentsResponse, isLoading } = useQuery({ queryKey: ['intents/with-examples-count'], @@ -80,35 +79,6 @@ const Intents: FC = () => { } }, [intents, searchParams]); - // TODO: This is not used at all at the moment - // TODO: If this is needed at some point, errors should be fixed - // TODO: Possibly relevant https://github.com/buerokratt/Training-Module/pull/663 - const turnIntentIntoServiceMutation = useMutation({ - mutationFn: ({ intent }: { intent: Intent }) => turnIntentIntoService(intent), - onMutate: () => { - setRefreshing(true); - }, - onSuccess: (_, { intent }) => { - toast.open({ - type: 'success', - title: t('global.notification'), - message: t('toast.intentToServiceSuccess'), - }); - window.location.href = `${serviceModuleGuiBaseUrl}/services/newService/${intent.intent}`; - }, - onError: (error: AxiosError) => { - toast.open({ - type: 'error', - title: t('global.notificationError'), - message: error.message, - }); - }, - onSettled: () => { - setRefreshing(false); - queryRefresh(selectedIntent?.id || ''); - }, - }); - const handleTabsValueChange = useCallback( (value: string) => { const selectedIntent = intents.find((intent) => intent.id === value); @@ -193,32 +163,6 @@ const Intents: FC = () => { )} - {turnIntentToServiceIntent !== null && ( - setTurnIntentToServiceIntent(null)} - footer={ - <> - - - - } - > -

{t('global.removeValidation')}

-
- )} - {refreshing && (

{t('global.updatingDataBody')}

diff --git a/GUI/src/services/intents.ts b/GUI/src/services/intents.ts index 43e5eb1e..ac8432cf 100644 --- a/GUI/src/services/intents.ts +++ b/GUI/src/services/intents.ts @@ -88,9 +88,3 @@ export async function turnExampleIntoIntent(data: { exampleName: string; intentN }); await rasaApi.post('intents/examples/delete', { intent: data.intentName, example: data.exampleName }); } - -export async function turnIntentIntoService(intent: Intent): Promise { - await rasaApi.post('intents/turn-into-service', { - intentName: intent.id, - }); -} From ef68b393b972b94c2cd8b0b370a533a5693385f3 Mon Sep 17 00:00:00 2001 From: Igor Krupenja Date: Fri, 6 Dec 2024 14:15:28 +0200 Subject: [PATCH 28/44] PoC without data mapping --- .../GET/rasa/response-by-intent-name.yml | 69 +++++++++++++++++++ .../pages/Training/Intents/IntentDetails.tsx | 3 +- 2 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 DSL/Ruuter.private/GET/rasa/response-by-intent-name.yml diff --git a/DSL/Ruuter.private/GET/rasa/response-by-intent-name.yml b/DSL/Ruuter.private/GET/rasa/response-by-intent-name.yml new file mode 100644 index 00000000..c2957c89 --- /dev/null +++ b/DSL/Ruuter.private/GET/rasa/response-by-intent-name.yml @@ -0,0 +1,69 @@ +declaration: + call: declare + version: 0.1 + # todo description + description: "Decription placeholder for 'RESPONSES-LIST'" + method: get + accepts: json + returns: json + namespace: training + +# assign_values: +# assign: +# params: +# response_name: "utter_common_klienditeenindajale_suunamine" +# response_text: "Suunan teid klienditeenindajale. Varuge natukene kannatust." +# next: getResponsesWithNameAndText + +getResponsesWithNameAndText: + call: http.post + args: + url: "[#TRAINING_OPENSEARCH]/responses/_search/template" + body: + id: "response-with-name-and-text" + params: + response_name: "utter_common_klienditeenindajale_suunamine" + response_text: "Suunan teid klienditeenindajale. Varuge natukene kannatust." + result: getResponsesResult + next: returnSuccess + +# mapResponsesData: +# call: http.post +# args: +# url: "[#TRAINING_DMAPPER]/hbs/training/get_responses" +# headers: +# type: "json" +# body: +# hits: ${getResponsesResult.response.body.hits.hits} +# result: responsesData +# next: returnSuccess + +returnSuccess: + return: ${getResponsesResult.response.body} + next: end +# getResponsesWithNameAndText: +# call: http.post +# args: +# url: "[#TRAINING_OPENSEARCH]/responses/_search/template" +# body: +# id: "response-with-name-and-text" +# params: +# response_name: "utter_common_klienditeenindajale_suunamine" +# response_text: "Suunan teid klienditeenindajale. Varuge natukene kannatust." +# result: getResponsesResult +# next: mapResponsesData + +# mapResponsesData: +# call: http.post +# args: +# url: "[#TRAINING_DMAPPER]/hbs/training/get_responses" +# headers: +# type: "json" +# body: +# hits: ${getResponsesResult.response.body.hits.hits} +# result: responsesData +# next: returnSuccess + +# returnSuccess: +# return: ${responsesData.response.body} +# next: end diff --git a/GUI/src/pages/Training/Intents/IntentDetails.tsx b/GUI/src/pages/Training/Intents/IntentDetails.tsx index 962a351d..c3af4617 100644 --- a/GUI/src/pages/Training/Intents/IntentDetails.tsx +++ b/GUI/src/pages/Training/Intents/IntentDetails.tsx @@ -128,10 +128,11 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li // TODO: need to fetch response for the selected intent only const { data: responsesResponse } = useQuery({ - queryKey: ['responses-list'], + queryKey: ['response-by-intent-name'], }); useEffect(() => { + console.log('responsesResponse', responsesResponse); setIntentResponse(responsesResponse); }, [responsesResponse, setIntentResponse]); From 705cb8f8fc7989b6b2df42536886dd015604e764 Mon Sep 17 00:00:00 2001 From: Igor Krupenja Date: Fri, 6 Dec 2024 14:52:26 +0200 Subject: [PATCH 29/44] PoC with poor format --- .../get_response_by_intent_name.handlebars | 32 ++++++++++++ .../GET/rasa/response-by-intent-name.yml | 50 +++++-------------- .../GET/rasa/responses-list.yml | 2 +- .../pages/Training/Intents/IntentDetails.tsx | 1 + 4 files changed, 46 insertions(+), 39 deletions(-) create mode 100644 DSL/DMapper/hbs/get_response_by_intent_name.handlebars diff --git a/DSL/DMapper/hbs/get_response_by_intent_name.handlebars b/DSL/DMapper/hbs/get_response_by_intent_name.handlebars new file mode 100644 index 00000000..eb751595 --- /dev/null +++ b/DSL/DMapper/hbs/get_response_by_intent_name.handlebars @@ -0,0 +1,32 @@ +{ +"responses": [ +{{#each response}} + { + "name": "{{_id}}", + "text": {{#if _source.response.[0].condition}}"{{{_source.response.[1].text}}}"{{else}}"{{{_source.response.[0].text}}}"{{/if}} + {{#if _source.response.[0].buttons}}, + "buttons": [ + {{#each _source.response.[0].buttons}} + { + "title": "{{title}}", + "payload": "{{{payload}}}" + }{{#unless @last}},{{/unless}} + {{/each}} + ] + {{/if}} + {{#if _source.response.[0].condition}}, + "condition": [ + {{#each _source.response.[0].condition}} + { + "type": "{{type}}", + "name": "{{name}}", + "value": {{#if value}}"{{{value}}}"{{else}}null{{/if}} + }{{#unless @last}},{{/unless}} + {{/each}} + ], + "conditionText": "{{{_source.response.[0].text}}}" + {{/if}} + }{{#unless @last}},{{/unless}} +{{/each}} +] +} \ No newline at end of file diff --git a/DSL/Ruuter.private/GET/rasa/response-by-intent-name.yml b/DSL/Ruuter.private/GET/rasa/response-by-intent-name.yml index c2957c89..25181da4 100644 --- a/DSL/Ruuter.private/GET/rasa/response-by-intent-name.yml +++ b/DSL/Ruuter.private/GET/rasa/response-by-intent-name.yml @@ -22,48 +22,22 @@ getResponsesWithNameAndText: body: id: "response-with-name-and-text" params: + # todo only name, remove hardcoded values response_name: "utter_common_klienditeenindajale_suunamine" response_text: "Suunan teid klienditeenindajale. Varuge natukene kannatust." result: getResponsesResult - next: returnSuccess -# mapResponsesData: -# call: http.post -# args: -# url: "[#TRAINING_DMAPPER]/hbs/training/get_responses" -# headers: -# type: "json" -# body: -# hits: ${getResponsesResult.response.body.hits.hits} -# result: responsesData -# next: returnSuccess +mapResponsesData: + call: http.post + args: + url: "[#TRAINING_DMAPPER]/hbs/training/get_response_by_intent_name" + headers: + type: "json" + body: + response: ${getResponsesResult.response.body.hits.hits} + result: responsesData + next: returnSuccess returnSuccess: - return: ${getResponsesResult.response.body} + return: ${responsesData.response.body.responses} next: end -# getResponsesWithNameAndText: -# call: http.post -# args: -# url: "[#TRAINING_OPENSEARCH]/responses/_search/template" -# body: -# id: "response-with-name-and-text" -# params: -# response_name: "utter_common_klienditeenindajale_suunamine" -# response_text: "Suunan teid klienditeenindajale. Varuge natukene kannatust." -# result: getResponsesResult -# next: mapResponsesData - -# mapResponsesData: -# call: http.post -# args: -# url: "[#TRAINING_DMAPPER]/hbs/training/get_responses" -# headers: -# type: "json" -# body: -# hits: ${getResponsesResult.response.body.hits.hits} -# result: responsesData -# next: returnSuccess - -# returnSuccess: -# return: ${responsesData.response.body} -# 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 c3af4617..0ccb5bf2 100644 --- a/GUI/src/pages/Training/Intents/IntentDetails.tsx +++ b/GUI/src/pages/Training/Intents/IntentDetails.tsx @@ -129,6 +129,7 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li // TODO: need to fetch response for the selected intent only const { data: responsesResponse } = useQuery({ queryKey: ['response-by-intent-name'], + // queryKey: ['responses-list'], }); useEffect(() => { From 9fa9066ed8d3d88736069f2427d38933592217a5 Mon Sep 17 00:00:00 2001 From: Igor Krupenja Date: Fri, 6 Dec 2024 15:17:16 +0200 Subject: [PATCH 30/44] PoC with better format --- .../get_response_by_intent_name.handlebars | 22 ++++++++----------- .../GET/rasa/response-by-intent-name.yml | 22 ++++++++++++++----- .../pages/Training/Intents/IntentDetails.tsx | 5 +++-- 3 files changed, 29 insertions(+), 20 deletions(-) diff --git a/DSL/DMapper/hbs/get_response_by_intent_name.handlebars b/DSL/DMapper/hbs/get_response_by_intent_name.handlebars index eb751595..b9db8a55 100644 --- a/DSL/DMapper/hbs/get_response_by_intent_name.handlebars +++ b/DSL/DMapper/hbs/get_response_by_intent_name.handlebars @@ -1,12 +1,10 @@ { -"responses": [ -{{#each response}} - { - "name": "{{_id}}", - "text": {{#if _source.response.[0].condition}}"{{{_source.response.[1].text}}}"{{else}}"{{{_source.response.[0].text}}}"{{/if}} - {{#if _source.response.[0].buttons}}, +"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}} + {{#if response.0._source.response.[0].buttons}}, "buttons": [ - {{#each _source.response.[0].buttons}} + {{#each response.0._source.response.[0].buttons}} { "title": "{{title}}", "payload": "{{{payload}}}" @@ -14,9 +12,9 @@ {{/each}} ] {{/if}} - {{#if _source.response.[0].condition}}, + {{#if response.0._source.response.[0].condition}}, "condition": [ - {{#each _source.response.[0].condition}} + {{#each response.0._source.response.[0].condition}} { "type": "{{type}}", "name": "{{name}}", @@ -24,9 +22,7 @@ }{{#unless @last}},{{/unless}} {{/each}} ], - "conditionText": "{{{_source.response.[0].text}}}" + "conditionText": "{{{response.0._source.response.[0].text}}}" {{/if}} - }{{#unless @last}},{{/unless}} -{{/each}} -] +} } \ No newline at end of file diff --git a/DSL/Ruuter.private/GET/rasa/response-by-intent-name.yml b/DSL/Ruuter.private/GET/rasa/response-by-intent-name.yml index 25181da4..4d5f0901 100644 --- a/DSL/Ruuter.private/GET/rasa/response-by-intent-name.yml +++ b/DSL/Ruuter.private/GET/rasa/response-by-intent-name.yml @@ -1,12 +1,25 @@ declaration: call: declare version: 0.1 - # todo description - description: "Decription placeholder for 'RESPONSES-LIST'" + description: "Get response by intent name" method: get accepts: json returns: json namespace: training + allowlist: + # headers: + # - field: cookie + # type: string + # description: "Cookie field" + params: + - field: intent_name + type: string + description: "Intent ID" + +assign_values: + assign: + response_name: ${"utter_" + incoming.params.intent_name} + # next: getResponsesWithNameAndText # assign_values: # assign: @@ -22,8 +35,7 @@ getResponsesWithNameAndText: body: id: "response-with-name-and-text" params: - # todo only name, remove hardcoded values - response_name: "utter_common_klienditeenindajale_suunamine" + response_name: ${response_name} response_text: "Suunan teid klienditeenindajale. Varuge natukene kannatust." result: getResponsesResult @@ -39,5 +51,5 @@ mapResponsesData: next: returnSuccess returnSuccess: - return: ${responsesData.response.body.responses} + return: ${responsesData.response.body.response} next: end diff --git a/GUI/src/pages/Training/Intents/IntentDetails.tsx b/GUI/src/pages/Training/Intents/IntentDetails.tsx index 0ccb5bf2..922cd65d 100644 --- a/GUI/src/pages/Training/Intents/IntentDetails.tsx +++ b/GUI/src/pages/Training/Intents/IntentDetails.tsx @@ -128,8 +128,9 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li // TODO: need to fetch response for the selected intent only const { data: responsesResponse } = useQuery({ - queryKey: ['response-by-intent-name'], - // queryKey: ['responses-list'], + // queryKey: [`response-by-intent-name?intent_name=${intentId}`], + // todo also search to invalidate in this file! + queryKey: ['responses-list'], }); useEffect(() => { From 8e97646f29f350191473bbc2724291c4553af344 Mon Sep 17 00:00:00 2001 From: Igor Krupenja Date: Fri, 6 Dec 2024 15:17:44 +0200 Subject: [PATCH 31/44] Clean up --- .../GET/rasa/response-by-intent-name.yml | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/DSL/Ruuter.private/GET/rasa/response-by-intent-name.yml b/DSL/Ruuter.private/GET/rasa/response-by-intent-name.yml index 4d5f0901..c3f06077 100644 --- a/DSL/Ruuter.private/GET/rasa/response-by-intent-name.yml +++ b/DSL/Ruuter.private/GET/rasa/response-by-intent-name.yml @@ -7,10 +7,6 @@ declaration: returns: json namespace: training allowlist: - # headers: - # - field: cookie - # type: string - # description: "Cookie field" params: - field: intent_name type: string @@ -19,14 +15,6 @@ declaration: assign_values: assign: response_name: ${"utter_" + incoming.params.intent_name} - # next: getResponsesWithNameAndText - -# assign_values: -# assign: -# params: -# response_name: "utter_common_klienditeenindajale_suunamine" -# response_text: "Suunan teid klienditeenindajale. Varuge natukene kannatust." -# next: getResponsesWithNameAndText getResponsesWithNameAndText: call: http.post From 96153ef3fa12785bfe5f26ce0c3b2555b41d067b Mon Sep 17 00:00:00 2001 From: Igor Krupenja Date: Fri, 6 Dec 2024 15:22:51 +0200 Subject: [PATCH 32/44] Cle --- .../get_response_by_intent_name.handlebars | 22 ------------------- .../pages/Training/Intents/IntentDetails.tsx | 9 ++++++-- 2 files changed, 7 insertions(+), 24 deletions(-) diff --git a/DSL/DMapper/hbs/get_response_by_intent_name.handlebars b/DSL/DMapper/hbs/get_response_by_intent_name.handlebars index b9db8a55..281a6fc0 100644 --- a/DSL/DMapper/hbs/get_response_by_intent_name.handlebars +++ b/DSL/DMapper/hbs/get_response_by_intent_name.handlebars @@ -2,27 +2,5 @@ "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}} - {{#if response.0._source.response.[0].buttons}}, - "buttons": [ - {{#each response.0._source.response.[0].buttons}} - { - "title": "{{title}}", - "payload": "{{{payload}}}" - }{{#unless @last}},{{/unless}} - {{/each}} - ] - {{/if}} - {{#if response.0._source.response.[0].condition}}, - "condition": [ - {{#each response.0._source.response.[0].condition}} - { - "type": "{{type}}", - "name": "{{name}}", - "value": {{#if value}}"{{{value}}}"{{else}}null{{/if}} - }{{#unless @last}},{{/unless}} - {{/each}} - ], - "conditionText": "{{{response.0._source.response.[0].text}}}" - {{/if}} } } \ No newline at end of file diff --git a/GUI/src/pages/Training/Intents/IntentDetails.tsx b/GUI/src/pages/Training/Intents/IntentDetails.tsx index 922cd65d..1d3cb34c 100644 --- a/GUI/src/pages/Training/Intents/IntentDetails.tsx +++ b/GUI/src/pages/Training/Intents/IntentDetails.tsx @@ -28,6 +28,11 @@ import LoadingDialog from 'components/LoadingDialog'; import useDocumentEscapeListener from 'hooks/useDocumentEscapeListener'; import { IntentWithExamplesCount } from 'types/intentWithExampleCounts'; +interface Response { + name: string; + text: string; +} + interface ResponsesResponse extends Array<{ name: string; @@ -128,9 +133,9 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li // TODO: need to fetch response for the selected intent only const { data: responsesResponse } = useQuery({ - // queryKey: [`response-by-intent-name?intent_name=${intentId}`], + queryKey: [`response-by-intent-name?intent_name=${intentId}`], // todo also search to invalidate in this file! - queryKey: ['responses-list'], + // queryKey: ['responses-list'], }); useEffect(() => { From c41897e75218b0bb0a154a53f5389553a8828a96 Mon Sep 17 00:00:00 2001 From: Igor Krupenja Date: Fri, 6 Dec 2024 15:31:03 +0200 Subject: [PATCH 33/44] PoC component --- .../GET/rasa/response-by-intent-name.yml | 1 + .../pages/Training/Intents/IntentDetails.tsx | 57 ++++++------------- 2 files changed, 19 insertions(+), 39 deletions(-) diff --git a/DSL/Ruuter.private/GET/rasa/response-by-intent-name.yml b/DSL/Ruuter.private/GET/rasa/response-by-intent-name.yml index c3f06077..3b5f42d2 100644 --- a/DSL/Ruuter.private/GET/rasa/response-by-intent-name.yml +++ b/DSL/Ruuter.private/GET/rasa/response-by-intent-name.yml @@ -1,3 +1,4 @@ +# todo rename declaration: call: declare version: 0.1 diff --git a/GUI/src/pages/Training/Intents/IntentDetails.tsx b/GUI/src/pages/Training/Intents/IntentDetails.tsx index 1d3cb34c..2c4b4e89 100644 --- a/GUI/src/pages/Training/Intents/IntentDetails.tsx +++ b/GUI/src/pages/Training/Intents/IntentDetails.tsx @@ -33,14 +33,9 @@ interface Response { text: string; } -interface ResponsesResponse - extends Array<{ - name: string; - response: { - name: string; - text: string; - }[]; - }> {} +interface ResponseResponse { + response: Response; +} interface IntentResponse { response: Intent; @@ -63,8 +58,8 @@ 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({ name: '', text: '' }); const [intentRule, setIntentRule] = useState(''); const queryClient = useQueryClient(); @@ -79,7 +74,7 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li setIntent(intentResponse.response); // Also reset form states on choosing another intent from list setEditingIntentTitle(null); - setIntentResponseText(''); + setResponse({ name: '', text: '' }); } }, [intentResponse]); @@ -87,21 +82,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; @@ -122,26 +102,25 @@ 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(['responses-list']); + 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({ + const { data: responseResponse } = useQuery({ queryKey: [`response-by-intent-name?intent_name=${intentId}`], // todo also search to invalidate in this file! // queryKey: ['responses-list'], }); useEffect(() => { - console.log('responsesResponse', responsesResponse); - setIntentResponse(responsesResponse); - }, [responsesResponse, setIntentResponse]); + if (responseResponse) setResponse(responseResponse.response); + }, [responseResponse, setResponse]); // TODO: need to fetch rules for the selected intent only const { data: rulesResponse } = useQuery({ @@ -417,17 +396,17 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li }); const handleIntentResponseSubmit = async () => { - if (intentResponseText === '' || !intent) return; + if (response.text === '' || !intent) return; const intentId = intent.id; addOrEditResponseMutation.mutate({ id: `utter_${intentId}`, - responseText: intentResponseText, - update: !!intentResponseName, + responseText: response.text, + update: !!response.name, }); - if (!intentResponseName) { + if (!response.name) { addRuleMutation.mutate({ data: { rule: `rule_${intentId}`, @@ -637,7 +616,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({ ...response, text: e.target.value })} disableHeightResize /> From 3d770c8388f8a902a15d82781f017181e821e326 Mon Sep 17 00:00:00 2001 From: Igor Krupenja Date: Fri, 6 Dec 2024 15:40:06 +0200 Subject: [PATCH 34/44] Clean up --- GUI/src/pages/Training/Intents/IntentDetails.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/GUI/src/pages/Training/Intents/IntentDetails.tsx b/GUI/src/pages/Training/Intents/IntentDetails.tsx index 2c4b4e89..2e079ceb 100644 --- a/GUI/src/pages/Training/Intents/IntentDetails.tsx +++ b/GUI/src/pages/Training/Intents/IntentDetails.tsx @@ -102,7 +102,9 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li setIntent(intentsResponse.response); setSelectedIntent(intentsResponse.response); - const responseResponse = await queryClient.fetchQuery(['responses-list']); + const responseResponse = await queryClient.fetchQuery([ + `response-by-intent-name?intent_name=${intentId}`, + ]); setResponse(responseResponse.response); const rulesResponse = await queryClient.fetchQuery(['rules']); @@ -111,11 +113,9 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li [addIntentRule, intentId, queryClient, setResponse, setSelectedIntent] ); - // TODO: need to fetch response for the selected intent only + // TODO rename does not work? need to invalidate? const { data: responseResponse } = useQuery({ queryKey: [`response-by-intent-name?intent_name=${intentId}`], - // todo also search to invalidate in this file! - // queryKey: ['responses-list'], }); useEffect(() => { From fe8ce3fdeed2ddec613e4f03cfe7c2ceb2a30959 Mon Sep 17 00:00:00 2001 From: Igor Krupenja Date: Fri, 6 Dec 2024 15:42:45 +0200 Subject: [PATCH 35/44] Fix name --- ...ame.handlebars => get_response_by_intent_id.handlebars} | 0 ...sponse-by-intent-name.yml => response-by-intent-id.yml} | 7 +++---- GUI/src/pages/Training/Intents/IntentDetails.tsx | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) rename DSL/DMapper/hbs/{get_response_by_intent_name.handlebars => get_response_by_intent_id.handlebars} (100%) rename DSL/Ruuter.private/GET/rasa/{response-by-intent-name.yml => response-by-intent-id.yml} (89%) diff --git a/DSL/DMapper/hbs/get_response_by_intent_name.handlebars b/DSL/DMapper/hbs/get_response_by_intent_id.handlebars similarity index 100% rename from DSL/DMapper/hbs/get_response_by_intent_name.handlebars rename to DSL/DMapper/hbs/get_response_by_intent_id.handlebars diff --git a/DSL/Ruuter.private/GET/rasa/response-by-intent-name.yml b/DSL/Ruuter.private/GET/rasa/response-by-intent-id.yml similarity index 89% rename from DSL/Ruuter.private/GET/rasa/response-by-intent-name.yml rename to DSL/Ruuter.private/GET/rasa/response-by-intent-id.yml index 3b5f42d2..2a91f21f 100644 --- a/DSL/Ruuter.private/GET/rasa/response-by-intent-name.yml +++ b/DSL/Ruuter.private/GET/rasa/response-by-intent-id.yml @@ -1,4 +1,3 @@ -# todo rename declaration: call: declare version: 0.1 @@ -9,13 +8,13 @@ declaration: namespace: training allowlist: params: - - field: intent_name + - field: intent type: string description: "Intent ID" assign_values: assign: - response_name: ${"utter_" + incoming.params.intent_name} + response_name: ${"utter_" + incoming.params.intent} getResponsesWithNameAndText: call: http.post @@ -31,7 +30,7 @@ getResponsesWithNameAndText: mapResponsesData: call: http.post args: - url: "[#TRAINING_DMAPPER]/hbs/training/get_response_by_intent_name" + url: "[#TRAINING_DMAPPER]/hbs/training/get_response_by_intent_id" headers: type: "json" body: diff --git a/GUI/src/pages/Training/Intents/IntentDetails.tsx b/GUI/src/pages/Training/Intents/IntentDetails.tsx index 2e079ceb..4c5686b6 100644 --- a/GUI/src/pages/Training/Intents/IntentDetails.tsx +++ b/GUI/src/pages/Training/Intents/IntentDetails.tsx @@ -103,7 +103,7 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li setSelectedIntent(intentsResponse.response); const responseResponse = await queryClient.fetchQuery([ - `response-by-intent-name?intent_name=${intentId}`, + `response-by-intent-id?intent=${intentId}`, ]); setResponse(responseResponse.response); @@ -115,7 +115,7 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li // TODO rename does not work? need to invalidate? const { data: responseResponse } = useQuery({ - queryKey: [`response-by-intent-name?intent_name=${intentId}`], + queryKey: [`response-by-intent-id?intent=${intentId}`], }); useEffect(() => { From 3c154c6b802831bff5ddcefb1c14d89021d8658e Mon Sep 17 00:00:00 2001 From: Igor Krupenja Date: Fri, 6 Dec 2024 15:54:17 +0200 Subject: [PATCH 36/44] Fix query --- DSL/OpenSearch/templates/response-with-name-and-text.json | 6 ++++-- DSL/Ruuter.private/GET/rasa/response-by-intent-id.yml | 1 - GUI/src/pages/Training/Intents/IntentDetails.tsx | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/DSL/OpenSearch/templates/response-with-name-and-text.json b/DSL/OpenSearch/templates/response-with-name-and-text.json index 18270ae5..ce90ec8f 100644 --- a/DSL/OpenSearch/templates/response-with-name-and-text.json +++ b/DSL/OpenSearch/templates/response-with-name-and-text.json @@ -11,13 +11,15 @@ "query": "*{{response_name}}*", "default_field": "name" } - }, + } + {{#response_text}}, { "query_string": { "query": "*{{response_text}}*", "default_field": "response.text" } } + {{/response_text}} ] } } @@ -27,4 +29,4 @@ "response_text": "" } } -} +} \ No newline at end of file diff --git a/DSL/Ruuter.private/GET/rasa/response-by-intent-id.yml b/DSL/Ruuter.private/GET/rasa/response-by-intent-id.yml index 2a91f21f..17c5354c 100644 --- a/DSL/Ruuter.private/GET/rasa/response-by-intent-id.yml +++ b/DSL/Ruuter.private/GET/rasa/response-by-intent-id.yml @@ -24,7 +24,6 @@ getResponsesWithNameAndText: id: "response-with-name-and-text" params: response_name: ${response_name} - response_text: "Suunan teid klienditeenindajale. Varuge natukene kannatust." result: getResponsesResult mapResponsesData: diff --git a/GUI/src/pages/Training/Intents/IntentDetails.tsx b/GUI/src/pages/Training/Intents/IntentDetails.tsx index 4c5686b6..fb048092 100644 --- a/GUI/src/pages/Training/Intents/IntentDetails.tsx +++ b/GUI/src/pages/Training/Intents/IntentDetails.tsx @@ -113,6 +113,7 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li [addIntentRule, intentId, queryClient, setResponse, setSelectedIntent] ); + // todo also need to click twice lol. why? // TODO rename does not work? need to invalidate? const { data: responseResponse } = useQuery({ queryKey: [`response-by-intent-id?intent=${intentId}`], From 265107cb2247f3bba1276a41d79292c59711b5e7 Mon Sep 17 00:00:00 2001 From: Igor Krupenja Date: Fri, 6 Dec 2024 16:25:44 +0200 Subject: [PATCH 37/44] Fix fetch --- DSL/DMapper/locations/data/domain.yml | 408 +++++++----------- .../pages/Training/Intents/IntentDetails.tsx | 32 +- 2 files changed, 187 insertions(+), 253 deletions(-) diff --git a/DSL/DMapper/locations/data/domain.yml b/DSL/DMapper/locations/data/domain.yml index 547395c0..b12cee67 100644 --- a/DSL/DMapper/locations/data/domain.yml +++ b/DSL/DMapper/locations/data/domain.yml @@ -1,11 +1,8 @@ -version: '3.0' -session_config: - session_expiration_time: 60 - carry_over_slots_to_new_session: true +version: "3.0" intents: - common_ask_csa - common_hüvasti_jätmine - - common_kinnitamine + - common_kinnitamine - common_eitamine - common_tänamine - common_tervitus @@ -31,7 +28,6 @@ intents: - common_service_estimated_subsistence_minimum - common_service_motor_vehicle_tax - common_service_school_holiday - entities: - asukoht - rk_liige @@ -48,280 +44,249 @@ entities: - last_holidays - by_year - reg_number - slots: common_teenus_ilm_asukoht: type: text influence_conversation: true mappings: - - type: from_entity - entity: asukoht - intent: common_teenus_ilm - + - type: from_entity + entity: asukoht + intent: common_teenus_ilm reg_number: type: text influence_conversation: true mappings: - - type: from_entity - entity: reg_number - intent: common_service_motor_vehicle_tax - - type: from_text - conditions: - - active_loop: common_service_motor_vehicle_tax_form - requested_slot: reg_number - + - type: from_entity + entity: reg_number + intent: common_service_motor_vehicle_tax + - type: from_text + conditions: + - active_loop: common_service_motor_vehicle_tax_form + requested_slot: reg_number company_name: type: text influence_conversation: true - mappings: - - type: from_text - conditions: - - active_loop: common_service_companies_form - requested_slot: company_name - + mappings: + - type: from_text + conditions: + - active_loop: common_service_companies_form + requested_slot: company_name trigger_intent: type: text influence_conversation: false mappings: - - type: from_intent - value: common_service_companies_employees - intent: common_service_companies_employees - - type: from_intent - value: common_service_companies_revenue - intent: common_service_companies_revenue - - type: from_intent - value: common_service_companies_workforce_taxes - intent: common_service_companies_workforce_taxes - - type: from_intent - value: common_service_companies_national_taxes - intent: common_service_companies_national_taxes - - type: from_intent - value: common_service_companies_beneficiaries - intent: common_service_companies_beneficiaries - - type: from_intent - value: common_service_companies_related_persons - intent: common_service_companies_related_persons - - type: from_intent - value: common_service_companies_contactdetails - intent: common_service_companies_contactdetails - - type: from_intent - value: common_service_companies_lihtandmed - intent: common_service_companies_lihtandmed - + - type: from_intent + value: common_service_companies_employees + intent: common_service_companies_employees + - type: from_intent + value: common_service_companies_revenue + intent: common_service_companies_revenue + - type: from_intent + value: common_service_companies_workforce_taxes + intent: common_service_companies_workforce_taxes + - type: from_intent + value: common_service_companies_national_taxes + intent: common_service_companies_national_taxes + - type: from_intent + value: common_service_companies_beneficiaries + intent: common_service_companies_beneficiaries + - type: from_intent + value: common_service_companies_related_persons + intent: common_service_companies_related_persons + - type: from_intent + value: common_service_companies_contactdetails + intent: common_service_companies_contactdetails + - type: from_intent + value: common_service_companies_lihtandmed + intent: common_service_companies_lihtandmed rk_liige: type: text influence_conversation: true mappings: - - type: from_entity - entity: rk_liige - intent: common_teenus_rk_isiku_kohalolu - + - type: from_entity + entity: rk_liige + intent: common_teenus_rk_isiku_kohalolu rk_hääletus: type: text influence_conversation: true mappings: - - type: from_entity - entity: rk_hääletus - intent: common_teenus_rk_hääletus - + - type: from_entity + entity: rk_hääletus + intent: common_teenus_rk_hääletus rk_hääletused: type: text influence_conversation: true mappings: - - type: from_entity - entity: rk_hääletused - intent: common_teenus_rk_hääletus - + - type: from_entity + entity: rk_hääletused + intent: common_teenus_rk_hääletus np_kallis: type: text influence_conversation: true mappings: - - type: from_entity - entity: np_kallis - intent: common_teenus_nordpool2 - + - type: from_entity + entity: np_kallis + intent: common_teenus_nordpool2 np_odav: type: text influence_conversation: true mappings: - - type: from_entity - entity: np_odav - intent: common_teenus_nordpool2 - + - type: from_entity + entity: np_odav + intent: common_teenus_nordpool2 by_year_statistics: type: text influence_conversation: true mappings: - - type: from_entity - entity: by_year - intent: common_service_CPI - - type: from_entity - entity: by_year - intent: common_service_unemployment_rate - + - type: from_entity + entity: by_year + intent: common_service_CPI + - type: from_entity + entity: by_year + intent: common_service_unemployment_rate by_month_statistics: type: text influence_conversation: true mappings: - - type: from_entity - entity: by_month - intent: common_service_CPI - + - type: from_entity + entity: by_month + intent: common_service_CPI today_holiday: type: text influence_conversation: true mappings: - - type: from_entity - entity: today - intent: common_service_holidays - + - type: from_entity + entity: today + intent: common_service_holidays next_holiday: type: text influence_conversation: true mappings: - - type: from_entity - entity: next_holiday - intent: common_service_holidays - + - type: from_entity + entity: next_holiday + intent: common_service_holidays next_holidays: type: text influence_conversation: true mappings: - - type: from_entity - entity: next_holidays - intent: common_service_holidays - + - type: from_entity + entity: next_holidays + intent: common_service_holidays last_holiday: type: text influence_conversation: true mappings: - - type: from_entity - entity: last_holiday - intent: common_service_holidays - + - type: from_entity + entity: last_holiday + intent: common_service_holidays last_holidays: type: text influence_conversation: true mappings: - - type: from_entity - entity: last_holidays - intent: common_service_holidays - + - type: from_entity + entity: last_holidays + intent: common_service_holidays by_name_holiday: type: text influence_conversation: true mappings: - - type: from_entity - entity: by_name - intent: common_service_holidays - + - type: from_entity + entity: by_name + intent: common_service_holidays by_month_holiday: type: text influence_conversation: true mappings: - - type: from_entity - entity: by_month - intent: common_service_holidays - + - type: from_entity + entity: by_month + intent: common_service_holidays today_school_holiday: type: text influence_conversation: true mappings: - - type: from_entity - entity: today - intent: common_service_school_holiday - + - type: from_entity + entity: today + intent: common_service_school_holiday next_school_holiday: type: text influence_conversation: true mappings: - - type: from_entity - entity: next_holiday - intent: common_service_school_holiday - + - type: from_entity + entity: next_holiday + intent: common_service_school_holiday next_school_holidays: type: text influence_conversation: true mappings: - - type: from_entity - entity: next_holidays - intent: common_service_school_holiday - + - type: from_entity + entity: next_holidays + intent: common_service_school_holiday last_school_holiday: type: text influence_conversation: true mappings: - - type: from_entity - entity: last_holiday - intent: common_service_school_holiday - + - type: from_entity + entity: last_holiday + intent: common_service_school_holiday last_school_holidays: type: text influence_conversation: true mappings: - - type: from_entity - entity: last_holidays - intent: common_service_school_holiday - + - type: from_entity + entity: last_holidays + intent: common_service_school_holiday by_name_school_holiday: type: text influence_conversation: true mappings: - - type: from_entity - entity: by_name - intent: common_service_school_holiday - + - type: from_entity + entity: by_name + intent: common_service_school_holiday by_month_school_holiday: type: text influence_conversation: true mappings: - - type: from_entity - entity: by_month - intent: common_service_school_holiday - + - type: from_entity + entity: by_month + intent: common_service_school_holiday affirm_deny: type: text influence_conversation: true mappings: - - type: from_text - conditions: - - active_loop: custom_fallback_form - requested_slot: affirm_deny - - active_loop: direct_to_customer_support_form - requested_slot: affirm_deny - + - type: from_text + conditions: + - active_loop: custom_fallback_form + requested_slot: affirm_deny + - active_loop: direct_to_customer_support_form + requested_slot: affirm_deny forms: common_service_companies_form: required_slots: - - company_name + - company_name custom_fallback_form: required_slots: - - affirm_deny + - affirm_deny direct_to_customer_support_form: required_slots: - - affirm_deny + - affirm_deny common_service_motor_vehicle_tax_form: required_slots: - - reg_number - + - reg_number actions: -- action_ask_custom_fallback_form_affirm_deny -- action_check_confidence -- action_react_to_affirm_deny_in_custom_fallback_form -- action_react_to_affirm_deny_in_direct_to_customer_support_form - + - action_ask_custom_fallback_form_affirm_deny + - action_check_confidence + - action_react_to_affirm_deny_in_custom_fallback_form + - action_react_to_affirm_deny_in_direct_to_customer_support_form responses: utter_common_teenus_ilm: - condition: - type: slot name: common_teenus_ilm_asukoht value: null - text: "Hetkel oskan ma öelda ilma Eesti raames piirkonnapõhiselt. Palun sisestage korrektne piirkonna nimi." + text: Hetkel oskan ma öelda ilma Eesti raames piirkonnapõhiselt. Palun sisestage korrektne piirkonna nimi. - text: "#common_service, /POST/weather/EE, ({common_teenus_ilm_asukoht})" - utter_ask_company_name: - - text: "Mis firma kohta sa infot soovid?" - + - text: Mis firma kohta sa infot soovid? utter_common_service_companies: - condition: - type: slot @@ -363,132 +328,93 @@ responses: name: trigger_intent value: common_service_companies_lihtandmed text: "#common_service, /POST/companies/search, (simple-company-data, {company_name})" - utter_fallback: - - text: "UNKNOWN" - + - text: UNKNOWN utter_common_ask_csa: - - text: "Kas suunan teid klienditeenindajale? (Jah/Ei)" - + - text: Kas suunan teid klienditeenindajale? (Jah/Ei) utter_common_klienditeenindajale_suunamine: - - text: "Suunan teid klienditeenindajale. Varuge natukene kannatust." - + - text: Suunan teid klienditeenindajale. Varuge natukene kannatust. sasasa utter_common_ei_suuna_klienditeenindajale: - - text: "Selge. Kuidas saan veel abiks olla?" - + - text: Selge. Kuidas saan veel abiks olla? utter_common_hüvasti_jätmine: - - text: "Head aega!" - + - text: Head aega! utter_common_tänamine: - - text: "Meeldiv kuulda. Kuidas saan veel abiks olla?" - + - text: Meeldiv kuulda. Kuidas saan veel abiks olla? utter_common_tervitus: - - text: "Kuidas saan abiks olla?" - + - text: Kuidas saan abiks olla? utter_common_teenus_citizien_initiative: - - text: "#common_service, /GET/public-initiatives/most-recent" - + - text: "#common_service, /GET/public-initiatives/most-recent" utter_common_teenus_citizien_initiative_popular: - - text: "#common_service, /GET/public-initiatives/most-popular" - + - text: "#common_service, /GET/public-initiatives/most-popular" utter_common_teenus_rk_isiku_kohalolu: - condition: - type: slot name: rk_liige value: null - text: "Riigikogu liikme kohaloleku nägemiseks palun proovige uuesti, kasutades Riigikogu liikme täisnime." + text: Riigikogu liikme kohaloleku nägemiseks palun proovige uuesti, kasutades Riigikogu liikme täisnime. - text: "#common_service, /GET/riigikogu/members-participation, ({rk_liige})" - utter_common_teenus_rk_hääletus_viimane: - - text: "#common_service, /GET/riigikogu/recent-voting" - + - text: "#common_service, /GET/riigikogu/recent-voting" utter_common_teenus_rk_hääletus_viimased: - - text: "#common_service, /GET/riigikogu/five-most-recent" - + - text: "#common_service, /GET/riigikogu/five-most-recent" utter_common_teenus_rk_hääletus_fallback: - - text: "Ma ei saanud täpselt aru. Palun proovige uuesti!" - + - text: Ma ei saanud täpselt aru. Palun proovige uuesti! utter_common_teenus_nordpool: - - text: "#common_service, /GET/electricity/current-price" - + - text: "#common_service, /GET/electricity/current-price" utter_common_teenus_nordpool_odav: - - text: "#common_service, /GET/electricity/lowest-price" - + - text: "#common_service, /GET/electricity/lowest-price" utter_common_teenus_nordpool_kallis: - - text: "#common_service, /GET/electricity/highest-price" - + - text: "#common_service, /GET/electricity/highest-price" utter_common_service_CPI_fallback: - - text: "#common_service, /GET/statistics-estonia/consumer-price-index" - + - text: "#common_service, /GET/statistics-estonia/consumer-price-index" utter_common_service_CPI_previous_year: - - text: "#common_service, /GET/statistics-estonia/consumer-price-index, (previous_year,,{by_year_statistics})" - + - text: "#common_service, /GET/statistics-estonia/consumer-price-index, (previous_year,,{by_year_statistics})" utter_common_service_CPI_previous_month: - - text: "#common_service, /GET/statistics-estonia/consumer-price-index, (previous_month,{by_month_statistics},)" - + - text: "#common_service, /GET/statistics-estonia/consumer-price-index, (previous_month,{by_month_statistics},)" utter_common_service_CPI_time_horizon: - - text: "#common_service, /GET/statistics-estonia/consumer-price-index, (previous_year,{by_month_statistics},{by_year_statistics})" - + - text: "#common_service, /GET/statistics-estonia/consumer-price-index, (previous_year,{by_month_statistics},{by_year_statistics})" utter_common_service_unemployment_rate: - - text: "#common_service, /GET/statistics-estonia/unemployment-rate, ({by_year_statistics})" - + - text: "#common_service, /GET/statistics-estonia/unemployment-rate, ({by_year_statistics})" utter_common_service_unemployment_rate_fallback: - - text: "#common_service, /GET/statistics-estonia/unemployment-rate" - + - text: "#common_service, /GET/statistics-estonia/unemployment-rate" utter_common_service_estimated_subsistence_minimum: - - text: "#common_service, /GET/statistics-estonia/estimated-subsistence-minimum" - + - text: "#common_service, /GET/statistics-estonia/estimated-subsistence-minimum" utter_common_service_holidays: - - text: "#common_service, /GET/calendar/national-holidays/national-holidays" - + - text: "#common_service, /GET/calendar/national-holidays/national-holidays" utter_common_service_holidays_next_holidays: - - text: "#common_service, /GET/calendar/national-holidays/eoy" - + - text: "#common_service, /GET/calendar/national-holidays/eoy" utter_common_service_holidays_last_holidays: - - text: "#common_service, /GET/calendar/national-holidays/ytd" - + - text: "#common_service, /GET/calendar/national-holidays/ytd" utter_common_service_holidays_last_holiday: - - text: "#common_service, /GET/calendar/national-holidays/previous" - + - text: "#common_service, /GET/calendar/national-holidays/previous" utter_common_service_holidays_next_holiday: - - text: "#common_service, /GET/calendar/national-holidays/next" - + - text: "#common_service, /GET/calendar/national-holidays/next" utter_common_service_holidays_today: - - text: "#common_service, /GET/calendar/national-holidays/today" - + - text: "#common_service, /GET/calendar/national-holidays/today" utter_common_service_holidays_by_name: - - text: "#common_service, /GET/calendar/national-holidays/find/by-name, ({by_name_holiday})" - + - text: "#common_service, /GET/calendar/national-holidays/find/by-name, ({by_name_holiday})" utter_common_service_holidays_by_month: - - text: "#common_service, /GET/calendar/national-holidays/find/by-month, ({by_month_holiday})" - + - text: "#common_service, /GET/calendar/national-holidays/find/by-month, ({by_month_holiday})" utter_ask_reg_number: - - text: "Mootorsõidukimaksu päringu tegemiseks palun sisestage Eestis registreeritud sõiduki kehtiv registreerimismärk. " - + - text: "Mootorsõidukimaksu päringu tegemiseks palun sisestage Eestis registreeritud sõiduki kehtiv registreerimismärk. " utter_common_service_motor_vehicle_tax: - - text: "#common_service, /GET/motor-vehicle-tax/registration-number, ({reg_number})" - + - text: "#common_service, /GET/motor-vehicle-tax/registration-number, ({reg_number})" utter_common_service_school_holiday: - - text: "#common_service, /GET/calendar/school-holidays/school-holidays" - + - text: "#common_service, /GET/calendar/school-holidays/school-holidays" utter_common_service_school_holiday_next_holidays: - - text: "#common_service, /GET/calendar/school-holidays/eoy" - + - text: "#common_service, /GET/calendar/school-holidays/eoy" utter_common_service_school_holiday_last_holidays: - - text: "#common_service, /GET/calendar/school-holidays/ytd" - + - text: "#common_service, /GET/calendar/school-holidays/ytd" utter_common_service_school_holiday_last_holiday: - - text: "#common_service, /GET/calendar/school-holidays/previous" - + - text: "#common_service, /GET/calendar/school-holidays/previous" utter_common_service_school_holiday_next_holiday: - - text: "#common_service, /GET/calendar/school-holidays/next" - + - text: "#common_service, /GET/calendar/school-holidays/next" utter_common_service_school_holiday_today: - - text: "#common_service, /GET/calendar/school-holidays/today" - + - text: "#common_service, /GET/calendar/school-holidays/today" utter_common_service_school_holiday_by_name: - - text: "#common_service, /GET/calendar/school-holidays/find/by-name, ({by_name_school_holiday})" - + - text: "#common_service, /GET/calendar/school-holidays/find/by-name, ({by_name_school_holiday})" utter_common_service_school_holiday_by_month: - - text: "#common_service, /GET/calendar/school-holidays/find/by-month, ({by_month_school_holiday})" - + - text: "#common_service, /GET/calendar/school-holidays/find/by-month, ({by_month_school_holiday})" +session_config: + session_expiration_time: 60 + carry_over_slots_to_new_session: true diff --git a/GUI/src/pages/Training/Intents/IntentDetails.tsx b/GUI/src/pages/Training/Intents/IntentDetails.tsx index fb048092..c8924189 100644 --- a/GUI/src/pages/Training/Intents/IntentDetails.tsx +++ b/GUI/src/pages/Training/Intents/IntentDetails.tsx @@ -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 [response, setResponse] = useState({ name: '', text: '' }); + 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); - setResponse({ name: '', text: '' }); } }, [intentResponse]); @@ -114,14 +112,19 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li ); // todo also need to click twice lol. why? - // TODO rename does not work? need to invalidate? + // todo rename does not work? need to invalidate? const { data: responseResponse } = useQuery({ queryKey: [`response-by-intent-id?intent=${intentId}`], }); useEffect(() => { - if (responseResponse) setResponse(responseResponse.response); - }, [responseResponse, setResponse]); + 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({ @@ -397,17 +400,17 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li }); const handleIntentResponseSubmit = async () => { - if (response.text === '' || !intent) return; + if (responseText === '' || !intent) return; const intentId = intent.id; addOrEditResponseMutation.mutate({ id: `utter_${intentId}`, - responseText: response.text, - update: !!response.name, + responseText, + update: !!responseName, }); - if (!response.name) { + if (!responseName) { addRuleMutation.mutate({ data: { rule: `rule_${intentId}`, @@ -617,7 +620,7 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li

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

= ({ intentId, setSelectedIntent, li hideLabel maxLength={RESPONSE_TEXT_LENGTH} showMaxLength - onChange={(e) => setResponse({ ...response, text: e.target.value })} + onChange={(e) => + setResponse({ + name: response?.name ?? '', + text: e.target.value ?? '', + }) + } disableHeightResize /> From 5def279c6a421f5357f6544bbdb3a33a0011d3c7 Mon Sep 17 00:00:00 2001 From: Igor Krupenja Date: Fri, 6 Dec 2024 16:27:57 +0200 Subject: [PATCH 38/44] Revert unrelated changes --- DSL/DMapper/locations/data/domain.yml | 408 +++++++++++++++----------- 1 file changed, 241 insertions(+), 167 deletions(-) diff --git a/DSL/DMapper/locations/data/domain.yml b/DSL/DMapper/locations/data/domain.yml index b12cee67..547395c0 100644 --- a/DSL/DMapper/locations/data/domain.yml +++ b/DSL/DMapper/locations/data/domain.yml @@ -1,8 +1,11 @@ -version: "3.0" +version: '3.0' +session_config: + session_expiration_time: 60 + carry_over_slots_to_new_session: true intents: - common_ask_csa - common_hüvasti_jätmine - - common_kinnitamine + - common_kinnitamine - common_eitamine - common_tänamine - common_tervitus @@ -28,6 +31,7 @@ intents: - common_service_estimated_subsistence_minimum - common_service_motor_vehicle_tax - common_service_school_holiday + entities: - asukoht - rk_liige @@ -44,249 +48,280 @@ entities: - last_holidays - by_year - reg_number + slots: common_teenus_ilm_asukoht: type: text influence_conversation: true mappings: - - type: from_entity - entity: asukoht - intent: common_teenus_ilm + - type: from_entity + entity: asukoht + intent: common_teenus_ilm + reg_number: type: text influence_conversation: true mappings: - - type: from_entity - entity: reg_number - intent: common_service_motor_vehicle_tax - - type: from_text - conditions: - - active_loop: common_service_motor_vehicle_tax_form - requested_slot: reg_number + - type: from_entity + entity: reg_number + intent: common_service_motor_vehicle_tax + - type: from_text + conditions: + - active_loop: common_service_motor_vehicle_tax_form + requested_slot: reg_number + company_name: type: text influence_conversation: true - mappings: - - type: from_text - conditions: - - active_loop: common_service_companies_form - requested_slot: company_name + mappings: + - type: from_text + conditions: + - active_loop: common_service_companies_form + requested_slot: company_name + trigger_intent: type: text influence_conversation: false mappings: - - type: from_intent - value: common_service_companies_employees - intent: common_service_companies_employees - - type: from_intent - value: common_service_companies_revenue - intent: common_service_companies_revenue - - type: from_intent - value: common_service_companies_workforce_taxes - intent: common_service_companies_workforce_taxes - - type: from_intent - value: common_service_companies_national_taxes - intent: common_service_companies_national_taxes - - type: from_intent - value: common_service_companies_beneficiaries - intent: common_service_companies_beneficiaries - - type: from_intent - value: common_service_companies_related_persons - intent: common_service_companies_related_persons - - type: from_intent - value: common_service_companies_contactdetails - intent: common_service_companies_contactdetails - - type: from_intent - value: common_service_companies_lihtandmed - intent: common_service_companies_lihtandmed + - type: from_intent + value: common_service_companies_employees + intent: common_service_companies_employees + - type: from_intent + value: common_service_companies_revenue + intent: common_service_companies_revenue + - type: from_intent + value: common_service_companies_workforce_taxes + intent: common_service_companies_workforce_taxes + - type: from_intent + value: common_service_companies_national_taxes + intent: common_service_companies_national_taxes + - type: from_intent + value: common_service_companies_beneficiaries + intent: common_service_companies_beneficiaries + - type: from_intent + value: common_service_companies_related_persons + intent: common_service_companies_related_persons + - type: from_intent + value: common_service_companies_contactdetails + intent: common_service_companies_contactdetails + - type: from_intent + value: common_service_companies_lihtandmed + intent: common_service_companies_lihtandmed + rk_liige: type: text influence_conversation: true mappings: - - type: from_entity - entity: rk_liige - intent: common_teenus_rk_isiku_kohalolu + - type: from_entity + entity: rk_liige + intent: common_teenus_rk_isiku_kohalolu + rk_hääletus: type: text influence_conversation: true mappings: - - type: from_entity - entity: rk_hääletus - intent: common_teenus_rk_hääletus + - type: from_entity + entity: rk_hääletus + intent: common_teenus_rk_hääletus + rk_hääletused: type: text influence_conversation: true mappings: - - type: from_entity - entity: rk_hääletused - intent: common_teenus_rk_hääletus + - type: from_entity + entity: rk_hääletused + intent: common_teenus_rk_hääletus + np_kallis: type: text influence_conversation: true mappings: - - type: from_entity - entity: np_kallis - intent: common_teenus_nordpool2 + - type: from_entity + entity: np_kallis + intent: common_teenus_nordpool2 + np_odav: type: text influence_conversation: true mappings: - - type: from_entity - entity: np_odav - intent: common_teenus_nordpool2 + - type: from_entity + entity: np_odav + intent: common_teenus_nordpool2 + by_year_statistics: type: text influence_conversation: true mappings: - - type: from_entity - entity: by_year - intent: common_service_CPI - - type: from_entity - entity: by_year - intent: common_service_unemployment_rate + - type: from_entity + entity: by_year + intent: common_service_CPI + - type: from_entity + entity: by_year + intent: common_service_unemployment_rate + by_month_statistics: type: text influence_conversation: true mappings: - - type: from_entity - entity: by_month - intent: common_service_CPI + - type: from_entity + entity: by_month + intent: common_service_CPI + today_holiday: type: text influence_conversation: true mappings: - - type: from_entity - entity: today - intent: common_service_holidays + - type: from_entity + entity: today + intent: common_service_holidays + next_holiday: type: text influence_conversation: true mappings: - - type: from_entity - entity: next_holiday - intent: common_service_holidays + - type: from_entity + entity: next_holiday + intent: common_service_holidays + next_holidays: type: text influence_conversation: true mappings: - - type: from_entity - entity: next_holidays - intent: common_service_holidays + - type: from_entity + entity: next_holidays + intent: common_service_holidays + last_holiday: type: text influence_conversation: true mappings: - - type: from_entity - entity: last_holiday - intent: common_service_holidays + - type: from_entity + entity: last_holiday + intent: common_service_holidays + last_holidays: type: text influence_conversation: true mappings: - - type: from_entity - entity: last_holidays - intent: common_service_holidays + - type: from_entity + entity: last_holidays + intent: common_service_holidays + by_name_holiday: type: text influence_conversation: true mappings: - - type: from_entity - entity: by_name - intent: common_service_holidays + - type: from_entity + entity: by_name + intent: common_service_holidays + by_month_holiday: type: text influence_conversation: true mappings: - - type: from_entity - entity: by_month - intent: common_service_holidays + - type: from_entity + entity: by_month + intent: common_service_holidays + today_school_holiday: type: text influence_conversation: true mappings: - - type: from_entity - entity: today - intent: common_service_school_holiday + - type: from_entity + entity: today + intent: common_service_school_holiday + next_school_holiday: type: text influence_conversation: true mappings: - - type: from_entity - entity: next_holiday - intent: common_service_school_holiday + - type: from_entity + entity: next_holiday + intent: common_service_school_holiday + next_school_holidays: type: text influence_conversation: true mappings: - - type: from_entity - entity: next_holidays - intent: common_service_school_holiday + - type: from_entity + entity: next_holidays + intent: common_service_school_holiday + last_school_holiday: type: text influence_conversation: true mappings: - - type: from_entity - entity: last_holiday - intent: common_service_school_holiday + - type: from_entity + entity: last_holiday + intent: common_service_school_holiday + last_school_holidays: type: text influence_conversation: true mappings: - - type: from_entity - entity: last_holidays - intent: common_service_school_holiday + - type: from_entity + entity: last_holidays + intent: common_service_school_holiday + by_name_school_holiday: type: text influence_conversation: true mappings: - - type: from_entity - entity: by_name - intent: common_service_school_holiday + - type: from_entity + entity: by_name + intent: common_service_school_holiday + by_month_school_holiday: type: text influence_conversation: true mappings: - - type: from_entity - entity: by_month - intent: common_service_school_holiday + - type: from_entity + entity: by_month + intent: common_service_school_holiday + affirm_deny: type: text influence_conversation: true mappings: - - type: from_text - conditions: - - active_loop: custom_fallback_form - requested_slot: affirm_deny - - active_loop: direct_to_customer_support_form - requested_slot: affirm_deny + - type: from_text + conditions: + - active_loop: custom_fallback_form + requested_slot: affirm_deny + - active_loop: direct_to_customer_support_form + requested_slot: affirm_deny + forms: common_service_companies_form: required_slots: - - company_name + - company_name custom_fallback_form: required_slots: - - affirm_deny + - affirm_deny direct_to_customer_support_form: required_slots: - - affirm_deny + - affirm_deny common_service_motor_vehicle_tax_form: required_slots: - - reg_number + - reg_number + actions: - - action_ask_custom_fallback_form_affirm_deny - - action_check_confidence - - action_react_to_affirm_deny_in_custom_fallback_form - - action_react_to_affirm_deny_in_direct_to_customer_support_form +- action_ask_custom_fallback_form_affirm_deny +- action_check_confidence +- action_react_to_affirm_deny_in_custom_fallback_form +- action_react_to_affirm_deny_in_direct_to_customer_support_form + responses: utter_common_teenus_ilm: - condition: - type: slot name: common_teenus_ilm_asukoht value: null - text: Hetkel oskan ma öelda ilma Eesti raames piirkonnapõhiselt. Palun sisestage korrektne piirkonna nimi. + text: "Hetkel oskan ma öelda ilma Eesti raames piirkonnapõhiselt. Palun sisestage korrektne piirkonna nimi." - text: "#common_service, /POST/weather/EE, ({common_teenus_ilm_asukoht})" + utter_ask_company_name: - - text: Mis firma kohta sa infot soovid? + - text: "Mis firma kohta sa infot soovid?" + utter_common_service_companies: - condition: - type: slot @@ -328,93 +363,132 @@ responses: name: trigger_intent value: common_service_companies_lihtandmed text: "#common_service, /POST/companies/search, (simple-company-data, {company_name})" + utter_fallback: - - text: UNKNOWN + - text: "UNKNOWN" + utter_common_ask_csa: - - text: Kas suunan teid klienditeenindajale? (Jah/Ei) + - text: "Kas suunan teid klienditeenindajale? (Jah/Ei)" + utter_common_klienditeenindajale_suunamine: - - text: Suunan teid klienditeenindajale. Varuge natukene kannatust. sasasa + - text: "Suunan teid klienditeenindajale. Varuge natukene kannatust." + utter_common_ei_suuna_klienditeenindajale: - - text: Selge. Kuidas saan veel abiks olla? + - text: "Selge. Kuidas saan veel abiks olla?" + utter_common_hüvasti_jätmine: - - text: Head aega! + - text: "Head aega!" + utter_common_tänamine: - - text: Meeldiv kuulda. Kuidas saan veel abiks olla? + - text: "Meeldiv kuulda. Kuidas saan veel abiks olla?" + utter_common_tervitus: - - text: Kuidas saan abiks olla? + - text: "Kuidas saan abiks olla?" + utter_common_teenus_citizien_initiative: - - text: "#common_service, /GET/public-initiatives/most-recent" + - text: "#common_service, /GET/public-initiatives/most-recent" + utter_common_teenus_citizien_initiative_popular: - - text: "#common_service, /GET/public-initiatives/most-popular" + - text: "#common_service, /GET/public-initiatives/most-popular" + utter_common_teenus_rk_isiku_kohalolu: - condition: - type: slot name: rk_liige value: null - text: Riigikogu liikme kohaloleku nägemiseks palun proovige uuesti, kasutades Riigikogu liikme täisnime. + text: "Riigikogu liikme kohaloleku nägemiseks palun proovige uuesti, kasutades Riigikogu liikme täisnime." - text: "#common_service, /GET/riigikogu/members-participation, ({rk_liige})" + utter_common_teenus_rk_hääletus_viimane: - - text: "#common_service, /GET/riigikogu/recent-voting" + - text: "#common_service, /GET/riigikogu/recent-voting" + utter_common_teenus_rk_hääletus_viimased: - - text: "#common_service, /GET/riigikogu/five-most-recent" + - text: "#common_service, /GET/riigikogu/five-most-recent" + utter_common_teenus_rk_hääletus_fallback: - - text: Ma ei saanud täpselt aru. Palun proovige uuesti! + - text: "Ma ei saanud täpselt aru. Palun proovige uuesti!" + utter_common_teenus_nordpool: - - text: "#common_service, /GET/electricity/current-price" + - text: "#common_service, /GET/electricity/current-price" + utter_common_teenus_nordpool_odav: - - text: "#common_service, /GET/electricity/lowest-price" + - text: "#common_service, /GET/electricity/lowest-price" + utter_common_teenus_nordpool_kallis: - - text: "#common_service, /GET/electricity/highest-price" + - text: "#common_service, /GET/electricity/highest-price" + utter_common_service_CPI_fallback: - - text: "#common_service, /GET/statistics-estonia/consumer-price-index" + - text: "#common_service, /GET/statistics-estonia/consumer-price-index" + utter_common_service_CPI_previous_year: - - text: "#common_service, /GET/statistics-estonia/consumer-price-index, (previous_year,,{by_year_statistics})" + - text: "#common_service, /GET/statistics-estonia/consumer-price-index, (previous_year,,{by_year_statistics})" + utter_common_service_CPI_previous_month: - - text: "#common_service, /GET/statistics-estonia/consumer-price-index, (previous_month,{by_month_statistics},)" + - text: "#common_service, /GET/statistics-estonia/consumer-price-index, (previous_month,{by_month_statistics},)" + utter_common_service_CPI_time_horizon: - - text: "#common_service, /GET/statistics-estonia/consumer-price-index, (previous_year,{by_month_statistics},{by_year_statistics})" + - text: "#common_service, /GET/statistics-estonia/consumer-price-index, (previous_year,{by_month_statistics},{by_year_statistics})" + utter_common_service_unemployment_rate: - - text: "#common_service, /GET/statistics-estonia/unemployment-rate, ({by_year_statistics})" + - text: "#common_service, /GET/statistics-estonia/unemployment-rate, ({by_year_statistics})" + utter_common_service_unemployment_rate_fallback: - - text: "#common_service, /GET/statistics-estonia/unemployment-rate" + - text: "#common_service, /GET/statistics-estonia/unemployment-rate" + utter_common_service_estimated_subsistence_minimum: - - text: "#common_service, /GET/statistics-estonia/estimated-subsistence-minimum" + - text: "#common_service, /GET/statistics-estonia/estimated-subsistence-minimum" + utter_common_service_holidays: - - text: "#common_service, /GET/calendar/national-holidays/national-holidays" + - text: "#common_service, /GET/calendar/national-holidays/national-holidays" + utter_common_service_holidays_next_holidays: - - text: "#common_service, /GET/calendar/national-holidays/eoy" + - text: "#common_service, /GET/calendar/national-holidays/eoy" + utter_common_service_holidays_last_holidays: - - text: "#common_service, /GET/calendar/national-holidays/ytd" + - text: "#common_service, /GET/calendar/national-holidays/ytd" + utter_common_service_holidays_last_holiday: - - text: "#common_service, /GET/calendar/national-holidays/previous" + - text: "#common_service, /GET/calendar/national-holidays/previous" + utter_common_service_holidays_next_holiday: - - text: "#common_service, /GET/calendar/national-holidays/next" + - text: "#common_service, /GET/calendar/national-holidays/next" + utter_common_service_holidays_today: - - text: "#common_service, /GET/calendar/national-holidays/today" + - text: "#common_service, /GET/calendar/national-holidays/today" + utter_common_service_holidays_by_name: - - text: "#common_service, /GET/calendar/national-holidays/find/by-name, ({by_name_holiday})" + - text: "#common_service, /GET/calendar/national-holidays/find/by-name, ({by_name_holiday})" + utter_common_service_holidays_by_month: - - text: "#common_service, /GET/calendar/national-holidays/find/by-month, ({by_month_holiday})" + - text: "#common_service, /GET/calendar/national-holidays/find/by-month, ({by_month_holiday})" + utter_ask_reg_number: - - text: "Mootorsõidukimaksu päringu tegemiseks palun sisestage Eestis registreeritud sõiduki kehtiv registreerimismärk. " + - text: "Mootorsõidukimaksu päringu tegemiseks palun sisestage Eestis registreeritud sõiduki kehtiv registreerimismärk. " + utter_common_service_motor_vehicle_tax: - - text: "#common_service, /GET/motor-vehicle-tax/registration-number, ({reg_number})" + - text: "#common_service, /GET/motor-vehicle-tax/registration-number, ({reg_number})" + utter_common_service_school_holiday: - - text: "#common_service, /GET/calendar/school-holidays/school-holidays" + - text: "#common_service, /GET/calendar/school-holidays/school-holidays" + utter_common_service_school_holiday_next_holidays: - - text: "#common_service, /GET/calendar/school-holidays/eoy" + - text: "#common_service, /GET/calendar/school-holidays/eoy" + utter_common_service_school_holiday_last_holidays: - - text: "#common_service, /GET/calendar/school-holidays/ytd" + - text: "#common_service, /GET/calendar/school-holidays/ytd" + utter_common_service_school_holiday_last_holiday: - - text: "#common_service, /GET/calendar/school-holidays/previous" + - text: "#common_service, /GET/calendar/school-holidays/previous" + utter_common_service_school_holiday_next_holiday: - - text: "#common_service, /GET/calendar/school-holidays/next" + - text: "#common_service, /GET/calendar/school-holidays/next" + utter_common_service_school_holiday_today: - - text: "#common_service, /GET/calendar/school-holidays/today" + - text: "#common_service, /GET/calendar/school-holidays/today" + utter_common_service_school_holiday_by_name: - - text: "#common_service, /GET/calendar/school-holidays/find/by-name, ({by_name_school_holiday})" + - text: "#common_service, /GET/calendar/school-holidays/find/by-name, ({by_name_school_holiday})" + utter_common_service_school_holiday_by_month: - - text: "#common_service, /GET/calendar/school-holidays/find/by-month, ({by_month_school_holiday})" -session_config: - session_expiration_time: 60 - carry_over_slots_to_new_session: true + - text: "#common_service, /GET/calendar/school-holidays/find/by-month, ({by_month_school_holiday})" + From 69213884037a5776fc7594fe028ff54308fa431a Mon Sep 17 00:00:00 2001 From: Igor Krupenja Date: Fri, 6 Dec 2024 16:28:11 +0200 Subject: [PATCH 39/44] Clean up --- GUI/src/pages/Training/Intents/IntentDetails.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/GUI/src/pages/Training/Intents/IntentDetails.tsx b/GUI/src/pages/Training/Intents/IntentDetails.tsx index c8924189..71f72bda 100644 --- a/GUI/src/pages/Training/Intents/IntentDetails.tsx +++ b/GUI/src/pages/Training/Intents/IntentDetails.tsx @@ -111,7 +111,6 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li [addIntentRule, intentId, queryClient, setResponse, setSelectedIntent] ); - // todo also need to click twice lol. why? // todo rename does not work? need to invalidate? const { data: responseResponse } = useQuery({ queryKey: [`response-by-intent-id?intent=${intentId}`], From 4e33c26891e1084c6bfa6be341b119b8b96f5dde Mon Sep 17 00:00:00 2001 From: Igor Krupenja Date: Fri, 6 Dec 2024 16:29:09 +0200 Subject: [PATCH 40/44] Clean up --- GUI/src/pages/Training/Intents/IntentDetails.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/GUI/src/pages/Training/Intents/IntentDetails.tsx b/GUI/src/pages/Training/Intents/IntentDetails.tsx index 71f72bda..f4cab1a7 100644 --- a/GUI/src/pages/Training/Intents/IntentDetails.tsx +++ b/GUI/src/pages/Training/Intents/IntentDetails.tsx @@ -27,6 +27,7 @@ import ConnectServiceToIntentModal from 'pages/ConnectServiceToIntentModal'; import LoadingDialog from 'components/LoadingDialog'; import useDocumentEscapeListener from 'hooks/useDocumentEscapeListener'; import { IntentWithExamplesCount } from 'types/intentWithExampleCounts'; +import { type } from 'os'; interface Response { name: string; @@ -117,9 +118,7 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li }); useEffect(() => { - if (responseResponse?.response) { - setResponse(responseResponse.response); - } + if (responseResponse?.response) setResponse(responseResponse.response); }, [responseResponse]); const responseText = response?.text ?? ''; From 52534ec5923e39b314aeb3e79f2e4b8980a26bba Mon Sep 17 00:00:00 2001 From: Igor Krupenja Date: Mon, 9 Dec 2024 09:32:31 +0200 Subject: [PATCH 41/44] Clean up --- GUI/src/pages/Training/Intents/IntentDetails.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/GUI/src/pages/Training/Intents/IntentDetails.tsx b/GUI/src/pages/Training/Intents/IntentDetails.tsx index f4cab1a7..9b4d16e8 100644 --- a/GUI/src/pages/Training/Intents/IntentDetails.tsx +++ b/GUI/src/pages/Training/Intents/IntentDetails.tsx @@ -27,7 +27,6 @@ import ConnectServiceToIntentModal from 'pages/ConnectServiceToIntentModal'; import LoadingDialog from 'components/LoadingDialog'; import useDocumentEscapeListener from 'hooks/useDocumentEscapeListener'; import { IntentWithExamplesCount } from 'types/intentWithExampleCounts'; -import { type } from 'os'; interface Response { name: string; @@ -351,10 +350,14 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li mutationFn: (intentResponseData: { id: string; responseText: string; update: boolean }) => editResponse(intentResponseData.id, intentResponseData.responseText, intentResponseData.update), onMutate: () => { + console.log('onMutate'); 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'), @@ -460,7 +463,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', From 746588bcfe69322e11c1d75fc3c835ca8ab87980 Mon Sep 17 00:00:00 2001 From: Igor Krupenja Date: Tue, 10 Dec 2024 11:30:47 +0200 Subject: [PATCH 42/44] Clean up --- DSL/Ruuter.private/GET/rasa/intents/by-id.yml | 1 - GUI/src/pages/Training/Intents/IntentDetails.tsx | 1 - 2 files changed, 2 deletions(-) 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/GUI/src/pages/Training/Intents/IntentDetails.tsx b/GUI/src/pages/Training/Intents/IntentDetails.tsx index 9b4d16e8..8f262725 100644 --- a/GUI/src/pages/Training/Intents/IntentDetails.tsx +++ b/GUI/src/pages/Training/Intents/IntentDetails.tsx @@ -111,7 +111,6 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li [addIntentRule, intentId, queryClient, setResponse, setSelectedIntent] ); - // todo rename does not work? need to invalidate? const { data: responseResponse } = useQuery({ queryKey: [`response-by-intent-id?intent=${intentId}`], }); From b8b60ecfb17c4c2b6add4e517a9f52f5441c16c7 Mon Sep 17 00:00:00 2001 From: Igor Krupenja Date: Tue, 10 Dec 2024 13:19:55 +0200 Subject: [PATCH 43/44] PoC request --- .../GET/rasa/rule-by-intent-id.yml | 43 +++++++++++++++++++ .../pages/Training/Intents/IntentDetails.tsx | 2 +- 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 DSL/Ruuter.private/GET/rasa/rule-by-intent-id.yml diff --git a/DSL/Ruuter.private/GET/rasa/rule-by-intent-id.yml b/DSL/Ruuter.private/GET/rasa/rule-by-intent-id.yml new file mode 100644 index 00000000..4d910305 --- /dev/null +++ b/DSL/Ruuter.private/GET/rasa/rule-by-intent-id.yml @@ -0,0 +1,43 @@ +# todo not ready +declaration: + call: declare + version: 0.1 + description: "Get rule by intent name" + method: get + accepts: json + returns: json + namespace: training + allowlist: + params: + - field: intent + type: string + description: "Intent ID" + +assign_values: + assign: + rule: ${"rule_" + incoming.params.intent} + +getRules: + call: http.post + args: + url: "[#TRAINING_OPENSEARCH]/rules/_search/template" + body: + id: "rule-with-name" + params: + rule: ${rule} + result: getRulesResult + +mapRulesData: + call: http.post + args: + url: "[#TRAINING_DMAPPER]/hbs/training/get_rules" + headers: + type: "json" + body: + hits: ${getRulesResult.response.body.hits.hits} + result: rulesData + next: returnSuccess + +returnSuccess: + return: ${rulesData.response.body.rules} + next: end diff --git a/GUI/src/pages/Training/Intents/IntentDetails.tsx b/GUI/src/pages/Training/Intents/IntentDetails.tsx index 8f262725..583cab2b 100644 --- a/GUI/src/pages/Training/Intents/IntentDetails.tsx +++ b/GUI/src/pages/Training/Intents/IntentDetails.tsx @@ -124,7 +124,7 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li // TODO: need to fetch rules for the selected intent only const { data: rulesResponse } = useQuery({ - queryKey: ['rules'], + queryKey: [`rule-by-intent-id?intent=${intentId}`], }); useEffect(() => { From c44fcd0e3e3761e7af97fe0c5adac758e1ed604e Mon Sep 17 00:00:00 2001 From: Igor Krupenja Date: Tue, 10 Dec 2024 14:04:12 +0200 Subject: [PATCH 44/44] Finish --- .../hbs/get_rule_by_intent_id.handlebars | 3 ++ .../GET/rasa/rule-by-intent-id.yml | 7 ++-- .../pages/Training/Intents/IntentDetails.tsx | 34 +++++++------------ 3 files changed, 18 insertions(+), 26 deletions(-) create mode 100644 DSL/DMapper/hbs/get_rule_by_intent_id.handlebars diff --git a/DSL/DMapper/hbs/get_rule_by_intent_id.handlebars b/DSL/DMapper/hbs/get_rule_by_intent_id.handlebars new file mode 100644 index 00000000..71a54921 --- /dev/null +++ b/DSL/DMapper/hbs/get_rule_by_intent_id.handlebars @@ -0,0 +1,3 @@ +{ + "id": "{{hits.0._source.rule}}" +} \ No newline at end of file diff --git a/DSL/Ruuter.private/GET/rasa/rule-by-intent-id.yml b/DSL/Ruuter.private/GET/rasa/rule-by-intent-id.yml index 4d910305..e4e3db21 100644 --- a/DSL/Ruuter.private/GET/rasa/rule-by-intent-id.yml +++ b/DSL/Ruuter.private/GET/rasa/rule-by-intent-id.yml @@ -1,4 +1,3 @@ -# todo not ready declaration: call: declare version: 0.1 @@ -30,14 +29,14 @@ getRules: mapRulesData: call: http.post args: - url: "[#TRAINING_DMAPPER]/hbs/training/get_rules" + url: "[#TRAINING_DMAPPER]/hbs/training/get_rule_by_intent_id" headers: type: "json" body: hits: ${getRulesResult.response.body.hits.hits} result: rulesData - next: returnSuccess + # next: returnSuccess returnSuccess: - return: ${rulesData.response.body.rules} + return: ${rulesData.response.body} next: end diff --git a/GUI/src/pages/Training/Intents/IntentDetails.tsx b/GUI/src/pages/Training/Intents/IntentDetails.tsx index 583cab2b..f39cee97 100644 --- a/GUI/src/pages/Training/Intents/IntentDetails.tsx +++ b/GUI/src/pages/Training/Intents/IntentDetails.tsx @@ -41,8 +41,8 @@ interface IntentResponse { response: Intent; } -interface RulesResponse { - response: Rule[]; +interface RuleResponse { + response: { id: string }; } interface IntentDetailsProps { @@ -80,18 +80,6 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li queryKey: [`intents/is-marked-for-service?intent=${intentId}`], }); - const addIntentRule = useCallback( - (rulesResponse: RulesResponse | undefined) => { - if (!rulesResponse) return; - - const intentRule = rulesResponse.response.find((rule: Rule) => rule.id === `rule_${intentId}`); - if (intentRule) { - setIntentRule(intentRule.id); - } - }, - [intentId] - ); - const queryRefresh = useCallback( async (intent?: string) => { const intentsResponse = await queryClient.fetchQuery([ @@ -105,10 +93,10 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li ]); setResponse(responseResponse.response); - const rulesResponse = await queryClient.fetchQuery(['rules']); - addIntentRule(rulesResponse); + const rulesResponse = await queryClient.fetchQuery([`rule-by-intent-id?intent=${intentId}`]); + setIntentRule(rulesResponse.response.id); }, - [addIntentRule, intentId, queryClient, setResponse, setSelectedIntent] + [intentId, queryClient, setResponse, setSelectedIntent] ); const { data: responseResponse } = useQuery({ @@ -122,14 +110,16 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li const responseText = response?.text ?? ''; const responseName = response?.name ?? ''; - // TODO: need to fetch rules for the selected intent only - const { data: rulesResponse } = useQuery({ + const { data: rulesResponse } = useQuery({ queryKey: [`rule-by-intent-id?intent=${intentId}`], }); useEffect(() => { - addIntentRule(rulesResponse); - }, [rulesResponse, addIntentRule]); + if (rulesResponse) setIntentRule(rulesResponse.response.id); + }, [rulesResponse]); + + console.log('rulesResponse', rulesResponse); + console.log('intentRule', intentRule); const markIntentServiceMutation = useMutation({ mutationFn: (data: { name: string; isForService: boolean }) => markForService(data), @@ -463,7 +453,7 @@ const IntentDetails: FC = ({ intentId, setSelectedIntent, li }, onSuccess: async () => { await queryClient.invalidateQueries([`intents/is-marked-for-service?intent=${intentId}`]); - await queryClient.invalidateQueries(['rules']); + await queryClient.invalidateQueries([`rule-by-intent-id?intent=${intentId}`]); toast.open({ type: 'success', title: t('global.notification'),