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/server.py
ADDED
|
@@ -0,0 +1,1588 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import concurrent.futures
|
|
3
|
+
import logging
|
|
4
|
+
import multiprocessing
|
|
5
|
+
import os
|
|
6
|
+
import traceback
|
|
7
|
+
import warnings
|
|
8
|
+
from collections import defaultdict
|
|
9
|
+
from functools import reduce, wraps
|
|
10
|
+
from http import HTTPStatus
|
|
11
|
+
from inspect import isawaitable
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import (
|
|
14
|
+
TYPE_CHECKING,
|
|
15
|
+
Any,
|
|
16
|
+
Callable,
|
|
17
|
+
Coroutine,
|
|
18
|
+
DefaultDict,
|
|
19
|
+
Dict,
|
|
20
|
+
List,
|
|
21
|
+
NoReturn,
|
|
22
|
+
Optional,
|
|
23
|
+
Text,
|
|
24
|
+
Union,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
import aiohttp
|
|
28
|
+
import jsonschema
|
|
29
|
+
from sanic import Sanic, response
|
|
30
|
+
from sanic.request import Request
|
|
31
|
+
from sanic.response import HTTPResponse
|
|
32
|
+
from sanic_cors import CORS
|
|
33
|
+
from sanic_jwt import Initialize, exceptions
|
|
34
|
+
|
|
35
|
+
import rasa
|
|
36
|
+
import rasa.core.utils
|
|
37
|
+
import rasa.nlu.test
|
|
38
|
+
import rasa.shared.core.events
|
|
39
|
+
import rasa.shared.nlu.training_data.schemas.data_schema
|
|
40
|
+
import rasa.shared.utils.common
|
|
41
|
+
import rasa.shared.utils.io
|
|
42
|
+
import rasa.utils.common
|
|
43
|
+
import rasa.utils.endpoints
|
|
44
|
+
import rasa.utils.io
|
|
45
|
+
from rasa.constants import MINIMUM_COMPATIBLE_VERSION
|
|
46
|
+
from rasa.core.agent import Agent
|
|
47
|
+
from rasa.core.channels.channel import (
|
|
48
|
+
CollectingOutputChannel,
|
|
49
|
+
OutputChannel,
|
|
50
|
+
UserMessage,
|
|
51
|
+
)
|
|
52
|
+
from rasa.core.constants import DEFAULT_RESPONSE_TIMEOUT
|
|
53
|
+
from rasa.core.persistor import parse_remote_storage
|
|
54
|
+
from rasa.core.test import test
|
|
55
|
+
from rasa.core.utils import AvailableEndpoints
|
|
56
|
+
from rasa.nlu.emulators.emulator import Emulator
|
|
57
|
+
from rasa.nlu.emulators.no_emulator import NoEmulator
|
|
58
|
+
from rasa.nlu.test import CVEvaluationResult
|
|
59
|
+
from rasa.shared.constants import (
|
|
60
|
+
DEFAULT_MODELS_PATH,
|
|
61
|
+
DEFAULT_SENDER_ID,
|
|
62
|
+
DOCS_BASE_URL,
|
|
63
|
+
DOCS_URL_TRAINING_DATA,
|
|
64
|
+
TEST_STORIES_FILE_PREFIX,
|
|
65
|
+
)
|
|
66
|
+
from rasa.shared.core.domain import Domain, InvalidDomain
|
|
67
|
+
from rasa.shared.core.events import Event
|
|
68
|
+
from rasa.shared.core.trackers import (
|
|
69
|
+
DialogueStateTracker,
|
|
70
|
+
EventVerbosity,
|
|
71
|
+
)
|
|
72
|
+
from rasa.shared.core.training_data.story_writer.yaml_story_writer import (
|
|
73
|
+
YAMLStoryWriter,
|
|
74
|
+
)
|
|
75
|
+
from rasa.shared.importers.importer import TrainingDataImporter
|
|
76
|
+
from rasa.shared.nlu.training_data.formats import RasaYAMLReader
|
|
77
|
+
from rasa.shared.utils.schemas.events import EVENTS_SCHEMA
|
|
78
|
+
from rasa.shared.utils.yaml import validate_training_data
|
|
79
|
+
from rasa.utils.common import TempDirectoryPath, get_temp_dir_name
|
|
80
|
+
from rasa.utils.endpoints import EndpointConfig
|
|
81
|
+
from rasa.utils.sanic_error_handler import register_custom_sanic_error_handler
|
|
82
|
+
|
|
83
|
+
if TYPE_CHECKING:
|
|
84
|
+
from ssl import SSLContext
|
|
85
|
+
|
|
86
|
+
from mypy_extensions import Arg, KwArg, VarArg
|
|
87
|
+
|
|
88
|
+
from rasa.core.processor import MessageProcessor
|
|
89
|
+
|
|
90
|
+
SanicResponse = Union[
|
|
91
|
+
response.HTTPResponse, Coroutine[Any, Any, response.HTTPResponse]
|
|
92
|
+
]
|
|
93
|
+
SanicView = Callable[
|
|
94
|
+
[Arg(Request, "request"), VarArg(), KwArg()],
|
|
95
|
+
Coroutine[Any, Any, SanicResponse],
|
|
96
|
+
]
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
logger = logging.getLogger(__name__)
|
|
100
|
+
|
|
101
|
+
JSON_CONTENT_TYPE = "application/json"
|
|
102
|
+
YAML_CONTENT_TYPE = "application/x-yaml"
|
|
103
|
+
|
|
104
|
+
OUTPUT_CHANNEL_QUERY_KEY = "output_channel"
|
|
105
|
+
USE_LATEST_INPUT_CHANNEL_AS_OUTPUT_CHANNEL = "latest"
|
|
106
|
+
EXECUTE_SIDE_EFFECTS_QUERY_KEY = "execute_side_effects"
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class ErrorResponse(Exception):
|
|
110
|
+
"""Common exception to handle failing API requests."""
|
|
111
|
+
|
|
112
|
+
def __init__(
|
|
113
|
+
self,
|
|
114
|
+
status: Union[int, HTTPStatus],
|
|
115
|
+
reason: Text,
|
|
116
|
+
message: Text,
|
|
117
|
+
details: Any = None,
|
|
118
|
+
help_url: Optional[Text] = None,
|
|
119
|
+
) -> None:
|
|
120
|
+
"""Creates error.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
status: The HTTP status code to return.
|
|
124
|
+
reason: Short summary of the error.
|
|
125
|
+
message: Detailed explanation of the error.
|
|
126
|
+
details: Additional details which describe the error. Must be serializable.
|
|
127
|
+
help_url: URL where users can get further help (e.g. docs).
|
|
128
|
+
"""
|
|
129
|
+
self.error_info = {
|
|
130
|
+
"version": rasa.__version__,
|
|
131
|
+
"status": "failure",
|
|
132
|
+
"message": message,
|
|
133
|
+
"reason": reason,
|
|
134
|
+
"details": details or {},
|
|
135
|
+
"help": help_url,
|
|
136
|
+
"code": status,
|
|
137
|
+
}
|
|
138
|
+
self.status = status
|
|
139
|
+
logger.error(message)
|
|
140
|
+
super(ErrorResponse, self).__init__()
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def _docs(sub_url: Text) -> Text:
|
|
144
|
+
"""Create a url to a subpart of the docs."""
|
|
145
|
+
return DOCS_BASE_URL + sub_url
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def ensure_loaded_agent(
|
|
149
|
+
app: Sanic, require_core_is_ready: bool = False
|
|
150
|
+
) -> Callable[[Callable], Callable[..., Any]]:
|
|
151
|
+
"""Wraps a request handler ensuring there is a loaded and usable agent.
|
|
152
|
+
|
|
153
|
+
Require the agent to have a loaded Core model if `require_core_is_ready` is
|
|
154
|
+
`True`.
|
|
155
|
+
"""
|
|
156
|
+
|
|
157
|
+
def decorator(f: Callable) -> Callable:
|
|
158
|
+
@wraps(f)
|
|
159
|
+
def decorated(*args: Any, **kwargs: Any) -> Any:
|
|
160
|
+
# noinspection PyUnresolvedReferences
|
|
161
|
+
if not app.ctx.agent or not app.ctx.agent.is_ready():
|
|
162
|
+
raise ErrorResponse(
|
|
163
|
+
HTTPStatus.CONFLICT,
|
|
164
|
+
"Conflict",
|
|
165
|
+
"No agent loaded. To continue processing, a "
|
|
166
|
+
"model of a trained agent needs to be loaded.",
|
|
167
|
+
help_url=_docs("/user-guide/configuring-http-api/"),
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
return f(*args, **kwargs)
|
|
171
|
+
|
|
172
|
+
return decorated
|
|
173
|
+
|
|
174
|
+
return decorator
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def ensure_conversation_exists() -> Callable[["SanicView"], "SanicView"]:
|
|
178
|
+
"""Wraps a request handler ensuring the conversation exists."""
|
|
179
|
+
|
|
180
|
+
def decorator(f: "SanicView") -> "SanicView":
|
|
181
|
+
@wraps(f)
|
|
182
|
+
async def decorated(
|
|
183
|
+
request: Request, *args: Any, **kwargs: Any
|
|
184
|
+
) -> "SanicResponse":
|
|
185
|
+
conversation_id = kwargs["conversation_id"]
|
|
186
|
+
if await request.app.ctx.agent.tracker_store.exists(conversation_id):
|
|
187
|
+
return await f(request, *args, **kwargs)
|
|
188
|
+
else:
|
|
189
|
+
raise ErrorResponse(
|
|
190
|
+
HTTPStatus.NOT_FOUND, "Not found", "Conversation ID not found."
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
return decorated
|
|
194
|
+
|
|
195
|
+
return decorator
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def requires_auth(
|
|
199
|
+
app: Sanic, token: Optional[Text] = None
|
|
200
|
+
) -> Callable[["SanicView"], "SanicView"]:
|
|
201
|
+
"""Wraps a request handler with token authentication."""
|
|
202
|
+
|
|
203
|
+
def decorator(f: "SanicView") -> "SanicView":
|
|
204
|
+
def conversation_id_from_args(args: Any, kwargs: Any) -> Optional[Text]:
|
|
205
|
+
argnames = rasa.shared.utils.common.arguments_of(f)
|
|
206
|
+
|
|
207
|
+
try:
|
|
208
|
+
sender_id_arg_idx = argnames.index("conversation_id")
|
|
209
|
+
if "conversation_id" in kwargs: # try to fetch from kwargs first
|
|
210
|
+
return kwargs["conversation_id"]
|
|
211
|
+
if sender_id_arg_idx < len(args):
|
|
212
|
+
return args[sender_id_arg_idx]
|
|
213
|
+
return None
|
|
214
|
+
except ValueError:
|
|
215
|
+
return None
|
|
216
|
+
|
|
217
|
+
async def sufficient_scope(
|
|
218
|
+
request: Request, *args: Any, **kwargs: Any
|
|
219
|
+
) -> Optional[bool]:
|
|
220
|
+
# This is a coroutine since `sanic-jwt==1.6`
|
|
221
|
+
jwt_data = await rasa.utils.common.call_potential_coroutine(
|
|
222
|
+
request.app.ctx.auth.extract_payload(request)
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
user = jwt_data.get("user", {})
|
|
226
|
+
|
|
227
|
+
username = user.get("username", None)
|
|
228
|
+
role = user.get("role", None)
|
|
229
|
+
|
|
230
|
+
if role == "admin":
|
|
231
|
+
return True
|
|
232
|
+
elif role == "user":
|
|
233
|
+
conversation_id = conversation_id_from_args(args, kwargs)
|
|
234
|
+
return conversation_id is not None and username == conversation_id
|
|
235
|
+
else:
|
|
236
|
+
return False
|
|
237
|
+
|
|
238
|
+
@wraps(f)
|
|
239
|
+
async def decorated(
|
|
240
|
+
request: Request, *args: Any, **kwargs: Any
|
|
241
|
+
) -> response.HTTPResponse:
|
|
242
|
+
provided = request.args.get("token", None)
|
|
243
|
+
|
|
244
|
+
# noinspection PyProtectedMember
|
|
245
|
+
if token is not None and provided == token:
|
|
246
|
+
result = f(request, *args, **kwargs)
|
|
247
|
+
return await result if isawaitable(result) else result
|
|
248
|
+
elif app.config.get(
|
|
249
|
+
"USE_JWT"
|
|
250
|
+
) and await rasa.utils.common.call_potential_coroutine(
|
|
251
|
+
# This is a coroutine since `sanic-jwt==1.6`
|
|
252
|
+
request.app.ctx.auth.is_authenticated(request)
|
|
253
|
+
):
|
|
254
|
+
if await sufficient_scope(request, *args, **kwargs):
|
|
255
|
+
result = f(request, *args, **kwargs)
|
|
256
|
+
return await result if isawaitable(result) else result
|
|
257
|
+
raise ErrorResponse(
|
|
258
|
+
HTTPStatus.FORBIDDEN,
|
|
259
|
+
"NotAuthorized",
|
|
260
|
+
"User has insufficient permissions.",
|
|
261
|
+
help_url=_docs(
|
|
262
|
+
"/user-guide/configuring-http-api/#security-considerations"
|
|
263
|
+
),
|
|
264
|
+
)
|
|
265
|
+
elif token is None and app.config.get("USE_JWT") is None:
|
|
266
|
+
# authentication is disabled
|
|
267
|
+
result = f(request, *args, **kwargs)
|
|
268
|
+
return await result if isawaitable(result) else result
|
|
269
|
+
raise ErrorResponse(
|
|
270
|
+
HTTPStatus.UNAUTHORIZED,
|
|
271
|
+
"NotAuthenticated",
|
|
272
|
+
"User is not authenticated.",
|
|
273
|
+
help_url=_docs(
|
|
274
|
+
"/user-guide/configuring-http-api/#security-considerations"
|
|
275
|
+
),
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
return decorated
|
|
279
|
+
|
|
280
|
+
return decorator
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def event_verbosity_parameter(
|
|
284
|
+
request: Request, default_verbosity: EventVerbosity
|
|
285
|
+
) -> EventVerbosity:
|
|
286
|
+
"""Create `EventVerbosity` object using request params if present."""
|
|
287
|
+
event_verbosity_str = request.args.get(
|
|
288
|
+
"include_events", default_verbosity.name
|
|
289
|
+
).upper()
|
|
290
|
+
try:
|
|
291
|
+
return EventVerbosity[event_verbosity_str]
|
|
292
|
+
except KeyError:
|
|
293
|
+
enum_values = ", ".join([e.name for e in EventVerbosity])
|
|
294
|
+
raise ErrorResponse(
|
|
295
|
+
HTTPStatus.BAD_REQUEST,
|
|
296
|
+
"BadRequest",
|
|
297
|
+
"Invalid parameter value for 'include_events'. "
|
|
298
|
+
"Should be one of {}".format(enum_values),
|
|
299
|
+
{"parameter": "include_events", "in": "query"},
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
async def get_test_stories(
|
|
304
|
+
processor: "MessageProcessor",
|
|
305
|
+
conversation_id: Text,
|
|
306
|
+
until_time: Optional[float],
|
|
307
|
+
fetch_all_sessions: bool = False,
|
|
308
|
+
) -> Text:
|
|
309
|
+
"""Retrieves test stories for all conversation sessions for `conversation_id`.
|
|
310
|
+
|
|
311
|
+
Uses `processor` to get the conversations.
|
|
312
|
+
|
|
313
|
+
Args:
|
|
314
|
+
processor: An instance of `MessageProcessor`.
|
|
315
|
+
conversation_id: Conversation ID to fetch stories for.
|
|
316
|
+
until_time: Timestamp up to which to include events.
|
|
317
|
+
fetch_all_sessions: Whether to fetch stories for all conversation sessions.
|
|
318
|
+
If `False`, only the last conversation session is retrieved.
|
|
319
|
+
|
|
320
|
+
Returns:
|
|
321
|
+
The stories for `conversation_id` in test format.
|
|
322
|
+
"""
|
|
323
|
+
if fetch_all_sessions:
|
|
324
|
+
trackers = await processor.get_trackers_for_all_conversation_sessions(
|
|
325
|
+
conversation_id
|
|
326
|
+
)
|
|
327
|
+
else:
|
|
328
|
+
trackers = [await processor.get_tracker(conversation_id)]
|
|
329
|
+
|
|
330
|
+
if until_time is not None:
|
|
331
|
+
trackers = [tracker.travel_back_in_time(until_time) for tracker in trackers]
|
|
332
|
+
# keep only non-empty trackers
|
|
333
|
+
trackers = [tracker for tracker in trackers if len(tracker.events)]
|
|
334
|
+
|
|
335
|
+
story_steps = []
|
|
336
|
+
|
|
337
|
+
more_than_one_story = len(trackers) > 1
|
|
338
|
+
|
|
339
|
+
for i, tracker in enumerate(trackers, 1):
|
|
340
|
+
tracker.sender_id = conversation_id
|
|
341
|
+
|
|
342
|
+
if more_than_one_story:
|
|
343
|
+
tracker.sender_id += f", story {i}"
|
|
344
|
+
|
|
345
|
+
story_steps += tracker.as_story().story_steps
|
|
346
|
+
|
|
347
|
+
return YAMLStoryWriter().dumps(story_steps, is_test_story=True)
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
async def update_conversation_with_events(
|
|
351
|
+
conversation_id: Text,
|
|
352
|
+
processor: "MessageProcessor",
|
|
353
|
+
domain: Domain,
|
|
354
|
+
events: List[Event],
|
|
355
|
+
) -> DialogueStateTracker:
|
|
356
|
+
"""Fetches or creates a tracker for `conversation_id` and appends `events` to it.
|
|
357
|
+
|
|
358
|
+
Args:
|
|
359
|
+
conversation_id: The ID of the conversation to update the tracker for.
|
|
360
|
+
processor: An instance of `MessageProcessor`.
|
|
361
|
+
domain: The domain associated with the current `Agent`.
|
|
362
|
+
events: The events to append to the tracker.
|
|
363
|
+
|
|
364
|
+
Returns:
|
|
365
|
+
The tracker for `conversation_id` with the updated events.
|
|
366
|
+
"""
|
|
367
|
+
if rasa.shared.core.events.do_events_begin_with_session_start(events):
|
|
368
|
+
tracker = await processor.get_tracker(conversation_id)
|
|
369
|
+
else:
|
|
370
|
+
tracker = await processor.fetch_tracker_with_initial_session(conversation_id)
|
|
371
|
+
|
|
372
|
+
for event in events:
|
|
373
|
+
tracker.update(event, domain)
|
|
374
|
+
|
|
375
|
+
return tracker
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
def validate_request_body(request: Request, error_message: Text) -> None:
|
|
379
|
+
"""Check if `request` has a body."""
|
|
380
|
+
if not request.body:
|
|
381
|
+
raise ErrorResponse(HTTPStatus.BAD_REQUEST, "BadRequest", error_message)
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
def validate_events_in_request_body(request: Request) -> None:
|
|
385
|
+
"""Validates events format in request body."""
|
|
386
|
+
if not isinstance(request.json, list):
|
|
387
|
+
events = [request.json]
|
|
388
|
+
else:
|
|
389
|
+
events = request.json
|
|
390
|
+
|
|
391
|
+
try:
|
|
392
|
+
jsonschema.validate(events, EVENTS_SCHEMA)
|
|
393
|
+
except jsonschema.ValidationError as error:
|
|
394
|
+
raise ErrorResponse(
|
|
395
|
+
HTTPStatus.BAD_REQUEST,
|
|
396
|
+
"BadRequest",
|
|
397
|
+
f"Failed to validate the events format. "
|
|
398
|
+
f"For more information about the format visit the docs. Error: {error}",
|
|
399
|
+
help_url=_docs("/pages/http-api"),
|
|
400
|
+
) from error
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
async def authenticate(_: Request) -> NoReturn:
|
|
404
|
+
"""Callback for authentication failed."""
|
|
405
|
+
raise exceptions.AuthenticationFailed(
|
|
406
|
+
"Direct JWT authentication not supported. You should already have "
|
|
407
|
+
"a valid JWT from an authentication provider, Rasa will just make "
|
|
408
|
+
"sure that the token is valid, but not issue new tokens."
|
|
409
|
+
)
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
def create_ssl_context(
|
|
413
|
+
ssl_certificate: Optional[Text],
|
|
414
|
+
ssl_keyfile: Optional[Text],
|
|
415
|
+
ssl_ca_file: Optional[Text] = None,
|
|
416
|
+
ssl_password: Optional[Text] = None,
|
|
417
|
+
) -> Optional["SSLContext"]:
|
|
418
|
+
"""Create an SSL context if a proper certificate is passed.
|
|
419
|
+
|
|
420
|
+
Args:
|
|
421
|
+
ssl_certificate: path to the SSL client certificate
|
|
422
|
+
ssl_keyfile: path to the SSL key file
|
|
423
|
+
ssl_ca_file: path to the SSL CA file for verification (optional)
|
|
424
|
+
ssl_password: SSL private key password (optional)
|
|
425
|
+
|
|
426
|
+
Returns:
|
|
427
|
+
SSL context if a valid certificate chain can be loaded, `None` otherwise.
|
|
428
|
+
|
|
429
|
+
"""
|
|
430
|
+
if ssl_certificate:
|
|
431
|
+
import ssl
|
|
432
|
+
|
|
433
|
+
ssl_context = ssl.create_default_context(
|
|
434
|
+
purpose=ssl.Purpose.CLIENT_AUTH, cafile=ssl_ca_file
|
|
435
|
+
)
|
|
436
|
+
ssl_context.load_cert_chain(
|
|
437
|
+
ssl_certificate, keyfile=ssl_keyfile, password=ssl_password
|
|
438
|
+
)
|
|
439
|
+
return ssl_context
|
|
440
|
+
else:
|
|
441
|
+
return None
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
def _create_emulator(mode: Optional[Text]) -> Emulator:
|
|
445
|
+
"""Create emulator for specified mode.
|
|
446
|
+
|
|
447
|
+
If no emulator is specified, we will use the Rasa NLU format.
|
|
448
|
+
"""
|
|
449
|
+
if mode is None:
|
|
450
|
+
return NoEmulator()
|
|
451
|
+
elif mode.lower() == "wit":
|
|
452
|
+
from rasa.nlu.emulators.wit import WitEmulator
|
|
453
|
+
|
|
454
|
+
return WitEmulator()
|
|
455
|
+
elif mode.lower() == "luis":
|
|
456
|
+
from rasa.nlu.emulators.luis import LUISEmulator
|
|
457
|
+
|
|
458
|
+
return LUISEmulator()
|
|
459
|
+
elif mode.lower() == "dialogflow":
|
|
460
|
+
from rasa.nlu.emulators.dialogflow import DialogflowEmulator
|
|
461
|
+
|
|
462
|
+
return DialogflowEmulator()
|
|
463
|
+
else:
|
|
464
|
+
raise ErrorResponse(
|
|
465
|
+
HTTPStatus.BAD_REQUEST,
|
|
466
|
+
"BadRequest",
|
|
467
|
+
"Invalid parameter value for 'emulation_mode'. "
|
|
468
|
+
"Should be one of 'WIT', 'LUIS', 'DIALOGFLOW'.",
|
|
469
|
+
{"parameter": "emulation_mode", "in": "query"},
|
|
470
|
+
)
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
async def _load_agent(
|
|
474
|
+
model_path: Optional[Text] = None,
|
|
475
|
+
model_server: Optional[EndpointConfig] = None,
|
|
476
|
+
remote_storage: Optional[Text] = None,
|
|
477
|
+
endpoints: Optional[AvailableEndpoints] = None,
|
|
478
|
+
) -> Agent:
|
|
479
|
+
try:
|
|
480
|
+
loaded_agent = await rasa.core.agent.load_agent(
|
|
481
|
+
model_path=model_path,
|
|
482
|
+
model_server=model_server,
|
|
483
|
+
remote_storage=remote_storage,
|
|
484
|
+
endpoints=endpoints,
|
|
485
|
+
)
|
|
486
|
+
except Exception as e:
|
|
487
|
+
logger.debug(traceback.format_exc())
|
|
488
|
+
raise ErrorResponse(
|
|
489
|
+
HTTPStatus.INTERNAL_SERVER_ERROR,
|
|
490
|
+
"LoadingError",
|
|
491
|
+
f"An unexpected error occurred. Error: {e}",
|
|
492
|
+
)
|
|
493
|
+
|
|
494
|
+
if not loaded_agent.is_ready():
|
|
495
|
+
raise ErrorResponse(
|
|
496
|
+
HTTPStatus.BAD_REQUEST,
|
|
497
|
+
"BadRequest",
|
|
498
|
+
f"Agent with name '{model_path}' could not be loaded.",
|
|
499
|
+
{"parameter": "model", "in": "query"},
|
|
500
|
+
)
|
|
501
|
+
|
|
502
|
+
return loaded_agent
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
def configure_cors(
|
|
506
|
+
app: Sanic, cors_origins: Union[Text, List[Text], None] = ""
|
|
507
|
+
) -> None:
|
|
508
|
+
"""Configure CORS origins for the given app."""
|
|
509
|
+
# Workaround so that socketio works with requests from other origins.
|
|
510
|
+
# https://github.com/miguelgrinberg/python-socketio/issues/205#issuecomment-493769183
|
|
511
|
+
app.config.CORS_AUTOMATIC_OPTIONS = True
|
|
512
|
+
app.config.CORS_SUPPORTS_CREDENTIALS = True
|
|
513
|
+
app.config.CORS_EXPOSE_HEADERS = "filename"
|
|
514
|
+
|
|
515
|
+
CORS(
|
|
516
|
+
app, resources={r"/*": {"origins": cors_origins or ""}}, automatic_options=True
|
|
517
|
+
)
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
def add_root_route(app: Sanic) -> None:
|
|
521
|
+
"""Add '/' route to return hello."""
|
|
522
|
+
|
|
523
|
+
@app.get("/")
|
|
524
|
+
async def hello(request: Request) -> HTTPResponse:
|
|
525
|
+
"""Check if the server is running and responds with the version."""
|
|
526
|
+
html_content = f"""
|
|
527
|
+
<html>
|
|
528
|
+
<body>
|
|
529
|
+
<p>Hello from Rasa: {rasa.__version__}</p>
|
|
530
|
+
<a href="./webhooks/inspector/inspect.html">Go to the inspector</a>
|
|
531
|
+
<script>
|
|
532
|
+
window.location.replace("./webhooks/socketio/inspect.html");
|
|
533
|
+
</script>
|
|
534
|
+
</body>
|
|
535
|
+
</html>
|
|
536
|
+
"""
|
|
537
|
+
return response.html(html_content)
|
|
538
|
+
|
|
539
|
+
@app.get("/license")
|
|
540
|
+
async def license(request: Request) -> HTTPResponse:
|
|
541
|
+
"""Respond with the license expiration date."""
|
|
542
|
+
from rasa.utils.licensing import (
|
|
543
|
+
get_license_expiration_date,
|
|
544
|
+
property_of_active_license,
|
|
545
|
+
)
|
|
546
|
+
|
|
547
|
+
body = {
|
|
548
|
+
"id": property_of_active_license(lambda active_license: active_license.jti),
|
|
549
|
+
"company": property_of_active_license(
|
|
550
|
+
lambda active_license: active_license.company
|
|
551
|
+
),
|
|
552
|
+
"scope": property_of_active_license(
|
|
553
|
+
lambda active_license: active_license.scope
|
|
554
|
+
),
|
|
555
|
+
"email": property_of_active_license(
|
|
556
|
+
lambda active_license: active_license.email
|
|
557
|
+
),
|
|
558
|
+
"expires": get_license_expiration_date(),
|
|
559
|
+
}
|
|
560
|
+
return response.json(
|
|
561
|
+
body=body,
|
|
562
|
+
headers={"Content-Type": "application/json"},
|
|
563
|
+
)
|
|
564
|
+
|
|
565
|
+
|
|
566
|
+
def async_if_callback_url(f: Callable[..., Coroutine]) -> Callable:
|
|
567
|
+
"""Decorator to enable async request handling.
|
|
568
|
+
|
|
569
|
+
If the incoming HTTP request specified a `callback_url` query parameter, the request
|
|
570
|
+
will return immediately with a 204 while the actual request response will
|
|
571
|
+
be sent to the `callback_url`. If an error happens, the error payload will also
|
|
572
|
+
be sent to the `callback_url`.
|
|
573
|
+
|
|
574
|
+
Args:
|
|
575
|
+
f: The request handler function which should be decorated.
|
|
576
|
+
|
|
577
|
+
Returns:
|
|
578
|
+
The decorated function.
|
|
579
|
+
"""
|
|
580
|
+
|
|
581
|
+
@wraps(f)
|
|
582
|
+
async def decorated_function(
|
|
583
|
+
request: Request, *args: Any, **kwargs: Any
|
|
584
|
+
) -> HTTPResponse:
|
|
585
|
+
callback_url = request.args.get("callback_url")
|
|
586
|
+
# Only process request asynchronously if the user specified a `callback_url`
|
|
587
|
+
# query parameter.
|
|
588
|
+
if not callback_url:
|
|
589
|
+
return await f(request, *args, **kwargs)
|
|
590
|
+
|
|
591
|
+
async def wrapped() -> None:
|
|
592
|
+
try:
|
|
593
|
+
result: HTTPResponse = await f(request, *args, **kwargs)
|
|
594
|
+
payload: Dict[Text, Any] = dict(
|
|
595
|
+
data=result.body, headers={"Content-Type": result.content_type}
|
|
596
|
+
)
|
|
597
|
+
logger.debug(
|
|
598
|
+
"Asynchronous processing of request was successful. "
|
|
599
|
+
"Sending result to callback URL."
|
|
600
|
+
)
|
|
601
|
+
|
|
602
|
+
except Exception as e:
|
|
603
|
+
if not isinstance(e, ErrorResponse):
|
|
604
|
+
logger.error(e)
|
|
605
|
+
e = ErrorResponse(
|
|
606
|
+
HTTPStatus.INTERNAL_SERVER_ERROR,
|
|
607
|
+
"UnexpectedError",
|
|
608
|
+
f"An unexpected error occurred. Error: {e}",
|
|
609
|
+
)
|
|
610
|
+
# If an error happens, we send the error payload to the `callback_url`
|
|
611
|
+
payload = dict(json=e.error_info)
|
|
612
|
+
logger.error(
|
|
613
|
+
"Error happened when processing request asynchronously. "
|
|
614
|
+
"Sending error to callback URL."
|
|
615
|
+
)
|
|
616
|
+
async with aiohttp.ClientSession() as session:
|
|
617
|
+
await session.post(callback_url, raise_for_status=True, **payload)
|
|
618
|
+
|
|
619
|
+
# Run the request in the background on the event loop
|
|
620
|
+
request.app.add_task(wrapped())
|
|
621
|
+
|
|
622
|
+
# The incoming request will return immediately with a 204
|
|
623
|
+
return response.empty()
|
|
624
|
+
|
|
625
|
+
return decorated_function
|
|
626
|
+
|
|
627
|
+
|
|
628
|
+
def run_in_thread(f: Callable[..., Coroutine]) -> Callable:
|
|
629
|
+
"""Decorator which runs request on a separate thread.
|
|
630
|
+
|
|
631
|
+
Some requests (e.g. training or cross-validation) are computional intense requests.
|
|
632
|
+
This means that they will block the event loop and hence the processing of other
|
|
633
|
+
requests. This decorator can be used to process these requests on a separate thread
|
|
634
|
+
to avoid blocking the processing of incoming requests.
|
|
635
|
+
|
|
636
|
+
Args:
|
|
637
|
+
f: The request handler function which should be decorated.
|
|
638
|
+
|
|
639
|
+
Returns:
|
|
640
|
+
The decorated function.
|
|
641
|
+
"""
|
|
642
|
+
|
|
643
|
+
@wraps(f)
|
|
644
|
+
async def decorated_function(
|
|
645
|
+
request: Request, *args: Any, **kwargs: Any
|
|
646
|
+
) -> HTTPResponse:
|
|
647
|
+
# Use a sync wrapper for our `async` function as `run_in_executor` only supports
|
|
648
|
+
# sync functions
|
|
649
|
+
def run() -> HTTPResponse:
|
|
650
|
+
return asyncio.run(f(request, *args, **kwargs))
|
|
651
|
+
|
|
652
|
+
with concurrent.futures.ThreadPoolExecutor() as pool:
|
|
653
|
+
return await request.app.loop.run_in_executor(pool, run)
|
|
654
|
+
|
|
655
|
+
return decorated_function
|
|
656
|
+
|
|
657
|
+
|
|
658
|
+
def inject_temp_dir(f: Callable[..., Coroutine]) -> Callable:
|
|
659
|
+
"""Decorator to inject a temporary directory before a request and clean up after.
|
|
660
|
+
|
|
661
|
+
Args:
|
|
662
|
+
f: The request handler function which should be decorated.
|
|
663
|
+
|
|
664
|
+
Returns:
|
|
665
|
+
The decorated function.
|
|
666
|
+
"""
|
|
667
|
+
|
|
668
|
+
@wraps(f)
|
|
669
|
+
async def decorated_function(*args: Any, **kwargs: Any) -> HTTPResponse:
|
|
670
|
+
with TempDirectoryPath(get_temp_dir_name()) as directory:
|
|
671
|
+
# Decorated request handles need to have a parameter `temporary_directory`
|
|
672
|
+
return await f(*args, temporary_directory=Path(directory), **kwargs)
|
|
673
|
+
|
|
674
|
+
return decorated_function
|
|
675
|
+
|
|
676
|
+
|
|
677
|
+
def create_app(
|
|
678
|
+
agent: Optional["Agent"] = None,
|
|
679
|
+
cors_origins: Union[Text, List[Text], None] = "*",
|
|
680
|
+
auth_token: Optional[Text] = None,
|
|
681
|
+
response_timeout: int = DEFAULT_RESPONSE_TIMEOUT,
|
|
682
|
+
jwt_secret: Optional[Text] = None,
|
|
683
|
+
jwt_private_key: Optional[Text] = None,
|
|
684
|
+
jwt_method: Text = "HS256",
|
|
685
|
+
endpoints: Optional[AvailableEndpoints] = None,
|
|
686
|
+
) -> Sanic:
|
|
687
|
+
"""Class representing a Rasa HTTP server."""
|
|
688
|
+
app = Sanic("rasa_server")
|
|
689
|
+
app.config.RESPONSE_TIMEOUT = response_timeout
|
|
690
|
+
configure_cors(app, cors_origins)
|
|
691
|
+
register_custom_sanic_error_handler(app)
|
|
692
|
+
|
|
693
|
+
# Reset Sanic warnings filter that allows the triggering of Sanic warnings
|
|
694
|
+
warnings.filterwarnings("ignore", category=DeprecationWarning, module=r"sanic.*")
|
|
695
|
+
|
|
696
|
+
# Set up the Sanic-JWT extension
|
|
697
|
+
if jwt_secret and jwt_method:
|
|
698
|
+
# `sanic-jwt` depends on having an available event loop when making the call to
|
|
699
|
+
# `Initialize`. If there is none, the server startup will fail with
|
|
700
|
+
# `There is no current event loop in thread 'MainThread'`.
|
|
701
|
+
try:
|
|
702
|
+
_ = asyncio.get_running_loop()
|
|
703
|
+
except RuntimeError:
|
|
704
|
+
new_loop = asyncio.new_event_loop()
|
|
705
|
+
asyncio.set_event_loop(new_loop)
|
|
706
|
+
|
|
707
|
+
# since we only want to check signatures, we don't actually care
|
|
708
|
+
# about the JWT method and set the passed secret as either symmetric
|
|
709
|
+
# or asymmetric key. jwt lib will choose the right one based on method
|
|
710
|
+
app.config["USE_JWT"] = True
|
|
711
|
+
Initialize(
|
|
712
|
+
app,
|
|
713
|
+
secret=jwt_secret,
|
|
714
|
+
private_key=jwt_private_key,
|
|
715
|
+
authenticate=authenticate,
|
|
716
|
+
algorithm=jwt_method,
|
|
717
|
+
user_id="username",
|
|
718
|
+
)
|
|
719
|
+
|
|
720
|
+
app.ctx.agent = agent
|
|
721
|
+
# Initialize shared object of type unsigned int for tracking
|
|
722
|
+
# the number of active training processes
|
|
723
|
+
app.ctx.active_training_processes = multiprocessing.Value("I", 0)
|
|
724
|
+
|
|
725
|
+
@app.exception(ErrorResponse)
|
|
726
|
+
async def handle_error_response(
|
|
727
|
+
request: Request, exception: ErrorResponse
|
|
728
|
+
) -> HTTPResponse:
|
|
729
|
+
return response.json(exception.error_info, status=exception.status)
|
|
730
|
+
|
|
731
|
+
add_root_route(app)
|
|
732
|
+
|
|
733
|
+
@app.get("/version")
|
|
734
|
+
async def version(request: Request) -> HTTPResponse:
|
|
735
|
+
"""Respond with the version number of the installed Rasa."""
|
|
736
|
+
return response.json(
|
|
737
|
+
{
|
|
738
|
+
"version": rasa.__version__,
|
|
739
|
+
"minimum_compatible_version": MINIMUM_COMPATIBLE_VERSION,
|
|
740
|
+
}
|
|
741
|
+
)
|
|
742
|
+
|
|
743
|
+
@app.get("/status")
|
|
744
|
+
@requires_auth(app, auth_token)
|
|
745
|
+
@ensure_loaded_agent(app)
|
|
746
|
+
async def status(request: Request) -> HTTPResponse:
|
|
747
|
+
"""Respond with the model name and the fingerprint of that model."""
|
|
748
|
+
return response.json(
|
|
749
|
+
{
|
|
750
|
+
"model_file": app.ctx.agent.processor.model_filename,
|
|
751
|
+
"model_id": app.ctx.agent.model_id,
|
|
752
|
+
"num_active_training_jobs": app.ctx.active_training_processes.value,
|
|
753
|
+
}
|
|
754
|
+
)
|
|
755
|
+
|
|
756
|
+
@app.get("/conversations/<conversation_id:path>/tracker")
|
|
757
|
+
@requires_auth(app, auth_token)
|
|
758
|
+
@ensure_loaded_agent(app)
|
|
759
|
+
async def retrieve_tracker(request: Request, conversation_id: Text) -> HTTPResponse:
|
|
760
|
+
"""Get a dump of a conversation's tracker including its events."""
|
|
761
|
+
verbosity = event_verbosity_parameter(request, EventVerbosity.AFTER_RESTART)
|
|
762
|
+
until_time = rasa.utils.endpoints.float_arg(request, "until")
|
|
763
|
+
should_start_session = rasa.utils.endpoints.bool_arg(
|
|
764
|
+
request, "start_session", default=True
|
|
765
|
+
)
|
|
766
|
+
|
|
767
|
+
if should_start_session:
|
|
768
|
+
tracker = (
|
|
769
|
+
await app.ctx.agent.processor.fetch_full_tracker_with_initial_session(
|
|
770
|
+
conversation_id,
|
|
771
|
+
output_channel=CollectingOutputChannel(),
|
|
772
|
+
)
|
|
773
|
+
)
|
|
774
|
+
else:
|
|
775
|
+
tracker = await app.ctx.agent.processor.get_tracker(conversation_id)
|
|
776
|
+
|
|
777
|
+
try:
|
|
778
|
+
if until_time is not None:
|
|
779
|
+
tracker = tracker.travel_back_in_time(until_time)
|
|
780
|
+
|
|
781
|
+
state = tracker.current_state(verbosity)
|
|
782
|
+
return response.json(state)
|
|
783
|
+
except Exception as e:
|
|
784
|
+
logger.debug(traceback.format_exc())
|
|
785
|
+
raise ErrorResponse(
|
|
786
|
+
HTTPStatus.INTERNAL_SERVER_ERROR,
|
|
787
|
+
"ConversationError",
|
|
788
|
+
f"An unexpected error occurred. Error: {e}",
|
|
789
|
+
)
|
|
790
|
+
|
|
791
|
+
@app.post("/conversations/<conversation_id:path>/tracker/events")
|
|
792
|
+
@requires_auth(app, auth_token)
|
|
793
|
+
@ensure_loaded_agent(app)
|
|
794
|
+
async def append_events(request: Request, conversation_id: Text) -> HTTPResponse:
|
|
795
|
+
"""Append a list of events to the state of a conversation."""
|
|
796
|
+
validate_events_in_request_body(request)
|
|
797
|
+
|
|
798
|
+
verbosity = event_verbosity_parameter(request, EventVerbosity.AFTER_RESTART)
|
|
799
|
+
|
|
800
|
+
try:
|
|
801
|
+
async with app.ctx.agent.lock_store.lock(conversation_id):
|
|
802
|
+
processor = app.ctx.agent.processor
|
|
803
|
+
events = _get_events_from_request_body(request)
|
|
804
|
+
|
|
805
|
+
tracker = await update_conversation_with_events(
|
|
806
|
+
conversation_id, processor, app.ctx.agent.domain, events
|
|
807
|
+
)
|
|
808
|
+
|
|
809
|
+
output_channel = _get_output_channel(request, tracker)
|
|
810
|
+
|
|
811
|
+
if rasa.utils.endpoints.bool_arg(
|
|
812
|
+
request, EXECUTE_SIDE_EFFECTS_QUERY_KEY, False
|
|
813
|
+
):
|
|
814
|
+
await processor.execute_side_effects(
|
|
815
|
+
events, tracker, output_channel
|
|
816
|
+
)
|
|
817
|
+
await app.ctx.agent.tracker_store.save(tracker)
|
|
818
|
+
return response.json(tracker.current_state(verbosity))
|
|
819
|
+
except Exception as e:
|
|
820
|
+
logger.debug(traceback.format_exc())
|
|
821
|
+
raise ErrorResponse(
|
|
822
|
+
HTTPStatus.INTERNAL_SERVER_ERROR,
|
|
823
|
+
"ConversationError",
|
|
824
|
+
f"An unexpected error occurred. Error: {e}",
|
|
825
|
+
)
|
|
826
|
+
|
|
827
|
+
def _get_events_from_request_body(request: Request) -> List[Event]:
|
|
828
|
+
events = request.json
|
|
829
|
+
|
|
830
|
+
if not isinstance(events, list):
|
|
831
|
+
events = [events]
|
|
832
|
+
|
|
833
|
+
events = [Event.from_parameters(event) for event in events]
|
|
834
|
+
events = [event for event in events if event]
|
|
835
|
+
|
|
836
|
+
if not events:
|
|
837
|
+
rasa.shared.utils.io.raise_warning(
|
|
838
|
+
f"Append event called, but could not extract a valid event. "
|
|
839
|
+
f"Request JSON: {request.json}"
|
|
840
|
+
)
|
|
841
|
+
raise ErrorResponse(
|
|
842
|
+
HTTPStatus.BAD_REQUEST,
|
|
843
|
+
"BadRequest",
|
|
844
|
+
"Couldn't extract a proper event from the request body.",
|
|
845
|
+
{"parameter": "", "in": "body"},
|
|
846
|
+
)
|
|
847
|
+
|
|
848
|
+
return events
|
|
849
|
+
|
|
850
|
+
@app.put("/conversations/<conversation_id:path>/tracker/events")
|
|
851
|
+
@requires_auth(app, auth_token)
|
|
852
|
+
@ensure_loaded_agent(app)
|
|
853
|
+
async def replace_events(request: Request, conversation_id: Text) -> HTTPResponse:
|
|
854
|
+
"""Use a list of events to set a conversations tracker to a state."""
|
|
855
|
+
validate_events_in_request_body(request)
|
|
856
|
+
|
|
857
|
+
verbosity = event_verbosity_parameter(request, EventVerbosity.AFTER_RESTART)
|
|
858
|
+
|
|
859
|
+
try:
|
|
860
|
+
async with app.ctx.agent.lock_store.lock(conversation_id):
|
|
861
|
+
tracker = DialogueStateTracker.from_dict(
|
|
862
|
+
conversation_id, request.json, app.ctx.agent.domain.slots
|
|
863
|
+
)
|
|
864
|
+
|
|
865
|
+
# will override an existing tracker with the same id!
|
|
866
|
+
await app.ctx.agent.tracker_store.save(tracker)
|
|
867
|
+
|
|
868
|
+
return response.json(tracker.current_state(verbosity))
|
|
869
|
+
except Exception as e:
|
|
870
|
+
logger.debug(traceback.format_exc())
|
|
871
|
+
raise ErrorResponse(
|
|
872
|
+
HTTPStatus.INTERNAL_SERVER_ERROR,
|
|
873
|
+
"ConversationError",
|
|
874
|
+
f"An unexpected error occurred. Error: {e}",
|
|
875
|
+
)
|
|
876
|
+
|
|
877
|
+
@app.get("/conversations/<conversation_id:path>/story")
|
|
878
|
+
@requires_auth(app, auth_token)
|
|
879
|
+
@ensure_loaded_agent(app)
|
|
880
|
+
async def retrieve_story(request: Request, conversation_id: Text) -> HTTPResponse:
|
|
881
|
+
"""Get an end-to-end story corresponding to this conversation."""
|
|
882
|
+
until_time = rasa.utils.endpoints.float_arg(request, "until")
|
|
883
|
+
fetch_all_sessions = rasa.utils.endpoints.bool_arg(
|
|
884
|
+
request, "all_sessions", default=False
|
|
885
|
+
)
|
|
886
|
+
|
|
887
|
+
try:
|
|
888
|
+
stories = await get_test_stories(
|
|
889
|
+
app.ctx.agent.processor,
|
|
890
|
+
conversation_id,
|
|
891
|
+
until_time,
|
|
892
|
+
fetch_all_sessions=fetch_all_sessions,
|
|
893
|
+
)
|
|
894
|
+
return response.text(stories)
|
|
895
|
+
except Exception as e:
|
|
896
|
+
logger.debug(traceback.format_exc())
|
|
897
|
+
raise ErrorResponse(
|
|
898
|
+
HTTPStatus.INTERNAL_SERVER_ERROR,
|
|
899
|
+
"ConversationError",
|
|
900
|
+
f"An unexpected error occurred. Error: {e}",
|
|
901
|
+
)
|
|
902
|
+
|
|
903
|
+
@app.post("/conversations/<conversation_id:path>/execute")
|
|
904
|
+
@requires_auth(app, auth_token)
|
|
905
|
+
@ensure_loaded_agent(app)
|
|
906
|
+
@ensure_conversation_exists()
|
|
907
|
+
async def execute_action(request: Request, conversation_id: Text) -> HTTPResponse:
|
|
908
|
+
rasa.shared.utils.io.raise_warning(
|
|
909
|
+
'The "POST /conversations/<conversation_id>/execute"'
|
|
910
|
+
" endpoint is deprecated. Inserting actions to the tracker externally"
|
|
911
|
+
" should be avoided. Actions should be predicted by the policies only.",
|
|
912
|
+
category=FutureWarning,
|
|
913
|
+
)
|
|
914
|
+
request_params = request.json
|
|
915
|
+
|
|
916
|
+
action_to_execute = request_params.get("name", None)
|
|
917
|
+
|
|
918
|
+
if not action_to_execute:
|
|
919
|
+
raise ErrorResponse(
|
|
920
|
+
HTTPStatus.BAD_REQUEST,
|
|
921
|
+
"BadRequest",
|
|
922
|
+
"Name of the action not provided in request body.",
|
|
923
|
+
{"parameter": "name", "in": "body"},
|
|
924
|
+
)
|
|
925
|
+
|
|
926
|
+
policy = request_params.get("policy", None)
|
|
927
|
+
confidence = request_params.get("confidence", None)
|
|
928
|
+
verbosity = event_verbosity_parameter(request, EventVerbosity.AFTER_RESTART)
|
|
929
|
+
|
|
930
|
+
try:
|
|
931
|
+
async with app.ctx.agent.lock_store.lock(conversation_id):
|
|
932
|
+
tracker = await (
|
|
933
|
+
app.ctx.agent.processor.fetch_tracker_and_update_session(
|
|
934
|
+
conversation_id
|
|
935
|
+
)
|
|
936
|
+
)
|
|
937
|
+
|
|
938
|
+
output_channel = _get_output_channel(request, tracker)
|
|
939
|
+
await app.ctx.agent.execute_action(
|
|
940
|
+
conversation_id,
|
|
941
|
+
action_to_execute,
|
|
942
|
+
output_channel,
|
|
943
|
+
policy,
|
|
944
|
+
confidence,
|
|
945
|
+
)
|
|
946
|
+
|
|
947
|
+
except Exception as e:
|
|
948
|
+
logger.debug(traceback.format_exc())
|
|
949
|
+
raise ErrorResponse(
|
|
950
|
+
HTTPStatus.INTERNAL_SERVER_ERROR,
|
|
951
|
+
"ConversationError",
|
|
952
|
+
f"An unexpected error occurred. Error: {e}",
|
|
953
|
+
)
|
|
954
|
+
|
|
955
|
+
state = tracker.current_state(verbosity)
|
|
956
|
+
|
|
957
|
+
response_body: Dict[Text, Any] = {"tracker": state}
|
|
958
|
+
|
|
959
|
+
if isinstance(output_channel, CollectingOutputChannel):
|
|
960
|
+
response_body["messages"] = output_channel.messages
|
|
961
|
+
|
|
962
|
+
return response.json(response_body)
|
|
963
|
+
|
|
964
|
+
@app.post("/conversations/<conversation_id:path>/trigger_intent")
|
|
965
|
+
@requires_auth(app, auth_token)
|
|
966
|
+
@ensure_loaded_agent(app)
|
|
967
|
+
async def trigger_intent(request: Request, conversation_id: Text) -> HTTPResponse:
|
|
968
|
+
request_params = request.json
|
|
969
|
+
|
|
970
|
+
intent_to_trigger = request_params.get("name")
|
|
971
|
+
entities = request_params.get("entities", [])
|
|
972
|
+
|
|
973
|
+
if not intent_to_trigger:
|
|
974
|
+
raise ErrorResponse(
|
|
975
|
+
HTTPStatus.BAD_REQUEST,
|
|
976
|
+
"BadRequest",
|
|
977
|
+
"Name of the intent not provided in request body.",
|
|
978
|
+
{"parameter": "name", "in": "body"},
|
|
979
|
+
)
|
|
980
|
+
|
|
981
|
+
verbosity = event_verbosity_parameter(request, EventVerbosity.AFTER_RESTART)
|
|
982
|
+
|
|
983
|
+
try:
|
|
984
|
+
async with app.ctx.agent.lock_store.lock(conversation_id):
|
|
985
|
+
tracker = await (
|
|
986
|
+
app.ctx.agent.processor.fetch_tracker_and_update_session(
|
|
987
|
+
conversation_id
|
|
988
|
+
)
|
|
989
|
+
)
|
|
990
|
+
output_channel = _get_output_channel(request, tracker)
|
|
991
|
+
if intent_to_trigger not in app.ctx.agent.domain.intents:
|
|
992
|
+
raise ErrorResponse(
|
|
993
|
+
HTTPStatus.NOT_FOUND,
|
|
994
|
+
"NotFound",
|
|
995
|
+
f"The intent {trigger_intent} does not exist in the domain.",
|
|
996
|
+
)
|
|
997
|
+
await app.ctx.agent.trigger_intent(
|
|
998
|
+
intent_name=intent_to_trigger,
|
|
999
|
+
entities=entities,
|
|
1000
|
+
output_channel=output_channel,
|
|
1001
|
+
tracker=tracker,
|
|
1002
|
+
)
|
|
1003
|
+
except ErrorResponse:
|
|
1004
|
+
raise
|
|
1005
|
+
except Exception as e:
|
|
1006
|
+
logger.debug(traceback.format_exc())
|
|
1007
|
+
raise ErrorResponse(
|
|
1008
|
+
HTTPStatus.INTERNAL_SERVER_ERROR,
|
|
1009
|
+
"ConversationError",
|
|
1010
|
+
f"An unexpected error occurred. Error: {e}",
|
|
1011
|
+
)
|
|
1012
|
+
|
|
1013
|
+
state = tracker.current_state(verbosity)
|
|
1014
|
+
|
|
1015
|
+
response_body: Dict[Text, Any] = {"tracker": state}
|
|
1016
|
+
|
|
1017
|
+
if isinstance(output_channel, CollectingOutputChannel):
|
|
1018
|
+
response_body["messages"] = output_channel.messages
|
|
1019
|
+
|
|
1020
|
+
return response.json(response_body)
|
|
1021
|
+
|
|
1022
|
+
@app.post("/conversations/<conversation_id:path>/predict")
|
|
1023
|
+
@requires_auth(app, auth_token)
|
|
1024
|
+
@ensure_loaded_agent(app)
|
|
1025
|
+
@ensure_conversation_exists()
|
|
1026
|
+
async def predict(request: Request, conversation_id: Text) -> HTTPResponse:
|
|
1027
|
+
try:
|
|
1028
|
+
# Fetches the appropriate bot response in a json format
|
|
1029
|
+
responses = await app.ctx.agent.predict_next_for_sender_id(conversation_id)
|
|
1030
|
+
responses["scores"] = sorted(
|
|
1031
|
+
responses["scores"], key=lambda k: (-k["score"], k["action"])
|
|
1032
|
+
)
|
|
1033
|
+
return response.json(responses)
|
|
1034
|
+
except Exception as e:
|
|
1035
|
+
logger.debug(traceback.format_exc())
|
|
1036
|
+
raise ErrorResponse(
|
|
1037
|
+
HTTPStatus.INTERNAL_SERVER_ERROR,
|
|
1038
|
+
"ConversationError",
|
|
1039
|
+
f"An unexpected error occurred. Error: {e}",
|
|
1040
|
+
)
|
|
1041
|
+
|
|
1042
|
+
@app.post("/conversations/<conversation_id:path>/messages")
|
|
1043
|
+
@requires_auth(app, auth_token)
|
|
1044
|
+
@ensure_loaded_agent(app)
|
|
1045
|
+
async def add_message(request: Request, conversation_id: Text) -> HTTPResponse:
|
|
1046
|
+
validate_request_body(
|
|
1047
|
+
request,
|
|
1048
|
+
"No message defined in request body. Add a message to the request body in "
|
|
1049
|
+
"order to add it to the tracker.",
|
|
1050
|
+
)
|
|
1051
|
+
|
|
1052
|
+
request_params = request.json
|
|
1053
|
+
|
|
1054
|
+
message = request_params.get("text")
|
|
1055
|
+
sender = request_params.get("sender")
|
|
1056
|
+
parse_data = request_params.get("parse_data")
|
|
1057
|
+
|
|
1058
|
+
verbosity = event_verbosity_parameter(request, EventVerbosity.AFTER_RESTART)
|
|
1059
|
+
|
|
1060
|
+
# TODO: implement for agent / bot
|
|
1061
|
+
if sender != "user":
|
|
1062
|
+
raise ErrorResponse(
|
|
1063
|
+
HTTPStatus.BAD_REQUEST,
|
|
1064
|
+
"BadRequest",
|
|
1065
|
+
"Currently, only user messages can be passed to this endpoint. "
|
|
1066
|
+
"Messages of sender '{}' cannot be handled.".format(sender),
|
|
1067
|
+
{"parameter": "sender", "in": "body"},
|
|
1068
|
+
)
|
|
1069
|
+
|
|
1070
|
+
user_message = UserMessage(message, None, conversation_id, parse_data)
|
|
1071
|
+
|
|
1072
|
+
try:
|
|
1073
|
+
async with app.ctx.agent.lock_store.lock(conversation_id):
|
|
1074
|
+
# cf. processor.handle_message (ignoring prediction loop run)
|
|
1075
|
+
tracker = await app.ctx.agent.processor.log_message(
|
|
1076
|
+
user_message, should_save_tracker=False
|
|
1077
|
+
)
|
|
1078
|
+
tracker = await app.ctx.agent.processor.run_action_extract_slots(
|
|
1079
|
+
user_message.output_channel, tracker
|
|
1080
|
+
)
|
|
1081
|
+
await app.ctx.agent.processor.save_tracker(tracker)
|
|
1082
|
+
|
|
1083
|
+
return response.json(tracker.current_state(verbosity))
|
|
1084
|
+
except Exception as e:
|
|
1085
|
+
logger.debug(traceback.format_exc())
|
|
1086
|
+
raise ErrorResponse(
|
|
1087
|
+
HTTPStatus.INTERNAL_SERVER_ERROR,
|
|
1088
|
+
"ConversationError",
|
|
1089
|
+
f"An unexpected error occurred. Error: {e}",
|
|
1090
|
+
)
|
|
1091
|
+
|
|
1092
|
+
@app.post("/model/train")
|
|
1093
|
+
@requires_auth(app, auth_token)
|
|
1094
|
+
@async_if_callback_url
|
|
1095
|
+
@run_in_thread
|
|
1096
|
+
@inject_temp_dir
|
|
1097
|
+
async def train(request: Request, temporary_directory: Path) -> HTTPResponse:
|
|
1098
|
+
validate_request_body(
|
|
1099
|
+
request,
|
|
1100
|
+
"You must provide training data in the request body in order to "
|
|
1101
|
+
"train your model.",
|
|
1102
|
+
)
|
|
1103
|
+
|
|
1104
|
+
training_payload = _training_payload_from_yaml(request, temporary_directory)
|
|
1105
|
+
|
|
1106
|
+
try:
|
|
1107
|
+
with app.ctx.active_training_processes.get_lock():
|
|
1108
|
+
app.ctx.active_training_processes.value += 1
|
|
1109
|
+
|
|
1110
|
+
from rasa.model_training import train
|
|
1111
|
+
|
|
1112
|
+
# pass `None` to run in default executor
|
|
1113
|
+
training_result = await train(**training_payload)
|
|
1114
|
+
|
|
1115
|
+
if training_result.model:
|
|
1116
|
+
filename = os.path.basename(training_result.model)
|
|
1117
|
+
|
|
1118
|
+
return await response.file(
|
|
1119
|
+
training_result.model,
|
|
1120
|
+
filename=filename,
|
|
1121
|
+
headers={"filename": filename},
|
|
1122
|
+
)
|
|
1123
|
+
else:
|
|
1124
|
+
raise ErrorResponse(
|
|
1125
|
+
HTTPStatus.INTERNAL_SERVER_ERROR,
|
|
1126
|
+
"TrainingError",
|
|
1127
|
+
"Ran training, but it finished without a trained model.",
|
|
1128
|
+
)
|
|
1129
|
+
except ErrorResponse as e:
|
|
1130
|
+
raise e
|
|
1131
|
+
except InvalidDomain as e:
|
|
1132
|
+
raise ErrorResponse(
|
|
1133
|
+
HTTPStatus.BAD_REQUEST,
|
|
1134
|
+
"InvalidDomainError",
|
|
1135
|
+
f"Provided domain file is invalid. Error: {e}",
|
|
1136
|
+
)
|
|
1137
|
+
except Exception as e:
|
|
1138
|
+
logger.error(traceback.format_exc())
|
|
1139
|
+
raise ErrorResponse(
|
|
1140
|
+
HTTPStatus.INTERNAL_SERVER_ERROR,
|
|
1141
|
+
"TrainingError",
|
|
1142
|
+
f"An unexpected error occurred during training. Error: {e}",
|
|
1143
|
+
)
|
|
1144
|
+
finally:
|
|
1145
|
+
with app.ctx.active_training_processes.get_lock():
|
|
1146
|
+
app.ctx.active_training_processes.value -= 1
|
|
1147
|
+
|
|
1148
|
+
@app.post("/model/test/stories")
|
|
1149
|
+
@requires_auth(app, auth_token)
|
|
1150
|
+
@ensure_loaded_agent(app, require_core_is_ready=True)
|
|
1151
|
+
@inject_temp_dir
|
|
1152
|
+
async def evaluate_stories(
|
|
1153
|
+
request: Request, temporary_directory: Path
|
|
1154
|
+
) -> HTTPResponse:
|
|
1155
|
+
"""Evaluate stories against the currently loaded model."""
|
|
1156
|
+
validate_request_body(
|
|
1157
|
+
request,
|
|
1158
|
+
"You must provide some stories in the request body in order to "
|
|
1159
|
+
"evaluate your model.",
|
|
1160
|
+
)
|
|
1161
|
+
|
|
1162
|
+
test_data = _test_data_file_from_payload(request, temporary_directory)
|
|
1163
|
+
|
|
1164
|
+
e2e = rasa.utils.endpoints.bool_arg(request, "e2e", default=False)
|
|
1165
|
+
|
|
1166
|
+
try:
|
|
1167
|
+
evaluation = await test(
|
|
1168
|
+
test_data, app.ctx.agent, e2e=e2e, disable_plotting=True
|
|
1169
|
+
)
|
|
1170
|
+
return response.json(evaluation)
|
|
1171
|
+
except Exception as e:
|
|
1172
|
+
logger.error(traceback.format_exc())
|
|
1173
|
+
raise ErrorResponse(
|
|
1174
|
+
HTTPStatus.INTERNAL_SERVER_ERROR,
|
|
1175
|
+
"TestingError",
|
|
1176
|
+
f"An unexpected error occurred during evaluation. Error: {e}",
|
|
1177
|
+
)
|
|
1178
|
+
|
|
1179
|
+
@app.post("/model/test/intents")
|
|
1180
|
+
@requires_auth(app, auth_token)
|
|
1181
|
+
@async_if_callback_url
|
|
1182
|
+
@run_in_thread
|
|
1183
|
+
@inject_temp_dir
|
|
1184
|
+
async def evaluate_intents(
|
|
1185
|
+
request: Request, temporary_directory: Path
|
|
1186
|
+
) -> HTTPResponse:
|
|
1187
|
+
"""Evaluate intents against a Rasa model."""
|
|
1188
|
+
validate_request_body(
|
|
1189
|
+
request,
|
|
1190
|
+
"You must provide some nlu data in the request body in order to "
|
|
1191
|
+
"evaluate your model.",
|
|
1192
|
+
)
|
|
1193
|
+
|
|
1194
|
+
cross_validation_folds = request.args.get("cross_validation_folds")
|
|
1195
|
+
is_yaml_payload = request.headers.get("Content-type") == YAML_CONTENT_TYPE
|
|
1196
|
+
test_coroutine = None
|
|
1197
|
+
|
|
1198
|
+
if is_yaml_payload:
|
|
1199
|
+
payload = _training_payload_from_yaml(request, temporary_directory)
|
|
1200
|
+
config_file = payload.get("config")
|
|
1201
|
+
test_data = payload.get("training_files")
|
|
1202
|
+
|
|
1203
|
+
if cross_validation_folds:
|
|
1204
|
+
test_coroutine = _cross_validate(
|
|
1205
|
+
test_data, config_file, int(cross_validation_folds)
|
|
1206
|
+
)
|
|
1207
|
+
else:
|
|
1208
|
+
payload = _nlu_training_payload_from_json(request, temporary_directory)
|
|
1209
|
+
test_data = payload.get("training_files")
|
|
1210
|
+
|
|
1211
|
+
if cross_validation_folds:
|
|
1212
|
+
raise ErrorResponse(
|
|
1213
|
+
HTTPStatus.BAD_REQUEST,
|
|
1214
|
+
"TestingError",
|
|
1215
|
+
"Cross-validation is only supported for YAML data.",
|
|
1216
|
+
)
|
|
1217
|
+
|
|
1218
|
+
if not cross_validation_folds:
|
|
1219
|
+
test_coroutine = _evaluate_model_using_test_set(
|
|
1220
|
+
request.args.get("model"), test_data
|
|
1221
|
+
)
|
|
1222
|
+
|
|
1223
|
+
try:
|
|
1224
|
+
if test_coroutine is not None:
|
|
1225
|
+
evaluation = await test_coroutine
|
|
1226
|
+
return response.json(evaluation)
|
|
1227
|
+
except Exception as e:
|
|
1228
|
+
logger.error(traceback.format_exc())
|
|
1229
|
+
raise ErrorResponse(
|
|
1230
|
+
HTTPStatus.INTERNAL_SERVER_ERROR,
|
|
1231
|
+
"TestingError",
|
|
1232
|
+
f"An unexpected error occurred during evaluation. Error: {e}",
|
|
1233
|
+
)
|
|
1234
|
+
|
|
1235
|
+
async def _evaluate_model_using_test_set(
|
|
1236
|
+
model_path: Text, test_data_file: Text
|
|
1237
|
+
) -> Dict:
|
|
1238
|
+
logger.info("Starting model evaluation using test set.")
|
|
1239
|
+
|
|
1240
|
+
eval_agent = app.ctx.agent
|
|
1241
|
+
|
|
1242
|
+
if model_path:
|
|
1243
|
+
model_server = app.ctx.agent.model_server
|
|
1244
|
+
if model_server is not None:
|
|
1245
|
+
model_server = model_server.copy()
|
|
1246
|
+
model_server.url = model_path
|
|
1247
|
+
# Set wait time between pulls to `0` so that the agent does not schedule
|
|
1248
|
+
# a job to pull the model from the server
|
|
1249
|
+
model_server.kwargs["wait_time_between_pulls"] = 0
|
|
1250
|
+
eval_agent = await _load_agent(
|
|
1251
|
+
model_path=model_path,
|
|
1252
|
+
model_server=model_server,
|
|
1253
|
+
remote_storage=app.ctx.agent.remote_storage,
|
|
1254
|
+
)
|
|
1255
|
+
|
|
1256
|
+
data_path = os.path.abspath(test_data_file)
|
|
1257
|
+
|
|
1258
|
+
if not eval_agent.is_ready():
|
|
1259
|
+
raise ErrorResponse(
|
|
1260
|
+
HTTPStatus.CONFLICT, "Conflict", "Loaded model file not found."
|
|
1261
|
+
)
|
|
1262
|
+
|
|
1263
|
+
return await rasa.nlu.test.run_evaluation(
|
|
1264
|
+
data_path, eval_agent.processor, disable_plotting=True, report_as_dict=True
|
|
1265
|
+
)
|
|
1266
|
+
|
|
1267
|
+
async def _cross_validate(data_file: Text, config_file: Text, folds: int) -> Dict:
|
|
1268
|
+
logger.info(f"Starting cross-validation with {folds} folds.")
|
|
1269
|
+
importer = TrainingDataImporter.load_from_dict(
|
|
1270
|
+
config=None, config_path=config_file, training_data_paths=[data_file]
|
|
1271
|
+
)
|
|
1272
|
+
config = importer.get_config()
|
|
1273
|
+
nlu_data = importer.get_nlu_data()
|
|
1274
|
+
|
|
1275
|
+
evaluations = await rasa.nlu.test.cross_validate(
|
|
1276
|
+
data=nlu_data,
|
|
1277
|
+
n_folds=folds,
|
|
1278
|
+
nlu_config=config,
|
|
1279
|
+
disable_plotting=True,
|
|
1280
|
+
errors=True,
|
|
1281
|
+
report_as_dict=True,
|
|
1282
|
+
)
|
|
1283
|
+
evaluation_results = _get_evaluation_results(*evaluations)
|
|
1284
|
+
|
|
1285
|
+
return evaluation_results
|
|
1286
|
+
|
|
1287
|
+
def _get_evaluation_results(
|
|
1288
|
+
intent_report: CVEvaluationResult,
|
|
1289
|
+
entity_report: CVEvaluationResult,
|
|
1290
|
+
response_selector_report: CVEvaluationResult,
|
|
1291
|
+
) -> Dict[Text, Any]:
|
|
1292
|
+
eval_name_mapping = {
|
|
1293
|
+
"intent_evaluation": intent_report,
|
|
1294
|
+
"entity_evaluation": entity_report,
|
|
1295
|
+
"response_selection_evaluation": response_selector_report,
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
result: DefaultDict[Text, Any] = defaultdict(dict)
|
|
1299
|
+
for evaluation_name, evaluation in eval_name_mapping.items():
|
|
1300
|
+
report = evaluation.evaluation.get("report", {})
|
|
1301
|
+
averages = report.get("weighted avg", {})
|
|
1302
|
+
result[evaluation_name]["report"] = report
|
|
1303
|
+
result[evaluation_name]["precision"] = averages.get("precision")
|
|
1304
|
+
result[evaluation_name]["f1_score"] = averages.get("1-score")
|
|
1305
|
+
result[evaluation_name]["errors"] = evaluation.evaluation.get("errors", [])
|
|
1306
|
+
|
|
1307
|
+
return result
|
|
1308
|
+
|
|
1309
|
+
@app.post("/model/predict")
|
|
1310
|
+
@requires_auth(app, auth_token)
|
|
1311
|
+
@ensure_loaded_agent(app, require_core_is_ready=True)
|
|
1312
|
+
async def tracker_predict(request: Request) -> HTTPResponse:
|
|
1313
|
+
"""Given a list of events, predicts the next action."""
|
|
1314
|
+
validate_events_in_request_body(request)
|
|
1315
|
+
|
|
1316
|
+
verbosity = event_verbosity_parameter(request, EventVerbosity.AFTER_RESTART)
|
|
1317
|
+
request_params = request.json
|
|
1318
|
+
try:
|
|
1319
|
+
tracker = DialogueStateTracker.from_dict(
|
|
1320
|
+
DEFAULT_SENDER_ID, request_params, app.ctx.agent.domain.slots
|
|
1321
|
+
)
|
|
1322
|
+
except Exception as e:
|
|
1323
|
+
logger.debug(traceback.format_exc())
|
|
1324
|
+
raise ErrorResponse(
|
|
1325
|
+
HTTPStatus.BAD_REQUEST,
|
|
1326
|
+
"BadRequest",
|
|
1327
|
+
f"Supplied events are not valid. {e}",
|
|
1328
|
+
{"parameter": "", "in": "body"},
|
|
1329
|
+
)
|
|
1330
|
+
|
|
1331
|
+
try:
|
|
1332
|
+
result = await app.ctx.agent.predict_next_with_tracker(tracker, verbosity)
|
|
1333
|
+
|
|
1334
|
+
return response.json(result)
|
|
1335
|
+
except Exception as e:
|
|
1336
|
+
logger.debug(traceback.format_exc())
|
|
1337
|
+
raise ErrorResponse(
|
|
1338
|
+
HTTPStatus.INTERNAL_SERVER_ERROR,
|
|
1339
|
+
"PredictionError",
|
|
1340
|
+
f"An unexpected error occurred. Error: {e}",
|
|
1341
|
+
)
|
|
1342
|
+
|
|
1343
|
+
@app.post("/model/parse")
|
|
1344
|
+
@requires_auth(app, auth_token)
|
|
1345
|
+
@ensure_loaded_agent(app)
|
|
1346
|
+
async def parse(request: Request) -> HTTPResponse:
|
|
1347
|
+
validate_request_body(
|
|
1348
|
+
request,
|
|
1349
|
+
"No text message defined in request_body. Add text message to request body "
|
|
1350
|
+
"in order to obtain the intent and extracted entities.",
|
|
1351
|
+
)
|
|
1352
|
+
emulation_mode = request.args.get("emulation_mode")
|
|
1353
|
+
emulator = _create_emulator(emulation_mode)
|
|
1354
|
+
|
|
1355
|
+
try:
|
|
1356
|
+
data = emulator.normalise_request_json(request.json)
|
|
1357
|
+
try:
|
|
1358
|
+
parsed_data = await app.ctx.agent.parse_message(data.get("text"))
|
|
1359
|
+
except Exception as e:
|
|
1360
|
+
logger.debug(traceback.format_exc())
|
|
1361
|
+
raise ErrorResponse(
|
|
1362
|
+
HTTPStatus.BAD_REQUEST,
|
|
1363
|
+
"ParsingError",
|
|
1364
|
+
f"An unexpected error occurred. Error: {e}",
|
|
1365
|
+
)
|
|
1366
|
+
response_data = emulator.normalise_response_json(parsed_data)
|
|
1367
|
+
|
|
1368
|
+
return response.json(response_data)
|
|
1369
|
+
|
|
1370
|
+
except Exception as e:
|
|
1371
|
+
logger.debug(traceback.format_exc())
|
|
1372
|
+
raise ErrorResponse(
|
|
1373
|
+
HTTPStatus.INTERNAL_SERVER_ERROR,
|
|
1374
|
+
"ParsingError",
|
|
1375
|
+
f"An unexpected error occurred. Error: {e}",
|
|
1376
|
+
)
|
|
1377
|
+
|
|
1378
|
+
@app.put("/model")
|
|
1379
|
+
@requires_auth(app, auth_token)
|
|
1380
|
+
async def load_model(request: Request) -> HTTPResponse:
|
|
1381
|
+
validate_request_body(request, "No path to model file defined in request_body.")
|
|
1382
|
+
|
|
1383
|
+
model_path = request.json.get("model_file", None)
|
|
1384
|
+
model_server = request.json.get("model_server", None)
|
|
1385
|
+
|
|
1386
|
+
remote_storage_argument = request.json.get("remote_storage", None)
|
|
1387
|
+
remote_storage = (
|
|
1388
|
+
parse_remote_storage(remote_storage_argument)
|
|
1389
|
+
if remote_storage_argument
|
|
1390
|
+
else None
|
|
1391
|
+
)
|
|
1392
|
+
|
|
1393
|
+
if model_server:
|
|
1394
|
+
try:
|
|
1395
|
+
model_server = EndpointConfig.from_dict(model_server)
|
|
1396
|
+
except TypeError as e:
|
|
1397
|
+
logger.debug(traceback.format_exc())
|
|
1398
|
+
raise ErrorResponse(
|
|
1399
|
+
HTTPStatus.BAD_REQUEST,
|
|
1400
|
+
"BadRequest",
|
|
1401
|
+
f"Supplied 'model_server' is not valid. Error: {e}",
|
|
1402
|
+
{"parameter": "model_server", "in": "body"},
|
|
1403
|
+
)
|
|
1404
|
+
|
|
1405
|
+
new_agent = await _load_agent(
|
|
1406
|
+
model_path=model_path,
|
|
1407
|
+
model_server=model_server,
|
|
1408
|
+
remote_storage=remote_storage,
|
|
1409
|
+
endpoints=endpoints,
|
|
1410
|
+
)
|
|
1411
|
+
new_agent.lock_store = app.ctx.agent.lock_store
|
|
1412
|
+
app.ctx.agent = new_agent
|
|
1413
|
+
|
|
1414
|
+
logger.debug(f"Successfully loaded model '{model_path}'.")
|
|
1415
|
+
return response.json(None, status=HTTPStatus.NO_CONTENT)
|
|
1416
|
+
|
|
1417
|
+
@app.delete("/model")
|
|
1418
|
+
@requires_auth(app, auth_token)
|
|
1419
|
+
async def unload_model(request: Request) -> HTTPResponse:
|
|
1420
|
+
model_file = app.ctx.agent.model_name
|
|
1421
|
+
|
|
1422
|
+
app.ctx.agent = Agent(lock_store=app.ctx.agent.lock_store)
|
|
1423
|
+
|
|
1424
|
+
logger.debug(f"Successfully unloaded model '{model_file}'.")
|
|
1425
|
+
return response.json(None, status=HTTPStatus.NO_CONTENT)
|
|
1426
|
+
|
|
1427
|
+
@app.get("/flows")
|
|
1428
|
+
@requires_auth(app, auth_token)
|
|
1429
|
+
async def get_flows(request: Request) -> HTTPResponse:
|
|
1430
|
+
"""Get all the flows currently stored by the agent."""
|
|
1431
|
+
processor = app.ctx.agent.processor
|
|
1432
|
+
flows = await processor.get_flows()
|
|
1433
|
+
return response.json(flows.as_json_list())
|
|
1434
|
+
|
|
1435
|
+
@app.get("/domain")
|
|
1436
|
+
@requires_auth(app, auth_token)
|
|
1437
|
+
@ensure_loaded_agent(app)
|
|
1438
|
+
async def get_domain(request: Request) -> HTTPResponse:
|
|
1439
|
+
"""Get current domain in yaml or json format."""
|
|
1440
|
+
# FIXME: this is a false positive mypy error after upgrading to 0.931
|
|
1441
|
+
accepts = request.headers.get("Accept", default=JSON_CONTENT_TYPE)
|
|
1442
|
+
if accepts.endswith("json"):
|
|
1443
|
+
domain = app.ctx.agent.domain.as_dict()
|
|
1444
|
+
return response.json(domain)
|
|
1445
|
+
elif accepts.endswith("yml") or accepts.endswith("yaml"):
|
|
1446
|
+
domain_yaml = app.ctx.agent.domain.as_yaml()
|
|
1447
|
+
return response.text(
|
|
1448
|
+
domain_yaml, status=HTTPStatus.OK, content_type=YAML_CONTENT_TYPE
|
|
1449
|
+
)
|
|
1450
|
+
else:
|
|
1451
|
+
raise ErrorResponse(
|
|
1452
|
+
HTTPStatus.NOT_ACCEPTABLE,
|
|
1453
|
+
"NotAcceptable",
|
|
1454
|
+
f"Invalid Accept header. Domain can be "
|
|
1455
|
+
f"provided as "
|
|
1456
|
+
f'json ("Accept: {JSON_CONTENT_TYPE}") or'
|
|
1457
|
+
f'yml ("Accept: {YAML_CONTENT_TYPE}"). '
|
|
1458
|
+
f"Make sure you've set the appropriate Accept "
|
|
1459
|
+
f"header.",
|
|
1460
|
+
)
|
|
1461
|
+
|
|
1462
|
+
return app
|
|
1463
|
+
|
|
1464
|
+
|
|
1465
|
+
def _get_output_channel(
|
|
1466
|
+
request: Request, tracker: Optional[DialogueStateTracker]
|
|
1467
|
+
) -> OutputChannel:
|
|
1468
|
+
"""Returns the `OutputChannel` which should be used for the bot's responses.
|
|
1469
|
+
|
|
1470
|
+
Args:
|
|
1471
|
+
request: HTTP request whose query parameters can specify which `OutputChannel`
|
|
1472
|
+
should be used.
|
|
1473
|
+
tracker: Tracker for the conversation. Used to get the latest input channel.
|
|
1474
|
+
|
|
1475
|
+
Returns:
|
|
1476
|
+
`OutputChannel` which should be used to return the bot's responses to.
|
|
1477
|
+
"""
|
|
1478
|
+
requested_output_channel = request.args.get(OUTPUT_CHANNEL_QUERY_KEY)
|
|
1479
|
+
|
|
1480
|
+
if (
|
|
1481
|
+
requested_output_channel == USE_LATEST_INPUT_CHANNEL_AS_OUTPUT_CHANNEL
|
|
1482
|
+
and tracker
|
|
1483
|
+
):
|
|
1484
|
+
requested_output_channel = tracker.get_latest_input_channel()
|
|
1485
|
+
|
|
1486
|
+
# Interactive training does not set `input_channels`, hence we have to be cautious
|
|
1487
|
+
registered_input_channels = getattr(request.app.ctx, "input_channels", None) or []
|
|
1488
|
+
matching_channels = [
|
|
1489
|
+
channel
|
|
1490
|
+
for channel in registered_input_channels
|
|
1491
|
+
if channel.name() == requested_output_channel
|
|
1492
|
+
]
|
|
1493
|
+
|
|
1494
|
+
# Check if matching channels can provide a valid output channel,
|
|
1495
|
+
# otherwise use `CollectingOutputChannel`
|
|
1496
|
+
return reduce(
|
|
1497
|
+
lambda output_channel_created_so_far, input_channel: (
|
|
1498
|
+
input_channel.get_output_channel() or output_channel_created_so_far
|
|
1499
|
+
),
|
|
1500
|
+
matching_channels,
|
|
1501
|
+
CollectingOutputChannel(),
|
|
1502
|
+
)
|
|
1503
|
+
|
|
1504
|
+
|
|
1505
|
+
def _test_data_file_from_payload(request: Request, temporary_directory: Path) -> Text:
|
|
1506
|
+
return str(
|
|
1507
|
+
_training_payload_from_yaml(
|
|
1508
|
+
request,
|
|
1509
|
+
temporary_directory,
|
|
1510
|
+
# test stories have to prefixed with `test_`
|
|
1511
|
+
file_name=f"{TEST_STORIES_FILE_PREFIX}data.yml",
|
|
1512
|
+
)["training_files"]
|
|
1513
|
+
)
|
|
1514
|
+
|
|
1515
|
+
|
|
1516
|
+
def _training_payload_from_yaml(
|
|
1517
|
+
request: Request, temp_dir: Path, file_name: Text = "data.yml"
|
|
1518
|
+
) -> Dict[Text, Any]:
|
|
1519
|
+
logger.debug("Extracting YAML training data from request body.")
|
|
1520
|
+
|
|
1521
|
+
decoded = request.body.decode(rasa.shared.utils.io.DEFAULT_ENCODING)
|
|
1522
|
+
_validate_yaml_training_payload(decoded)
|
|
1523
|
+
|
|
1524
|
+
training_data = temp_dir / file_name
|
|
1525
|
+
rasa.shared.utils.io.write_text_file(decoded, training_data)
|
|
1526
|
+
|
|
1527
|
+
model_output_directory = str(temp_dir)
|
|
1528
|
+
if rasa.utils.endpoints.bool_arg(request, "save_to_default_model_directory", True):
|
|
1529
|
+
model_output_directory = DEFAULT_MODELS_PATH
|
|
1530
|
+
|
|
1531
|
+
return dict(
|
|
1532
|
+
domain=str(training_data),
|
|
1533
|
+
config=str(training_data),
|
|
1534
|
+
training_files=str(temp_dir),
|
|
1535
|
+
output=model_output_directory,
|
|
1536
|
+
force_training=rasa.utils.endpoints.bool_arg(request, "force_training", False),
|
|
1537
|
+
core_additional_arguments=_extract_core_additional_arguments(request),
|
|
1538
|
+
nlu_additional_arguments=_extract_nlu_additional_arguments(request),
|
|
1539
|
+
)
|
|
1540
|
+
|
|
1541
|
+
|
|
1542
|
+
def _nlu_training_payload_from_json(
|
|
1543
|
+
request: Request, temp_dir: Path, file_name: Text = "data.json"
|
|
1544
|
+
) -> Dict[Text, Any]:
|
|
1545
|
+
logger.debug("Extracting JSON training data from request body.")
|
|
1546
|
+
|
|
1547
|
+
validate_training_data(
|
|
1548
|
+
request.json,
|
|
1549
|
+
rasa.shared.nlu.training_data.schemas.data_schema.rasa_nlu_data_schema(),
|
|
1550
|
+
)
|
|
1551
|
+
training_data = temp_dir / file_name
|
|
1552
|
+
rasa.shared.utils.io.dump_obj_as_json_to_file(training_data, request.json)
|
|
1553
|
+
|
|
1554
|
+
model_output_directory = str(temp_dir)
|
|
1555
|
+
if rasa.utils.endpoints.bool_arg(request, "save_to_default_model_directory", True):
|
|
1556
|
+
model_output_directory = DEFAULT_MODELS_PATH
|
|
1557
|
+
|
|
1558
|
+
return dict(
|
|
1559
|
+
domain=str(training_data),
|
|
1560
|
+
config=str(training_data),
|
|
1561
|
+
training_files=str(temp_dir),
|
|
1562
|
+
output=model_output_directory,
|
|
1563
|
+
force_training=rasa.utils.endpoints.bool_arg(request, "force_training", False),
|
|
1564
|
+
core_additional_arguments=_extract_core_additional_arguments(request),
|
|
1565
|
+
nlu_additional_arguments=_extract_nlu_additional_arguments(request),
|
|
1566
|
+
)
|
|
1567
|
+
|
|
1568
|
+
|
|
1569
|
+
def _validate_yaml_training_payload(yaml_text: Text) -> None:
|
|
1570
|
+
try:
|
|
1571
|
+
RasaYAMLReader().validate(yaml_text)
|
|
1572
|
+
except Exception as e:
|
|
1573
|
+
raise ErrorResponse(
|
|
1574
|
+
HTTPStatus.BAD_REQUEST,
|
|
1575
|
+
"BadRequest",
|
|
1576
|
+
f"The request body does not contain valid YAML. Error: {e}",
|
|
1577
|
+
help_url=DOCS_URL_TRAINING_DATA,
|
|
1578
|
+
)
|
|
1579
|
+
|
|
1580
|
+
|
|
1581
|
+
def _extract_core_additional_arguments(request: Request) -> Dict[Text, Any]:
|
|
1582
|
+
return {
|
|
1583
|
+
"augmentation_factor": rasa.utils.endpoints.int_arg(request, "augmentation", 50)
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
|
|
1587
|
+
def _extract_nlu_additional_arguments(request: Request) -> Dict[Text, Any]:
|
|
1588
|
+
return {"num_threads": rasa.utils.endpoints.int_arg(request, "num_threads", 1)}
|