rasa-pro 3.12.0.dev1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of rasa-pro might be problematic. Click here for more details.
- README.md +41 -0
- rasa/__init__.py +9 -0
- rasa/__main__.py +177 -0
- rasa/anonymization/__init__.py +2 -0
- rasa/anonymization/anonymisation_rule_yaml_reader.py +91 -0
- rasa/anonymization/anonymization_pipeline.py +286 -0
- rasa/anonymization/anonymization_rule_executor.py +260 -0
- rasa/anonymization/anonymization_rule_orchestrator.py +120 -0
- rasa/anonymization/schemas/config.yml +47 -0
- rasa/anonymization/utils.py +118 -0
- rasa/api.py +160 -0
- rasa/cli/__init__.py +5 -0
- rasa/cli/arguments/__init__.py +0 -0
- rasa/cli/arguments/data.py +106 -0
- rasa/cli/arguments/default_arguments.py +207 -0
- rasa/cli/arguments/evaluate.py +65 -0
- rasa/cli/arguments/export.py +51 -0
- rasa/cli/arguments/interactive.py +74 -0
- rasa/cli/arguments/run.py +219 -0
- rasa/cli/arguments/shell.py +17 -0
- rasa/cli/arguments/test.py +211 -0
- rasa/cli/arguments/train.py +279 -0
- rasa/cli/arguments/visualize.py +34 -0
- rasa/cli/arguments/x.py +30 -0
- rasa/cli/data.py +354 -0
- rasa/cli/dialogue_understanding_test.py +251 -0
- rasa/cli/e2e_test.py +259 -0
- rasa/cli/evaluate.py +222 -0
- rasa/cli/export.py +250 -0
- rasa/cli/inspect.py +75 -0
- rasa/cli/interactive.py +166 -0
- rasa/cli/license.py +65 -0
- rasa/cli/llm_fine_tuning.py +403 -0
- rasa/cli/markers.py +78 -0
- rasa/cli/project_templates/__init__.py +0 -0
- rasa/cli/project_templates/calm/actions/__init__.py +0 -0
- rasa/cli/project_templates/calm/actions/action_template.py +27 -0
- rasa/cli/project_templates/calm/actions/add_contact.py +30 -0
- rasa/cli/project_templates/calm/actions/db.py +57 -0
- rasa/cli/project_templates/calm/actions/list_contacts.py +22 -0
- rasa/cli/project_templates/calm/actions/remove_contact.py +35 -0
- rasa/cli/project_templates/calm/config.yml +10 -0
- rasa/cli/project_templates/calm/credentials.yml +33 -0
- rasa/cli/project_templates/calm/data/flows/add_contact.yml +31 -0
- rasa/cli/project_templates/calm/data/flows/list_contacts.yml +14 -0
- rasa/cli/project_templates/calm/data/flows/remove_contact.yml +29 -0
- rasa/cli/project_templates/calm/db/contacts.json +10 -0
- rasa/cli/project_templates/calm/domain/add_contact.yml +39 -0
- rasa/cli/project_templates/calm/domain/list_contacts.yml +17 -0
- rasa/cli/project_templates/calm/domain/remove_contact.yml +38 -0
- rasa/cli/project_templates/calm/domain/shared.yml +10 -0
- rasa/cli/project_templates/calm/e2e_tests/cancelations/user_cancels_during_a_correction.yml +16 -0
- rasa/cli/project_templates/calm/e2e_tests/cancelations/user_changes_mind_on_a_whim.yml +7 -0
- rasa/cli/project_templates/calm/e2e_tests/corrections/user_corrects_contact_handle.yml +20 -0
- rasa/cli/project_templates/calm/e2e_tests/corrections/user_corrects_contact_name.yml +19 -0
- rasa/cli/project_templates/calm/e2e_tests/happy_paths/user_adds_contact_to_their_list.yml +15 -0
- rasa/cli/project_templates/calm/e2e_tests/happy_paths/user_lists_contacts.yml +5 -0
- rasa/cli/project_templates/calm/e2e_tests/happy_paths/user_removes_contact.yml +11 -0
- rasa/cli/project_templates/calm/e2e_tests/happy_paths/user_removes_contact_from_list.yml +12 -0
- rasa/cli/project_templates/calm/endpoints.yml +58 -0
- rasa/cli/project_templates/default/actions/__init__.py +0 -0
- rasa/cli/project_templates/default/actions/actions.py +27 -0
- rasa/cli/project_templates/default/config.yml +44 -0
- rasa/cli/project_templates/default/credentials.yml +33 -0
- rasa/cli/project_templates/default/data/nlu.yml +91 -0
- rasa/cli/project_templates/default/data/rules.yml +13 -0
- rasa/cli/project_templates/default/data/stories.yml +30 -0
- rasa/cli/project_templates/default/domain.yml +34 -0
- rasa/cli/project_templates/default/endpoints.yml +42 -0
- rasa/cli/project_templates/default/tests/test_stories.yml +91 -0
- rasa/cli/project_templates/tutorial/actions/__init__.py +0 -0
- rasa/cli/project_templates/tutorial/actions/actions.py +22 -0
- rasa/cli/project_templates/tutorial/config.yml +12 -0
- rasa/cli/project_templates/tutorial/credentials.yml +33 -0
- rasa/cli/project_templates/tutorial/data/flows.yml +8 -0
- rasa/cli/project_templates/tutorial/data/patterns.yml +11 -0
- rasa/cli/project_templates/tutorial/domain.yml +35 -0
- rasa/cli/project_templates/tutorial/endpoints.yml +55 -0
- rasa/cli/run.py +143 -0
- rasa/cli/scaffold.py +273 -0
- rasa/cli/shell.py +141 -0
- rasa/cli/studio/__init__.py +0 -0
- rasa/cli/studio/download.py +62 -0
- rasa/cli/studio/studio.py +296 -0
- rasa/cli/studio/train.py +59 -0
- rasa/cli/studio/upload.py +62 -0
- rasa/cli/telemetry.py +102 -0
- rasa/cli/test.py +280 -0
- rasa/cli/train.py +278 -0
- rasa/cli/utils.py +484 -0
- rasa/cli/visualize.py +40 -0
- rasa/cli/x.py +206 -0
- rasa/constants.py +45 -0
- rasa/core/__init__.py +17 -0
- rasa/core/actions/__init__.py +0 -0
- rasa/core/actions/action.py +1318 -0
- rasa/core/actions/action_clean_stack.py +59 -0
- rasa/core/actions/action_exceptions.py +24 -0
- rasa/core/actions/action_hangup.py +29 -0
- rasa/core/actions/action_repeat_bot_messages.py +89 -0
- rasa/core/actions/action_run_slot_rejections.py +210 -0
- rasa/core/actions/action_trigger_chitchat.py +31 -0
- rasa/core/actions/action_trigger_flow.py +109 -0
- rasa/core/actions/action_trigger_search.py +31 -0
- rasa/core/actions/constants.py +5 -0
- rasa/core/actions/custom_action_executor.py +191 -0
- rasa/core/actions/direct_custom_actions_executor.py +109 -0
- rasa/core/actions/e2e_stub_custom_action_executor.py +72 -0
- rasa/core/actions/forms.py +741 -0
- rasa/core/actions/grpc_custom_action_executor.py +251 -0
- rasa/core/actions/http_custom_action_executor.py +145 -0
- rasa/core/actions/loops.py +114 -0
- rasa/core/actions/two_stage_fallback.py +186 -0
- rasa/core/agent.py +559 -0
- rasa/core/auth_retry_tracker_store.py +122 -0
- rasa/core/brokers/__init__.py +0 -0
- rasa/core/brokers/broker.py +126 -0
- rasa/core/brokers/file.py +58 -0
- rasa/core/brokers/kafka.py +324 -0
- rasa/core/brokers/pika.py +388 -0
- rasa/core/brokers/sql.py +86 -0
- rasa/core/channels/__init__.py +61 -0
- rasa/core/channels/botframework.py +338 -0
- rasa/core/channels/callback.py +84 -0
- rasa/core/channels/channel.py +456 -0
- rasa/core/channels/console.py +241 -0
- rasa/core/channels/development_inspector.py +197 -0
- rasa/core/channels/facebook.py +419 -0
- rasa/core/channels/hangouts.py +329 -0
- rasa/core/channels/inspector/.eslintrc.cjs +25 -0
- rasa/core/channels/inspector/.gitignore +23 -0
- rasa/core/channels/inspector/README.md +54 -0
- rasa/core/channels/inspector/assets/favicon.ico +0 -0
- rasa/core/channels/inspector/assets/rasa-chat.js +2 -0
- rasa/core/channels/inspector/custom.d.ts +3 -0
- rasa/core/channels/inspector/dist/assets/arc-861ddd57.js +1 -0
- rasa/core/channels/inspector/dist/assets/array-9f3ba611.js +1 -0
- rasa/core/channels/inspector/dist/assets/c4Diagram-d0fbc5ce-921f02db.js +10 -0
- rasa/core/channels/inspector/dist/assets/classDiagram-936ed81e-b436c4f8.js +2 -0
- rasa/core/channels/inspector/dist/assets/classDiagram-v2-c3cb15f1-511a23cb.js +2 -0
- rasa/core/channels/inspector/dist/assets/createText-62fc7601-ef476ecd.js +7 -0
- rasa/core/channels/inspector/dist/assets/edges-f2ad444c-f1878e0a.js +4 -0
- rasa/core/channels/inspector/dist/assets/erDiagram-9d236eb7-fac75185.js +51 -0
- rasa/core/channels/inspector/dist/assets/flowDb-1972c806-201c5bbc.js +6 -0
- rasa/core/channels/inspector/dist/assets/flowDiagram-7ea5b25a-f904ae41.js +4 -0
- rasa/core/channels/inspector/dist/assets/flowDiagram-v2-855bc5b3-b080d6f2.js +1 -0
- rasa/core/channels/inspector/dist/assets/flowchart-elk-definition-abe16c3d-1813da66.js +139 -0
- rasa/core/channels/inspector/dist/assets/ganttDiagram-9b5ea136-872af172.js +266 -0
- rasa/core/channels/inspector/dist/assets/gitGraphDiagram-99d0ae7c-34a0af5a.js +70 -0
- rasa/core/channels/inspector/dist/assets/ibm-plex-mono-v4-latin-regular-128cfa44.ttf +0 -0
- rasa/core/channels/inspector/dist/assets/ibm-plex-mono-v4-latin-regular-21dbcb97.woff +0 -0
- rasa/core/channels/inspector/dist/assets/ibm-plex-mono-v4-latin-regular-222b5e26.svg +329 -0
- rasa/core/channels/inspector/dist/assets/ibm-plex-mono-v4-latin-regular-9ad89b2a.woff2 +0 -0
- rasa/core/channels/inspector/dist/assets/index-2c4b9a3b-42ba3e3d.js +1 -0
- rasa/core/channels/inspector/dist/assets/index-37817b51.js +1317 -0
- rasa/core/channels/inspector/dist/assets/index-3ee28881.css +1 -0
- rasa/core/channels/inspector/dist/assets/infoDiagram-736b4530-6b731386.js +7 -0
- rasa/core/channels/inspector/dist/assets/init-77b53fdd.js +1 -0
- rasa/core/channels/inspector/dist/assets/journeyDiagram-df861f2b-e8579ac6.js +139 -0
- rasa/core/channels/inspector/dist/assets/lato-v14-latin-700-60c05ee4.woff +0 -0
- rasa/core/channels/inspector/dist/assets/lato-v14-latin-700-8335d9b8.svg +438 -0
- rasa/core/channels/inspector/dist/assets/lato-v14-latin-700-9cc39c75.ttf +0 -0
- rasa/core/channels/inspector/dist/assets/lato-v14-latin-700-ead13ccf.woff2 +0 -0
- rasa/core/channels/inspector/dist/assets/lato-v14-latin-regular-16705655.woff2 +0 -0
- rasa/core/channels/inspector/dist/assets/lato-v14-latin-regular-5aeb07f9.woff +0 -0
- rasa/core/channels/inspector/dist/assets/lato-v14-latin-regular-9c459044.ttf +0 -0
- rasa/core/channels/inspector/dist/assets/lato-v14-latin-regular-9e2898a4.svg +435 -0
- rasa/core/channels/inspector/dist/assets/layout-89e6403a.js +1 -0
- rasa/core/channels/inspector/dist/assets/line-dc73d3fc.js +1 -0
- rasa/core/channels/inspector/dist/assets/linear-f5b1d2bc.js +1 -0
- rasa/core/channels/inspector/dist/assets/mindmap-definition-beec6740-82cb74fa.js +109 -0
- rasa/core/channels/inspector/dist/assets/ordinal-ba9b4969.js +1 -0
- rasa/core/channels/inspector/dist/assets/path-53f90ab3.js +1 -0
- rasa/core/channels/inspector/dist/assets/pieDiagram-dbbf0591-bdf5f29b.js +35 -0
- rasa/core/channels/inspector/dist/assets/quadrantDiagram-4d7f4fd6-c7a0cbe4.js +7 -0
- rasa/core/channels/inspector/dist/assets/requirementDiagram-6fc4c22a-7ec5410f.js +52 -0
- rasa/core/channels/inspector/dist/assets/sankeyDiagram-8f13d901-caee5554.js +8 -0
- rasa/core/channels/inspector/dist/assets/sequenceDiagram-b655622a-2935f8db.js +122 -0
- rasa/core/channels/inspector/dist/assets/stateDiagram-59f0c015-8f5d9693.js +1 -0
- rasa/core/channels/inspector/dist/assets/stateDiagram-v2-2b26beab-d565d1de.js +1 -0
- rasa/core/channels/inspector/dist/assets/styles-080da4f6-75ad421d.js +110 -0
- rasa/core/channels/inspector/dist/assets/styles-3dcbcfbf-7e764226.js +159 -0
- rasa/core/channels/inspector/dist/assets/styles-9c745c82-7a4e0e61.js +207 -0
- rasa/core/channels/inspector/dist/assets/svgDrawCommon-4835440b-4019d1bf.js +1 -0
- rasa/core/channels/inspector/dist/assets/timeline-definition-5b62e21b-01ea12df.js +61 -0
- rasa/core/channels/inspector/dist/assets/xychartDiagram-2b33534f-89407137.js +7 -0
- rasa/core/channels/inspector/dist/index.html +42 -0
- rasa/core/channels/inspector/index.html +40 -0
- rasa/core/channels/inspector/jest.config.ts +13 -0
- rasa/core/channels/inspector/package.json +52 -0
- rasa/core/channels/inspector/setupTests.ts +2 -0
- rasa/core/channels/inspector/src/App.tsx +220 -0
- rasa/core/channels/inspector/src/components/Chat.tsx +95 -0
- rasa/core/channels/inspector/src/components/DiagramFlow.tsx +108 -0
- rasa/core/channels/inspector/src/components/DialogueInformation.tsx +187 -0
- rasa/core/channels/inspector/src/components/DialogueStack.tsx +136 -0
- rasa/core/channels/inspector/src/components/ExpandIcon.tsx +16 -0
- rasa/core/channels/inspector/src/components/FullscreenButton.tsx +45 -0
- rasa/core/channels/inspector/src/components/LoadingSpinner.tsx +22 -0
- rasa/core/channels/inspector/src/components/NoActiveFlow.tsx +21 -0
- rasa/core/channels/inspector/src/components/RasaLogo.tsx +32 -0
- rasa/core/channels/inspector/src/components/SaraDiagrams.tsx +39 -0
- rasa/core/channels/inspector/src/components/Slots.tsx +91 -0
- rasa/core/channels/inspector/src/components/Welcome.tsx +54 -0
- rasa/core/channels/inspector/src/helpers/audiostream.ts +191 -0
- rasa/core/channels/inspector/src/helpers/formatters.test.ts +392 -0
- rasa/core/channels/inspector/src/helpers/formatters.ts +306 -0
- rasa/core/channels/inspector/src/helpers/utils.ts +127 -0
- rasa/core/channels/inspector/src/main.tsx +13 -0
- rasa/core/channels/inspector/src/theme/Button/Button.ts +29 -0
- rasa/core/channels/inspector/src/theme/Heading/Heading.ts +31 -0
- rasa/core/channels/inspector/src/theme/Input/Input.ts +27 -0
- rasa/core/channels/inspector/src/theme/Link/Link.ts +10 -0
- rasa/core/channels/inspector/src/theme/Modal/Modal.ts +47 -0
- rasa/core/channels/inspector/src/theme/Table/Table.tsx +38 -0
- rasa/core/channels/inspector/src/theme/Tooltip/Tooltip.ts +12 -0
- rasa/core/channels/inspector/src/theme/base/breakpoints.ts +8 -0
- rasa/core/channels/inspector/src/theme/base/colors.ts +88 -0
- rasa/core/channels/inspector/src/theme/base/fonts/fontFaces.css +29 -0
- rasa/core/channels/inspector/src/theme/base/fonts/ibm-plex-mono-v4-latin/ibm-plex-mono-v4-latin-regular.eot +0 -0
- rasa/core/channels/inspector/src/theme/base/fonts/ibm-plex-mono-v4-latin/ibm-plex-mono-v4-latin-regular.svg +329 -0
- rasa/core/channels/inspector/src/theme/base/fonts/ibm-plex-mono-v4-latin/ibm-plex-mono-v4-latin-regular.ttf +0 -0
- rasa/core/channels/inspector/src/theme/base/fonts/ibm-plex-mono-v4-latin/ibm-plex-mono-v4-latin-regular.woff +0 -0
- rasa/core/channels/inspector/src/theme/base/fonts/ibm-plex-mono-v4-latin/ibm-plex-mono-v4-latin-regular.woff2 +0 -0
- rasa/core/channels/inspector/src/theme/base/fonts/lato-v14-latin/lato-v14-latin-700.eot +0 -0
- rasa/core/channels/inspector/src/theme/base/fonts/lato-v14-latin/lato-v14-latin-700.svg +438 -0
- rasa/core/channels/inspector/src/theme/base/fonts/lato-v14-latin/lato-v14-latin-700.ttf +0 -0
- rasa/core/channels/inspector/src/theme/base/fonts/lato-v14-latin/lato-v14-latin-700.woff +0 -0
- rasa/core/channels/inspector/src/theme/base/fonts/lato-v14-latin/lato-v14-latin-700.woff2 +0 -0
- rasa/core/channels/inspector/src/theme/base/fonts/lato-v14-latin/lato-v14-latin-regular.eot +0 -0
- rasa/core/channels/inspector/src/theme/base/fonts/lato-v14-latin/lato-v14-latin-regular.svg +435 -0
- rasa/core/channels/inspector/src/theme/base/fonts/lato-v14-latin/lato-v14-latin-regular.ttf +0 -0
- rasa/core/channels/inspector/src/theme/base/fonts/lato-v14-latin/lato-v14-latin-regular.woff +0 -0
- rasa/core/channels/inspector/src/theme/base/fonts/lato-v14-latin/lato-v14-latin-regular.woff2 +0 -0
- rasa/core/channels/inspector/src/theme/base/radii.ts +9 -0
- rasa/core/channels/inspector/src/theme/base/shadows.ts +7 -0
- rasa/core/channels/inspector/src/theme/base/sizes.ts +7 -0
- rasa/core/channels/inspector/src/theme/base/space.ts +15 -0
- rasa/core/channels/inspector/src/theme/base/styles.ts +13 -0
- rasa/core/channels/inspector/src/theme/base/typography.ts +24 -0
- rasa/core/channels/inspector/src/theme/base/zIndices.ts +19 -0
- rasa/core/channels/inspector/src/theme/index.ts +101 -0
- rasa/core/channels/inspector/src/types.ts +84 -0
- rasa/core/channels/inspector/src/vite-env.d.ts +1 -0
- rasa/core/channels/inspector/tests/__mocks__/fileMock.ts +1 -0
- rasa/core/channels/inspector/tests/__mocks__/matchMedia.ts +16 -0
- rasa/core/channels/inspector/tests/__mocks__/styleMock.ts +1 -0
- rasa/core/channels/inspector/tests/renderWithProviders.tsx +14 -0
- rasa/core/channels/inspector/tsconfig.json +26 -0
- rasa/core/channels/inspector/tsconfig.node.json +10 -0
- rasa/core/channels/inspector/vite.config.ts +8 -0
- rasa/core/channels/inspector/yarn.lock +6249 -0
- rasa/core/channels/mattermost.py +229 -0
- rasa/core/channels/rasa_chat.py +126 -0
- rasa/core/channels/rest.py +230 -0
- rasa/core/channels/rocketchat.py +174 -0
- rasa/core/channels/slack.py +620 -0
- rasa/core/channels/socketio.py +302 -0
- rasa/core/channels/telegram.py +298 -0
- rasa/core/channels/twilio.py +169 -0
- rasa/core/channels/vier_cvg.py +374 -0
- rasa/core/channels/voice_ready/__init__.py +0 -0
- rasa/core/channels/voice_ready/audiocodes.py +501 -0
- rasa/core/channels/voice_ready/jambonz.py +121 -0
- rasa/core/channels/voice_ready/jambonz_protocol.py +396 -0
- rasa/core/channels/voice_ready/twilio_voice.py +403 -0
- rasa/core/channels/voice_ready/utils.py +37 -0
- rasa/core/channels/voice_stream/__init__.py +0 -0
- rasa/core/channels/voice_stream/asr/__init__.py +0 -0
- rasa/core/channels/voice_stream/asr/asr_engine.py +89 -0
- rasa/core/channels/voice_stream/asr/asr_event.py +18 -0
- rasa/core/channels/voice_stream/asr/azure.py +130 -0
- rasa/core/channels/voice_stream/asr/deepgram.py +90 -0
- rasa/core/channels/voice_stream/audio_bytes.py +8 -0
- rasa/core/channels/voice_stream/browser_audio.py +107 -0
- rasa/core/channels/voice_stream/call_state.py +23 -0
- rasa/core/channels/voice_stream/tts/__init__.py +0 -0
- rasa/core/channels/voice_stream/tts/azure.py +106 -0
- rasa/core/channels/voice_stream/tts/cartesia.py +118 -0
- rasa/core/channels/voice_stream/tts/tts_cache.py +27 -0
- rasa/core/channels/voice_stream/tts/tts_engine.py +58 -0
- rasa/core/channels/voice_stream/twilio_media_streams.py +173 -0
- rasa/core/channels/voice_stream/util.py +57 -0
- rasa/core/channels/voice_stream/voice_channel.py +427 -0
- rasa/core/channels/webexteams.py +134 -0
- rasa/core/concurrent_lock_store.py +210 -0
- rasa/core/constants.py +112 -0
- rasa/core/evaluation/__init__.py +0 -0
- rasa/core/evaluation/marker.py +267 -0
- rasa/core/evaluation/marker_base.py +923 -0
- rasa/core/evaluation/marker_stats.py +293 -0
- rasa/core/evaluation/marker_tracker_loader.py +103 -0
- rasa/core/exceptions.py +29 -0
- rasa/core/exporter.py +284 -0
- rasa/core/featurizers/__init__.py +0 -0
- rasa/core/featurizers/precomputation.py +410 -0
- rasa/core/featurizers/single_state_featurizer.py +421 -0
- rasa/core/featurizers/tracker_featurizers.py +1262 -0
- rasa/core/http_interpreter.py +89 -0
- rasa/core/information_retrieval/__init__.py +7 -0
- rasa/core/information_retrieval/faiss.py +124 -0
- rasa/core/information_retrieval/information_retrieval.py +137 -0
- rasa/core/information_retrieval/milvus.py +59 -0
- rasa/core/information_retrieval/qdrant.py +96 -0
- rasa/core/jobs.py +63 -0
- rasa/core/lock.py +139 -0
- rasa/core/lock_store.py +343 -0
- rasa/core/migrate.py +403 -0
- rasa/core/nlg/__init__.py +3 -0
- rasa/core/nlg/callback.py +146 -0
- rasa/core/nlg/contextual_response_rephraser.py +320 -0
- rasa/core/nlg/generator.py +230 -0
- rasa/core/nlg/interpolator.py +143 -0
- rasa/core/nlg/response.py +155 -0
- rasa/core/nlg/summarize.py +70 -0
- rasa/core/persistor.py +538 -0
- rasa/core/policies/__init__.py +0 -0
- rasa/core/policies/ensemble.py +329 -0
- rasa/core/policies/enterprise_search_policy.py +905 -0
- rasa/core/policies/enterprise_search_prompt_template.jinja2 +25 -0
- rasa/core/policies/enterprise_search_prompt_with_citation_template.jinja2 +60 -0
- rasa/core/policies/flow_policy.py +205 -0
- rasa/core/policies/flows/__init__.py +0 -0
- rasa/core/policies/flows/flow_exceptions.py +44 -0
- rasa/core/policies/flows/flow_executor.py +754 -0
- rasa/core/policies/flows/flow_step_result.py +43 -0
- rasa/core/policies/intentless_policy.py +1031 -0
- rasa/core/policies/intentless_prompt_template.jinja2 +22 -0
- rasa/core/policies/memoization.py +538 -0
- rasa/core/policies/policy.py +725 -0
- rasa/core/policies/rule_policy.py +1273 -0
- rasa/core/policies/ted_policy.py +2169 -0
- rasa/core/policies/unexpected_intent_policy.py +1022 -0
- rasa/core/processor.py +1465 -0
- rasa/core/run.py +342 -0
- rasa/core/secrets_manager/__init__.py +0 -0
- rasa/core/secrets_manager/constants.py +36 -0
- rasa/core/secrets_manager/endpoints.py +391 -0
- rasa/core/secrets_manager/factory.py +241 -0
- rasa/core/secrets_manager/secret_manager.py +262 -0
- rasa/core/secrets_manager/vault.py +584 -0
- rasa/core/test.py +1335 -0
- rasa/core/tracker_store.py +1703 -0
- rasa/core/train.py +105 -0
- rasa/core/training/__init__.py +89 -0
- rasa/core/training/converters/__init__.py +0 -0
- rasa/core/training/converters/responses_prefix_converter.py +119 -0
- rasa/core/training/interactive.py +1744 -0
- rasa/core/training/story_conflict.py +381 -0
- rasa/core/training/training.py +93 -0
- rasa/core/utils.py +366 -0
- rasa/core/visualize.py +70 -0
- rasa/dialogue_understanding/__init__.py +0 -0
- rasa/dialogue_understanding/coexistence/__init__.py +0 -0
- rasa/dialogue_understanding/coexistence/constants.py +4 -0
- rasa/dialogue_understanding/coexistence/intent_based_router.py +196 -0
- rasa/dialogue_understanding/coexistence/llm_based_router.py +327 -0
- rasa/dialogue_understanding/coexistence/router_template.jinja2 +12 -0
- rasa/dialogue_understanding/commands/__init__.py +61 -0
- rasa/dialogue_understanding/commands/can_not_handle_command.py +70 -0
- rasa/dialogue_understanding/commands/cancel_flow_command.py +125 -0
- rasa/dialogue_understanding/commands/change_flow_command.py +44 -0
- rasa/dialogue_understanding/commands/chit_chat_answer_command.py +57 -0
- rasa/dialogue_understanding/commands/clarify_command.py +86 -0
- rasa/dialogue_understanding/commands/command.py +85 -0
- rasa/dialogue_understanding/commands/correct_slots_command.py +297 -0
- rasa/dialogue_understanding/commands/error_command.py +79 -0
- rasa/dialogue_understanding/commands/free_form_answer_command.py +9 -0
- rasa/dialogue_understanding/commands/handle_code_change_command.py +73 -0
- rasa/dialogue_understanding/commands/human_handoff_command.py +66 -0
- rasa/dialogue_understanding/commands/knowledge_answer_command.py +57 -0
- rasa/dialogue_understanding/commands/noop_command.py +54 -0
- rasa/dialogue_understanding/commands/repeat_bot_messages_command.py +60 -0
- rasa/dialogue_understanding/commands/restart_command.py +58 -0
- rasa/dialogue_understanding/commands/session_end_command.py +61 -0
- rasa/dialogue_understanding/commands/session_start_command.py +59 -0
- rasa/dialogue_understanding/commands/set_slot_command.py +160 -0
- rasa/dialogue_understanding/commands/skip_question_command.py +75 -0
- rasa/dialogue_understanding/commands/start_flow_command.py +107 -0
- rasa/dialogue_understanding/commands/user_silence_command.py +59 -0
- rasa/dialogue_understanding/commands/utils.py +45 -0
- rasa/dialogue_understanding/generator/__init__.py +21 -0
- rasa/dialogue_understanding/generator/command_generator.py +464 -0
- rasa/dialogue_understanding/generator/constants.py +27 -0
- rasa/dialogue_understanding/generator/flow_document_template.jinja2 +4 -0
- rasa/dialogue_understanding/generator/flow_retrieval.py +466 -0
- rasa/dialogue_understanding/generator/llm_based_command_generator.py +500 -0
- rasa/dialogue_understanding/generator/llm_command_generator.py +67 -0
- rasa/dialogue_understanding/generator/multi_step/__init__.py +0 -0
- rasa/dialogue_understanding/generator/multi_step/fill_slots_prompt.jinja2 +62 -0
- rasa/dialogue_understanding/generator/multi_step/handle_flows_prompt.jinja2 +38 -0
- rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +920 -0
- rasa/dialogue_understanding/generator/nlu_command_adapter.py +261 -0
- rasa/dialogue_understanding/generator/single_step/__init__.py +0 -0
- rasa/dialogue_understanding/generator/single_step/command_prompt_template.jinja2 +60 -0
- rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +486 -0
- rasa/dialogue_understanding/patterns/__init__.py +0 -0
- rasa/dialogue_understanding/patterns/cancel.py +111 -0
- rasa/dialogue_understanding/patterns/cannot_handle.py +43 -0
- rasa/dialogue_understanding/patterns/chitchat.py +37 -0
- rasa/dialogue_understanding/patterns/clarify.py +97 -0
- rasa/dialogue_understanding/patterns/code_change.py +41 -0
- rasa/dialogue_understanding/patterns/collect_information.py +90 -0
- rasa/dialogue_understanding/patterns/completed.py +40 -0
- rasa/dialogue_understanding/patterns/continue_interrupted.py +42 -0
- rasa/dialogue_understanding/patterns/correction.py +278 -0
- rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +301 -0
- rasa/dialogue_understanding/patterns/human_handoff.py +37 -0
- rasa/dialogue_understanding/patterns/internal_error.py +47 -0
- rasa/dialogue_understanding/patterns/repeat.py +37 -0
- rasa/dialogue_understanding/patterns/restart.py +37 -0
- rasa/dialogue_understanding/patterns/search.py +37 -0
- rasa/dialogue_understanding/patterns/session_start.py +37 -0
- rasa/dialogue_understanding/patterns/skip_question.py +38 -0
- rasa/dialogue_understanding/patterns/user_silence.py +37 -0
- rasa/dialogue_understanding/processor/__init__.py +0 -0
- rasa/dialogue_understanding/processor/command_processor.py +720 -0
- rasa/dialogue_understanding/processor/command_processor_component.py +43 -0
- rasa/dialogue_understanding/stack/__init__.py +0 -0
- rasa/dialogue_understanding/stack/dialogue_stack.py +178 -0
- rasa/dialogue_understanding/stack/frames/__init__.py +19 -0
- rasa/dialogue_understanding/stack/frames/chit_chat_frame.py +27 -0
- rasa/dialogue_understanding/stack/frames/dialogue_stack_frame.py +137 -0
- rasa/dialogue_understanding/stack/frames/flow_stack_frame.py +157 -0
- rasa/dialogue_understanding/stack/frames/pattern_frame.py +10 -0
- rasa/dialogue_understanding/stack/frames/search_frame.py +27 -0
- rasa/dialogue_understanding/stack/utils.py +211 -0
- rasa/dialogue_understanding/utils.py +14 -0
- rasa/dialogue_understanding_test/__init__.py +0 -0
- rasa/dialogue_understanding_test/command_metric_calculation.py +12 -0
- rasa/dialogue_understanding_test/constants.py +17 -0
- rasa/dialogue_understanding_test/du_test_case.py +118 -0
- rasa/dialogue_understanding_test/du_test_result.py +11 -0
- rasa/dialogue_understanding_test/du_test_runner.py +93 -0
- rasa/dialogue_understanding_test/io.py +54 -0
- rasa/dialogue_understanding_test/validation.py +22 -0
- rasa/e2e_test/__init__.py +0 -0
- rasa/e2e_test/aggregate_test_stats_calculator.py +134 -0
- rasa/e2e_test/assertions.py +1345 -0
- rasa/e2e_test/assertions_schema.yml +129 -0
- rasa/e2e_test/constants.py +31 -0
- rasa/e2e_test/e2e_config.py +220 -0
- rasa/e2e_test/e2e_config_schema.yml +26 -0
- rasa/e2e_test/e2e_test_case.py +569 -0
- rasa/e2e_test/e2e_test_converter.py +363 -0
- rasa/e2e_test/e2e_test_converter_prompt.jinja2 +70 -0
- rasa/e2e_test/e2e_test_coverage_report.py +364 -0
- rasa/e2e_test/e2e_test_result.py +54 -0
- rasa/e2e_test/e2e_test_runner.py +1192 -0
- rasa/e2e_test/e2e_test_schema.yml +181 -0
- rasa/e2e_test/pykwalify_extensions.py +39 -0
- rasa/e2e_test/stub_custom_action.py +70 -0
- rasa/e2e_test/utils/__init__.py +0 -0
- rasa/e2e_test/utils/e2e_yaml_utils.py +55 -0
- rasa/e2e_test/utils/io.py +598 -0
- rasa/e2e_test/utils/validation.py +178 -0
- rasa/engine/__init__.py +0 -0
- rasa/engine/caching.py +463 -0
- rasa/engine/constants.py +17 -0
- rasa/engine/exceptions.py +14 -0
- rasa/engine/graph.py +642 -0
- rasa/engine/loader.py +48 -0
- rasa/engine/recipes/__init__.py +0 -0
- rasa/engine/recipes/config_files/default_config.yml +41 -0
- rasa/engine/recipes/default_components.py +97 -0
- rasa/engine/recipes/default_recipe.py +1272 -0
- rasa/engine/recipes/graph_recipe.py +79 -0
- rasa/engine/recipes/recipe.py +93 -0
- rasa/engine/runner/__init__.py +0 -0
- rasa/engine/runner/dask.py +250 -0
- rasa/engine/runner/interface.py +49 -0
- rasa/engine/storage/__init__.py +0 -0
- rasa/engine/storage/local_model_storage.py +244 -0
- rasa/engine/storage/resource.py +110 -0
- rasa/engine/storage/storage.py +199 -0
- rasa/engine/training/__init__.py +0 -0
- rasa/engine/training/components.py +176 -0
- rasa/engine/training/fingerprinting.py +64 -0
- rasa/engine/training/graph_trainer.py +256 -0
- rasa/engine/training/hooks.py +164 -0
- rasa/engine/validation.py +1451 -0
- rasa/env.py +14 -0
- rasa/exceptions.py +69 -0
- rasa/graph_components/__init__.py +0 -0
- rasa/graph_components/converters/__init__.py +0 -0
- rasa/graph_components/converters/nlu_message_converter.py +48 -0
- rasa/graph_components/providers/__init__.py +0 -0
- rasa/graph_components/providers/domain_for_core_training_provider.py +87 -0
- rasa/graph_components/providers/domain_provider.py +71 -0
- rasa/graph_components/providers/flows_provider.py +74 -0
- rasa/graph_components/providers/forms_provider.py +44 -0
- rasa/graph_components/providers/nlu_training_data_provider.py +56 -0
- rasa/graph_components/providers/responses_provider.py +44 -0
- rasa/graph_components/providers/rule_only_provider.py +49 -0
- rasa/graph_components/providers/story_graph_provider.py +96 -0
- rasa/graph_components/providers/training_tracker_provider.py +55 -0
- rasa/graph_components/validators/__init__.py +0 -0
- rasa/graph_components/validators/default_recipe_validator.py +550 -0
- rasa/graph_components/validators/finetuning_validator.py +302 -0
- rasa/hooks.py +111 -0
- rasa/jupyter.py +63 -0
- rasa/llm_fine_tuning/__init__.py +0 -0
- rasa/llm_fine_tuning/annotation_module.py +241 -0
- rasa/llm_fine_tuning/conversations.py +144 -0
- rasa/llm_fine_tuning/llm_data_preparation_module.py +178 -0
- rasa/llm_fine_tuning/paraphrasing/__init__.py +0 -0
- rasa/llm_fine_tuning/paraphrasing/conversation_rephraser.py +281 -0
- rasa/llm_fine_tuning/paraphrasing/default_rephrase_prompt_template.jina2 +44 -0
- rasa/llm_fine_tuning/paraphrasing/rephrase_validator.py +121 -0
- rasa/llm_fine_tuning/paraphrasing/rephrased_user_message.py +10 -0
- rasa/llm_fine_tuning/paraphrasing_module.py +128 -0
- rasa/llm_fine_tuning/storage.py +174 -0
- rasa/llm_fine_tuning/train_test_split_module.py +441 -0
- rasa/markers/__init__.py +0 -0
- rasa/markers/marker.py +269 -0
- rasa/markers/marker_base.py +828 -0
- rasa/markers/upload.py +74 -0
- rasa/markers/validate.py +21 -0
- rasa/model.py +118 -0
- rasa/model_manager/__init__.py +0 -0
- rasa/model_manager/config.py +40 -0
- rasa/model_manager/model_api.py +559 -0
- rasa/model_manager/runner_service.py +286 -0
- rasa/model_manager/socket_bridge.py +146 -0
- rasa/model_manager/studio_jwt_auth.py +86 -0
- rasa/model_manager/trainer_service.py +325 -0
- rasa/model_manager/utils.py +87 -0
- rasa/model_manager/warm_rasa_process.py +187 -0
- rasa/model_service.py +112 -0
- rasa/model_testing.py +457 -0
- rasa/model_training.py +596 -0
- rasa/nlu/__init__.py +7 -0
- rasa/nlu/classifiers/__init__.py +3 -0
- rasa/nlu/classifiers/classifier.py +5 -0
- rasa/nlu/classifiers/diet_classifier.py +1881 -0
- rasa/nlu/classifiers/fallback_classifier.py +192 -0
- rasa/nlu/classifiers/keyword_intent_classifier.py +188 -0
- rasa/nlu/classifiers/logistic_regression_classifier.py +253 -0
- rasa/nlu/classifiers/mitie_intent_classifier.py +156 -0
- rasa/nlu/classifiers/regex_message_handler.py +56 -0
- rasa/nlu/classifiers/sklearn_intent_classifier.py +330 -0
- rasa/nlu/constants.py +77 -0
- rasa/nlu/convert.py +40 -0
- rasa/nlu/emulators/__init__.py +0 -0
- rasa/nlu/emulators/dialogflow.py +55 -0
- rasa/nlu/emulators/emulator.py +49 -0
- rasa/nlu/emulators/luis.py +86 -0
- rasa/nlu/emulators/no_emulator.py +10 -0
- rasa/nlu/emulators/wit.py +56 -0
- rasa/nlu/extractors/__init__.py +0 -0
- rasa/nlu/extractors/crf_entity_extractor.py +715 -0
- rasa/nlu/extractors/duckling_entity_extractor.py +206 -0
- rasa/nlu/extractors/entity_synonyms.py +178 -0
- rasa/nlu/extractors/extractor.py +470 -0
- rasa/nlu/extractors/mitie_entity_extractor.py +293 -0
- rasa/nlu/extractors/regex_entity_extractor.py +220 -0
- rasa/nlu/extractors/spacy_entity_extractor.py +95 -0
- rasa/nlu/featurizers/__init__.py +0 -0
- rasa/nlu/featurizers/dense_featurizer/__init__.py +0 -0
- rasa/nlu/featurizers/dense_featurizer/convert_featurizer.py +445 -0
- rasa/nlu/featurizers/dense_featurizer/dense_featurizer.py +57 -0
- rasa/nlu/featurizers/dense_featurizer/lm_featurizer.py +768 -0
- rasa/nlu/featurizers/dense_featurizer/mitie_featurizer.py +170 -0
- rasa/nlu/featurizers/dense_featurizer/spacy_featurizer.py +132 -0
- rasa/nlu/featurizers/featurizer.py +89 -0
- rasa/nlu/featurizers/sparse_featurizer/__init__.py +0 -0
- rasa/nlu/featurizers/sparse_featurizer/count_vectors_featurizer.py +867 -0
- rasa/nlu/featurizers/sparse_featurizer/lexical_syntactic_featurizer.py +571 -0
- rasa/nlu/featurizers/sparse_featurizer/regex_featurizer.py +271 -0
- rasa/nlu/featurizers/sparse_featurizer/sparse_featurizer.py +9 -0
- rasa/nlu/model.py +24 -0
- rasa/nlu/run.py +27 -0
- rasa/nlu/selectors/__init__.py +0 -0
- rasa/nlu/selectors/response_selector.py +987 -0
- rasa/nlu/test.py +1940 -0
- rasa/nlu/tokenizers/__init__.py +0 -0
- rasa/nlu/tokenizers/jieba_tokenizer.py +148 -0
- rasa/nlu/tokenizers/mitie_tokenizer.py +75 -0
- rasa/nlu/tokenizers/spacy_tokenizer.py +72 -0
- rasa/nlu/tokenizers/tokenizer.py +239 -0
- rasa/nlu/tokenizers/whitespace_tokenizer.py +95 -0
- rasa/nlu/utils/__init__.py +35 -0
- rasa/nlu/utils/bilou_utils.py +462 -0
- rasa/nlu/utils/hugging_face/__init__.py +0 -0
- rasa/nlu/utils/hugging_face/registry.py +108 -0
- rasa/nlu/utils/hugging_face/transformers_pre_post_processors.py +311 -0
- rasa/nlu/utils/mitie_utils.py +113 -0
- rasa/nlu/utils/pattern_utils.py +168 -0
- rasa/nlu/utils/spacy_utils.py +310 -0
- rasa/plugin.py +90 -0
- rasa/server.py +1588 -0
- rasa/shared/__init__.py +0 -0
- rasa/shared/constants.py +311 -0
- rasa/shared/core/__init__.py +0 -0
- rasa/shared/core/command_payload_reader.py +109 -0
- rasa/shared/core/constants.py +180 -0
- rasa/shared/core/conversation.py +46 -0
- rasa/shared/core/domain.py +2172 -0
- rasa/shared/core/events.py +2559 -0
- rasa/shared/core/flows/__init__.py +7 -0
- rasa/shared/core/flows/flow.py +562 -0
- rasa/shared/core/flows/flow_path.py +84 -0
- rasa/shared/core/flows/flow_step.py +146 -0
- rasa/shared/core/flows/flow_step_links.py +319 -0
- rasa/shared/core/flows/flow_step_sequence.py +70 -0
- rasa/shared/core/flows/flows_list.py +258 -0
- rasa/shared/core/flows/flows_yaml_schema.json +303 -0
- rasa/shared/core/flows/nlu_trigger.py +117 -0
- rasa/shared/core/flows/steps/__init__.py +24 -0
- rasa/shared/core/flows/steps/action.py +56 -0
- rasa/shared/core/flows/steps/call.py +64 -0
- rasa/shared/core/flows/steps/collect.py +112 -0
- rasa/shared/core/flows/steps/constants.py +5 -0
- rasa/shared/core/flows/steps/continuation.py +36 -0
- rasa/shared/core/flows/steps/end.py +22 -0
- rasa/shared/core/flows/steps/internal.py +44 -0
- rasa/shared/core/flows/steps/link.py +51 -0
- rasa/shared/core/flows/steps/no_operation.py +48 -0
- rasa/shared/core/flows/steps/set_slots.py +50 -0
- rasa/shared/core/flows/steps/start.py +30 -0
- rasa/shared/core/flows/utils.py +39 -0
- rasa/shared/core/flows/validation.py +735 -0
- rasa/shared/core/flows/yaml_flows_io.py +405 -0
- rasa/shared/core/generator.py +908 -0
- rasa/shared/core/slot_mappings.py +526 -0
- rasa/shared/core/slots.py +654 -0
- rasa/shared/core/trackers.py +1183 -0
- rasa/shared/core/training_data/__init__.py +0 -0
- rasa/shared/core/training_data/loading.py +89 -0
- rasa/shared/core/training_data/story_reader/__init__.py +0 -0
- rasa/shared/core/training_data/story_reader/story_reader.py +129 -0
- rasa/shared/core/training_data/story_reader/story_step_builder.py +168 -0
- rasa/shared/core/training_data/story_reader/yaml_story_reader.py +888 -0
- rasa/shared/core/training_data/story_writer/__init__.py +0 -0
- rasa/shared/core/training_data/story_writer/story_writer.py +76 -0
- rasa/shared/core/training_data/story_writer/yaml_story_writer.py +444 -0
- rasa/shared/core/training_data/structures.py +858 -0
- rasa/shared/core/training_data/visualization.html +146 -0
- rasa/shared/core/training_data/visualization.py +603 -0
- rasa/shared/data.py +249 -0
- rasa/shared/engine/__init__.py +0 -0
- rasa/shared/engine/caching.py +26 -0
- rasa/shared/exceptions.py +167 -0
- rasa/shared/importers/__init__.py +0 -0
- rasa/shared/importers/importer.py +770 -0
- rasa/shared/importers/multi_project.py +215 -0
- rasa/shared/importers/rasa.py +108 -0
- rasa/shared/importers/remote_importer.py +196 -0
- rasa/shared/importers/utils.py +36 -0
- rasa/shared/nlu/__init__.py +0 -0
- rasa/shared/nlu/constants.py +53 -0
- rasa/shared/nlu/interpreter.py +10 -0
- rasa/shared/nlu/training_data/__init__.py +0 -0
- rasa/shared/nlu/training_data/entities_parser.py +208 -0
- rasa/shared/nlu/training_data/features.py +492 -0
- rasa/shared/nlu/training_data/formats/__init__.py +10 -0
- rasa/shared/nlu/training_data/formats/dialogflow.py +163 -0
- rasa/shared/nlu/training_data/formats/luis.py +87 -0
- rasa/shared/nlu/training_data/formats/rasa.py +135 -0
- rasa/shared/nlu/training_data/formats/rasa_yaml.py +618 -0
- rasa/shared/nlu/training_data/formats/readerwriter.py +244 -0
- rasa/shared/nlu/training_data/formats/wit.py +52 -0
- rasa/shared/nlu/training_data/loading.py +137 -0
- rasa/shared/nlu/training_data/lookup_tables_parser.py +30 -0
- rasa/shared/nlu/training_data/message.py +490 -0
- rasa/shared/nlu/training_data/schemas/__init__.py +0 -0
- rasa/shared/nlu/training_data/schemas/data_schema.py +85 -0
- rasa/shared/nlu/training_data/schemas/nlu.yml +53 -0
- rasa/shared/nlu/training_data/schemas/responses.yml +70 -0
- rasa/shared/nlu/training_data/synonyms_parser.py +42 -0
- rasa/shared/nlu/training_data/training_data.py +729 -0
- rasa/shared/nlu/training_data/util.py +223 -0
- rasa/shared/providers/__init__.py +0 -0
- rasa/shared/providers/_configs/__init__.py +0 -0
- rasa/shared/providers/_configs/azure_openai_client_config.py +677 -0
- rasa/shared/providers/_configs/client_config.py +59 -0
- rasa/shared/providers/_configs/default_litellm_client_config.py +132 -0
- rasa/shared/providers/_configs/huggingface_local_embedding_client_config.py +236 -0
- rasa/shared/providers/_configs/litellm_router_client_config.py +222 -0
- rasa/shared/providers/_configs/model_group_config.py +173 -0
- rasa/shared/providers/_configs/openai_client_config.py +177 -0
- rasa/shared/providers/_configs/rasa_llm_client_config.py +75 -0
- rasa/shared/providers/_configs/self_hosted_llm_client_config.py +178 -0
- rasa/shared/providers/_configs/utils.py +117 -0
- rasa/shared/providers/_ssl_verification_utils.py +124 -0
- rasa/shared/providers/_utils.py +79 -0
- rasa/shared/providers/constants.py +7 -0
- rasa/shared/providers/embedding/__init__.py +0 -0
- rasa/shared/providers/embedding/_base_litellm_embedding_client.py +243 -0
- rasa/shared/providers/embedding/_langchain_embedding_client_adapter.py +74 -0
- rasa/shared/providers/embedding/azure_openai_embedding_client.py +335 -0
- rasa/shared/providers/embedding/default_litellm_embedding_client.py +126 -0
- rasa/shared/providers/embedding/embedding_client.py +90 -0
- rasa/shared/providers/embedding/embedding_response.py +41 -0
- rasa/shared/providers/embedding/huggingface_local_embedding_client.py +191 -0
- rasa/shared/providers/embedding/litellm_router_embedding_client.py +138 -0
- rasa/shared/providers/embedding/openai_embedding_client.py +172 -0
- rasa/shared/providers/llm/__init__.py +0 -0
- rasa/shared/providers/llm/_base_litellm_client.py +265 -0
- rasa/shared/providers/llm/azure_openai_llm_client.py +415 -0
- rasa/shared/providers/llm/default_litellm_llm_client.py +110 -0
- rasa/shared/providers/llm/litellm_router_llm_client.py +202 -0
- rasa/shared/providers/llm/llm_client.py +78 -0
- rasa/shared/providers/llm/llm_response.py +50 -0
- rasa/shared/providers/llm/openai_llm_client.py +161 -0
- rasa/shared/providers/llm/rasa_llm_client.py +120 -0
- rasa/shared/providers/llm/self_hosted_llm_client.py +276 -0
- rasa/shared/providers/mappings.py +94 -0
- rasa/shared/providers/router/__init__.py +0 -0
- rasa/shared/providers/router/_base_litellm_router_client.py +185 -0
- rasa/shared/providers/router/router_client.py +75 -0
- rasa/shared/utils/__init__.py +0 -0
- rasa/shared/utils/cli.py +102 -0
- rasa/shared/utils/common.py +324 -0
- rasa/shared/utils/constants.py +4 -0
- rasa/shared/utils/health_check/__init__.py +0 -0
- rasa/shared/utils/health_check/embeddings_health_check_mixin.py +31 -0
- rasa/shared/utils/health_check/health_check.py +258 -0
- rasa/shared/utils/health_check/llm_health_check_mixin.py +31 -0
- rasa/shared/utils/io.py +499 -0
- rasa/shared/utils/llm.py +764 -0
- rasa/shared/utils/pykwalify_extensions.py +27 -0
- rasa/shared/utils/schemas/__init__.py +0 -0
- rasa/shared/utils/schemas/config.yml +2 -0
- rasa/shared/utils/schemas/domain.yml +145 -0
- rasa/shared/utils/schemas/events.py +214 -0
- rasa/shared/utils/schemas/model_config.yml +36 -0
- rasa/shared/utils/schemas/stories.yml +173 -0
- rasa/shared/utils/yaml.py +1068 -0
- rasa/studio/__init__.py +0 -0
- rasa/studio/auth.py +270 -0
- rasa/studio/config.py +136 -0
- rasa/studio/constants.py +19 -0
- rasa/studio/data_handler.py +368 -0
- rasa/studio/download.py +489 -0
- rasa/studio/results_logger.py +137 -0
- rasa/studio/train.py +134 -0
- rasa/studio/upload.py +563 -0
- rasa/telemetry.py +1876 -0
- rasa/tracing/__init__.py +0 -0
- rasa/tracing/config.py +355 -0
- rasa/tracing/constants.py +62 -0
- rasa/tracing/instrumentation/__init__.py +0 -0
- rasa/tracing/instrumentation/attribute_extractors.py +765 -0
- rasa/tracing/instrumentation/instrumentation.py +1306 -0
- rasa/tracing/instrumentation/intentless_policy_instrumentation.py +144 -0
- rasa/tracing/instrumentation/metrics.py +294 -0
- rasa/tracing/metric_instrument_provider.py +205 -0
- rasa/utils/__init__.py +0 -0
- rasa/utils/beta.py +83 -0
- rasa/utils/cli.py +28 -0
- rasa/utils/common.py +639 -0
- rasa/utils/converter.py +53 -0
- rasa/utils/endpoints.py +331 -0
- rasa/utils/io.py +252 -0
- rasa/utils/json_utils.py +60 -0
- rasa/utils/licensing.py +542 -0
- rasa/utils/log_utils.py +181 -0
- rasa/utils/mapper.py +210 -0
- rasa/utils/ml_utils.py +147 -0
- rasa/utils/plotting.py +362 -0
- rasa/utils/sanic_error_handler.py +32 -0
- rasa/utils/singleton.py +23 -0
- rasa/utils/tensorflow/__init__.py +0 -0
- rasa/utils/tensorflow/callback.py +112 -0
- rasa/utils/tensorflow/constants.py +116 -0
- rasa/utils/tensorflow/crf.py +492 -0
- rasa/utils/tensorflow/data_generator.py +440 -0
- rasa/utils/tensorflow/environment.py +161 -0
- rasa/utils/tensorflow/exceptions.py +5 -0
- rasa/utils/tensorflow/feature_array.py +366 -0
- rasa/utils/tensorflow/layers.py +1565 -0
- rasa/utils/tensorflow/layers_utils.py +113 -0
- rasa/utils/tensorflow/metrics.py +281 -0
- rasa/utils/tensorflow/model_data.py +798 -0
- rasa/utils/tensorflow/model_data_utils.py +499 -0
- rasa/utils/tensorflow/models.py +935 -0
- rasa/utils/tensorflow/rasa_layers.py +1094 -0
- rasa/utils/tensorflow/transformer.py +640 -0
- rasa/utils/tensorflow/types.py +6 -0
- rasa/utils/train_utils.py +572 -0
- rasa/utils/url_tools.py +53 -0
- rasa/utils/yaml.py +54 -0
- rasa/validator.py +1644 -0
- rasa/version.py +3 -0
- rasa_pro-3.12.0.dev1.dist-info/METADATA +199 -0
- rasa_pro-3.12.0.dev1.dist-info/NOTICE +5 -0
- rasa_pro-3.12.0.dev1.dist-info/RECORD +790 -0
- rasa_pro-3.12.0.dev1.dist-info/WHEEL +4 -0
- rasa_pro-3.12.0.dev1.dist-info/entry_points.txt +3 -0
rasa/core/processor.py
ADDED
|
@@ -0,0 +1,1465 @@
|
|
|
1
|
+
import inspect
|
|
2
|
+
import copy
|
|
3
|
+
import logging
|
|
4
|
+
import structlog
|
|
5
|
+
import os
|
|
6
|
+
import re
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
import tarfile
|
|
9
|
+
import time
|
|
10
|
+
from types import LambdaType
|
|
11
|
+
from typing import Any, Dict, List, Optional, TYPE_CHECKING, Text, Tuple, Union
|
|
12
|
+
from rasa.core.actions.action_exceptions import ActionExecutionRejection
|
|
13
|
+
from rasa.core.actions.forms import FormAction
|
|
14
|
+
from rasa.core.http_interpreter import RasaNLUHttpInterpreter
|
|
15
|
+
from rasa.dialogue_understanding.commands import (
|
|
16
|
+
Command,
|
|
17
|
+
NoopCommand,
|
|
18
|
+
SetSlotCommand,
|
|
19
|
+
CannotHandleCommand,
|
|
20
|
+
)
|
|
21
|
+
from rasa.engine import loader
|
|
22
|
+
from rasa.engine.constants import (
|
|
23
|
+
PLACEHOLDER_MESSAGE,
|
|
24
|
+
PLACEHOLDER_TRACKER,
|
|
25
|
+
PLACEHOLDER_ENDPOINTS,
|
|
26
|
+
)
|
|
27
|
+
from rasa.engine.runner.dask import DaskGraphRunner
|
|
28
|
+
from rasa.engine.storage.local_model_storage import LocalModelStorage
|
|
29
|
+
from rasa.engine.storage.storage import ModelMetadata
|
|
30
|
+
from rasa.model import get_latest_model
|
|
31
|
+
from rasa.plugin import plugin_manager
|
|
32
|
+
from rasa.shared.core.flows import FlowsList
|
|
33
|
+
from rasa.shared.data import TrainingType, create_regex_pattern_reader
|
|
34
|
+
import rasa.shared.utils.io
|
|
35
|
+
import rasa.core.actions.action
|
|
36
|
+
from rasa.core import jobs
|
|
37
|
+
from rasa.core.actions.action import Action
|
|
38
|
+
from rasa.core.channels.channel import (
|
|
39
|
+
CollectingOutputChannel,
|
|
40
|
+
OutputChannel,
|
|
41
|
+
UserMessage,
|
|
42
|
+
)
|
|
43
|
+
import rasa.core.utils
|
|
44
|
+
from rasa.core.policies.policy import PolicyPrediction
|
|
45
|
+
from rasa.engine.runner.interface import GraphRunner
|
|
46
|
+
from rasa.exceptions import ActionLimitReached, ModelNotFound
|
|
47
|
+
from rasa.shared.core.constants import (
|
|
48
|
+
ACTION_CORRECT_FLOW_SLOT,
|
|
49
|
+
SLOT_CONSECUTIVE_SILENCE_TIMEOUTS,
|
|
50
|
+
SLOT_SILENCE_TIMEOUT,
|
|
51
|
+
USER_INTENT_RESTART,
|
|
52
|
+
ACTION_LISTEN_NAME,
|
|
53
|
+
ACTION_SESSION_START_NAME,
|
|
54
|
+
FOLLOWUP_ACTION,
|
|
55
|
+
SESSION_START_METADATA_SLOT,
|
|
56
|
+
ACTION_EXTRACT_SLOTS,
|
|
57
|
+
USER_INTENT_SILENCE_TIMEOUT,
|
|
58
|
+
)
|
|
59
|
+
from rasa.shared.core.events import (
|
|
60
|
+
ActionExecutionRejected,
|
|
61
|
+
BotUttered,
|
|
62
|
+
Event,
|
|
63
|
+
ReminderCancelled,
|
|
64
|
+
ReminderScheduled,
|
|
65
|
+
SlotSet,
|
|
66
|
+
UserUttered,
|
|
67
|
+
ActionExecuted,
|
|
68
|
+
)
|
|
69
|
+
from rasa.shared.constants import (
|
|
70
|
+
ASSISTANT_ID_KEY,
|
|
71
|
+
DOCS_URL_DOMAINS,
|
|
72
|
+
DEFAULT_SENDER_ID,
|
|
73
|
+
ROUTE_TO_CALM_SLOT,
|
|
74
|
+
DOCS_URL_NLU_BASED_POLICIES,
|
|
75
|
+
UTTER_PREFIX,
|
|
76
|
+
RASA_PATTERN_CANNOT_HANDLE_INVALID_INTENT,
|
|
77
|
+
)
|
|
78
|
+
from rasa.core.nlg import NaturalLanguageGenerator
|
|
79
|
+
from rasa.core.lock_store import LockStore
|
|
80
|
+
from rasa.utils.common import TempDirectoryPath, get_temp_dir_name
|
|
81
|
+
import rasa.core.tracker_store
|
|
82
|
+
import rasa.core.actions.action
|
|
83
|
+
import rasa.shared.core.trackers
|
|
84
|
+
from rasa.shared.core.trackers import DialogueStateTracker, EventVerbosity
|
|
85
|
+
from rasa.shared.nlu.constants import (
|
|
86
|
+
COMMANDS,
|
|
87
|
+
ENTITIES,
|
|
88
|
+
INTENT,
|
|
89
|
+
INTENT_NAME_KEY,
|
|
90
|
+
INTENT_RESPONSE_KEY,
|
|
91
|
+
PREDICTED_CONFIDENCE_KEY,
|
|
92
|
+
FULL_RETRIEVAL_INTENT_NAME_KEY,
|
|
93
|
+
RESPONSE_SELECTOR,
|
|
94
|
+
RESPONSE,
|
|
95
|
+
TEXT,
|
|
96
|
+
)
|
|
97
|
+
from rasa.shared.nlu.training_data.message import Message
|
|
98
|
+
from rasa.utils.endpoints import EndpointConfig
|
|
99
|
+
|
|
100
|
+
if TYPE_CHECKING:
|
|
101
|
+
from rasa.core.utils import AvailableEndpoints
|
|
102
|
+
|
|
103
|
+
logger = logging.getLogger(__name__)
|
|
104
|
+
structlogger = structlog.get_logger()
|
|
105
|
+
|
|
106
|
+
MAX_NUMBER_OF_PREDICTIONS = int(os.environ.get("MAX_NUMBER_OF_PREDICTIONS", "10"))
|
|
107
|
+
MAX_NUMBER_OF_PREDICTIONS_CALM = int(
|
|
108
|
+
os.environ.get("MAX_NUMBER_OF_PREDICTIONS_CALM", "1000")
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class MessageProcessor:
|
|
113
|
+
"""The message processor is interface for communicating with a bot model."""
|
|
114
|
+
|
|
115
|
+
def __init__(
|
|
116
|
+
self,
|
|
117
|
+
model_path: Union[Text, Path],
|
|
118
|
+
tracker_store: rasa.core.tracker_store.TrackerStore,
|
|
119
|
+
lock_store: LockStore,
|
|
120
|
+
generator: NaturalLanguageGenerator,
|
|
121
|
+
action_endpoint: Optional[EndpointConfig] = None,
|
|
122
|
+
max_number_of_predictions: int = MAX_NUMBER_OF_PREDICTIONS,
|
|
123
|
+
max_number_of_predictions_calm: int = MAX_NUMBER_OF_PREDICTIONS_CALM,
|
|
124
|
+
on_circuit_break: Optional[LambdaType] = None,
|
|
125
|
+
http_interpreter: Optional[RasaNLUHttpInterpreter] = None,
|
|
126
|
+
endpoints: Optional["AvailableEndpoints"] = None,
|
|
127
|
+
) -> None:
|
|
128
|
+
"""Initializes a `MessageProcessor`."""
|
|
129
|
+
self.nlg = generator
|
|
130
|
+
self.tracker_store = tracker_store
|
|
131
|
+
self.lock_store = lock_store
|
|
132
|
+
self.on_circuit_break = on_circuit_break
|
|
133
|
+
self.action_endpoint = action_endpoint
|
|
134
|
+
self.model_filename, self.model_metadata, self.graph_runner = self._load_model(
|
|
135
|
+
model_path
|
|
136
|
+
)
|
|
137
|
+
self.endpoints = endpoints
|
|
138
|
+
|
|
139
|
+
self.max_number_of_predictions = max_number_of_predictions
|
|
140
|
+
self.max_number_of_predictions_calm = max_number_of_predictions_calm
|
|
141
|
+
self.is_calm_assistant = self._is_calm_assistant()
|
|
142
|
+
|
|
143
|
+
if self.model_metadata.assistant_id is None:
|
|
144
|
+
rasa.shared.utils.io.raise_warning(
|
|
145
|
+
f"The model metadata does not contain a value for the "
|
|
146
|
+
f"'{ASSISTANT_ID_KEY}' attribute. Check that 'config.yml' "
|
|
147
|
+
f"file contains a value for the '{ASSISTANT_ID_KEY}' key "
|
|
148
|
+
f"and re-train the model. Failure to do so will result in "
|
|
149
|
+
f"streaming events without a unique assistant identifier.",
|
|
150
|
+
UserWarning,
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
self.model_path = Path(model_path)
|
|
154
|
+
self.domain = self.model_metadata.domain
|
|
155
|
+
self.http_interpreter = http_interpreter
|
|
156
|
+
|
|
157
|
+
@staticmethod
|
|
158
|
+
def _load_model(
|
|
159
|
+
model_path: Union[Text, Path],
|
|
160
|
+
) -> Tuple[Text, ModelMetadata, GraphRunner]:
|
|
161
|
+
"""Unpacks a model from a given path using the graph model loader."""
|
|
162
|
+
try:
|
|
163
|
+
if os.path.isfile(model_path):
|
|
164
|
+
model_tar = model_path
|
|
165
|
+
else:
|
|
166
|
+
model_file_path = get_latest_model(model_path)
|
|
167
|
+
if not model_file_path:
|
|
168
|
+
raise ModelNotFound(f"No model found at path '{model_path}'.")
|
|
169
|
+
model_tar = model_file_path
|
|
170
|
+
except TypeError:
|
|
171
|
+
raise ModelNotFound(f"Model {model_path} can not be loaded.")
|
|
172
|
+
|
|
173
|
+
logger.info(f"Loading model {model_tar}...")
|
|
174
|
+
with TempDirectoryPath(get_temp_dir_name()) as temporary_directory:
|
|
175
|
+
try:
|
|
176
|
+
metadata, runner = loader.load_predict_graph_runner(
|
|
177
|
+
Path(temporary_directory),
|
|
178
|
+
Path(model_tar),
|
|
179
|
+
LocalModelStorage,
|
|
180
|
+
DaskGraphRunner,
|
|
181
|
+
)
|
|
182
|
+
return os.path.basename(model_tar), metadata, runner
|
|
183
|
+
except tarfile.ReadError:
|
|
184
|
+
raise ModelNotFound(f"Model {model_path} can not be loaded.")
|
|
185
|
+
|
|
186
|
+
async def handle_message(
|
|
187
|
+
self, message: UserMessage
|
|
188
|
+
) -> Optional[List[Dict[Text, Any]]]:
|
|
189
|
+
"""Handle a single message with this processor."""
|
|
190
|
+
# preprocess message if necessary
|
|
191
|
+
tracker = await self.log_message(message, should_save_tracker=False)
|
|
192
|
+
|
|
193
|
+
if self.model_metadata.training_type == TrainingType.NLU:
|
|
194
|
+
await self.save_tracker(tracker)
|
|
195
|
+
rasa.shared.utils.io.raise_warning(
|
|
196
|
+
"No core model. Skipping action prediction and execution.",
|
|
197
|
+
docs=DOCS_URL_NLU_BASED_POLICIES,
|
|
198
|
+
)
|
|
199
|
+
return None
|
|
200
|
+
|
|
201
|
+
if not self.message_contains_commands(tracker.latest_message):
|
|
202
|
+
tracker = await self.run_action_extract_slots(
|
|
203
|
+
message.output_channel, tracker
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
await self._run_prediction_loop(message.output_channel, tracker)
|
|
207
|
+
|
|
208
|
+
await self.run_anonymization_pipeline(tracker)
|
|
209
|
+
|
|
210
|
+
await self.save_tracker(tracker)
|
|
211
|
+
|
|
212
|
+
if isinstance(message.output_channel, CollectingOutputChannel):
|
|
213
|
+
return message.output_channel.messages
|
|
214
|
+
|
|
215
|
+
return None
|
|
216
|
+
|
|
217
|
+
async def run_action_extract_slots(
|
|
218
|
+
self, output_channel: OutputChannel, tracker: DialogueStateTracker
|
|
219
|
+
) -> DialogueStateTracker:
|
|
220
|
+
"""Run action to extract slots and update the tracker accordingly.
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
output_channel: Output channel associated with the incoming user message.
|
|
224
|
+
tracker: A tracker representing a conversation state.
|
|
225
|
+
|
|
226
|
+
Returns:
|
|
227
|
+
the given (updated) tracker
|
|
228
|
+
"""
|
|
229
|
+
action_extract_slots = rasa.core.actions.action.action_for_name_or_text(
|
|
230
|
+
ACTION_EXTRACT_SLOTS, self.domain, self.action_endpoint
|
|
231
|
+
)
|
|
232
|
+
metadata = await self._add_flows_to_metadata()
|
|
233
|
+
|
|
234
|
+
extraction_events = await action_extract_slots.run(
|
|
235
|
+
output_channel, self.nlg, tracker, self.domain, metadata
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
await self._send_bot_messages(extraction_events, tracker, output_channel)
|
|
239
|
+
|
|
240
|
+
tracker.update_with_events(extraction_events)
|
|
241
|
+
|
|
242
|
+
structlogger.debug(
|
|
243
|
+
"processor.extract.slots",
|
|
244
|
+
action_extract_slot=ACTION_EXTRACT_SLOTS,
|
|
245
|
+
len_extraction_events=len(extraction_events),
|
|
246
|
+
rasa_events=copy.deepcopy(extraction_events),
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
return tracker
|
|
250
|
+
|
|
251
|
+
async def run_anonymization_pipeline(self, tracker: DialogueStateTracker) -> None:
|
|
252
|
+
"""Run the anonymization pipeline on the new tracker events.
|
|
253
|
+
|
|
254
|
+
Args:
|
|
255
|
+
tracker: A tracker representing a conversation state.
|
|
256
|
+
"""
|
|
257
|
+
anonymization_pipeline = plugin_manager().hook.get_anonymization_pipeline()
|
|
258
|
+
if anonymization_pipeline is None:
|
|
259
|
+
return None
|
|
260
|
+
|
|
261
|
+
old_tracker = await self.tracker_store.retrieve(tracker.sender_id)
|
|
262
|
+
new_events = rasa.shared.core.trackers.TrackerEventDiffEngine.event_difference(
|
|
263
|
+
old_tracker, tracker
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
for event in new_events:
|
|
267
|
+
body = {"sender_id": tracker.sender_id}
|
|
268
|
+
body.update(event.as_dict())
|
|
269
|
+
anonymization_pipeline.run(body)
|
|
270
|
+
|
|
271
|
+
async def predict_next_for_sender_id(
|
|
272
|
+
self, sender_id: Text
|
|
273
|
+
) -> Optional[Dict[Text, Any]]:
|
|
274
|
+
"""Predict the next action for the given sender_id.
|
|
275
|
+
|
|
276
|
+
Args:
|
|
277
|
+
sender_id: Conversation ID.
|
|
278
|
+
|
|
279
|
+
Returns:
|
|
280
|
+
The prediction for the next action. `None` if no domain or policies loaded.
|
|
281
|
+
"""
|
|
282
|
+
tracker = await self.fetch_tracker_and_update_session(sender_id)
|
|
283
|
+
result = await self.predict_next_with_tracker(tracker)
|
|
284
|
+
|
|
285
|
+
# save tracker state to continue conversation from this state
|
|
286
|
+
await self.save_tracker(tracker)
|
|
287
|
+
|
|
288
|
+
return result
|
|
289
|
+
|
|
290
|
+
async def predict_next_with_tracker(
|
|
291
|
+
self,
|
|
292
|
+
tracker: DialogueStateTracker,
|
|
293
|
+
verbosity: EventVerbosity = EventVerbosity.AFTER_RESTART,
|
|
294
|
+
) -> Optional[Dict[Text, Any]]:
|
|
295
|
+
"""Predict the next action for a given conversation state.
|
|
296
|
+
|
|
297
|
+
Args:
|
|
298
|
+
tracker: A tracker representing a conversation state.
|
|
299
|
+
verbosity: Verbosity for the returned conversation state.
|
|
300
|
+
|
|
301
|
+
Returns:
|
|
302
|
+
The prediction for the next action. `None` if no domain or policies loaded.
|
|
303
|
+
"""
|
|
304
|
+
if self.model_metadata.training_type == TrainingType.NLU:
|
|
305
|
+
rasa.shared.utils.io.raise_warning(
|
|
306
|
+
"No core model. Skipping action prediction and execution.",
|
|
307
|
+
docs=DOCS_URL_NLU_BASED_POLICIES,
|
|
308
|
+
)
|
|
309
|
+
return None
|
|
310
|
+
|
|
311
|
+
prediction = await self._predict_next_with_tracker(tracker)
|
|
312
|
+
|
|
313
|
+
scores = [
|
|
314
|
+
{"action": a, "score": p}
|
|
315
|
+
for a, p in zip(self.domain.action_names_or_texts, prediction.probabilities)
|
|
316
|
+
]
|
|
317
|
+
return {
|
|
318
|
+
"scores": scores,
|
|
319
|
+
"policy": prediction.policy_name,
|
|
320
|
+
"confidence": prediction.max_confidence,
|
|
321
|
+
"tracker": tracker.current_state(verbosity),
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
async def _update_tracker_session(
|
|
325
|
+
self,
|
|
326
|
+
tracker: DialogueStateTracker,
|
|
327
|
+
output_channel: OutputChannel,
|
|
328
|
+
metadata: Optional[Dict] = None,
|
|
329
|
+
) -> None:
|
|
330
|
+
"""Check the current session in `tracker` and update it if expired.
|
|
331
|
+
|
|
332
|
+
An 'action_session_start' is run if the latest tracker session has expired,
|
|
333
|
+
or if the tracker does not yet contain any events (only those after the last
|
|
334
|
+
restart are considered).
|
|
335
|
+
|
|
336
|
+
Args:
|
|
337
|
+
metadata: Data sent from client associated with the incoming user message.
|
|
338
|
+
tracker: Tracker to inspect.
|
|
339
|
+
output_channel: Output channel for potential utterances in a custom
|
|
340
|
+
`ActionSessionStart`.
|
|
341
|
+
"""
|
|
342
|
+
if not tracker.applied_events() or self._has_session_expired(tracker):
|
|
343
|
+
logger.debug(
|
|
344
|
+
f"Starting a new session for conversation ID '{tracker.sender_id}'."
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
action_session_start = self._get_action(ACTION_SESSION_START_NAME)
|
|
348
|
+
|
|
349
|
+
if metadata:
|
|
350
|
+
tracker.update(
|
|
351
|
+
SlotSet(SESSION_START_METADATA_SLOT, metadata), self.domain
|
|
352
|
+
)
|
|
353
|
+
|
|
354
|
+
await self._run_action(
|
|
355
|
+
action=action_session_start,
|
|
356
|
+
tracker=tracker,
|
|
357
|
+
output_channel=output_channel,
|
|
358
|
+
nlg=self.nlg,
|
|
359
|
+
prediction=PolicyPrediction.for_action_name(
|
|
360
|
+
self.domain, ACTION_SESSION_START_NAME
|
|
361
|
+
),
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
async def fetch_tracker_and_update_session(
|
|
365
|
+
self,
|
|
366
|
+
sender_id: Text,
|
|
367
|
+
output_channel: Optional[OutputChannel] = None,
|
|
368
|
+
metadata: Optional[Dict] = None,
|
|
369
|
+
) -> DialogueStateTracker:
|
|
370
|
+
"""Fetches tracker for `sender_id` and updates its conversation session.
|
|
371
|
+
|
|
372
|
+
If a new tracker is created, `action_session_start` is run.
|
|
373
|
+
|
|
374
|
+
Args:
|
|
375
|
+
metadata: Data sent from client associated with the incoming user message.
|
|
376
|
+
output_channel: Output channel associated with the incoming user message.
|
|
377
|
+
sender_id: Conversation ID for which to fetch the tracker.
|
|
378
|
+
|
|
379
|
+
Returns:
|
|
380
|
+
Tracker for `sender_id`.
|
|
381
|
+
"""
|
|
382
|
+
tracker = await self.get_tracker(sender_id)
|
|
383
|
+
|
|
384
|
+
await self._update_tracker_session(tracker, output_channel, metadata)
|
|
385
|
+
|
|
386
|
+
return tracker
|
|
387
|
+
|
|
388
|
+
async def fetch_tracker_with_initial_session(
|
|
389
|
+
self,
|
|
390
|
+
sender_id: Text,
|
|
391
|
+
output_channel: Optional[OutputChannel] = None,
|
|
392
|
+
metadata: Optional[Dict] = None,
|
|
393
|
+
) -> DialogueStateTracker:
|
|
394
|
+
"""Fetches tracker for `sender_id` and runs a session start on a new one.
|
|
395
|
+
|
|
396
|
+
Args:
|
|
397
|
+
metadata: Data sent from client associated with the incoming user message.
|
|
398
|
+
output_channel: Output channel associated with the incoming user message.
|
|
399
|
+
sender_id: Conversation ID for which to fetch the tracker.
|
|
400
|
+
|
|
401
|
+
Returns:
|
|
402
|
+
Tracker for `sender_id`.
|
|
403
|
+
"""
|
|
404
|
+
tracker = await self.get_tracker(sender_id)
|
|
405
|
+
|
|
406
|
+
# run session start only if the tracker is empty
|
|
407
|
+
if not tracker.events:
|
|
408
|
+
await self._update_tracker_session(tracker, output_channel, metadata)
|
|
409
|
+
|
|
410
|
+
return tracker
|
|
411
|
+
|
|
412
|
+
async def get_tracker(self, conversation_id: Text) -> DialogueStateTracker:
|
|
413
|
+
"""Get the tracker for a conversation.
|
|
414
|
+
|
|
415
|
+
In contrast to `fetch_tracker_and_update_session` this does not add any
|
|
416
|
+
`action_session_start` or `session_start` events at the beginning of a
|
|
417
|
+
conversation.
|
|
418
|
+
|
|
419
|
+
Args:
|
|
420
|
+
conversation_id: The ID of the conversation for which the history should be
|
|
421
|
+
retrieved.
|
|
422
|
+
|
|
423
|
+
Returns:
|
|
424
|
+
Tracker for the conversation. Creates an empty tracker in case it's a new
|
|
425
|
+
conversation.
|
|
426
|
+
"""
|
|
427
|
+
conversation_id = conversation_id or DEFAULT_SENDER_ID
|
|
428
|
+
|
|
429
|
+
tracker = await self.tracker_store.get_or_create_tracker(
|
|
430
|
+
conversation_id, append_action_listen=False
|
|
431
|
+
)
|
|
432
|
+
tracker.model_id = self.model_metadata.model_id
|
|
433
|
+
if tracker.assistant_id is None:
|
|
434
|
+
tracker.assistant_id = self.model_metadata.assistant_id
|
|
435
|
+
return tracker
|
|
436
|
+
|
|
437
|
+
async def fetch_full_tracker_with_initial_session(
|
|
438
|
+
self,
|
|
439
|
+
conversation_id: Text,
|
|
440
|
+
output_channel: Optional[OutputChannel] = None,
|
|
441
|
+
metadata: Optional[Dict] = None,
|
|
442
|
+
) -> DialogueStateTracker:
|
|
443
|
+
"""Get the full tracker for a conversation, including events after a restart.
|
|
444
|
+
|
|
445
|
+
Args:
|
|
446
|
+
conversation_id: The ID of the conversation for which the history should be
|
|
447
|
+
retrieved.
|
|
448
|
+
output_channel: Output channel associated with the incoming user message.
|
|
449
|
+
metadata: Data sent from client associated with the incoming user message.
|
|
450
|
+
|
|
451
|
+
Returns:
|
|
452
|
+
Tracker for the conversation. Creates an empty tracker with a new session
|
|
453
|
+
initialized in case it's a new conversation.
|
|
454
|
+
"""
|
|
455
|
+
conversation_id = conversation_id or DEFAULT_SENDER_ID
|
|
456
|
+
|
|
457
|
+
tracker = await self.tracker_store.get_or_create_full_tracker(
|
|
458
|
+
conversation_id, False
|
|
459
|
+
)
|
|
460
|
+
tracker.model_id = self.model_metadata.model_id
|
|
461
|
+
|
|
462
|
+
if tracker.assistant_id is None:
|
|
463
|
+
tracker.assistant_id = self.model_metadata.assistant_id
|
|
464
|
+
|
|
465
|
+
if not tracker.events:
|
|
466
|
+
await self._update_tracker_session(tracker, output_channel, metadata)
|
|
467
|
+
|
|
468
|
+
return tracker
|
|
469
|
+
|
|
470
|
+
async def get_trackers_for_all_conversation_sessions(
|
|
471
|
+
self, conversation_id: Text
|
|
472
|
+
) -> List[DialogueStateTracker]:
|
|
473
|
+
"""Fetches all trackers for a conversation.
|
|
474
|
+
|
|
475
|
+
Individual trackers are returned for each conversation session found
|
|
476
|
+
for `conversation_id`.
|
|
477
|
+
|
|
478
|
+
Args:
|
|
479
|
+
conversation_id: The ID of the conversation for which the trackers should
|
|
480
|
+
be retrieved.
|
|
481
|
+
|
|
482
|
+
Returns:
|
|
483
|
+
Trackers for the conversation.
|
|
484
|
+
"""
|
|
485
|
+
conversation_id = conversation_id or DEFAULT_SENDER_ID
|
|
486
|
+
|
|
487
|
+
tracker = await self.tracker_store.retrieve_full_tracker(conversation_id)
|
|
488
|
+
|
|
489
|
+
return rasa.shared.core.trackers.get_trackers_for_conversation_sessions(tracker)
|
|
490
|
+
|
|
491
|
+
async def log_message(
|
|
492
|
+
self, message: UserMessage, should_save_tracker: bool = True
|
|
493
|
+
) -> DialogueStateTracker:
|
|
494
|
+
"""Log `message` on tracker belonging to the message's conversation_id.
|
|
495
|
+
|
|
496
|
+
Optionally save the tracker if `should_save_tracker` is `True`. Tracker saving
|
|
497
|
+
can be skipped if the tracker returned by this method is used for further
|
|
498
|
+
processing and saved at a later stage.
|
|
499
|
+
"""
|
|
500
|
+
tracker = await self.fetch_tracker_and_update_session(
|
|
501
|
+
message.sender_id, message.output_channel, message.metadata
|
|
502
|
+
)
|
|
503
|
+
|
|
504
|
+
await self._handle_message_with_tracker(message, tracker)
|
|
505
|
+
|
|
506
|
+
if should_save_tracker:
|
|
507
|
+
await self.save_tracker(tracker)
|
|
508
|
+
|
|
509
|
+
return tracker
|
|
510
|
+
|
|
511
|
+
async def execute_action(
|
|
512
|
+
self,
|
|
513
|
+
sender_id: Text,
|
|
514
|
+
action_name: Text,
|
|
515
|
+
output_channel: OutputChannel,
|
|
516
|
+
nlg: NaturalLanguageGenerator,
|
|
517
|
+
prediction: PolicyPrediction,
|
|
518
|
+
) -> Optional[DialogueStateTracker]:
|
|
519
|
+
"""Execute an action for a conversation.
|
|
520
|
+
|
|
521
|
+
Note that this might lead to unexpected bot behavior. Rather use an intent
|
|
522
|
+
to execute certain behavior within a conversation (e.g. by using
|
|
523
|
+
`trigger_external_user_uttered`).
|
|
524
|
+
|
|
525
|
+
Args:
|
|
526
|
+
sender_id: The ID of the conversation.
|
|
527
|
+
action_name: The name of the action which should be executed.
|
|
528
|
+
output_channel: The output channel which should be used for bot responses.
|
|
529
|
+
nlg: The response generator.
|
|
530
|
+
prediction: The prediction for the action.
|
|
531
|
+
|
|
532
|
+
Returns:
|
|
533
|
+
The new conversation state. Note that the new state is also persisted.
|
|
534
|
+
"""
|
|
535
|
+
# we have a Tracker instance for each user
|
|
536
|
+
# which maintains conversation state
|
|
537
|
+
tracker = await self.fetch_tracker_and_update_session(sender_id, output_channel)
|
|
538
|
+
|
|
539
|
+
action = self._get_action(action_name)
|
|
540
|
+
await self._run_action(action, tracker, output_channel, nlg, prediction)
|
|
541
|
+
|
|
542
|
+
# save tracker state to continue conversation from this state
|
|
543
|
+
await self.save_tracker(tracker)
|
|
544
|
+
|
|
545
|
+
return tracker
|
|
546
|
+
|
|
547
|
+
async def predict_next_with_tracker_if_should(
|
|
548
|
+
self, tracker: DialogueStateTracker
|
|
549
|
+
) -> Tuple[rasa.core.actions.action.Action, PolicyPrediction]:
|
|
550
|
+
"""Predicts the next action the bot should take after seeing x.
|
|
551
|
+
|
|
552
|
+
This should be overwritten by more advanced policies to use
|
|
553
|
+
ML to predict the action.
|
|
554
|
+
|
|
555
|
+
Returns:
|
|
556
|
+
The index of the next action and prediction of the policy.
|
|
557
|
+
|
|
558
|
+
Raises:
|
|
559
|
+
ActionLimitReached if the limit of actions to predict has been reached.
|
|
560
|
+
"""
|
|
561
|
+
should_predict_another_action = self.should_predict_another_action(
|
|
562
|
+
tracker.latest_action_name
|
|
563
|
+
)
|
|
564
|
+
|
|
565
|
+
if self.is_action_limit_reached(tracker, should_predict_another_action):
|
|
566
|
+
raise ActionLimitReached(
|
|
567
|
+
"The limit of actions to predict has been reached."
|
|
568
|
+
)
|
|
569
|
+
|
|
570
|
+
prediction = await self._predict_next_with_tracker(tracker)
|
|
571
|
+
|
|
572
|
+
action = rasa.core.actions.action.action_for_index(
|
|
573
|
+
prediction.max_confidence_index, self.domain, self.action_endpoint
|
|
574
|
+
)
|
|
575
|
+
|
|
576
|
+
logger.debug(
|
|
577
|
+
f"Predicted next action '{action.name()}' with confidence "
|
|
578
|
+
f"{prediction.max_confidence:.2f}."
|
|
579
|
+
)
|
|
580
|
+
|
|
581
|
+
return action, prediction
|
|
582
|
+
|
|
583
|
+
@staticmethod
|
|
584
|
+
def _is_reminder(e: Event, name: Text) -> bool:
|
|
585
|
+
return isinstance(e, ReminderScheduled) and e.name == name
|
|
586
|
+
|
|
587
|
+
@staticmethod
|
|
588
|
+
def _is_reminder_still_valid(
|
|
589
|
+
tracker: DialogueStateTracker, reminder_event: ReminderScheduled
|
|
590
|
+
) -> bool:
|
|
591
|
+
"""Check if the conversation has been restarted after reminder."""
|
|
592
|
+
for e in reversed(tracker.applied_events()):
|
|
593
|
+
if MessageProcessor._is_reminder(e, reminder_event.name):
|
|
594
|
+
return True
|
|
595
|
+
return False # not found in applied events --> has been restarted
|
|
596
|
+
|
|
597
|
+
@staticmethod
|
|
598
|
+
def _has_message_after_reminder(
|
|
599
|
+
tracker: DialogueStateTracker, reminder_event: ReminderScheduled
|
|
600
|
+
) -> bool:
|
|
601
|
+
"""Check if the user sent a message after the reminder."""
|
|
602
|
+
for e in reversed(tracker.events):
|
|
603
|
+
if MessageProcessor._is_reminder(e, reminder_event.name):
|
|
604
|
+
return False
|
|
605
|
+
|
|
606
|
+
if isinstance(e, UserUttered) and e.text:
|
|
607
|
+
return True
|
|
608
|
+
|
|
609
|
+
return True # tracker has probably been restarted
|
|
610
|
+
|
|
611
|
+
async def handle_reminder(
|
|
612
|
+
self,
|
|
613
|
+
reminder_event: ReminderScheduled,
|
|
614
|
+
sender_id: Text,
|
|
615
|
+
output_channel: OutputChannel,
|
|
616
|
+
) -> None:
|
|
617
|
+
"""Handle a reminder that is triggered asynchronously."""
|
|
618
|
+
async with self.lock_store.lock(sender_id):
|
|
619
|
+
tracker = await self.fetch_tracker_and_update_session(
|
|
620
|
+
sender_id, output_channel
|
|
621
|
+
)
|
|
622
|
+
|
|
623
|
+
if (
|
|
624
|
+
reminder_event.kill_on_user_message
|
|
625
|
+
and self._has_message_after_reminder(tracker, reminder_event)
|
|
626
|
+
or not self._is_reminder_still_valid(tracker, reminder_event)
|
|
627
|
+
):
|
|
628
|
+
logger.debug(
|
|
629
|
+
f"Canceled reminder because it is outdated ({reminder_event})."
|
|
630
|
+
)
|
|
631
|
+
else:
|
|
632
|
+
intent = reminder_event.intent
|
|
633
|
+
entities: Union[List[Dict], Dict] = reminder_event.entities or {}
|
|
634
|
+
await self.trigger_external_user_uttered(
|
|
635
|
+
intent, entities, tracker, output_channel
|
|
636
|
+
)
|
|
637
|
+
|
|
638
|
+
async def trigger_external_user_uttered(
|
|
639
|
+
self,
|
|
640
|
+
intent_name: Text,
|
|
641
|
+
entities: Optional[Union[List[Dict[Text, Any]], Dict[Text, Text]]],
|
|
642
|
+
tracker: DialogueStateTracker,
|
|
643
|
+
output_channel: OutputChannel,
|
|
644
|
+
) -> None:
|
|
645
|
+
"""Triggers an external message.
|
|
646
|
+
|
|
647
|
+
Triggers an external message (like a user message, but invisible;
|
|
648
|
+
used, e.g., by a reminder or the trigger_intent endpoint).
|
|
649
|
+
|
|
650
|
+
Args:
|
|
651
|
+
intent_name: Name of the intent to be triggered.
|
|
652
|
+
entities: Entities to be passed on.
|
|
653
|
+
tracker: The tracker to which the event should be added.
|
|
654
|
+
output_channel: The output channel.
|
|
655
|
+
"""
|
|
656
|
+
if isinstance(entities, list):
|
|
657
|
+
entity_list = entities
|
|
658
|
+
elif isinstance(entities, dict):
|
|
659
|
+
# Allow for a short-hand notation {"ent1": "val1", "ent2": "val2", ...}.
|
|
660
|
+
# Useful if properties like 'start', 'end', or 'extractor' are not given,
|
|
661
|
+
# e.g. for external events.
|
|
662
|
+
entity_list = [
|
|
663
|
+
{"entity": ent, "value": val} for ent, val in entities.items()
|
|
664
|
+
]
|
|
665
|
+
elif not entities:
|
|
666
|
+
entity_list = []
|
|
667
|
+
else:
|
|
668
|
+
rasa.shared.utils.io.raise_warning(
|
|
669
|
+
f"Invalid entity specification: {entities}. Assuming no entities."
|
|
670
|
+
)
|
|
671
|
+
entity_list = []
|
|
672
|
+
|
|
673
|
+
# Set the new event's input channel to the latest input channel, so
|
|
674
|
+
# that we don't lose this property.
|
|
675
|
+
input_channel = tracker.get_latest_input_channel()
|
|
676
|
+
|
|
677
|
+
tracker.update(
|
|
678
|
+
UserUttered.create_external(intent_name, entity_list, input_channel),
|
|
679
|
+
self.domain,
|
|
680
|
+
)
|
|
681
|
+
|
|
682
|
+
tracker = await self.run_action_extract_slots(output_channel, tracker)
|
|
683
|
+
|
|
684
|
+
await self._run_prediction_loop(output_channel, tracker)
|
|
685
|
+
# save tracker state to continue conversation from this state
|
|
686
|
+
await self.save_tracker(tracker)
|
|
687
|
+
|
|
688
|
+
@staticmethod
|
|
689
|
+
def _log_slots(tracker: DialogueStateTracker) -> None:
|
|
690
|
+
# Log currently set slots
|
|
691
|
+
slots = {s.name: s.value for s in tracker.slots.values() if s.value is not None}
|
|
692
|
+
|
|
693
|
+
structlogger.debug("processor.slots.log", slots=slots)
|
|
694
|
+
|
|
695
|
+
def _check_for_unseen_features(self, parse_data: Dict[Text, Any]) -> None:
|
|
696
|
+
"""Warns the user if the NLU parse data contains unrecognized features.
|
|
697
|
+
|
|
698
|
+
Checks intents and entities picked up by the NLU parsing
|
|
699
|
+
against the domain and warns the user of those that don't match.
|
|
700
|
+
Also considers a list of default intents that are valid but don't
|
|
701
|
+
need to be listed in the domain.
|
|
702
|
+
|
|
703
|
+
Args:
|
|
704
|
+
parse_data: Message parse data to check against the domain.
|
|
705
|
+
"""
|
|
706
|
+
if not self.domain or self.domain.is_empty():
|
|
707
|
+
return
|
|
708
|
+
|
|
709
|
+
intent = parse_data["intent"][INTENT_NAME_KEY]
|
|
710
|
+
if intent and intent not in self.domain.intents:
|
|
711
|
+
rasa.shared.utils.io.raise_warning(
|
|
712
|
+
f"Parsed an intent '{intent}' "
|
|
713
|
+
f"which is not defined in the domain. "
|
|
714
|
+
f"Please make sure all intents are listed in the domain.",
|
|
715
|
+
docs=DOCS_URL_DOMAINS,
|
|
716
|
+
)
|
|
717
|
+
|
|
718
|
+
entities = parse_data["entities"] or []
|
|
719
|
+
for element in entities:
|
|
720
|
+
entity = element["entity"]
|
|
721
|
+
if entity and entity not in self.domain.entities:
|
|
722
|
+
rasa.shared.utils.io.raise_warning(
|
|
723
|
+
f"Parsed an entity '{entity}' "
|
|
724
|
+
f"which is not defined in the domain. "
|
|
725
|
+
f"Please make sure all entities are listed in the domain.",
|
|
726
|
+
docs=DOCS_URL_DOMAINS,
|
|
727
|
+
)
|
|
728
|
+
|
|
729
|
+
def _get_action(
|
|
730
|
+
self, action_name: Text
|
|
731
|
+
) -> Optional[rasa.core.actions.action.Action]:
|
|
732
|
+
return rasa.core.actions.action.action_for_name_or_text(
|
|
733
|
+
action_name, self.domain, self.action_endpoint
|
|
734
|
+
)
|
|
735
|
+
|
|
736
|
+
async def parse_message(
|
|
737
|
+
self,
|
|
738
|
+
message: UserMessage,
|
|
739
|
+
tracker: Optional[DialogueStateTracker] = None,
|
|
740
|
+
only_output_properties: bool = True,
|
|
741
|
+
) -> Dict[Text, Any]:
|
|
742
|
+
"""Interprets the passed message.
|
|
743
|
+
|
|
744
|
+
Args:
|
|
745
|
+
message: Message to handle.
|
|
746
|
+
tracker: Tracker to use.
|
|
747
|
+
only_output_properties: If `True`, restrict the output to
|
|
748
|
+
Message.only_output_properties.
|
|
749
|
+
|
|
750
|
+
Returns:
|
|
751
|
+
Parsed data extracted from the message.
|
|
752
|
+
"""
|
|
753
|
+
if self.http_interpreter:
|
|
754
|
+
parse_data = await self.http_interpreter.parse(message)
|
|
755
|
+
else:
|
|
756
|
+
regex_reader = create_regex_pattern_reader(message, self.domain)
|
|
757
|
+
|
|
758
|
+
processed_message = Message({TEXT: message.text})
|
|
759
|
+
if regex_reader:
|
|
760
|
+
processed_message = regex_reader.unpack_regex_message(
|
|
761
|
+
message=processed_message, domain=self.domain
|
|
762
|
+
)
|
|
763
|
+
|
|
764
|
+
# Invalid use of slash syntax, sanitize the message before passing
|
|
765
|
+
# it to the graph
|
|
766
|
+
if (
|
|
767
|
+
processed_message.starts_with_slash_syntax()
|
|
768
|
+
and not processed_message.has_intent()
|
|
769
|
+
and not processed_message.has_commands()
|
|
770
|
+
):
|
|
771
|
+
message = self._sanitize_message(message)
|
|
772
|
+
|
|
773
|
+
# Intent or commands are not explicitly present. Pass message to graph.
|
|
774
|
+
if not (processed_message.has_intent() or processed_message.has_commands()):
|
|
775
|
+
parse_data = await self._parse_message_with_graph(
|
|
776
|
+
message, tracker, only_output_properties
|
|
777
|
+
)
|
|
778
|
+
|
|
779
|
+
# Intents or commands are presents. Bypasses the standard parsing
|
|
780
|
+
# pipeline.
|
|
781
|
+
else:
|
|
782
|
+
parse_data = await self._parse_message_with_commands_and_intents(
|
|
783
|
+
processed_message, tracker, only_output_properties
|
|
784
|
+
)
|
|
785
|
+
|
|
786
|
+
self._update_full_retrieval_intent(parse_data)
|
|
787
|
+
structlogger.debug(
|
|
788
|
+
"processor.message.parse",
|
|
789
|
+
parse_data_text=copy.deepcopy(parse_data["text"]),
|
|
790
|
+
parse_data_intent=parse_data["intent"],
|
|
791
|
+
parse_data_entities=copy.deepcopy(parse_data["entities"]),
|
|
792
|
+
)
|
|
793
|
+
|
|
794
|
+
self._check_for_unseen_features(parse_data)
|
|
795
|
+
# resetting timeouts variables whenever something that is not a timeout occurs
|
|
796
|
+
if (
|
|
797
|
+
parse_data.get(INTENT, {}).get(INTENT_NAME_KEY)
|
|
798
|
+
!= USER_INTENT_SILENCE_TIMEOUT
|
|
799
|
+
and tracker
|
|
800
|
+
):
|
|
801
|
+
if (
|
|
802
|
+
SLOT_CONSECUTIVE_SILENCE_TIMEOUTS in tracker.slots
|
|
803
|
+
and tracker.slots[SLOT_CONSECUTIVE_SILENCE_TIMEOUTS].value != 0.0
|
|
804
|
+
):
|
|
805
|
+
tracker.update(SlotSet(SLOT_CONSECUTIVE_SILENCE_TIMEOUTS, 0.0))
|
|
806
|
+
if (
|
|
807
|
+
SLOT_SILENCE_TIMEOUT in tracker.slots
|
|
808
|
+
and tracker.slots[SLOT_SILENCE_TIMEOUT].value
|
|
809
|
+
!= tracker.slots[SLOT_SILENCE_TIMEOUT].initial_value
|
|
810
|
+
):
|
|
811
|
+
tracker.update(
|
|
812
|
+
SlotSet(
|
|
813
|
+
SLOT_SILENCE_TIMEOUT,
|
|
814
|
+
tracker.slots[SLOT_SILENCE_TIMEOUT].initial_value,
|
|
815
|
+
)
|
|
816
|
+
)
|
|
817
|
+
|
|
818
|
+
return parse_data
|
|
819
|
+
|
|
820
|
+
def _sanitize_message(self, message: UserMessage) -> UserMessage:
|
|
821
|
+
"""Sanitize user message by removing prepended slashes before the
|
|
822
|
+
actual content.
|
|
823
|
+
"""
|
|
824
|
+
# Regex pattern to match leading slashes and any whitespace before
|
|
825
|
+
# actual content
|
|
826
|
+
pattern = r"^[/\s]+"
|
|
827
|
+
# Remove the matched pattern from the beginning of the message
|
|
828
|
+
message.text = re.sub(pattern, "", message.text).strip()
|
|
829
|
+
return message
|
|
830
|
+
|
|
831
|
+
async def _parse_message_with_commands_and_intents(
|
|
832
|
+
self,
|
|
833
|
+
message: Message,
|
|
834
|
+
tracker: Optional[DialogueStateTracker] = None,
|
|
835
|
+
only_output_properties: bool = True,
|
|
836
|
+
) -> Dict[Text, Any]:
|
|
837
|
+
"""Parses the message to handle commands or intent trigger."""
|
|
838
|
+
parse_data: Dict[Text, Any] = {
|
|
839
|
+
TEXT: "",
|
|
840
|
+
INTENT: {INTENT_NAME_KEY: None, PREDICTED_CONFIDENCE_KEY: 0.0},
|
|
841
|
+
ENTITIES: [],
|
|
842
|
+
}
|
|
843
|
+
parse_data.update(
|
|
844
|
+
message.as_dict(only_output_properties=only_output_properties)
|
|
845
|
+
)
|
|
846
|
+
|
|
847
|
+
commands = parse_data.get(COMMANDS, [])
|
|
848
|
+
|
|
849
|
+
# add commands from intent payloads
|
|
850
|
+
if tracker and not commands:
|
|
851
|
+
nlu_adapted_commands = await self._nlu_to_commands(parse_data, tracker)
|
|
852
|
+
commands += nlu_adapted_commands
|
|
853
|
+
|
|
854
|
+
if (
|
|
855
|
+
tracker.has_coexistence_routing_slot
|
|
856
|
+
and tracker.get_slot(ROUTE_TO_CALM_SLOT) is None
|
|
857
|
+
):
|
|
858
|
+
# if we are currently not routing to either CALM or dm1
|
|
859
|
+
# we make a sticky routing to CALM if there are any commands
|
|
860
|
+
# from the trigger intent parsing
|
|
861
|
+
# or a sticky routing to dm1 if there are no commands
|
|
862
|
+
commands += [
|
|
863
|
+
SetSlotCommand(
|
|
864
|
+
ROUTE_TO_CALM_SLOT, len(nlu_adapted_commands) > 0
|
|
865
|
+
).as_dict()
|
|
866
|
+
]
|
|
867
|
+
|
|
868
|
+
parse_data[COMMANDS] = commands
|
|
869
|
+
return parse_data
|
|
870
|
+
|
|
871
|
+
def _update_full_retrieval_intent(self, parse_data: Dict[Text, Any]) -> None:
|
|
872
|
+
"""Update the parse data with the full retrieval intent.
|
|
873
|
+
|
|
874
|
+
Args:
|
|
875
|
+
parse_data: Message parse data to update.
|
|
876
|
+
"""
|
|
877
|
+
intent_name = parse_data.get(INTENT, {}).get(INTENT_NAME_KEY)
|
|
878
|
+
response_selector = parse_data.get(RESPONSE_SELECTOR, {})
|
|
879
|
+
all_retrieval_intents = response_selector.get("all_retrieval_intents", [])
|
|
880
|
+
if intent_name and intent_name in all_retrieval_intents:
|
|
881
|
+
retrieval_intent = (
|
|
882
|
+
response_selector.get(intent_name, {})
|
|
883
|
+
.get(RESPONSE, {})
|
|
884
|
+
.get(INTENT_RESPONSE_KEY)
|
|
885
|
+
)
|
|
886
|
+
parse_data[INTENT][FULL_RETRIEVAL_INTENT_NAME_KEY] = retrieval_intent
|
|
887
|
+
|
|
888
|
+
async def _nlu_to_commands(
|
|
889
|
+
self, parse_data: Dict[str, Any], tracker: DialogueStateTracker
|
|
890
|
+
) -> List[Dict[str, Any]]:
|
|
891
|
+
"""Converts the NLU parse data to commands using the adaptor.
|
|
892
|
+
|
|
893
|
+
This is used if we receive intents/entities directly using `/intent{...}`
|
|
894
|
+
syntax. In this case, the nlu graph is not run. Therefore, we need to
|
|
895
|
+
convert the parse data to commands outside the graph.
|
|
896
|
+
"""
|
|
897
|
+
from rasa.dialogue_understanding.generator.nlu_command_adapter import (
|
|
898
|
+
NLUCommandAdapter,
|
|
899
|
+
)
|
|
900
|
+
|
|
901
|
+
commands = NLUCommandAdapter.convert_nlu_to_commands(
|
|
902
|
+
Message(parse_data), tracker, await self.get_flows(), self.domain
|
|
903
|
+
)
|
|
904
|
+
|
|
905
|
+
# if there are no converted commands and parsed data contains invalid intent
|
|
906
|
+
# add CannotHandleCommand as fallback
|
|
907
|
+
if len(commands) == 0 and self._contains_undefined_intent(Message(parse_data)):
|
|
908
|
+
structlogger.warning(
|
|
909
|
+
"processor.message.nlu_to_commands.invalid_intent",
|
|
910
|
+
event_info=(
|
|
911
|
+
f"No NLU commands converted and parsed data contains"
|
|
912
|
+
f"invalid intent: {parse_data[INTENT]['name']}. "
|
|
913
|
+
f"Returning CannotHandleCommand() as a fallback."
|
|
914
|
+
),
|
|
915
|
+
invalid_intent=parse_data[INTENT]["name"],
|
|
916
|
+
)
|
|
917
|
+
commands.append(
|
|
918
|
+
CannotHandleCommand(RASA_PATTERN_CANNOT_HANDLE_INVALID_INTENT)
|
|
919
|
+
)
|
|
920
|
+
|
|
921
|
+
return [command.as_dict() for command in commands]
|
|
922
|
+
|
|
923
|
+
def _contains_undefined_intent(self, message: Message) -> bool:
|
|
924
|
+
"""Checks if the message contains an intent that is undefined
|
|
925
|
+
in the domain.
|
|
926
|
+
"""
|
|
927
|
+
intent_name = message.get(INTENT, {}).get("name")
|
|
928
|
+
return intent_name is not None and intent_name not in self.domain.intents
|
|
929
|
+
|
|
930
|
+
async def _parse_message_with_graph(
|
|
931
|
+
self,
|
|
932
|
+
message: UserMessage,
|
|
933
|
+
tracker: Optional[DialogueStateTracker] = None,
|
|
934
|
+
only_output_properties: bool = True,
|
|
935
|
+
) -> Dict[Text, Any]:
|
|
936
|
+
"""Interprets the passed message.
|
|
937
|
+
|
|
938
|
+
Arguments:
|
|
939
|
+
message: Message to handle
|
|
940
|
+
tracker: Tracker to use
|
|
941
|
+
only_output_properties: If `True`, restrict the output to
|
|
942
|
+
Message.only_output_properties.
|
|
943
|
+
|
|
944
|
+
Returns:
|
|
945
|
+
Parsed data extracted from the message.
|
|
946
|
+
"""
|
|
947
|
+
results = await self.graph_runner.run(
|
|
948
|
+
inputs={PLACEHOLDER_MESSAGE: [message], PLACEHOLDER_TRACKER: tracker},
|
|
949
|
+
targets=[self.model_metadata.nlu_target],
|
|
950
|
+
)
|
|
951
|
+
parsed_messages = results[self.model_metadata.nlu_target]
|
|
952
|
+
parsed_message = parsed_messages[0]
|
|
953
|
+
parse_data = {
|
|
954
|
+
TEXT: "",
|
|
955
|
+
INTENT: {INTENT_NAME_KEY: None, PREDICTED_CONFIDENCE_KEY: 0.0},
|
|
956
|
+
ENTITIES: [],
|
|
957
|
+
COMMANDS: [],
|
|
958
|
+
}
|
|
959
|
+
parse_data.update(
|
|
960
|
+
parsed_message.as_dict(only_output_properties=only_output_properties)
|
|
961
|
+
)
|
|
962
|
+
return parse_data
|
|
963
|
+
|
|
964
|
+
async def _handle_message_with_tracker(
|
|
965
|
+
self, message: UserMessage, tracker: DialogueStateTracker
|
|
966
|
+
) -> None:
|
|
967
|
+
if message.parse_data:
|
|
968
|
+
parse_data = message.parse_data
|
|
969
|
+
else:
|
|
970
|
+
parse_data = await self.parse_message(message, tracker)
|
|
971
|
+
|
|
972
|
+
# don't ever directly mutate the tracker
|
|
973
|
+
# - instead pass its events to log
|
|
974
|
+
tracker.update(
|
|
975
|
+
UserUttered(
|
|
976
|
+
message.text,
|
|
977
|
+
parse_data["intent"],
|
|
978
|
+
parse_data["entities"],
|
|
979
|
+
parse_data,
|
|
980
|
+
input_channel=message.input_channel,
|
|
981
|
+
message_id=message.message_id,
|
|
982
|
+
metadata=message.metadata,
|
|
983
|
+
),
|
|
984
|
+
self.domain,
|
|
985
|
+
)
|
|
986
|
+
|
|
987
|
+
if parse_data["entities"]:
|
|
988
|
+
self._log_slots(tracker)
|
|
989
|
+
|
|
990
|
+
logger.debug(
|
|
991
|
+
f"Logged UserUtterance - tracker now has {len(tracker.events)} events."
|
|
992
|
+
)
|
|
993
|
+
|
|
994
|
+
@staticmethod
|
|
995
|
+
def _should_handle_message(tracker: DialogueStateTracker) -> bool:
|
|
996
|
+
return not tracker.is_paused() or (
|
|
997
|
+
tracker.latest_message is not None
|
|
998
|
+
and tracker.latest_message.intent.get(INTENT_NAME_KEY)
|
|
999
|
+
== USER_INTENT_RESTART
|
|
1000
|
+
)
|
|
1001
|
+
|
|
1002
|
+
def _tracker_state_specific_action_limit(
|
|
1003
|
+
self, tracker: DialogueStateTracker
|
|
1004
|
+
) -> int:
|
|
1005
|
+
"""Select the action limit based on the tracker state.
|
|
1006
|
+
|
|
1007
|
+
This function determines the maximum number of predictions that should be
|
|
1008
|
+
made during a dialogue conversation. Typically, the number of predictions
|
|
1009
|
+
is limited to the number of actions executed so far in the conversation.
|
|
1010
|
+
However, in certain states (e.g., when the user is correcting the
|
|
1011
|
+
conversation flow), more predictions may be allowed as the system traverses
|
|
1012
|
+
through a long dialogue flow.
|
|
1013
|
+
|
|
1014
|
+
Additionally, if the `ROUTE_TO_CALM_SLOT` is present in the tracker slots,
|
|
1015
|
+
the action limit is adjusted to a separate limit for CALM-based flows.
|
|
1016
|
+
|
|
1017
|
+
Args:
|
|
1018
|
+
tracker: instance of DialogueStateTracker.
|
|
1019
|
+
|
|
1020
|
+
Returns:
|
|
1021
|
+
The maximum number of predictions to make.
|
|
1022
|
+
"""
|
|
1023
|
+
# Check if it is a CALM assistant and if so, that the `ROUTE_TO_CALM_SLOT`
|
|
1024
|
+
# is either not present or set to `True`.
|
|
1025
|
+
# If it does, use the specific prediction limit for CALM assistants.
|
|
1026
|
+
# Otherwise, use the default prediction limit.
|
|
1027
|
+
if self.is_calm_assistant and (
|
|
1028
|
+
not tracker.has_coexistence_routing_slot
|
|
1029
|
+
or tracker.get_slot(ROUTE_TO_CALM_SLOT)
|
|
1030
|
+
):
|
|
1031
|
+
max_number_of_predictions = self.max_number_of_predictions_calm
|
|
1032
|
+
else:
|
|
1033
|
+
max_number_of_predictions = self.max_number_of_predictions
|
|
1034
|
+
|
|
1035
|
+
reversed_events = list(tracker.events)[::-1]
|
|
1036
|
+
is_conversation_in_flow_correction = False
|
|
1037
|
+
for e in reversed_events:
|
|
1038
|
+
if isinstance(e, ActionExecuted):
|
|
1039
|
+
if e.action_name in (ACTION_LISTEN_NAME, ACTION_SESSION_START_NAME):
|
|
1040
|
+
break
|
|
1041
|
+
elif e.action_name == ACTION_CORRECT_FLOW_SLOT:
|
|
1042
|
+
is_conversation_in_flow_correction = True
|
|
1043
|
+
break
|
|
1044
|
+
|
|
1045
|
+
if is_conversation_in_flow_correction:
|
|
1046
|
+
# allow for more predictions to be made as we might be traversing through
|
|
1047
|
+
# a long flow. We multiply the number of predictions by 10 to allow for
|
|
1048
|
+
# more predictions to be made - the factor is a best guess.
|
|
1049
|
+
return max_number_of_predictions * 5
|
|
1050
|
+
|
|
1051
|
+
# Return the default
|
|
1052
|
+
return max_number_of_predictions
|
|
1053
|
+
|
|
1054
|
+
def is_action_limit_reached(
|
|
1055
|
+
self, tracker: DialogueStateTracker, should_predict_another_action: bool
|
|
1056
|
+
) -> bool:
|
|
1057
|
+
"""Check whether the maximum number of predictions has been met.
|
|
1058
|
+
|
|
1059
|
+
Args:
|
|
1060
|
+
tracker: instance of DialogueStateTracker.
|
|
1061
|
+
should_predict_another_action: Whether the last executed action allows
|
|
1062
|
+
for more actions to be predicted or not.
|
|
1063
|
+
|
|
1064
|
+
Returns:
|
|
1065
|
+
`True` if the limit of actions to predict has been reached.
|
|
1066
|
+
"""
|
|
1067
|
+
reversed_events = list(tracker.events)[::-1]
|
|
1068
|
+
num_predicted_actions = 0
|
|
1069
|
+
state_specific_action_limit = self._tracker_state_specific_action_limit(tracker)
|
|
1070
|
+
|
|
1071
|
+
for e in reversed_events:
|
|
1072
|
+
if isinstance(e, ActionExecuted):
|
|
1073
|
+
if e.action_name in (ACTION_LISTEN_NAME, ACTION_SESSION_START_NAME):
|
|
1074
|
+
break
|
|
1075
|
+
num_predicted_actions += 1
|
|
1076
|
+
|
|
1077
|
+
return (
|
|
1078
|
+
num_predicted_actions >= state_specific_action_limit
|
|
1079
|
+
and should_predict_another_action
|
|
1080
|
+
)
|
|
1081
|
+
|
|
1082
|
+
async def _run_prediction_loop(
|
|
1083
|
+
self, output_channel: OutputChannel, tracker: DialogueStateTracker
|
|
1084
|
+
) -> None:
|
|
1085
|
+
# keep taking actions decided by the policy until it chooses to 'listen'
|
|
1086
|
+
should_predict_another_action = True
|
|
1087
|
+
|
|
1088
|
+
tracker = await self.run_command_processor(tracker)
|
|
1089
|
+
|
|
1090
|
+
# action loop. predicts actions until we hit action listen
|
|
1091
|
+
while should_predict_another_action and self._should_handle_message(tracker):
|
|
1092
|
+
# this actually just calls the policy's method by the same name
|
|
1093
|
+
try:
|
|
1094
|
+
action, prediction = await self.predict_next_with_tracker_if_should(
|
|
1095
|
+
tracker
|
|
1096
|
+
)
|
|
1097
|
+
except ActionLimitReached:
|
|
1098
|
+
logger.warning(
|
|
1099
|
+
"Circuit breaker tripped. Stopped predicting "
|
|
1100
|
+
f"more actions for sender '{tracker.sender_id}'."
|
|
1101
|
+
)
|
|
1102
|
+
if self.on_circuit_break:
|
|
1103
|
+
# call a registered callback
|
|
1104
|
+
self.on_circuit_break(tracker, output_channel, self.nlg)
|
|
1105
|
+
break
|
|
1106
|
+
|
|
1107
|
+
if prediction.is_end_to_end_prediction:
|
|
1108
|
+
logger.debug(
|
|
1109
|
+
f"An end-to-end prediction was made which has triggered the 2nd "
|
|
1110
|
+
f"execution of the default action '{ACTION_EXTRACT_SLOTS}'."
|
|
1111
|
+
)
|
|
1112
|
+
tracker = await self.run_action_extract_slots(output_channel, tracker)
|
|
1113
|
+
|
|
1114
|
+
should_predict_another_action = await self._run_action(
|
|
1115
|
+
action, tracker, output_channel, self.nlg, prediction
|
|
1116
|
+
)
|
|
1117
|
+
|
|
1118
|
+
@staticmethod
|
|
1119
|
+
def should_predict_another_action(action_name: Text) -> bool:
|
|
1120
|
+
"""Determine whether the processor should predict another action.
|
|
1121
|
+
|
|
1122
|
+
Args:
|
|
1123
|
+
action_name: Name of the latest executed action.
|
|
1124
|
+
|
|
1125
|
+
Returns:
|
|
1126
|
+
`False` if `action_name` is `ACTION_LISTEN_NAME` or
|
|
1127
|
+
`ACTION_SESSION_START_NAME`, otherwise `True`.
|
|
1128
|
+
"""
|
|
1129
|
+
return action_name not in (ACTION_LISTEN_NAME, ACTION_SESSION_START_NAME)
|
|
1130
|
+
|
|
1131
|
+
async def execute_side_effects(
|
|
1132
|
+
self,
|
|
1133
|
+
events: List[Event],
|
|
1134
|
+
tracker: DialogueStateTracker,
|
|
1135
|
+
output_channel: Optional[OutputChannel],
|
|
1136
|
+
) -> None:
|
|
1137
|
+
"""Attach tracker, send bot messages, schedule and cancel reminders."""
|
|
1138
|
+
if output_channel:
|
|
1139
|
+
output_channel.attach_tracker_state(tracker)
|
|
1140
|
+
await self._send_bot_messages(events, tracker, output_channel)
|
|
1141
|
+
await self._schedule_reminders(events, tracker, output_channel)
|
|
1142
|
+
await self._cancel_reminders(events, tracker)
|
|
1143
|
+
|
|
1144
|
+
@staticmethod
|
|
1145
|
+
async def _send_bot_messages(
|
|
1146
|
+
events: List[Event],
|
|
1147
|
+
tracker: DialogueStateTracker,
|
|
1148
|
+
output_channel: OutputChannel,
|
|
1149
|
+
) -> None:
|
|
1150
|
+
"""Send all the bot messages that are logged in the events array."""
|
|
1151
|
+
for e in events:
|
|
1152
|
+
if not isinstance(e, BotUttered):
|
|
1153
|
+
continue
|
|
1154
|
+
|
|
1155
|
+
await output_channel.send_response(tracker.sender_id, e.message())
|
|
1156
|
+
|
|
1157
|
+
async def _schedule_reminders(
|
|
1158
|
+
self,
|
|
1159
|
+
events: List[Event],
|
|
1160
|
+
tracker: DialogueStateTracker,
|
|
1161
|
+
output_channel: OutputChannel,
|
|
1162
|
+
) -> None:
|
|
1163
|
+
"""Uses the scheduler to time a job to trigger the passed reminder.
|
|
1164
|
+
|
|
1165
|
+
Reminders with the same `id` property will overwrite one another
|
|
1166
|
+
(i.e. only one of them will eventually run).
|
|
1167
|
+
"""
|
|
1168
|
+
for e in events:
|
|
1169
|
+
if not isinstance(e, ReminderScheduled):
|
|
1170
|
+
continue
|
|
1171
|
+
|
|
1172
|
+
(await jobs.scheduler()).add_job(
|
|
1173
|
+
self.handle_reminder,
|
|
1174
|
+
"date",
|
|
1175
|
+
run_date=e.trigger_date_time,
|
|
1176
|
+
args=[e, tracker.sender_id, output_channel],
|
|
1177
|
+
id=e.name,
|
|
1178
|
+
replace_existing=True,
|
|
1179
|
+
name=e.scheduled_job_name(tracker.sender_id),
|
|
1180
|
+
)
|
|
1181
|
+
|
|
1182
|
+
@staticmethod
|
|
1183
|
+
async def _cancel_reminders(
|
|
1184
|
+
events: List[Event], tracker: DialogueStateTracker
|
|
1185
|
+
) -> None:
|
|
1186
|
+
"""Cancel reminders that match the `ReminderCancelled` event."""
|
|
1187
|
+
# All Reminders specified by ReminderCancelled events will be cancelled
|
|
1188
|
+
for event in events:
|
|
1189
|
+
if isinstance(event, ReminderCancelled):
|
|
1190
|
+
scheduler = await jobs.scheduler()
|
|
1191
|
+
for scheduled_job in scheduler.get_jobs():
|
|
1192
|
+
if event.cancels_job_with_name(
|
|
1193
|
+
scheduled_job.name, tracker.sender_id
|
|
1194
|
+
):
|
|
1195
|
+
scheduler.remove_job(scheduled_job.id)
|
|
1196
|
+
|
|
1197
|
+
async def run_command_processor(
|
|
1198
|
+
self, tracker: DialogueStateTracker
|
|
1199
|
+
) -> DialogueStateTracker:
|
|
1200
|
+
"""Run the command processor to apply commands to the stack.
|
|
1201
|
+
|
|
1202
|
+
The command processor applies all the commands from the NLU pipeline to the
|
|
1203
|
+
dialogue stack. The dialogue stack then acts as base for decision making for
|
|
1204
|
+
the policies that can use it.
|
|
1205
|
+
|
|
1206
|
+
Args:
|
|
1207
|
+
tracker: the dialogue state tracker
|
|
1208
|
+
|
|
1209
|
+
Returns:
|
|
1210
|
+
An updated tracker after commands have been applied
|
|
1211
|
+
"""
|
|
1212
|
+
target = "command_processor"
|
|
1213
|
+
results = await self.graph_runner.run(
|
|
1214
|
+
inputs={PLACEHOLDER_TRACKER: tracker.copy()}, targets=[target]
|
|
1215
|
+
)
|
|
1216
|
+
events = results[target]
|
|
1217
|
+
tracker.update_with_events(events)
|
|
1218
|
+
return tracker
|
|
1219
|
+
|
|
1220
|
+
async def get_flows(self) -> FlowsList:
|
|
1221
|
+
"""Get the list of flows from the graph."""
|
|
1222
|
+
target = "flows_provider"
|
|
1223
|
+
results = await self.graph_runner.run(inputs={}, targets=[target])
|
|
1224
|
+
return results[target]
|
|
1225
|
+
|
|
1226
|
+
async def _add_flows_to_metadata(self) -> Dict[Text, Any]:
|
|
1227
|
+
"""Convert the flows to metadata."""
|
|
1228
|
+
flows = await self.get_flows()
|
|
1229
|
+
flows_metadata = {}
|
|
1230
|
+
for flow in flows.underlying_flows:
|
|
1231
|
+
flow_as_json = flow.as_json()
|
|
1232
|
+
flow_as_json.pop("id")
|
|
1233
|
+
flows_metadata[flow.id] = flow_as_json
|
|
1234
|
+
|
|
1235
|
+
return {"all_flows": flows_metadata}
|
|
1236
|
+
|
|
1237
|
+
async def _run_action(
|
|
1238
|
+
self,
|
|
1239
|
+
action: rasa.core.actions.action.Action,
|
|
1240
|
+
tracker: DialogueStateTracker,
|
|
1241
|
+
output_channel: OutputChannel,
|
|
1242
|
+
nlg: NaturalLanguageGenerator,
|
|
1243
|
+
prediction: PolicyPrediction,
|
|
1244
|
+
) -> bool:
|
|
1245
|
+
# events and return values are used to update
|
|
1246
|
+
# the tracker state after an action has been taken
|
|
1247
|
+
try:
|
|
1248
|
+
# Use temporary tracker as we might need to discard the policy events in
|
|
1249
|
+
# case of a rejection.
|
|
1250
|
+
temporary_tracker = tracker.copy()
|
|
1251
|
+
temporary_tracker.update_with_events(prediction.events)
|
|
1252
|
+
|
|
1253
|
+
run_args = inspect.getfullargspec(action.run).args
|
|
1254
|
+
if "metadata" in run_args:
|
|
1255
|
+
metadata: Optional[Dict] = prediction.action_metadata
|
|
1256
|
+
|
|
1257
|
+
if isinstance(action, FormAction):
|
|
1258
|
+
flows_metadata = await self._add_flows_to_metadata()
|
|
1259
|
+
metadata = prediction.action_metadata or {}
|
|
1260
|
+
metadata.update(flows_metadata)
|
|
1261
|
+
|
|
1262
|
+
events = await action.run(
|
|
1263
|
+
output_channel,
|
|
1264
|
+
nlg,
|
|
1265
|
+
temporary_tracker,
|
|
1266
|
+
self.domain,
|
|
1267
|
+
metadata=metadata,
|
|
1268
|
+
)
|
|
1269
|
+
else:
|
|
1270
|
+
events = await action.run(
|
|
1271
|
+
output_channel, nlg, temporary_tracker, self.domain
|
|
1272
|
+
)
|
|
1273
|
+
except ActionExecutionRejection:
|
|
1274
|
+
events = [
|
|
1275
|
+
ActionExecutionRejected(
|
|
1276
|
+
action.name(), prediction.policy_name, prediction.max_confidence
|
|
1277
|
+
)
|
|
1278
|
+
]
|
|
1279
|
+
tracker.update(events[0])
|
|
1280
|
+
return self.should_predict_another_action(action.name())
|
|
1281
|
+
except Exception:
|
|
1282
|
+
structlogger.exception(
|
|
1283
|
+
"rasa.core.processor.run_action.exception",
|
|
1284
|
+
event_info=f"Encountered an exception while "
|
|
1285
|
+
f"running action '{action.name()}'."
|
|
1286
|
+
f"Bot will continue, but the actions events are lost. "
|
|
1287
|
+
f"Please check the logs of your action server for "
|
|
1288
|
+
f"more information.",
|
|
1289
|
+
)
|
|
1290
|
+
events = []
|
|
1291
|
+
|
|
1292
|
+
self._log_action_on_tracker(tracker, action, events, prediction)
|
|
1293
|
+
|
|
1294
|
+
if any(isinstance(e, UserUttered) for e in events):
|
|
1295
|
+
logger.debug(
|
|
1296
|
+
f"A `UserUttered` event was returned by executing "
|
|
1297
|
+
f"action '{action.name()}'. This will run the default action "
|
|
1298
|
+
f"'{ACTION_EXTRACT_SLOTS}'."
|
|
1299
|
+
)
|
|
1300
|
+
tracker = await self.run_action_extract_slots(output_channel, tracker)
|
|
1301
|
+
|
|
1302
|
+
if action.name() != ACTION_LISTEN_NAME and not action.name().startswith(
|
|
1303
|
+
UTTER_PREFIX
|
|
1304
|
+
):
|
|
1305
|
+
self._log_slots(tracker)
|
|
1306
|
+
|
|
1307
|
+
await self.execute_side_effects(events, tracker, output_channel)
|
|
1308
|
+
|
|
1309
|
+
return self.should_predict_another_action(action.name())
|
|
1310
|
+
|
|
1311
|
+
def _log_action_on_tracker(
|
|
1312
|
+
self,
|
|
1313
|
+
tracker: DialogueStateTracker,
|
|
1314
|
+
action: Action,
|
|
1315
|
+
events: Optional[List[Event]],
|
|
1316
|
+
prediction: PolicyPrediction,
|
|
1317
|
+
) -> None:
|
|
1318
|
+
# Ensures that the code still works even if a lazy programmer missed
|
|
1319
|
+
# to type `return []` at the end of an action or the run method
|
|
1320
|
+
# returns `None` for some other reason.
|
|
1321
|
+
if events is None:
|
|
1322
|
+
events = []
|
|
1323
|
+
|
|
1324
|
+
action_was_rejected_manually = any(
|
|
1325
|
+
isinstance(event, ActionExecutionRejected) for event in events
|
|
1326
|
+
)
|
|
1327
|
+
if not action_was_rejected_manually:
|
|
1328
|
+
structlogger.debug(
|
|
1329
|
+
"processor.actions.policy_prediction",
|
|
1330
|
+
prediction_events=copy.deepcopy(prediction.events),
|
|
1331
|
+
policy_name=prediction.policy_name,
|
|
1332
|
+
action_name=action.name(),
|
|
1333
|
+
)
|
|
1334
|
+
tracker.update_with_events(prediction.events)
|
|
1335
|
+
|
|
1336
|
+
# log the action and its produced events
|
|
1337
|
+
tracker.update(action.event_for_successful_execution(prediction))
|
|
1338
|
+
|
|
1339
|
+
structlogger.debug(
|
|
1340
|
+
"processor.actions.log",
|
|
1341
|
+
action_name=action.name(),
|
|
1342
|
+
rasa_events=copy.deepcopy(events),
|
|
1343
|
+
)
|
|
1344
|
+
tracker.update_with_events(events)
|
|
1345
|
+
|
|
1346
|
+
def _has_session_expired(self, tracker: DialogueStateTracker) -> bool:
|
|
1347
|
+
"""Determine whether the latest session in `tracker` has expired.
|
|
1348
|
+
|
|
1349
|
+
Args:
|
|
1350
|
+
tracker: Tracker to inspect.
|
|
1351
|
+
|
|
1352
|
+
Returns:
|
|
1353
|
+
`True` if the session in `tracker` has expired, `False` otherwise.
|
|
1354
|
+
"""
|
|
1355
|
+
if not self.domain.session_config.are_sessions_enabled():
|
|
1356
|
+
# tracker has never expired if sessions are disabled
|
|
1357
|
+
return False
|
|
1358
|
+
|
|
1359
|
+
user_uttered_event: Optional[UserUttered] = tracker.get_last_event_for(
|
|
1360
|
+
UserUttered
|
|
1361
|
+
)
|
|
1362
|
+
|
|
1363
|
+
if not user_uttered_event:
|
|
1364
|
+
# there is no user event so far so the session should not be considered
|
|
1365
|
+
# expired
|
|
1366
|
+
return False
|
|
1367
|
+
|
|
1368
|
+
time_delta_in_seconds = time.time() - user_uttered_event.timestamp
|
|
1369
|
+
has_expired = (
|
|
1370
|
+
time_delta_in_seconds / 60
|
|
1371
|
+
> self.domain.session_config.session_expiration_time
|
|
1372
|
+
)
|
|
1373
|
+
if has_expired:
|
|
1374
|
+
logger.debug(
|
|
1375
|
+
f"The latest session for conversation ID '{tracker.sender_id}' has "
|
|
1376
|
+
f"expired."
|
|
1377
|
+
)
|
|
1378
|
+
|
|
1379
|
+
return has_expired
|
|
1380
|
+
|
|
1381
|
+
async def save_tracker(self, tracker: DialogueStateTracker) -> None:
|
|
1382
|
+
"""Save the given tracker to the tracker store.
|
|
1383
|
+
|
|
1384
|
+
Args:
|
|
1385
|
+
tracker: Tracker to be saved.
|
|
1386
|
+
"""
|
|
1387
|
+
await self.tracker_store.save(tracker)
|
|
1388
|
+
|
|
1389
|
+
async def _predict_next_with_tracker(
|
|
1390
|
+
self, tracker: DialogueStateTracker
|
|
1391
|
+
) -> PolicyPrediction:
|
|
1392
|
+
"""Collect predictions from ensemble and return action and predictions."""
|
|
1393
|
+
followup_action = tracker.followup_action
|
|
1394
|
+
if followup_action:
|
|
1395
|
+
tracker.clear_followup_action()
|
|
1396
|
+
if followup_action in self.domain.action_names_or_texts:
|
|
1397
|
+
prediction = PolicyPrediction.for_action_name(
|
|
1398
|
+
self.domain, followup_action, FOLLOWUP_ACTION
|
|
1399
|
+
)
|
|
1400
|
+
return prediction
|
|
1401
|
+
|
|
1402
|
+
logger.error(
|
|
1403
|
+
f"Trying to run unknown follow-up action '{followup_action}'. "
|
|
1404
|
+
"Instead of running that, Rasa Pro will ignore the action "
|
|
1405
|
+
"and predict the next action."
|
|
1406
|
+
)
|
|
1407
|
+
|
|
1408
|
+
target = self.model_metadata.core_target
|
|
1409
|
+
if not target:
|
|
1410
|
+
raise ValueError("Cannot predict next action if there is no core target.")
|
|
1411
|
+
|
|
1412
|
+
results = await self.graph_runner.run(
|
|
1413
|
+
inputs={
|
|
1414
|
+
PLACEHOLDER_TRACKER: tracker,
|
|
1415
|
+
PLACEHOLDER_ENDPOINTS: self.endpoints,
|
|
1416
|
+
},
|
|
1417
|
+
targets=[target],
|
|
1418
|
+
)
|
|
1419
|
+
policy_prediction = results[target]
|
|
1420
|
+
return policy_prediction
|
|
1421
|
+
|
|
1422
|
+
@staticmethod
|
|
1423
|
+
def message_contains_commands(latest_message: Optional[UserUttered]) -> bool:
|
|
1424
|
+
"""Check if the latest message contains commands."""
|
|
1425
|
+
if latest_message is None:
|
|
1426
|
+
return False
|
|
1427
|
+
|
|
1428
|
+
commands = [
|
|
1429
|
+
Command.command_from_json(command) for command in latest_message.commands
|
|
1430
|
+
]
|
|
1431
|
+
filtered_commands = [
|
|
1432
|
+
command
|
|
1433
|
+
for command in commands
|
|
1434
|
+
if not (
|
|
1435
|
+
isinstance(command, SetSlotCommand)
|
|
1436
|
+
and command.name == ROUTE_TO_CALM_SLOT
|
|
1437
|
+
)
|
|
1438
|
+
and not isinstance(command, NoopCommand)
|
|
1439
|
+
]
|
|
1440
|
+
|
|
1441
|
+
return len(filtered_commands) > 0
|
|
1442
|
+
|
|
1443
|
+
def _is_calm_assistant(self) -> bool:
|
|
1444
|
+
"""Inspects the nodes of the graph schema to determine whether
|
|
1445
|
+
any node is associated with the `FlowPolicy`, which is indicative of a
|
|
1446
|
+
CALM assistant setup.
|
|
1447
|
+
|
|
1448
|
+
Returns:
|
|
1449
|
+
bool: True if any node in the graph schema uses `FlowPolicy`.
|
|
1450
|
+
"""
|
|
1451
|
+
# Get the graph schema's nodes from the graph runner.
|
|
1452
|
+
nodes: dict[str, Any] = self.graph_runner._graph_schema.nodes # type: ignore[attr-defined]
|
|
1453
|
+
|
|
1454
|
+
flow_policy_class_path = "rasa.core.policies.flow_policy.FlowPolicy"
|
|
1455
|
+
# Iterate over the nodes and check if any node uses `FlowPolicy`.
|
|
1456
|
+
for node_name, schema_node in nodes.items():
|
|
1457
|
+
if (
|
|
1458
|
+
schema_node.uses is not None
|
|
1459
|
+
and f"{schema_node.uses.__module__}.{schema_node.uses.__name__}"
|
|
1460
|
+
== flow_policy_class_path
|
|
1461
|
+
):
|
|
1462
|
+
return True
|
|
1463
|
+
|
|
1464
|
+
# Return False if no node is found using `FlowPolicy`.
|
|
1465
|
+
return False
|