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
|
@@ -0,0 +1,1345 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import dataclasses
|
|
4
|
+
import json
|
|
5
|
+
import re
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from enum import Enum
|
|
8
|
+
from functools import lru_cache
|
|
9
|
+
from typing import (
|
|
10
|
+
Any,
|
|
11
|
+
Callable,
|
|
12
|
+
Dict,
|
|
13
|
+
List,
|
|
14
|
+
Optional,
|
|
15
|
+
Set,
|
|
16
|
+
TYPE_CHECKING,
|
|
17
|
+
Text,
|
|
18
|
+
Tuple,
|
|
19
|
+
Type,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
import pandas as pd
|
|
23
|
+
import structlog
|
|
24
|
+
|
|
25
|
+
import rasa.shared.utils.common
|
|
26
|
+
from rasa.core.constants import (
|
|
27
|
+
DOMAIN_GROUND_TRUTH_METADATA_KEY,
|
|
28
|
+
UTTER_SOURCE_METADATA_KEY,
|
|
29
|
+
)
|
|
30
|
+
from rasa.core.policies.enterprise_search_policy import (
|
|
31
|
+
SEARCH_QUERY_METADATA_KEY,
|
|
32
|
+
SEARCH_RESULTS_METADATA_KEY,
|
|
33
|
+
)
|
|
34
|
+
from rasa.dialogue_understanding.patterns.clarify import FLOW_PATTERN_CLARIFICATION
|
|
35
|
+
from rasa.shared.core.constants import DEFAULT_SLOT_NAMES
|
|
36
|
+
from rasa.shared.core.events import (
|
|
37
|
+
ActionExecuted,
|
|
38
|
+
BotUttered,
|
|
39
|
+
DefinePrevUserUtteredFeaturization,
|
|
40
|
+
DialogueStackUpdated,
|
|
41
|
+
Event,
|
|
42
|
+
FlowCancelled,
|
|
43
|
+
FlowCompleted,
|
|
44
|
+
FlowStarted,
|
|
45
|
+
SlotSet,
|
|
46
|
+
)
|
|
47
|
+
from rasa.shared.exceptions import RasaException
|
|
48
|
+
from rasa.utils.common import update_mlflow_log_level
|
|
49
|
+
from rasa.utils.json_utils import SetEncoder
|
|
50
|
+
|
|
51
|
+
if TYPE_CHECKING:
|
|
52
|
+
from rasa.e2e_test.e2e_config import LLMJudgeConfig
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
structlogger = structlog.get_logger()
|
|
56
|
+
|
|
57
|
+
DEFAULT_THRESHOLD = 0.5
|
|
58
|
+
ELIGIBLE_UTTER_SOURCE_METADATA = [
|
|
59
|
+
"EnterpriseSearchPolicy",
|
|
60
|
+
"ContextualResponseRephraser",
|
|
61
|
+
"IntentlessPolicy",
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class AssertionType(Enum):
|
|
66
|
+
FLOW_STARTED = "flow_started"
|
|
67
|
+
FLOW_COMPLETED = "flow_completed"
|
|
68
|
+
FLOW_CANCELLED = "flow_cancelled"
|
|
69
|
+
PATTERN_CLARIFICATION_CONTAINS = "pattern_clarification_contains"
|
|
70
|
+
ACTION_EXECUTED = "action_executed"
|
|
71
|
+
SLOT_WAS_SET = "slot_was_set"
|
|
72
|
+
SLOT_WAS_NOT_SET = "slot_was_not_set"
|
|
73
|
+
BOT_UTTERED = "bot_uttered"
|
|
74
|
+
BOT_DID_NOT_UTTER = "bot_did_not_utter"
|
|
75
|
+
GENERATIVE_RESPONSE_IS_RELEVANT = "generative_response_is_relevant"
|
|
76
|
+
GENERATIVE_RESPONSE_IS_GROUNDED = "generative_response_is_grounded"
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@lru_cache(maxsize=1)
|
|
80
|
+
def _get_all_assertion_subclasses() -> Dict[str, Type[Assertion]]:
|
|
81
|
+
return {
|
|
82
|
+
sub_class.type(): sub_class
|
|
83
|
+
for sub_class in rasa.shared.utils.common.all_subclasses(Assertion)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class InvalidAssertionType(RasaException):
|
|
88
|
+
"""Raised if an assertion type is invalid."""
|
|
89
|
+
|
|
90
|
+
def __init__(self, assertion_type: str) -> None:
|
|
91
|
+
"""Creates a `InvalidAssertionType`.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
assertion_type: The invalid assertion type.
|
|
95
|
+
"""
|
|
96
|
+
super().__init__(f"Invalid assertion type '{assertion_type}'.")
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@dataclass
|
|
100
|
+
class Assertion:
|
|
101
|
+
"""Base class for storing assertions."""
|
|
102
|
+
|
|
103
|
+
@classmethod
|
|
104
|
+
def type(cls) -> str:
|
|
105
|
+
"""Returns the type of the assertion."""
|
|
106
|
+
raise NotImplementedError
|
|
107
|
+
|
|
108
|
+
@staticmethod
|
|
109
|
+
def from_dict(assertion_dict: Dict[Text, Any]) -> Assertion:
|
|
110
|
+
"""Creates an assertion from a dictionary."""
|
|
111
|
+
raise NotImplementedError
|
|
112
|
+
|
|
113
|
+
def as_dict(self) -> Dict[str, Any]:
|
|
114
|
+
"""Return the `Assertion` as a dictionary.
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
The `Assertion` as a dictionary.
|
|
118
|
+
"""
|
|
119
|
+
data = dataclasses.asdict(self)
|
|
120
|
+
data["type"] = self.type()
|
|
121
|
+
return data
|
|
122
|
+
|
|
123
|
+
@staticmethod
|
|
124
|
+
def create_typed_assertion(data: Dict[str, Any]) -> Assertion:
|
|
125
|
+
"""Creates a `Assertion` from a dictionary.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
data: The dictionary to create the `Assertion` from.
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
The created `Assertion`.
|
|
132
|
+
"""
|
|
133
|
+
typ = next(iter(data.keys()))
|
|
134
|
+
|
|
135
|
+
subclass_mapping = _get_all_assertion_subclasses()
|
|
136
|
+
|
|
137
|
+
clazz = subclass_mapping.get(typ)
|
|
138
|
+
|
|
139
|
+
if clazz is None:
|
|
140
|
+
structlogger.warning("assertion.unknown_type", data=data)
|
|
141
|
+
raise InvalidAssertionType(typ)
|
|
142
|
+
|
|
143
|
+
try:
|
|
144
|
+
return clazz.from_dict(data)
|
|
145
|
+
except NotImplementedError:
|
|
146
|
+
structlogger.warning("assertion.unknown_type", data=data)
|
|
147
|
+
raise InvalidAssertionType(typ)
|
|
148
|
+
|
|
149
|
+
def run(
|
|
150
|
+
self,
|
|
151
|
+
turn_events: List[Event],
|
|
152
|
+
prior_events: List[Event],
|
|
153
|
+
assertion_order_error_message: str = "",
|
|
154
|
+
**kwargs: Any,
|
|
155
|
+
) -> Tuple[Optional[AssertionFailure], Optional[Event]]:
|
|
156
|
+
"""Run the assertion on the given events for that user turn.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
turn_events: The events to run the assertion on.
|
|
160
|
+
prior_events: All events prior to the current turn.
|
|
161
|
+
assertion_order_error_message: The error message to append if the assertion
|
|
162
|
+
order is enabled.
|
|
163
|
+
kwargs: Additional keyword arguments.
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
A tuple of the assertion failure and the matching event if the assertion
|
|
167
|
+
passes, otherwise `None`.
|
|
168
|
+
"""
|
|
169
|
+
raise NotImplementedError
|
|
170
|
+
|
|
171
|
+
def _generate_assertion_failure(
|
|
172
|
+
self,
|
|
173
|
+
error_message: str,
|
|
174
|
+
prior_events: List[Event],
|
|
175
|
+
turn_events: List[Event],
|
|
176
|
+
line: Optional[int] = None,
|
|
177
|
+
) -> Tuple[AssertionFailure, None]:
|
|
178
|
+
return AssertionFailure(
|
|
179
|
+
assertion=self,
|
|
180
|
+
error_message=error_message,
|
|
181
|
+
actual_events_transcript=create_actual_events_transcript(
|
|
182
|
+
prior_events, turn_events
|
|
183
|
+
),
|
|
184
|
+
error_line=line,
|
|
185
|
+
), None
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
@dataclass
|
|
189
|
+
class FlowStartedAssertion(Assertion):
|
|
190
|
+
"""Class for storing the flow started assertion."""
|
|
191
|
+
|
|
192
|
+
flow_id: str
|
|
193
|
+
line: Optional[int] = None
|
|
194
|
+
|
|
195
|
+
@classmethod
|
|
196
|
+
def type(cls) -> str:
|
|
197
|
+
return AssertionType.FLOW_STARTED.value
|
|
198
|
+
|
|
199
|
+
@staticmethod
|
|
200
|
+
def from_dict(assertion_dict: Dict[Text, Any]) -> FlowStartedAssertion:
|
|
201
|
+
return FlowStartedAssertion(
|
|
202
|
+
flow_id=assertion_dict.get(AssertionType.FLOW_STARTED.value),
|
|
203
|
+
line=assertion_dict.lc.line + 1 if hasattr(assertion_dict, "lc") else None,
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
def run(
|
|
207
|
+
self,
|
|
208
|
+
turn_events: List[Event],
|
|
209
|
+
prior_events: List[Event],
|
|
210
|
+
assertion_order_error_message: str = "",
|
|
211
|
+
**kwargs: Any,
|
|
212
|
+
) -> Tuple[Optional[AssertionFailure], Optional[Event]]:
|
|
213
|
+
"""Run the flow started assertion on the given events for that user turn."""
|
|
214
|
+
try:
|
|
215
|
+
matching_event = next(
|
|
216
|
+
event
|
|
217
|
+
for event in turn_events
|
|
218
|
+
if isinstance(event, FlowStarted) and event.flow_id == self.flow_id
|
|
219
|
+
)
|
|
220
|
+
except StopIteration:
|
|
221
|
+
error_message = f"Flow with id '{self.flow_id}' did not start."
|
|
222
|
+
error_message += assertion_order_error_message
|
|
223
|
+
|
|
224
|
+
return self._generate_assertion_failure(
|
|
225
|
+
error_message, prior_events, turn_events, self.line
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
return None, matching_event
|
|
229
|
+
|
|
230
|
+
def __hash__(self) -> int:
|
|
231
|
+
return hash(json.dumps(self.as_dict()))
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
@dataclass
|
|
235
|
+
class FlowCompletedAssertion(Assertion):
|
|
236
|
+
"""Class for storing the flow completed assertion."""
|
|
237
|
+
|
|
238
|
+
flow_id: str
|
|
239
|
+
flow_step_id: Optional[str] = None
|
|
240
|
+
line: Optional[int] = None
|
|
241
|
+
|
|
242
|
+
@classmethod
|
|
243
|
+
def type(cls) -> str:
|
|
244
|
+
return AssertionType.FLOW_COMPLETED.value
|
|
245
|
+
|
|
246
|
+
@staticmethod
|
|
247
|
+
def from_dict(assertion_dict: Dict[Text, Any]) -> FlowCompletedAssertion:
|
|
248
|
+
line = assertion_dict.lc.line + 1 if hasattr(assertion_dict, "lc") else None
|
|
249
|
+
assertion_dict = assertion_dict.get(AssertionType.FLOW_COMPLETED.value, {})
|
|
250
|
+
|
|
251
|
+
return FlowCompletedAssertion(
|
|
252
|
+
flow_id=assertion_dict.get("flow_id"),
|
|
253
|
+
flow_step_id=assertion_dict.get("flow_step_id"),
|
|
254
|
+
line=line,
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
def run(
|
|
258
|
+
self,
|
|
259
|
+
turn_events: List[Event],
|
|
260
|
+
prior_events: List[Event],
|
|
261
|
+
assertion_order_error_message: str = "",
|
|
262
|
+
**kwargs: Any,
|
|
263
|
+
) -> Tuple[Optional[AssertionFailure], Optional[Event]]:
|
|
264
|
+
"""Run the flow completed assertion on the given events for that user turn."""
|
|
265
|
+
try:
|
|
266
|
+
matching_event = next(
|
|
267
|
+
event
|
|
268
|
+
for event in turn_events
|
|
269
|
+
if isinstance(event, FlowCompleted) and event.flow_id == self.flow_id
|
|
270
|
+
)
|
|
271
|
+
except StopIteration:
|
|
272
|
+
error_message = f"Flow with id '{self.flow_id}' did not complete."
|
|
273
|
+
error_message += assertion_order_error_message
|
|
274
|
+
|
|
275
|
+
return self._generate_assertion_failure(
|
|
276
|
+
error_message, prior_events, turn_events, self.line
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
if (
|
|
280
|
+
self.flow_step_id is not None
|
|
281
|
+
and matching_event.step_id != self.flow_step_id
|
|
282
|
+
):
|
|
283
|
+
error_message = (
|
|
284
|
+
f"Flow with id '{self.flow_id}' did not complete "
|
|
285
|
+
f"at expected step id '{self.flow_step_id}'. The actual "
|
|
286
|
+
f"step id was '{matching_event.step_id}'."
|
|
287
|
+
)
|
|
288
|
+
error_message += assertion_order_error_message
|
|
289
|
+
return self._generate_assertion_failure(
|
|
290
|
+
error_message, prior_events, turn_events, self.line
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
return None, matching_event
|
|
294
|
+
|
|
295
|
+
def __hash__(self) -> int:
|
|
296
|
+
return hash(json.dumps(self.as_dict()))
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
@dataclass
|
|
300
|
+
class FlowCancelledAssertion(Assertion):
|
|
301
|
+
"""Class for storing the flow cancelled assertion."""
|
|
302
|
+
|
|
303
|
+
flow_id: str
|
|
304
|
+
flow_step_id: Optional[str] = None
|
|
305
|
+
line: Optional[int] = None
|
|
306
|
+
|
|
307
|
+
@classmethod
|
|
308
|
+
def type(cls) -> str:
|
|
309
|
+
return AssertionType.FLOW_CANCELLED.value
|
|
310
|
+
|
|
311
|
+
@staticmethod
|
|
312
|
+
def from_dict(assertion_dict: Dict[Text, Any]) -> FlowCancelledAssertion:
|
|
313
|
+
line = assertion_dict.lc.line + 1 if hasattr(assertion_dict, "lc") else None
|
|
314
|
+
assertion_dict = assertion_dict.get(AssertionType.FLOW_CANCELLED.value, {})
|
|
315
|
+
|
|
316
|
+
return FlowCancelledAssertion(
|
|
317
|
+
flow_id=assertion_dict.get("flow_id"),
|
|
318
|
+
flow_step_id=assertion_dict.get("flow_step_id"),
|
|
319
|
+
line=line,
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
def run(
|
|
323
|
+
self,
|
|
324
|
+
turn_events: List[Event],
|
|
325
|
+
prior_events: List[Event],
|
|
326
|
+
assertion_order_error_message: str = "",
|
|
327
|
+
**kwargs: Any,
|
|
328
|
+
) -> Tuple[Optional[AssertionFailure], Optional[Event]]:
|
|
329
|
+
"""Run the flow cancelled assertion on the given events for that user turn."""
|
|
330
|
+
try:
|
|
331
|
+
matching_event = next(
|
|
332
|
+
event
|
|
333
|
+
for event in turn_events
|
|
334
|
+
if isinstance(event, FlowCancelled) and event.flow_id == self.flow_id
|
|
335
|
+
)
|
|
336
|
+
except StopIteration:
|
|
337
|
+
error_message = f"Flow with id '{self.flow_id}' was not cancelled."
|
|
338
|
+
error_message += assertion_order_error_message
|
|
339
|
+
|
|
340
|
+
return self._generate_assertion_failure(
|
|
341
|
+
error_message, prior_events, turn_events, self.line
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
if (
|
|
345
|
+
self.flow_step_id is not None
|
|
346
|
+
and matching_event.step_id != self.flow_step_id
|
|
347
|
+
):
|
|
348
|
+
error_message = (
|
|
349
|
+
f"Flow with id '{self.flow_id}' was not cancelled "
|
|
350
|
+
f"at expected step id '{self.flow_step_id}'. The actual "
|
|
351
|
+
f"step id was '{matching_event.step_id}'."
|
|
352
|
+
)
|
|
353
|
+
error_message += assertion_order_error_message
|
|
354
|
+
|
|
355
|
+
return self._generate_assertion_failure(
|
|
356
|
+
error_message, prior_events, turn_events, self.line
|
|
357
|
+
)
|
|
358
|
+
|
|
359
|
+
return None, matching_event
|
|
360
|
+
|
|
361
|
+
def __hash__(self) -> int:
|
|
362
|
+
return hash(json.dumps(self.as_dict()))
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
@dataclass
|
|
366
|
+
class PatternClarificationContainsAssertion(Assertion):
|
|
367
|
+
"""Class for storing the pattern clarification contains assertion."""
|
|
368
|
+
|
|
369
|
+
flow_names: Set[str]
|
|
370
|
+
line: Optional[int] = None
|
|
371
|
+
|
|
372
|
+
@classmethod
|
|
373
|
+
def type(cls) -> str:
|
|
374
|
+
return AssertionType.PATTERN_CLARIFICATION_CONTAINS.value
|
|
375
|
+
|
|
376
|
+
@staticmethod
|
|
377
|
+
def from_dict(
|
|
378
|
+
assertion_dict: Dict[Text, Any],
|
|
379
|
+
) -> PatternClarificationContainsAssertion:
|
|
380
|
+
return PatternClarificationContainsAssertion(
|
|
381
|
+
flow_names=set(
|
|
382
|
+
assertion_dict.get(
|
|
383
|
+
AssertionType.PATTERN_CLARIFICATION_CONTAINS.value, []
|
|
384
|
+
)
|
|
385
|
+
),
|
|
386
|
+
line=assertion_dict.lc.line + 1 if hasattr(assertion_dict, "lc") else None,
|
|
387
|
+
)
|
|
388
|
+
|
|
389
|
+
def run(
|
|
390
|
+
self,
|
|
391
|
+
turn_events: List[Event],
|
|
392
|
+
prior_events: List[Event],
|
|
393
|
+
assertion_order_error_message: str = "",
|
|
394
|
+
**kwargs: Any,
|
|
395
|
+
) -> Tuple[Optional[AssertionFailure], Optional[Event]]:
|
|
396
|
+
"""Run the flow completed assertion on the given events for that user turn."""
|
|
397
|
+
try:
|
|
398
|
+
matching_event = next(
|
|
399
|
+
event
|
|
400
|
+
for event in turn_events
|
|
401
|
+
if isinstance(event, FlowStarted)
|
|
402
|
+
and event.flow_id == FLOW_PATTERN_CLARIFICATION
|
|
403
|
+
)
|
|
404
|
+
except StopIteration:
|
|
405
|
+
error_message = f"'{FLOW_PATTERN_CLARIFICATION}' pattern did not trigger."
|
|
406
|
+
error_message += assertion_order_error_message
|
|
407
|
+
|
|
408
|
+
return self._generate_assertion_failure(
|
|
409
|
+
error_message, prior_events, turn_events, self.line
|
|
410
|
+
)
|
|
411
|
+
|
|
412
|
+
actual_flow_names = set(matching_event.metadata.get("names", set()))
|
|
413
|
+
if actual_flow_names != self.flow_names:
|
|
414
|
+
error_message = (
|
|
415
|
+
f"'{FLOW_PATTERN_CLARIFICATION}' pattern did not contain "
|
|
416
|
+
f"the expected options. Expected options: {self.flow_names}. "
|
|
417
|
+
)
|
|
418
|
+
error_message += assertion_order_error_message
|
|
419
|
+
|
|
420
|
+
return self._generate_assertion_failure(
|
|
421
|
+
error_message, prior_events, turn_events, self.line
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
return None, matching_event
|
|
425
|
+
|
|
426
|
+
def __hash__(self) -> int:
|
|
427
|
+
return hash(json.dumps(self.as_dict(), cls=SetEncoder))
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
@dataclass
|
|
431
|
+
class ActionExecutedAssertion(Assertion):
|
|
432
|
+
"""Class for storing the action executed assertion."""
|
|
433
|
+
|
|
434
|
+
action_name: str
|
|
435
|
+
line: Optional[int] = None
|
|
436
|
+
|
|
437
|
+
@classmethod
|
|
438
|
+
def type(cls) -> str:
|
|
439
|
+
return AssertionType.ACTION_EXECUTED.value
|
|
440
|
+
|
|
441
|
+
@staticmethod
|
|
442
|
+
def from_dict(assertion_dict: Dict[Text, Any]) -> ActionExecutedAssertion:
|
|
443
|
+
return ActionExecutedAssertion(
|
|
444
|
+
action_name=assertion_dict.get(AssertionType.ACTION_EXECUTED.value),
|
|
445
|
+
line=assertion_dict.lc.line + 1 if hasattr(assertion_dict, "lc") else None,
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
def run(
|
|
449
|
+
self,
|
|
450
|
+
turn_events: List[Event],
|
|
451
|
+
prior_events: List[Event],
|
|
452
|
+
assertion_order_error_message: str = "",
|
|
453
|
+
**kwargs: Any,
|
|
454
|
+
) -> Tuple[Optional[AssertionFailure], Optional[Event]]:
|
|
455
|
+
"""Run the action executed assertion on the given events for that user turn."""
|
|
456
|
+
step_index = kwargs.get("step_index")
|
|
457
|
+
original_turn_events, turn_events = _get_turn_events_based_on_step_index(
|
|
458
|
+
step_index, turn_events, prior_events
|
|
459
|
+
)
|
|
460
|
+
|
|
461
|
+
try:
|
|
462
|
+
matching_event = next(
|
|
463
|
+
event
|
|
464
|
+
for event in turn_events
|
|
465
|
+
if isinstance(event, ActionExecuted)
|
|
466
|
+
and event.action_name == self.action_name
|
|
467
|
+
)
|
|
468
|
+
except StopIteration:
|
|
469
|
+
error_message = f"Action '{self.action_name}' did not execute."
|
|
470
|
+
error_message += assertion_order_error_message
|
|
471
|
+
|
|
472
|
+
return self._generate_assertion_failure(
|
|
473
|
+
error_message, prior_events, original_turn_events, self.line
|
|
474
|
+
)
|
|
475
|
+
|
|
476
|
+
return None, matching_event
|
|
477
|
+
|
|
478
|
+
def __hash__(self) -> int:
|
|
479
|
+
return hash(json.dumps(self.as_dict()))
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
@dataclass
|
|
483
|
+
class AssertedSlot:
|
|
484
|
+
"""Class for storing information asserted about slots."""
|
|
485
|
+
|
|
486
|
+
name: str
|
|
487
|
+
value: Any
|
|
488
|
+
line: Optional[int] = None
|
|
489
|
+
|
|
490
|
+
@staticmethod
|
|
491
|
+
def from_dict(slot_dict: Dict[Text, Any]) -> AssertedSlot:
|
|
492
|
+
return AssertedSlot(
|
|
493
|
+
name=slot_dict.get("name"),
|
|
494
|
+
value=slot_dict.get("value", "value key is undefined"),
|
|
495
|
+
line=slot_dict.lc.line + 1 if hasattr(slot_dict, "lc") else None,
|
|
496
|
+
)
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
@dataclass
|
|
500
|
+
class SlotWasSetAssertion(Assertion):
|
|
501
|
+
"""Class for storing the slot was set assertion."""
|
|
502
|
+
|
|
503
|
+
slots: List[AssertedSlot]
|
|
504
|
+
|
|
505
|
+
@classmethod
|
|
506
|
+
def type(cls) -> str:
|
|
507
|
+
return AssertionType.SLOT_WAS_SET.value
|
|
508
|
+
|
|
509
|
+
@staticmethod
|
|
510
|
+
def from_dict(assertion_dict: Dict[Text, Any]) -> SlotWasSetAssertion:
|
|
511
|
+
return SlotWasSetAssertion(
|
|
512
|
+
slots=[
|
|
513
|
+
AssertedSlot.from_dict(slot)
|
|
514
|
+
for slot in assertion_dict.get(AssertionType.SLOT_WAS_SET.value, [])
|
|
515
|
+
],
|
|
516
|
+
)
|
|
517
|
+
|
|
518
|
+
def run(
|
|
519
|
+
self,
|
|
520
|
+
turn_events: List[Event],
|
|
521
|
+
prior_events: List[Event],
|
|
522
|
+
assertion_order_error_message: str = "",
|
|
523
|
+
**kwargs: Any,
|
|
524
|
+
) -> Tuple[Optional[AssertionFailure], Optional[Event]]:
|
|
525
|
+
"""Run the slot_was_set assertion on the given events for that user turn."""
|
|
526
|
+
matching_event = None
|
|
527
|
+
|
|
528
|
+
step_index = kwargs.get("step_index")
|
|
529
|
+
original_turn_events, turn_events = _get_turn_events_based_on_step_index(
|
|
530
|
+
step_index, turn_events, prior_events
|
|
531
|
+
)
|
|
532
|
+
|
|
533
|
+
for slot in self.slots:
|
|
534
|
+
matching_events = [
|
|
535
|
+
event
|
|
536
|
+
for event in turn_events
|
|
537
|
+
if isinstance(event, SlotSet) and event.key == slot.name
|
|
538
|
+
]
|
|
539
|
+
if not matching_events:
|
|
540
|
+
error_message = f"Slot '{slot.name}' was not set."
|
|
541
|
+
error_message += assertion_order_error_message
|
|
542
|
+
|
|
543
|
+
return self._generate_assertion_failure(
|
|
544
|
+
error_message, prior_events, turn_events, slot.line
|
|
545
|
+
)
|
|
546
|
+
|
|
547
|
+
if slot.value == "value key is undefined":
|
|
548
|
+
matching_event = matching_events[0]
|
|
549
|
+
structlogger.debug(
|
|
550
|
+
"slot_was_set_assertion.run",
|
|
551
|
+
last_event_seen=matching_event,
|
|
552
|
+
event_info="Slot value is not asserted and we have "
|
|
553
|
+
"multiple events for the same slot. "
|
|
554
|
+
"We will mark the first event as last event seen.",
|
|
555
|
+
)
|
|
556
|
+
continue
|
|
557
|
+
|
|
558
|
+
try:
|
|
559
|
+
matching_event = next(
|
|
560
|
+
event for event in matching_events if event.value == slot.value
|
|
561
|
+
)
|
|
562
|
+
except StopIteration:
|
|
563
|
+
error_message = (
|
|
564
|
+
f"Slot '{slot.name}' was set to a different value "
|
|
565
|
+
f"'{matching_events[-1].value}' than the "
|
|
566
|
+
f"expected '{slot.value}' value."
|
|
567
|
+
)
|
|
568
|
+
error_message += assertion_order_error_message
|
|
569
|
+
|
|
570
|
+
return self._generate_assertion_failure(
|
|
571
|
+
error_message, prior_events, original_turn_events, slot.line
|
|
572
|
+
)
|
|
573
|
+
|
|
574
|
+
return None, matching_event
|
|
575
|
+
|
|
576
|
+
def __hash__(self) -> int:
|
|
577
|
+
return hash(json.dumps(self.as_dict()))
|
|
578
|
+
|
|
579
|
+
|
|
580
|
+
@dataclass
|
|
581
|
+
class SlotWasNotSetAssertion(Assertion):
|
|
582
|
+
"""Class for storing the slot was not set assertion."""
|
|
583
|
+
|
|
584
|
+
slots: List[AssertedSlot]
|
|
585
|
+
|
|
586
|
+
@classmethod
|
|
587
|
+
def type(cls) -> str:
|
|
588
|
+
return AssertionType.SLOT_WAS_NOT_SET.value
|
|
589
|
+
|
|
590
|
+
@staticmethod
|
|
591
|
+
def from_dict(assertion_dict: Dict[Text, Any]) -> SlotWasNotSetAssertion:
|
|
592
|
+
return SlotWasNotSetAssertion(
|
|
593
|
+
slots=[
|
|
594
|
+
AssertedSlot.from_dict(slot)
|
|
595
|
+
for slot in assertion_dict.get(AssertionType.SLOT_WAS_NOT_SET.value, [])
|
|
596
|
+
]
|
|
597
|
+
)
|
|
598
|
+
|
|
599
|
+
def run(
|
|
600
|
+
self,
|
|
601
|
+
turn_events: List[Event],
|
|
602
|
+
prior_events: List[Event],
|
|
603
|
+
assertion_order_error_message: str = "",
|
|
604
|
+
**kwargs: Any,
|
|
605
|
+
) -> Tuple[Optional[AssertionFailure], Optional[Event]]:
|
|
606
|
+
"""Run the slot_was_not_set assertion on the given events for that user turn."""
|
|
607
|
+
matching_event = None
|
|
608
|
+
|
|
609
|
+
step_index = kwargs.get("step_index")
|
|
610
|
+
original_turn_events, turn_events = _get_turn_events_based_on_step_index(
|
|
611
|
+
step_index, turn_events, prior_events
|
|
612
|
+
)
|
|
613
|
+
|
|
614
|
+
for slot in self.slots:
|
|
615
|
+
matching_events = [
|
|
616
|
+
event
|
|
617
|
+
for event in turn_events
|
|
618
|
+
if isinstance(event, SlotSet) and event.key == slot.name
|
|
619
|
+
]
|
|
620
|
+
if not matching_events:
|
|
621
|
+
continue
|
|
622
|
+
|
|
623
|
+
# take the most recent event in the list of matching events
|
|
624
|
+
# since that is the final value in the tracker for that user turn
|
|
625
|
+
matching_event = matching_events[-1]
|
|
626
|
+
|
|
627
|
+
if (
|
|
628
|
+
slot.value == "value key is undefined"
|
|
629
|
+
and matching_event.value is not None
|
|
630
|
+
):
|
|
631
|
+
error_message = (
|
|
632
|
+
f"Slot '{slot.name}' was set to '{matching_event.value}' but "
|
|
633
|
+
f"it should not have been set."
|
|
634
|
+
)
|
|
635
|
+
error_message += assertion_order_error_message
|
|
636
|
+
|
|
637
|
+
return self._generate_assertion_failure(
|
|
638
|
+
error_message, prior_events, turn_events, slot.line
|
|
639
|
+
)
|
|
640
|
+
|
|
641
|
+
if matching_event.value == slot.value:
|
|
642
|
+
error_message = (
|
|
643
|
+
f"Slot '{slot.name}' was set to '{slot.value}' "
|
|
644
|
+
f"but it should not have been set."
|
|
645
|
+
)
|
|
646
|
+
error_message += assertion_order_error_message
|
|
647
|
+
|
|
648
|
+
return self._generate_assertion_failure(
|
|
649
|
+
error_message, prior_events, original_turn_events, slot.line
|
|
650
|
+
)
|
|
651
|
+
|
|
652
|
+
return None, matching_event
|
|
653
|
+
|
|
654
|
+
def __hash__(self) -> int:
|
|
655
|
+
return hash(json.dumps(self.as_dict()))
|
|
656
|
+
|
|
657
|
+
|
|
658
|
+
@dataclass
|
|
659
|
+
class AssertedButton:
|
|
660
|
+
"""Class for storing information asserted about buttons."""
|
|
661
|
+
|
|
662
|
+
title: str
|
|
663
|
+
payload: Optional[str] = None
|
|
664
|
+
|
|
665
|
+
@staticmethod
|
|
666
|
+
def from_dict(button_dict: Dict[Text, Any]) -> AssertedButton:
|
|
667
|
+
return AssertedButton(
|
|
668
|
+
title=button_dict.get("title"),
|
|
669
|
+
payload=button_dict.get("payload"),
|
|
670
|
+
)
|
|
671
|
+
|
|
672
|
+
|
|
673
|
+
@dataclass
|
|
674
|
+
class BotUtteredAssertion(Assertion):
|
|
675
|
+
"""Class for storing the bot uttered assertion."""
|
|
676
|
+
|
|
677
|
+
utter_name: Optional[str] = None
|
|
678
|
+
text_matches: Optional[str] = None
|
|
679
|
+
buttons: Optional[List[AssertedButton]] = None
|
|
680
|
+
line: Optional[int] = None
|
|
681
|
+
|
|
682
|
+
@classmethod
|
|
683
|
+
def type(cls) -> str:
|
|
684
|
+
return AssertionType.BOT_UTTERED.value
|
|
685
|
+
|
|
686
|
+
@staticmethod
|
|
687
|
+
def from_dict(assertion_dict: Dict[Text, Any]) -> BotUtteredAssertion:
|
|
688
|
+
utter_name, text_matches, buttons = (
|
|
689
|
+
BotUtteredAssertion._extract_assertion_properties(assertion_dict)
|
|
690
|
+
)
|
|
691
|
+
|
|
692
|
+
if BotUtteredAssertion._assertion_is_empty(utter_name, text_matches, buttons):
|
|
693
|
+
raise RasaException(
|
|
694
|
+
"A 'bot_uttered' assertion is empty, it should contain at least one "
|
|
695
|
+
"of the allowed properties: 'utter_name', 'text_matches', 'buttons'."
|
|
696
|
+
)
|
|
697
|
+
|
|
698
|
+
return BotUtteredAssertion(
|
|
699
|
+
utter_name=utter_name,
|
|
700
|
+
text_matches=text_matches,
|
|
701
|
+
buttons=buttons,
|
|
702
|
+
line=assertion_dict.lc.line + 1 if hasattr(assertion_dict, "lc") else None,
|
|
703
|
+
)
|
|
704
|
+
|
|
705
|
+
@staticmethod
|
|
706
|
+
def _extract_assertion_properties(
|
|
707
|
+
assertion_dict: Dict[Text, Any],
|
|
708
|
+
) -> Tuple[Optional[str], Optional[str], List[AssertedButton]]:
|
|
709
|
+
"""Extracts the assertion properties from a dictionary."""
|
|
710
|
+
assertion_dict = assertion_dict.get(AssertionType.BOT_UTTERED.value, {})
|
|
711
|
+
utter_name = assertion_dict.get("utter_name")
|
|
712
|
+
text_matches = assertion_dict.get("text_matches")
|
|
713
|
+
buttons = [
|
|
714
|
+
AssertedButton.from_dict(button)
|
|
715
|
+
for button in assertion_dict.get("buttons", [])
|
|
716
|
+
]
|
|
717
|
+
|
|
718
|
+
return utter_name, text_matches, buttons
|
|
719
|
+
|
|
720
|
+
@staticmethod
|
|
721
|
+
def _assertion_is_empty(
|
|
722
|
+
utter_name: Optional[str],
|
|
723
|
+
text_matches: Optional[str],
|
|
724
|
+
buttons: List[AssertedButton],
|
|
725
|
+
) -> bool:
|
|
726
|
+
"""Validate if the bot uttered assertion is empty."""
|
|
727
|
+
if not utter_name and not text_matches and not buttons:
|
|
728
|
+
return True
|
|
729
|
+
|
|
730
|
+
return False
|
|
731
|
+
|
|
732
|
+
def run(
|
|
733
|
+
self,
|
|
734
|
+
turn_events: List[Event],
|
|
735
|
+
prior_events: List[Event],
|
|
736
|
+
assertion_order_error_message: str = "",
|
|
737
|
+
**kwargs: Any,
|
|
738
|
+
) -> Tuple[Optional[AssertionFailure], Optional[Event]]:
|
|
739
|
+
"""Run the bot_uttered assertion on the given events for that user turn."""
|
|
740
|
+
matching_event = None
|
|
741
|
+
error_messages = []
|
|
742
|
+
|
|
743
|
+
step_index = kwargs.get("step_index")
|
|
744
|
+
original_turn_events, turn_events = _get_turn_events_based_on_step_index(
|
|
745
|
+
step_index, turn_events, prior_events
|
|
746
|
+
)
|
|
747
|
+
|
|
748
|
+
if self.utter_name is not None:
|
|
749
|
+
try:
|
|
750
|
+
matching_event = next(
|
|
751
|
+
event
|
|
752
|
+
for event in turn_events
|
|
753
|
+
if isinstance(event, BotUttered)
|
|
754
|
+
and event.metadata.get("utter_action") == self.utter_name
|
|
755
|
+
)
|
|
756
|
+
except StopIteration:
|
|
757
|
+
error_messages.append(
|
|
758
|
+
f"Bot did not utter '{self.utter_name}' response."
|
|
759
|
+
)
|
|
760
|
+
|
|
761
|
+
if self.text_matches is not None:
|
|
762
|
+
pattern = re.compile(self.text_matches)
|
|
763
|
+
try:
|
|
764
|
+
matching_event = next(
|
|
765
|
+
event
|
|
766
|
+
for event in turn_events
|
|
767
|
+
if isinstance(event, BotUttered) and pattern.search(event.text)
|
|
768
|
+
)
|
|
769
|
+
except StopIteration:
|
|
770
|
+
error_messages.append(
|
|
771
|
+
f"Bot did not utter any response which "
|
|
772
|
+
f"matches the provided text pattern "
|
|
773
|
+
f"'{self.text_matches}'."
|
|
774
|
+
)
|
|
775
|
+
|
|
776
|
+
if self.buttons:
|
|
777
|
+
try:
|
|
778
|
+
matching_event = next(
|
|
779
|
+
event
|
|
780
|
+
for event in turn_events
|
|
781
|
+
if isinstance(event, BotUttered) and self._buttons_match(event)
|
|
782
|
+
)
|
|
783
|
+
except StopIteration:
|
|
784
|
+
error_messages.append(
|
|
785
|
+
"Bot did not utter any response with the expected buttons."
|
|
786
|
+
)
|
|
787
|
+
|
|
788
|
+
if error_messages:
|
|
789
|
+
error_message = " ".join(error_messages)
|
|
790
|
+
error_message += assertion_order_error_message
|
|
791
|
+
return self._generate_assertion_failure(
|
|
792
|
+
error_message, prior_events, original_turn_events, self.line
|
|
793
|
+
)
|
|
794
|
+
|
|
795
|
+
return None, matching_event
|
|
796
|
+
|
|
797
|
+
def _buttons_match(self, event: BotUttered) -> bool:
|
|
798
|
+
"""Check if the bot response contains the expected buttons."""
|
|
799
|
+
# a button is a dictionary with keys 'title' and 'payload'
|
|
800
|
+
actual_buttons = event.data.get("buttons", [])
|
|
801
|
+
if not actual_buttons:
|
|
802
|
+
return False
|
|
803
|
+
|
|
804
|
+
return all(
|
|
805
|
+
self._button_matches(actual_button, expected_button)
|
|
806
|
+
for actual_button, expected_button in zip(actual_buttons, self.buttons)
|
|
807
|
+
)
|
|
808
|
+
|
|
809
|
+
@staticmethod
|
|
810
|
+
def _button_matches(
|
|
811
|
+
actual_button: Dict[str, Any], expected_button: AssertedButton
|
|
812
|
+
) -> bool:
|
|
813
|
+
"""Check if the actual button matches the expected button."""
|
|
814
|
+
return (
|
|
815
|
+
actual_button.get("title") == expected_button.title
|
|
816
|
+
and actual_button.get("payload") == expected_button.payload
|
|
817
|
+
)
|
|
818
|
+
|
|
819
|
+
def __hash__(self) -> int:
|
|
820
|
+
return hash(json.dumps(self.as_dict()))
|
|
821
|
+
|
|
822
|
+
|
|
823
|
+
@dataclass
|
|
824
|
+
class BotDidNotUtterAssertion(Assertion):
|
|
825
|
+
"""Class for the 'bot_did_not_utter' assertion."""
|
|
826
|
+
|
|
827
|
+
utter_name: Optional[str] = None
|
|
828
|
+
text_matches: Optional[str] = None
|
|
829
|
+
buttons: Optional[List[AssertedButton]] = None
|
|
830
|
+
line: Optional[int] = None
|
|
831
|
+
|
|
832
|
+
@classmethod
|
|
833
|
+
def type(cls) -> str:
|
|
834
|
+
return AssertionType.BOT_DID_NOT_UTTER.value
|
|
835
|
+
|
|
836
|
+
@staticmethod
|
|
837
|
+
def from_dict(assertion_dict: Dict[Text, Any]) -> BotDidNotUtterAssertion:
|
|
838
|
+
"""Creates a BotDidNotUtterAssertion from a dictionary."""
|
|
839
|
+
assertion_dict = assertion_dict.get(AssertionType.BOT_DID_NOT_UTTER.value, {})
|
|
840
|
+
utter_name = assertion_dict.get("utter_name")
|
|
841
|
+
text_matches = assertion_dict.get("text_matches")
|
|
842
|
+
buttons = [
|
|
843
|
+
AssertedButton.from_dict(button)
|
|
844
|
+
for button in assertion_dict.get("buttons", [])
|
|
845
|
+
]
|
|
846
|
+
|
|
847
|
+
if not utter_name and not text_matches and not buttons:
|
|
848
|
+
raise RasaException(
|
|
849
|
+
"A 'bot_did_not_utter' assertion is empty. "
|
|
850
|
+
"It should contain at least one of the allowed properties: "
|
|
851
|
+
"'utter_name', 'text_matches', or 'buttons'."
|
|
852
|
+
)
|
|
853
|
+
|
|
854
|
+
return BotDidNotUtterAssertion(
|
|
855
|
+
utter_name=utter_name,
|
|
856
|
+
text_matches=text_matches,
|
|
857
|
+
buttons=buttons,
|
|
858
|
+
line=assertion_dict.lc.line + 1 if hasattr(assertion_dict, "lc") else None,
|
|
859
|
+
)
|
|
860
|
+
|
|
861
|
+
def run(
|
|
862
|
+
self,
|
|
863
|
+
turn_events: List[Event],
|
|
864
|
+
prior_events: List[Event],
|
|
865
|
+
assertion_order_error_message: str = "",
|
|
866
|
+
**kwargs: Any,
|
|
867
|
+
) -> Tuple[Optional[AssertionFailure], Optional[Event]]:
|
|
868
|
+
"""Checks that the bot did not utter the specified messages or buttons."""
|
|
869
|
+
step_index = kwargs.get("step_index")
|
|
870
|
+
original_turn_events, turn_events = _get_turn_events_based_on_step_index(
|
|
871
|
+
step_index, turn_events, prior_events
|
|
872
|
+
)
|
|
873
|
+
|
|
874
|
+
for event in turn_events:
|
|
875
|
+
if isinstance(event, BotUttered):
|
|
876
|
+
error_messages = []
|
|
877
|
+
if self._utter_name_matches(event):
|
|
878
|
+
error_messages.append(
|
|
879
|
+
f"Bot uttered a forbidden utterance '{self.utter_name}'."
|
|
880
|
+
)
|
|
881
|
+
if self._text_matches(event):
|
|
882
|
+
error_messages.append(
|
|
883
|
+
f"Bot uttered a forbidden message matching "
|
|
884
|
+
f"the pattern '{self.text_matches}'."
|
|
885
|
+
)
|
|
886
|
+
if self._buttons_match(event):
|
|
887
|
+
error_messages.append(
|
|
888
|
+
"Bot uttered a forbidden response with specified buttons."
|
|
889
|
+
)
|
|
890
|
+
|
|
891
|
+
if error_messages:
|
|
892
|
+
error_message = " ".join(error_messages)
|
|
893
|
+
error_message += assertion_order_error_message
|
|
894
|
+
return self._generate_assertion_failure(
|
|
895
|
+
error_message, prior_events, original_turn_events, self.line
|
|
896
|
+
)
|
|
897
|
+
return None, None
|
|
898
|
+
|
|
899
|
+
def _utter_name_matches(self, event: BotUttered) -> bool:
|
|
900
|
+
if self.utter_name is not None:
|
|
901
|
+
if event.metadata.get("utter_action") == self.utter_name:
|
|
902
|
+
return True
|
|
903
|
+
return False
|
|
904
|
+
|
|
905
|
+
def _text_matches(self, event: BotUttered) -> bool:
|
|
906
|
+
if self.text_matches is not None:
|
|
907
|
+
pattern = re.compile(self.text_matches)
|
|
908
|
+
if pattern.search(event.text):
|
|
909
|
+
return True
|
|
910
|
+
return False
|
|
911
|
+
|
|
912
|
+
def _buttons_match(self, event: BotUttered) -> bool:
|
|
913
|
+
"""Check if the bot response contains any of the forbidden buttons."""
|
|
914
|
+
if self.buttons is None:
|
|
915
|
+
return False
|
|
916
|
+
|
|
917
|
+
actual_buttons = event.data.get("buttons", [])
|
|
918
|
+
if not actual_buttons:
|
|
919
|
+
return False
|
|
920
|
+
|
|
921
|
+
for actual_button in actual_buttons:
|
|
922
|
+
if any(
|
|
923
|
+
self._is_forbidden_button(actual_button, forbidden_button)
|
|
924
|
+
for forbidden_button in self.buttons
|
|
925
|
+
):
|
|
926
|
+
return True
|
|
927
|
+
return False
|
|
928
|
+
|
|
929
|
+
@staticmethod
|
|
930
|
+
def _is_forbidden_button(
|
|
931
|
+
actual_button: Dict[str, Any], forbidden_button: AssertedButton
|
|
932
|
+
) -> bool:
|
|
933
|
+
"""Check if the button matches any of the forbidden buttons."""
|
|
934
|
+
actual_title = actual_button.get("title")
|
|
935
|
+
actual_payload = actual_button.get("payload")
|
|
936
|
+
|
|
937
|
+
title_matches = forbidden_button.title == actual_title
|
|
938
|
+
payload_matches = forbidden_button.payload == actual_payload
|
|
939
|
+
if title_matches and payload_matches:
|
|
940
|
+
return True
|
|
941
|
+
return False
|
|
942
|
+
|
|
943
|
+
def __hash__(self) -> int:
|
|
944
|
+
"""Hash method to ensure the assertion is hashable."""
|
|
945
|
+
return hash(json.dumps(self.as_dict()))
|
|
946
|
+
|
|
947
|
+
|
|
948
|
+
@dataclass
|
|
949
|
+
class GenerativeResponseMixin(Assertion):
|
|
950
|
+
"""Mixin class for storing generative response assertions."""
|
|
951
|
+
|
|
952
|
+
threshold: float = DEFAULT_THRESHOLD
|
|
953
|
+
utter_name: Optional[str] = None
|
|
954
|
+
line: Optional[int] = None
|
|
955
|
+
metric_adjective: Optional[str] = None
|
|
956
|
+
metric_name: Optional[str] = None
|
|
957
|
+
mlflow_metric: Callable = print
|
|
958
|
+
|
|
959
|
+
@classmethod
|
|
960
|
+
def type(cls) -> str:
|
|
961
|
+
return ""
|
|
962
|
+
|
|
963
|
+
def _get_ground_truth(self, matching_event: BotUttered) -> str:
|
|
964
|
+
raise NotImplementedError
|
|
965
|
+
|
|
966
|
+
def as_dict(self) -> Dict[str, Any]:
|
|
967
|
+
data = super().as_dict()
|
|
968
|
+
data.pop("metric_name")
|
|
969
|
+
data.pop("metric_adjective")
|
|
970
|
+
data.pop("mlflow_metric")
|
|
971
|
+
|
|
972
|
+
return data
|
|
973
|
+
|
|
974
|
+
def _run_llm_evaluation(
|
|
975
|
+
self,
|
|
976
|
+
matching_event: BotUttered,
|
|
977
|
+
step_text: str,
|
|
978
|
+
llm_judge_config: "LLMJudgeConfig",
|
|
979
|
+
assertion_order_error_message: str,
|
|
980
|
+
prior_events: List[Event],
|
|
981
|
+
turn_events: List[Event],
|
|
982
|
+
) -> Tuple[Optional[AssertionFailure], Optional[Event]]:
|
|
983
|
+
"""Run the LLM evaluation on the given event."""
|
|
984
|
+
import mlflow
|
|
985
|
+
|
|
986
|
+
# we need to configure the log level for mlflow
|
|
987
|
+
# after a local import to avoid unnecessary logs
|
|
988
|
+
update_mlflow_log_level()
|
|
989
|
+
|
|
990
|
+
# extract user question from event if available
|
|
991
|
+
user_question_from_event = matching_event.metadata.get(
|
|
992
|
+
SEARCH_QUERY_METADATA_KEY
|
|
993
|
+
)
|
|
994
|
+
user_question = (
|
|
995
|
+
user_question_from_event if user_question_from_event else step_text
|
|
996
|
+
)
|
|
997
|
+
|
|
998
|
+
ground_truth = self._get_ground_truth(matching_event)
|
|
999
|
+
|
|
1000
|
+
eval_data = pd.DataFrame(
|
|
1001
|
+
{
|
|
1002
|
+
"inputs": [user_question],
|
|
1003
|
+
"ground_truth": [ground_truth],
|
|
1004
|
+
"predictions": [matching_event.text],
|
|
1005
|
+
}
|
|
1006
|
+
)
|
|
1007
|
+
|
|
1008
|
+
model_uri = llm_judge_config.get_model_uri()
|
|
1009
|
+
|
|
1010
|
+
structlogger.debug(
|
|
1011
|
+
f"generative_response_is_{self.metric_adjective}_assertion.run_llm_evaluation",
|
|
1012
|
+
model_uri=model_uri,
|
|
1013
|
+
)
|
|
1014
|
+
|
|
1015
|
+
with mlflow.start_run():
|
|
1016
|
+
results = mlflow.evaluate(
|
|
1017
|
+
data=eval_data,
|
|
1018
|
+
targets="ground_truth",
|
|
1019
|
+
predictions="predictions",
|
|
1020
|
+
model_type="question-answering",
|
|
1021
|
+
evaluators="default",
|
|
1022
|
+
extra_metrics=[
|
|
1023
|
+
self.mlflow_metric(model_uri),
|
|
1024
|
+
],
|
|
1025
|
+
)
|
|
1026
|
+
|
|
1027
|
+
# Evaluation result for each data record is available in `results.tables`.
|
|
1028
|
+
eval_table = results.tables["eval_results_table"]
|
|
1029
|
+
score = eval_table.iloc[0][f"{self.metric_name}/v1/score"]
|
|
1030
|
+
justification = eval_table.iloc[0][f"{self.metric_name}/v1/justification"]
|
|
1031
|
+
|
|
1032
|
+
# convert 1-5 score to 0-1 float
|
|
1033
|
+
score = score * 20 / 100 if score is not None else 0
|
|
1034
|
+
|
|
1035
|
+
structlogger.debug(
|
|
1036
|
+
f"generative_response_is_{self.metric_adjective}_assertion.run_results",
|
|
1037
|
+
matching_event=repr(matching_event),
|
|
1038
|
+
score=score,
|
|
1039
|
+
justification=justification,
|
|
1040
|
+
)
|
|
1041
|
+
|
|
1042
|
+
if score < self.threshold:
|
|
1043
|
+
error_message = (
|
|
1044
|
+
f"Generative response '{matching_event.text}' "
|
|
1045
|
+
f"given to the user input '{user_question}' "
|
|
1046
|
+
f"was not {self.metric_adjective}. "
|
|
1047
|
+
f"Expected score to be above '{self.threshold}' threshold, "
|
|
1048
|
+
f"but was '{score}'. The explanation for this score is: "
|
|
1049
|
+
f"{justification}."
|
|
1050
|
+
)
|
|
1051
|
+
error_message += assertion_order_error_message
|
|
1052
|
+
|
|
1053
|
+
return self._generate_assertion_failure(
|
|
1054
|
+
error_message, prior_events, turn_events, self.line
|
|
1055
|
+
)
|
|
1056
|
+
|
|
1057
|
+
return None, matching_event
|
|
1058
|
+
|
|
1059
|
+
def _run_assertion_with_utter_name(
|
|
1060
|
+
self,
|
|
1061
|
+
matching_events: List[BotUttered],
|
|
1062
|
+
step_text: str,
|
|
1063
|
+
llm_judge_config: "LLMJudgeConfig",
|
|
1064
|
+
assertion_order_error_message: str,
|
|
1065
|
+
prior_events: List[Event],
|
|
1066
|
+
turn_events: List[Event],
|
|
1067
|
+
) -> Tuple[Optional[AssertionFailure], Optional[Event]]:
|
|
1068
|
+
"""Assert metric for the given utter name."""
|
|
1069
|
+
try:
|
|
1070
|
+
matching_event = next(
|
|
1071
|
+
event
|
|
1072
|
+
for event in matching_events
|
|
1073
|
+
if event.metadata.get("utter_action") == self.utter_name
|
|
1074
|
+
)
|
|
1075
|
+
except StopIteration:
|
|
1076
|
+
error_message = f"Bot did not utter '{self.utter_name}' response."
|
|
1077
|
+
error_message += assertion_order_error_message
|
|
1078
|
+
|
|
1079
|
+
return self._generate_assertion_failure(
|
|
1080
|
+
error_message, prior_events, turn_events, self.line
|
|
1081
|
+
)
|
|
1082
|
+
|
|
1083
|
+
return self._run_llm_evaluation(
|
|
1084
|
+
matching_event,
|
|
1085
|
+
step_text,
|
|
1086
|
+
llm_judge_config,
|
|
1087
|
+
assertion_order_error_message,
|
|
1088
|
+
prior_events,
|
|
1089
|
+
turn_events,
|
|
1090
|
+
)
|
|
1091
|
+
|
|
1092
|
+
def _run_assertion_for_multiple_generative_responses(
|
|
1093
|
+
self,
|
|
1094
|
+
matching_events: List[BotUttered],
|
|
1095
|
+
step_text: str,
|
|
1096
|
+
llm_judge_config: "LLMJudgeConfig",
|
|
1097
|
+
assertion_order_error_message: str,
|
|
1098
|
+
prior_events: List[Event],
|
|
1099
|
+
turn_events: List[Event],
|
|
1100
|
+
) -> Tuple[Optional[AssertionFailure], Optional[Event]]:
|
|
1101
|
+
"""Run LLM evaluation for multiple bot utterances."""
|
|
1102
|
+
structlogger.debug(
|
|
1103
|
+
f"generative_response_is_{self.metric_adjective}_assertion.run",
|
|
1104
|
+
event_info="Multiple generative responses found, "
|
|
1105
|
+
"we will evaluate each of the responses.",
|
|
1106
|
+
)
|
|
1107
|
+
|
|
1108
|
+
passing_events = set()
|
|
1109
|
+
for event in matching_events:
|
|
1110
|
+
failure, event_result = self._run_llm_evaluation(
|
|
1111
|
+
event,
|
|
1112
|
+
step_text,
|
|
1113
|
+
llm_judge_config,
|
|
1114
|
+
assertion_order_error_message,
|
|
1115
|
+
prior_events,
|
|
1116
|
+
turn_events,
|
|
1117
|
+
)
|
|
1118
|
+
if event_result is not None:
|
|
1119
|
+
passing_events.add(event_result)
|
|
1120
|
+
else:
|
|
1121
|
+
if not passing_events:
|
|
1122
|
+
error_message = (
|
|
1123
|
+
f"None of the generative responses issued by either the "
|
|
1124
|
+
f"Enterprise Search Policy, IntentlessPolicy or the "
|
|
1125
|
+
f"Contextual Response Rephraser were {self.metric_adjective}."
|
|
1126
|
+
)
|
|
1127
|
+
error_message += assertion_order_error_message
|
|
1128
|
+
|
|
1129
|
+
return self._generate_assertion_failure(
|
|
1130
|
+
error_message, prior_events, turn_events, self.line
|
|
1131
|
+
)
|
|
1132
|
+
|
|
1133
|
+
return None, list(passing_events)[-1]
|
|
1134
|
+
|
|
1135
|
+
def run(
|
|
1136
|
+
self,
|
|
1137
|
+
turn_events: List[Event],
|
|
1138
|
+
prior_events: List[Event],
|
|
1139
|
+
assertion_order_error_message: str = "",
|
|
1140
|
+
llm_judge_config: Optional["LLMJudgeConfig"] = None,
|
|
1141
|
+
step_text: Optional[str] = None,
|
|
1142
|
+
**kwargs: Any,
|
|
1143
|
+
) -> Tuple[Optional[AssertionFailure], Optional[Event]]:
|
|
1144
|
+
"""Run the LLM evaluation on the given events for that user turn."""
|
|
1145
|
+
matching_events: List[BotUttered] = _find_matching_generative_events(
|
|
1146
|
+
turn_events
|
|
1147
|
+
)
|
|
1148
|
+
|
|
1149
|
+
if not matching_events:
|
|
1150
|
+
error_message = (
|
|
1151
|
+
"No generative response issued by either the Enterprise Search Policy, "
|
|
1152
|
+
"IntentlessPolicy or the Contextual Response Rephraser was found, "
|
|
1153
|
+
"but one was expected."
|
|
1154
|
+
)
|
|
1155
|
+
error_message += assertion_order_error_message
|
|
1156
|
+
|
|
1157
|
+
return self._generate_assertion_failure(
|
|
1158
|
+
error_message, prior_events, turn_events, self.line
|
|
1159
|
+
)
|
|
1160
|
+
|
|
1161
|
+
if self.utter_name is not None:
|
|
1162
|
+
return self._run_assertion_with_utter_name(
|
|
1163
|
+
matching_events,
|
|
1164
|
+
step_text,
|
|
1165
|
+
llm_judge_config,
|
|
1166
|
+
assertion_order_error_message,
|
|
1167
|
+
prior_events,
|
|
1168
|
+
turn_events,
|
|
1169
|
+
)
|
|
1170
|
+
|
|
1171
|
+
if len(matching_events) > 1:
|
|
1172
|
+
return self._run_assertion_for_multiple_generative_responses(
|
|
1173
|
+
matching_events,
|
|
1174
|
+
step_text,
|
|
1175
|
+
llm_judge_config,
|
|
1176
|
+
assertion_order_error_message,
|
|
1177
|
+
prior_events,
|
|
1178
|
+
turn_events,
|
|
1179
|
+
)
|
|
1180
|
+
|
|
1181
|
+
matching_event = matching_events[0]
|
|
1182
|
+
|
|
1183
|
+
return self._run_llm_evaluation(
|
|
1184
|
+
matching_event,
|
|
1185
|
+
step_text,
|
|
1186
|
+
llm_judge_config,
|
|
1187
|
+
assertion_order_error_message,
|
|
1188
|
+
prior_events,
|
|
1189
|
+
turn_events,
|
|
1190
|
+
)
|
|
1191
|
+
|
|
1192
|
+
|
|
1193
|
+
@dataclass
|
|
1194
|
+
class GenerativeResponseIsRelevantAssertion(GenerativeResponseMixin):
|
|
1195
|
+
"""Class for storing the generative response is relevant assertion."""
|
|
1196
|
+
|
|
1197
|
+
def _get_ground_truth(self, matching_event: BotUttered) -> str:
|
|
1198
|
+
return ""
|
|
1199
|
+
|
|
1200
|
+
@classmethod
|
|
1201
|
+
def type(cls) -> str:
|
|
1202
|
+
return AssertionType.GENERATIVE_RESPONSE_IS_RELEVANT.value
|
|
1203
|
+
|
|
1204
|
+
@staticmethod
|
|
1205
|
+
def from_dict(
|
|
1206
|
+
assertion_dict: Dict[Text, Any],
|
|
1207
|
+
) -> GenerativeResponseIsRelevantAssertion:
|
|
1208
|
+
import mlflow
|
|
1209
|
+
|
|
1210
|
+
assertion_dict = assertion_dict.get(
|
|
1211
|
+
AssertionType.GENERATIVE_RESPONSE_IS_RELEVANT.value, {}
|
|
1212
|
+
)
|
|
1213
|
+
return GenerativeResponseIsRelevantAssertion(
|
|
1214
|
+
threshold=assertion_dict.get("threshold", DEFAULT_THRESHOLD),
|
|
1215
|
+
utter_name=assertion_dict.get("utter_name"),
|
|
1216
|
+
line=assertion_dict.lc.line + 1 if hasattr(assertion_dict, "lc") else None,
|
|
1217
|
+
metric_name="answer_relevance",
|
|
1218
|
+
metric_adjective="relevant",
|
|
1219
|
+
mlflow_metric=mlflow.metrics.genai.answer_relevance,
|
|
1220
|
+
)
|
|
1221
|
+
|
|
1222
|
+
def __hash__(self) -> int:
|
|
1223
|
+
return hash(json.dumps(self.as_dict()))
|
|
1224
|
+
|
|
1225
|
+
|
|
1226
|
+
@dataclass
|
|
1227
|
+
class GenerativeResponseIsGroundedAssertion(GenerativeResponseMixin):
|
|
1228
|
+
"""Class for storing the generative response is grounded assertion."""
|
|
1229
|
+
|
|
1230
|
+
ground_truth: Optional[str] = None
|
|
1231
|
+
|
|
1232
|
+
@classmethod
|
|
1233
|
+
def type(cls) -> str:
|
|
1234
|
+
return AssertionType.GENERATIVE_RESPONSE_IS_GROUNDED.value
|
|
1235
|
+
|
|
1236
|
+
@staticmethod
|
|
1237
|
+
def from_dict(
|
|
1238
|
+
assertion_dict: Dict[Text, Any],
|
|
1239
|
+
) -> GenerativeResponseIsGroundedAssertion:
|
|
1240
|
+
import mlflow
|
|
1241
|
+
|
|
1242
|
+
assertion_dict = assertion_dict.get(
|
|
1243
|
+
AssertionType.GENERATIVE_RESPONSE_IS_GROUNDED.value, {}
|
|
1244
|
+
)
|
|
1245
|
+
return GenerativeResponseIsGroundedAssertion(
|
|
1246
|
+
threshold=assertion_dict.get("threshold", DEFAULT_THRESHOLD),
|
|
1247
|
+
utter_name=assertion_dict.get("utter_name"),
|
|
1248
|
+
ground_truth=assertion_dict.get("ground_truth"),
|
|
1249
|
+
line=assertion_dict.lc.line + 1 if hasattr(assertion_dict, "lc") else None,
|
|
1250
|
+
metric_name="answer_correctness",
|
|
1251
|
+
metric_adjective="grounded",
|
|
1252
|
+
mlflow_metric=mlflow.metrics.genai.answer_correctness,
|
|
1253
|
+
)
|
|
1254
|
+
|
|
1255
|
+
def __hash__(self) -> int:
|
|
1256
|
+
return hash(json.dumps(self.as_dict()))
|
|
1257
|
+
|
|
1258
|
+
def _get_ground_truth(self, matching_event: BotUttered) -> str:
|
|
1259
|
+
# extract ground truth from event if available or use the provided ground truth
|
|
1260
|
+
ground_truth_event_metadata = matching_event.metadata.get(
|
|
1261
|
+
SEARCH_RESULTS_METADATA_KEY, ""
|
|
1262
|
+
) or matching_event.metadata.get(DOMAIN_GROUND_TRUTH_METADATA_KEY, "")
|
|
1263
|
+
|
|
1264
|
+
if isinstance(ground_truth_event_metadata, list):
|
|
1265
|
+
ground_truth_event_metadata = "\n".join(ground_truth_event_metadata)
|
|
1266
|
+
|
|
1267
|
+
ground_truth = (
|
|
1268
|
+
self.ground_truth
|
|
1269
|
+
if self.ground_truth is not None
|
|
1270
|
+
else ground_truth_event_metadata
|
|
1271
|
+
)
|
|
1272
|
+
|
|
1273
|
+
return ground_truth
|
|
1274
|
+
|
|
1275
|
+
|
|
1276
|
+
@dataclass
|
|
1277
|
+
class AssertionFailure:
|
|
1278
|
+
"""Class for storing the assertion failure."""
|
|
1279
|
+
|
|
1280
|
+
assertion: Assertion
|
|
1281
|
+
error_message: Text
|
|
1282
|
+
actual_events_transcript: List[Text]
|
|
1283
|
+
error_line: Optional[int] = None
|
|
1284
|
+
|
|
1285
|
+
def as_dict(self) -> Dict[Text, Any]:
|
|
1286
|
+
"""Returns the assertion failure as a dictionary."""
|
|
1287
|
+
return {
|
|
1288
|
+
"assertion": self.assertion.as_dict(),
|
|
1289
|
+
"error_message": self.error_message,
|
|
1290
|
+
"actual_events_transcript": self.actual_events_transcript,
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
|
|
1294
|
+
def create_actual_events_transcript(
|
|
1295
|
+
prior_events: List[Event], turn_events: List[Event]
|
|
1296
|
+
) -> List[Text]:
|
|
1297
|
+
"""Create the actual events transcript for the assertion failure."""
|
|
1298
|
+
all_events = prior_events + turn_events
|
|
1299
|
+
|
|
1300
|
+
event_transcript = []
|
|
1301
|
+
|
|
1302
|
+
for event in all_events:
|
|
1303
|
+
if isinstance(event, SlotSet) and event.key in DEFAULT_SLOT_NAMES:
|
|
1304
|
+
continue
|
|
1305
|
+
if isinstance(event, DefinePrevUserUtteredFeaturization):
|
|
1306
|
+
continue
|
|
1307
|
+
if isinstance(event, DialogueStackUpdated):
|
|
1308
|
+
continue
|
|
1309
|
+
|
|
1310
|
+
event_transcript.append(repr(event))
|
|
1311
|
+
|
|
1312
|
+
return event_transcript
|
|
1313
|
+
|
|
1314
|
+
|
|
1315
|
+
def _find_matching_generative_events(turn_events: List[Event]) -> List[BotUttered]:
|
|
1316
|
+
"""Find the matching events for the generative response assertions."""
|
|
1317
|
+
return [
|
|
1318
|
+
event
|
|
1319
|
+
for event in turn_events
|
|
1320
|
+
if isinstance(event, BotUttered)
|
|
1321
|
+
and event.metadata.get(UTTER_SOURCE_METADATA_KEY)
|
|
1322
|
+
in ELIGIBLE_UTTER_SOURCE_METADATA
|
|
1323
|
+
]
|
|
1324
|
+
|
|
1325
|
+
|
|
1326
|
+
def _get_turn_events_based_on_step_index(
|
|
1327
|
+
step_index: int, turn_events: List[Event], prior_events: List[Event]
|
|
1328
|
+
) -> Tuple[List[Event], List[Event]]:
|
|
1329
|
+
"""Get the turn events based on the step index.
|
|
1330
|
+
|
|
1331
|
+
For the first step, we need to include the prior events as well
|
|
1332
|
+
in the same user turn. For the subsequent steps, we only need the
|
|
1333
|
+
events that follow the user uttered event on which the tracker
|
|
1334
|
+
was originally sliced by.
|
|
1335
|
+
|
|
1336
|
+
Returns:
|
|
1337
|
+
List[Event]: The copy of turn_events
|
|
1338
|
+
List[Event]: The turn events based on the step index
|
|
1339
|
+
|
|
1340
|
+
"""
|
|
1341
|
+
original_turn_events = turn_events[:]
|
|
1342
|
+
if step_index == 0:
|
|
1343
|
+
return original_turn_events, prior_events + turn_events
|
|
1344
|
+
|
|
1345
|
+
return original_turn_events, turn_events
|