rasa-pro 3.12.22__py3-none-any.whl → 3.13.0__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.
- rasa/__main__.py +3 -4
- rasa/api.py +1 -1
- rasa/cli/dialogue_understanding_test.py +1 -1
- rasa/cli/e2e_test.py +1 -8
- rasa/cli/evaluate.py +2 -2
- rasa/cli/export.py +5 -3
- rasa/cli/inspect.py +7 -0
- rasa/cli/llm_fine_tuning.py +1 -1
- rasa/cli/project_templates/default/config.yml +5 -32
- rasa/cli/project_templates/{calm → default}/e2e_tests/cancelations/user_cancels_during_a_correction.yml +1 -1
- rasa/cli/project_templates/{calm → default}/e2e_tests/cancelations/user_changes_mind_on_a_whim.yml +1 -1
- rasa/cli/project_templates/{calm → default}/e2e_tests/corrections/user_corrects_contact_handle.yml +1 -1
- rasa/cli/project_templates/{calm → default}/e2e_tests/corrections/user_corrects_contact_name.yml +1 -1
- rasa/cli/project_templates/{calm → default}/e2e_tests/happy_paths/user_adds_contact_to_their_list.yml +1 -1
- rasa/cli/project_templates/{calm → default}/e2e_tests/happy_paths/user_lists_contacts.yml +1 -1
- rasa/cli/project_templates/{calm → default}/e2e_tests/happy_paths/user_removes_contact.yml +1 -1
- rasa/cli/project_templates/{calm → default}/e2e_tests/happy_paths/user_removes_contact_from_list.yml +1 -1
- rasa/cli/project_templates/default/endpoints.yml +18 -2
- rasa/cli/project_templates/defaults.py +133 -0
- rasa/cli/project_templates/tutorial/config.yml +1 -1
- rasa/cli/project_templates/tutorial/endpoints.yml +1 -1
- rasa/cli/run.py +1 -1
- rasa/cli/scaffold.py +2 -3
- rasa/cli/shell.py +6 -1
- rasa/cli/studio/download.py +0 -22
- rasa/cli/studio/link.py +36 -0
- rasa/cli/studio/pull.py +79 -0
- rasa/cli/studio/push.py +78 -0
- rasa/cli/studio/studio.py +12 -0
- rasa/cli/studio/train.py +1 -5
- rasa/cli/studio/upload.py +6 -4
- rasa/cli/train.py +5 -1
- rasa/cli/utils.py +1 -1
- rasa/cli/x.py +1 -1
- rasa/constants.py +2 -0
- rasa/core/__init__.py +0 -16
- rasa/core/actions/action.py +43 -29
- rasa/core/actions/action_repeat_bot_messages.py +18 -22
- rasa/core/actions/action_run_slot_rejections.py +1 -2
- rasa/core/agent.py +24 -3
- rasa/core/available_endpoints.py +146 -0
- rasa/core/brokers/kafka.py +4 -0
- rasa/core/brokers/pika.py +5 -2
- rasa/core/brokers/sql.py +1 -1
- rasa/core/channels/__init__.py +3 -0
- rasa/core/channels/botframework.py +2 -2
- rasa/core/channels/channel.py +2 -2
- rasa/core/channels/development_inspector.py +1 -1
- rasa/core/channels/facebook.py +1 -4
- rasa/core/channels/hangouts.py +8 -5
- rasa/core/channels/inspector/.eslintrc.cjs +12 -6
- rasa/core/channels/inspector/.prettierrc +5 -0
- rasa/core/channels/inspector/README.md +11 -5
- rasa/core/channels/inspector/dist/assets/{arc-9f75cc3b.js → arc-371401b1.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{blockDiagram-38ab4fdb-7f34db23.js → blockDiagram-38ab4fdb-3f126156.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{c4Diagram-3d4e48cf-948bab2c.js → c4Diagram-3d4e48cf-12f22eb7.js} +1 -1
- rasa/core/channels/inspector/dist/assets/channel-f1efda17.js +1 -0
- rasa/core/channels/inspector/dist/assets/{classDiagram-70f12bd4-53b0dd0e.js → classDiagram-70f12bd4-03b1d386.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{classDiagram-v2-f2320105-fdf789e7.js → classDiagram-v2-f2320105-84f69d63.js} +1 -1
- rasa/core/channels/inspector/dist/assets/clone-fdf164e2.js +1 -0
- rasa/core/channels/inspector/dist/assets/{createText-2e5e7dd3-87c4ece5.js → createText-2e5e7dd3-ca47fd38.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{edges-e0da2a9e-5a8b0749.js → edges-e0da2a9e-f837ca8a.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{erDiagram-9861fffd-66da90e2.js → erDiagram-9861fffd-8717ac54.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{flowDb-956e92f1-10044f05.js → flowDb-956e92f1-94f38b83.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{flowDiagram-66a62f08-f338f66a.js → flowDiagram-66a62f08-b616f9fb.js} +1 -1
- rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-7d7a1629.js +1 -0
- rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-4a651766-b13140aa.js → flowchart-elk-definition-4a651766-f5d24bb8.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{ganttDiagram-c361ad54-f2b4a55a.js → ganttDiagram-c361ad54-b43ba8d9.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-72cf32ee-dedc298d.js → gitGraphDiagram-72cf32ee-c3aafaa5.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{graph-4ede11ff.js → graph-0d0a2c10.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{index-3862675e-65549d37.js → index-3862675e-58ea0305.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{index-3a23e736.js → index-cce6f8a1.js} +123 -123
- rasa/core/channels/inspector/dist/assets/{infoDiagram-f8f76790-65439671.js → infoDiagram-f8f76790-b8f60461.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{journeyDiagram-49397b02-56d03d98.js → journeyDiagram-49397b02-95be5545.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{layout-dd48f7f4.js → layout-da885b9b.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{line-1569ad2c.js → line-f1c817d3.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{linear-48bf4935.js → linear-d42801e6.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{mindmap-definition-fc14e90a-688504c1.js → mindmap-definition-fc14e90a-a38923a6.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{pieDiagram-8a3498a8-78b6d7e6.js → pieDiagram-8a3498a8-ca6e71e9.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{quadrantDiagram-120e2f19-048b84b3.js → quadrantDiagram-120e2f19-b290dae9.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{requirementDiagram-deff3bca-dd67f107.js → requirementDiagram-deff3bca-03f02ceb.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{sankeyDiagram-04a897e0-8128436e.js → sankeyDiagram-04a897e0-c49eee40.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{sequenceDiagram-704730f1-1a0d1461.js → sequenceDiagram-704730f1-b2cd6a3d.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{stateDiagram-587899a1-46d388ed.js → stateDiagram-587899a1-e53a2028.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-d93cdb3a-ea42951a.js → stateDiagram-v2-d93cdb3a-e1982a03.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-6aaf32cf-7427ed0c.js → styles-6aaf32cf-d0226ca5.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-9a916d00-ff5e5a16.js → styles-9a916d00-0e21dc00.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-c10674c1-7b3680cf.js → styles-c10674c1-9588494e.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{svgDrawCommon-08f97a94-f860f2ad.js → svgDrawCommon-08f97a94-be478d4f.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{timeline-definition-85554ec2-2eebf0c8.js → timeline-definition-85554ec2-74631749.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{xychartDiagram-e933f94c-5d7f4e96.js → xychartDiagram-e933f94c-a043552f.js} +1 -1
- rasa/core/channels/inspector/dist/index.html +1 -1
- rasa/core/channels/inspector/package.json +3 -1
- rasa/core/channels/inspector/src/App.tsx +91 -90
- rasa/core/channels/inspector/src/components/Chat.tsx +45 -41
- rasa/core/channels/inspector/src/components/DiagramFlow.tsx +40 -40
- rasa/core/channels/inspector/src/components/DialogueInformation.tsx +57 -57
- rasa/core/channels/inspector/src/components/DialogueStack.tsx +36 -27
- rasa/core/channels/inspector/src/components/ExpandIcon.tsx +4 -4
- rasa/core/channels/inspector/src/components/FullscreenButton.tsx +7 -7
- rasa/core/channels/inspector/src/components/LoadingSpinner.tsx +28 -12
- rasa/core/channels/inspector/src/components/NoActiveFlow.tsx +9 -9
- rasa/core/channels/inspector/src/components/RasaLogo.tsx +5 -5
- rasa/core/channels/inspector/src/components/RecruitmentPanel.tsx +55 -60
- rasa/core/channels/inspector/src/components/SaraDiagrams.tsx +5 -5
- rasa/core/channels/inspector/src/components/Slots.tsx +22 -22
- rasa/core/channels/inspector/src/components/Welcome.tsx +28 -31
- rasa/core/channels/inspector/src/helpers/audio/audiostream.ts +245 -0
- rasa/core/channels/inspector/src/helpers/audio/microphone-processor.js +12 -0
- rasa/core/channels/inspector/src/helpers/audio/playback-processor.js +36 -0
- rasa/core/channels/inspector/src/helpers/conversation.ts +7 -7
- rasa/core/channels/inspector/src/helpers/formatters.test.ts +181 -181
- rasa/core/channels/inspector/src/helpers/formatters.ts +111 -111
- rasa/core/channels/inspector/src/helpers/utils.ts +78 -61
- rasa/core/channels/inspector/src/main.tsx +8 -8
- rasa/core/channels/inspector/src/theme/Button/Button.ts +8 -8
- rasa/core/channels/inspector/src/theme/Heading/Heading.ts +7 -7
- rasa/core/channels/inspector/src/theme/Input/Input.ts +9 -9
- rasa/core/channels/inspector/src/theme/Link/Link.ts +6 -6
- rasa/core/channels/inspector/src/theme/Modal/Modal.ts +13 -13
- rasa/core/channels/inspector/src/theme/Table/Table.tsx +10 -10
- rasa/core/channels/inspector/src/theme/Tooltip/Tooltip.ts +5 -5
- rasa/core/channels/inspector/src/theme/base/breakpoints.ts +7 -7
- rasa/core/channels/inspector/src/theme/base/colors.ts +64 -64
- rasa/core/channels/inspector/src/theme/base/fonts/fontFaces.css +21 -18
- rasa/core/channels/inspector/src/theme/base/radii.ts +8 -8
- rasa/core/channels/inspector/src/theme/base/shadows.ts +5 -5
- rasa/core/channels/inspector/src/theme/base/sizes.ts +5 -5
- rasa/core/channels/inspector/src/theme/base/space.ts +12 -12
- rasa/core/channels/inspector/src/theme/base/styles.ts +5 -5
- rasa/core/channels/inspector/src/theme/base/typography.ts +12 -12
- rasa/core/channels/inspector/src/theme/base/zIndices.ts +3 -3
- rasa/core/channels/inspector/src/theme/index.ts +38 -38
- rasa/core/channels/inspector/src/types.ts +56 -50
- rasa/core/channels/inspector/yarn.lock +5 -0
- rasa/core/channels/mattermost.py +1 -1
- rasa/core/channels/rasa_chat.py +2 -4
- rasa/core/channels/rest.py +5 -4
- rasa/core/channels/socketio.py +56 -41
- rasa/core/channels/studio_chat.py +329 -68
- rasa/core/channels/vier_cvg.py +1 -2
- rasa/core/channels/voice_ready/audiocodes.py +4 -11
- rasa/core/channels/voice_ready/jambonz.py +5 -6
- rasa/core/channels/voice_ready/twilio_voice.py +13 -12
- rasa/core/channels/voice_ready/utils.py +22 -0
- rasa/core/channels/voice_stream/audiocodes.py +13 -16
- rasa/core/channels/voice_stream/browser_audio.py +1 -1
- rasa/core/channels/voice_stream/genesys.py +37 -18
- rasa/core/channels/voice_stream/jambonz.py +232 -0
- rasa/core/channels/voice_stream/tts/__init__.py +8 -0
- rasa/core/channels/voice_stream/twilio_media_streams.py +15 -12
- rasa/core/channels/voice_stream/voice_channel.py +71 -27
- rasa/core/concurrent_lock_store.py +24 -10
- rasa/core/evaluation/marker_tracker_loader.py +1 -1
- rasa/core/exporter.py +37 -1
- rasa/core/http_interpreter.py +3 -7
- rasa/core/information_retrieval/faiss.py +18 -11
- rasa/core/information_retrieval/ingestion/faq_parser.py +158 -0
- rasa/core/jobs.py +2 -1
- rasa/core/lock_store.py +151 -60
- rasa/core/nlg/contextual_response_rephraser.py +17 -7
- rasa/core/nlg/generator.py +5 -22
- rasa/core/nlg/interpolator.py +2 -3
- rasa/core/nlg/response.py +6 -43
- rasa/core/nlg/summarize.py +1 -1
- rasa/core/nlg/translate.py +0 -8
- rasa/core/policies/enterprise_search_policy.py +305 -189
- rasa/core/policies/enterprise_search_policy_config.py +241 -0
- rasa/core/policies/enterprise_search_prompt_with_relevancy_check_and_citation_template.jinja2 +67 -0
- rasa/core/policies/flow_policy.py +1 -1
- rasa/core/policies/flows/flow_executor.py +102 -17
- rasa/core/policies/intentless_policy.py +56 -17
- rasa/core/processor.py +70 -49
- rasa/core/run.py +33 -11
- rasa/core/tracker_stores/__init__.py +0 -0
- rasa/core/{auth_retry_tracker_store.py → tracker_stores/auth_retry_tracker_store.py} +66 -1
- rasa/core/tracker_stores/dynamo_tracker_store.py +256 -0
- rasa/core/tracker_stores/mongo_tracker_store.py +223 -0
- rasa/core/tracker_stores/redis_tracker_store.py +252 -0
- rasa/core/tracker_stores/sql_tracker_store.py +582 -0
- rasa/core/tracker_stores/tracker_store.py +839 -0
- rasa/core/training/interactive.py +1 -1
- rasa/core/utils.py +24 -95
- rasa/dialogue_understanding/coexistence/intent_based_router.py +2 -1
- rasa/dialogue_understanding/coexistence/llm_based_router.py +13 -11
- rasa/dialogue_understanding/commands/can_not_handle_command.py +2 -0
- rasa/dialogue_understanding/commands/cancel_flow_command.py +3 -1
- rasa/dialogue_understanding/commands/chit_chat_answer_command.py +2 -0
- rasa/dialogue_understanding/commands/clarify_command.py +6 -2
- rasa/dialogue_understanding/commands/command_syntax_manager.py +1 -0
- rasa/dialogue_understanding/commands/correct_slots_command.py +5 -6
- rasa/dialogue_understanding/commands/error_command.py +1 -1
- rasa/dialogue_understanding/commands/human_handoff_command.py +3 -3
- rasa/dialogue_understanding/commands/knowledge_answer_command.py +2 -0
- rasa/dialogue_understanding/commands/repeat_bot_messages_command.py +2 -0
- rasa/dialogue_understanding/commands/set_slot_command.py +8 -4
- rasa/dialogue_understanding/commands/skip_question_command.py +3 -3
- rasa/dialogue_understanding/commands/start_flow_command.py +7 -3
- rasa/dialogue_understanding/generator/__init__.py +7 -1
- rasa/dialogue_understanding/generator/command_generator.py +4 -2
- rasa/dialogue_understanding/generator/command_parser.py +2 -2
- rasa/dialogue_understanding/generator/command_parser_validator.py +63 -0
- rasa/dialogue_understanding/generator/llm_based_command_generator.py +1 -2
- rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +3 -2
- rasa/dialogue_understanding/generator/nlu_command_adapter.py +2 -2
- rasa/dialogue_understanding/generator/prompt_templates/command_prompt_template.jinja2 +0 -2
- rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_claude_3_5_sonnet_20240620_template.jinja2 +1 -0
- rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_gpt_4o_2024_11_20_template.jinja2 +1 -0
- rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v3_claude_3_5_sonnet_20240620_template.jinja2 +79 -0
- rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v3_gpt_4o_2024_11_20_template.jinja2 +79 -0
- rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +26 -461
- rasa/dialogue_understanding/generator/single_step/search_ready_llm_command_generator.py +147 -0
- rasa/dialogue_understanding/generator/single_step/single_step_based_llm_command_generator.py +461 -0
- rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +20 -64
- rasa/dialogue_understanding/patterns/cancel.py +1 -2
- rasa/dialogue_understanding/patterns/clarify.py +1 -1
- rasa/dialogue_understanding/patterns/correction.py +2 -2
- rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +42 -27
- rasa/dialogue_understanding/patterns/domain_for_patterns.py +190 -0
- rasa/dialogue_understanding/processor/command_processor.py +6 -7
- rasa/dialogue_understanding_test/command_metric_calculation.py +7 -40
- rasa/dialogue_understanding_test/command_metrics.py +38 -0
- rasa/dialogue_understanding_test/du_test_case.py +58 -25
- rasa/dialogue_understanding_test/du_test_result.py +228 -132
- rasa/dialogue_understanding_test/du_test_runner.py +11 -2
- rasa/dialogue_understanding_test/du_test_schema.yml +3 -3
- rasa/dialogue_understanding_test/io.py +35 -8
- rasa/e2e_test/constants.py +1 -1
- rasa/e2e_test/e2e_test_runner.py +1 -1
- rasa/e2e_test/e2e_test_schema.yml +3 -3
- rasa/engine/constants.py +1 -1
- rasa/engine/graph.py +2 -2
- rasa/engine/recipes/default_recipe.py +1 -1
- rasa/engine/validation.py +3 -2
- rasa/hooks.py +2 -30
- rasa/llm_fine_tuning/paraphrasing/conversation_rephraser.py +2 -6
- rasa/model_manager/model_api.py +89 -1
- rasa/model_manager/runner_service.py +20 -4
- rasa/model_manager/socket_bridge.py +0 -7
- rasa/model_manager/trainer_service.py +10 -4
- rasa/plugin.py +2 -15
- rasa/privacy/__init__.py +0 -0
- rasa/privacy/constants.py +83 -0
- rasa/privacy/event_broker_utils.py +77 -0
- rasa/privacy/privacy_config.py +281 -0
- rasa/privacy/privacy_config_schema.json +86 -0
- rasa/privacy/privacy_filter.py +393 -0
- rasa/privacy/privacy_manager.py +594 -0
- rasa/server.py +23 -2
- rasa/shared/constants.py +17 -0
- rasa/shared/core/command_payload_reader.py +1 -5
- rasa/shared/core/constants.py +4 -3
- rasa/shared/core/domain.py +172 -11
- rasa/shared/core/events.py +100 -6
- rasa/shared/core/flows/flow.py +30 -5
- rasa/shared/core/flows/flow_step.py +19 -3
- rasa/shared/core/flows/flow_step_links.py +15 -0
- rasa/shared/core/flows/flow_step_sequence.py +6 -0
- rasa/shared/core/flows/flows_yaml_schema.json +3 -0
- rasa/shared/core/flows/nlu_trigger.py +13 -0
- rasa/shared/core/flows/steps/action.py +7 -4
- rasa/shared/core/flows/steps/call.py +11 -4
- rasa/shared/core/flows/steps/collect.py +71 -6
- rasa/shared/core/flows/steps/internal.py +6 -1
- rasa/shared/core/flows/steps/link.py +7 -4
- rasa/shared/core/flows/steps/no_operation.py +7 -4
- rasa/shared/core/flows/steps/set_slots.py +8 -4
- rasa/shared/core/flows/validation.py +25 -5
- rasa/shared/core/flows/yaml_flows_io.py +106 -5
- rasa/shared/core/slots.py +29 -1
- rasa/shared/core/trackers.py +21 -10
- rasa/shared/core/training_data/story_reader/yaml_story_reader.py +1 -4
- rasa/shared/importers/importer.py +8 -0
- rasa/shared/providers/_configs/azure_openai_client_config.py +2 -2
- rasa/shared/providers/_configs/default_litellm_client_config.py +1 -1
- rasa/shared/providers/_configs/huggingface_local_embedding_client_config.py +1 -1
- rasa/shared/providers/_configs/openai_client_config.py +1 -1
- rasa/shared/providers/_configs/rasa_llm_client_config.py +1 -1
- rasa/shared/providers/_configs/self_hosted_llm_client_config.py +1 -1
- rasa/shared/providers/_configs/utils.py +0 -99
- rasa/shared/providers/llm/default_litellm_llm_client.py +2 -2
- rasa/shared/utils/common.py +43 -1
- rasa/shared/utils/configs.py +110 -0
- rasa/shared/utils/constants.py +0 -3
- rasa/shared/utils/llm.py +245 -8
- rasa/shared/utils/pykwalify_extensions.py +0 -9
- rasa/shared/utils/yaml.py +32 -0
- rasa/studio/constants.py +1 -0
- rasa/studio/data_handler.py +33 -12
- rasa/studio/download.py +117 -435
- rasa/studio/link.py +211 -0
- rasa/studio/prompts.py +221 -0
- rasa/studio/pull/__init__.py +0 -0
- rasa/studio/pull/data.py +222 -0
- rasa/studio/pull/domains.py +60 -0
- rasa/studio/pull/pull.py +239 -0
- rasa/studio/push.py +138 -0
- rasa/studio/results_logger.py +6 -1
- rasa/studio/train.py +1 -1
- rasa/studio/upload.py +243 -72
- rasa/studio/utils.py +33 -0
- rasa/telemetry.py +83 -26
- rasa/tracing/config.py +4 -5
- rasa/tracing/constants.py +19 -1
- rasa/tracing/instrumentation/attribute_extractors.py +68 -16
- rasa/tracing/instrumentation/instrumentation.py +54 -3
- rasa/tracing/instrumentation/metrics.py +98 -15
- rasa/tracing/metric_instrument_provider.py +75 -3
- rasa/utils/common.py +43 -22
- rasa/utils/endpoints.py +22 -1
- rasa/utils/licensing.py +2 -3
- rasa/utils/log_utils.py +1 -45
- rasa/validator.py +2 -8
- rasa/version.py +1 -1
- {rasa_pro-3.12.22.dist-info → rasa_pro-3.13.0.dist-info}/METADATA +11 -12
- {rasa_pro-3.12.22.dist-info → rasa_pro-3.13.0.dist-info}/RECORD +333 -309
- rasa/anonymization/__init__.py +0 -2
- rasa/anonymization/anonymisation_rule_yaml_reader.py +0 -91
- rasa/anonymization/anonymization_pipeline.py +0 -286
- rasa/anonymization/anonymization_rule_executor.py +0 -266
- rasa/anonymization/anonymization_rule_orchestrator.py +0 -119
- rasa/anonymization/schemas/config.yml +0 -47
- rasa/anonymization/utils.py +0 -118
- rasa/cli/project_templates/calm/config.yml +0 -10
- rasa/cli/project_templates/calm/credentials.yml +0 -33
- rasa/cli/project_templates/calm/endpoints.yml +0 -58
- rasa/cli/project_templates/default/actions/actions.py +0 -27
- rasa/cli/project_templates/default/data/nlu.yml +0 -91
- rasa/cli/project_templates/default/data/rules.yml +0 -13
- rasa/cli/project_templates/default/data/stories.yml +0 -30
- rasa/cli/project_templates/default/domain.yml +0 -34
- rasa/cli/project_templates/default/tests/test_stories.yml +0 -91
- rasa/core/channels/inspector/dist/assets/channel-dfa68278.js +0 -1
- rasa/core/channels/inspector/dist/assets/clone-edb7f119.js +0 -1
- rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-65e7c670.js +0 -1
- rasa/core/channels/inspector/src/helpers/audiostream.ts +0 -191
- rasa/core/tracker_store.py +0 -1792
- /rasa/cli/project_templates/{calm → default}/actions/action_template.py +0 -0
- /rasa/cli/project_templates/{calm → default}/actions/add_contact.py +0 -0
- /rasa/cli/project_templates/{calm → default}/actions/db.py +0 -0
- /rasa/cli/project_templates/{calm → default}/actions/list_contacts.py +0 -0
- /rasa/cli/project_templates/{calm → default}/actions/remove_contact.py +0 -0
- /rasa/cli/project_templates/{calm → default}/data/flows/add_contact.yml +0 -0
- /rasa/cli/project_templates/{calm → default}/data/flows/list_contacts.yml +0 -0
- /rasa/cli/project_templates/{calm → default}/data/flows/remove_contact.yml +0 -0
- /rasa/cli/project_templates/{calm → default}/db/contacts.json +0 -0
- /rasa/cli/project_templates/{calm → default}/domain/add_contact.yml +0 -0
- /rasa/cli/project_templates/{calm → default}/domain/list_contacts.yml +0 -0
- /rasa/cli/project_templates/{calm → default}/domain/remove_contact.yml +0 -0
- /rasa/cli/project_templates/{calm → default}/domain/shared.yml +0 -0
- /rasa/{cli/project_templates/calm/actions → core/information_retrieval/ingestion}/__init__.py +0 -0
- {rasa_pro-3.12.22.dist-info → rasa_pro-3.13.0.dist-info}/NOTICE +0 -0
- {rasa_pro-3.12.22.dist-info → rasa_pro-3.13.0.dist-info}/WHEEL +0 -0
- {rasa_pro-3.12.22.dist-info → rasa_pro-3.13.0.dist-info}/entry_points.txt +0 -0
rasa/core/processor.py
CHANGED
|
@@ -12,7 +12,7 @@ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Text, Tuple, Union
|
|
|
12
12
|
import structlog
|
|
13
13
|
|
|
14
14
|
import rasa.core.actions.action
|
|
15
|
-
import rasa.core.tracker_store
|
|
15
|
+
import rasa.core.tracker_stores.tracker_store
|
|
16
16
|
import rasa.core.utils
|
|
17
17
|
import rasa.shared.core.trackers
|
|
18
18
|
import rasa.shared.utils.io
|
|
@@ -75,8 +75,8 @@ from rasa.shared.core.constants import (
|
|
|
75
75
|
ACTION_SESSION_START_NAME,
|
|
76
76
|
FOLLOWUP_ACTION,
|
|
77
77
|
SESSION_START_METADATA_SLOT,
|
|
78
|
+
SILENCE_TIMEOUT_SLOT,
|
|
78
79
|
SLOT_CONSECUTIVE_SILENCE_TIMEOUTS,
|
|
79
|
-
SLOT_SILENCE_TIMEOUT,
|
|
80
80
|
USER_INTENT_RESTART,
|
|
81
81
|
USER_INTENT_SILENCE_TIMEOUT,
|
|
82
82
|
SetSlotExtractor,
|
|
@@ -112,7 +112,8 @@ from rasa.utils.common import TempDirectoryPath, get_temp_dir_name
|
|
|
112
112
|
from rasa.utils.endpoints import EndpointConfig
|
|
113
113
|
|
|
114
114
|
if TYPE_CHECKING:
|
|
115
|
-
from rasa.core.
|
|
115
|
+
from rasa.core.available_endpoints import AvailableEndpoints
|
|
116
|
+
from rasa.privacy.privacy_manager import BackgroundPrivacyManager
|
|
116
117
|
|
|
117
118
|
logger = logging.getLogger(__name__)
|
|
118
119
|
structlogger = structlog.get_logger()
|
|
@@ -129,7 +130,7 @@ class MessageProcessor:
|
|
|
129
130
|
def __init__(
|
|
130
131
|
self,
|
|
131
132
|
model_path: Union[Text, Path],
|
|
132
|
-
tracker_store: rasa.core.tracker_store.TrackerStore,
|
|
133
|
+
tracker_store: rasa.core.tracker_stores.tracker_store.TrackerStore,
|
|
133
134
|
lock_store: LockStore,
|
|
134
135
|
generator: NaturalLanguageGenerator,
|
|
135
136
|
action_endpoint: Optional[EndpointConfig] = None,
|
|
@@ -138,6 +139,7 @@ class MessageProcessor:
|
|
|
138
139
|
on_circuit_break: Optional[LambdaType] = None,
|
|
139
140
|
http_interpreter: Optional[RasaNLUHttpInterpreter] = None,
|
|
140
141
|
endpoints: Optional["AvailableEndpoints"] = None,
|
|
142
|
+
privacy_manager: Optional["BackgroundPrivacyManager"] = None,
|
|
141
143
|
) -> None:
|
|
142
144
|
"""Initializes a `MessageProcessor`."""
|
|
143
145
|
self.nlg = generator
|
|
@@ -167,6 +169,9 @@ class MessageProcessor:
|
|
|
167
169
|
self.model_path = Path(model_path)
|
|
168
170
|
self.domain = self.model_metadata.domain
|
|
169
171
|
self.http_interpreter = http_interpreter
|
|
172
|
+
self.privacy_manager = privacy_manager
|
|
173
|
+
if self.privacy_manager is not None:
|
|
174
|
+
self.privacy_manager.validate_sensitive_slots_in_domain(self.domain)
|
|
170
175
|
|
|
171
176
|
@staticmethod
|
|
172
177
|
def _load_model(
|
|
@@ -216,15 +221,36 @@ class MessageProcessor:
|
|
|
216
221
|
|
|
217
222
|
await self._run_prediction_loop(message.output_channel, tracker)
|
|
218
223
|
|
|
219
|
-
await self.run_anonymization_pipeline(tracker)
|
|
220
|
-
|
|
221
224
|
await self.save_tracker(tracker)
|
|
222
225
|
|
|
226
|
+
self.trigger_anonymization(tracker)
|
|
227
|
+
|
|
223
228
|
if isinstance(message.output_channel, CollectingOutputChannel):
|
|
224
229
|
return message.output_channel.messages
|
|
225
230
|
|
|
226
231
|
return None
|
|
227
232
|
|
|
233
|
+
def trigger_anonymization(self, tracker: DialogueStateTracker) -> None:
|
|
234
|
+
if self.privacy_manager is None:
|
|
235
|
+
structlogger.debug(
|
|
236
|
+
"processor.trigger_anonymization.skipping.pii_management_not_enabled",
|
|
237
|
+
)
|
|
238
|
+
return None
|
|
239
|
+
|
|
240
|
+
if not self.privacy_manager.event_brokers:
|
|
241
|
+
structlogger.debug(
|
|
242
|
+
"processor.trigger_anonymization.skipping.no_event_brokers",
|
|
243
|
+
)
|
|
244
|
+
return None
|
|
245
|
+
|
|
246
|
+
structlogger.info(
|
|
247
|
+
"rasa.core.processor.trigger_anonymization",
|
|
248
|
+
sender_id=tracker.sender_id,
|
|
249
|
+
event_info="Triggering anonymization for publishing anonymized "
|
|
250
|
+
"events to the event broker.",
|
|
251
|
+
)
|
|
252
|
+
return self.privacy_manager.run(tracker)
|
|
253
|
+
|
|
228
254
|
async def run_action_extract_slots(
|
|
229
255
|
self,
|
|
230
256
|
output_channel: OutputChannel,
|
|
@@ -265,26 +291,6 @@ class MessageProcessor:
|
|
|
265
291
|
|
|
266
292
|
return tracker
|
|
267
293
|
|
|
268
|
-
async def run_anonymization_pipeline(self, tracker: DialogueStateTracker) -> None:
|
|
269
|
-
"""Run the anonymization pipeline on the new tracker events.
|
|
270
|
-
|
|
271
|
-
Args:
|
|
272
|
-
tracker: A tracker representing a conversation state.
|
|
273
|
-
"""
|
|
274
|
-
anonymization_pipeline = plugin_manager().hook.get_anonymization_pipeline()
|
|
275
|
-
if anonymization_pipeline is None:
|
|
276
|
-
return None
|
|
277
|
-
|
|
278
|
-
old_tracker = await self.tracker_store.retrieve(tracker.sender_id)
|
|
279
|
-
new_events = rasa.shared.core.trackers.TrackerEventDiffEngine.event_difference(
|
|
280
|
-
old_tracker, tracker
|
|
281
|
-
)
|
|
282
|
-
|
|
283
|
-
for event in new_events:
|
|
284
|
-
body = {"sender_id": tracker.sender_id}
|
|
285
|
-
body.update(event.as_dict())
|
|
286
|
-
anonymization_pipeline.run(body)
|
|
287
|
-
|
|
288
294
|
async def predict_next_for_sender_id(
|
|
289
295
|
self, sender_id: Text
|
|
290
296
|
) -> Optional[Dict[Text, Any]]:
|
|
@@ -822,28 +828,8 @@ class MessageProcessor:
|
|
|
822
828
|
)
|
|
823
829
|
|
|
824
830
|
self._check_for_unseen_features(parse_data)
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
parse_data.get(INTENT, {}).get(INTENT_NAME_KEY)
|
|
828
|
-
!= USER_INTENT_SILENCE_TIMEOUT
|
|
829
|
-
and tracker
|
|
830
|
-
):
|
|
831
|
-
if (
|
|
832
|
-
SLOT_CONSECUTIVE_SILENCE_TIMEOUTS in tracker.slots
|
|
833
|
-
and tracker.slots[SLOT_CONSECUTIVE_SILENCE_TIMEOUTS].value != 0.0
|
|
834
|
-
):
|
|
835
|
-
tracker.update(SlotSet(SLOT_CONSECUTIVE_SILENCE_TIMEOUTS, 0.0))
|
|
836
|
-
if (
|
|
837
|
-
SLOT_SILENCE_TIMEOUT in tracker.slots
|
|
838
|
-
and tracker.slots[SLOT_SILENCE_TIMEOUT].value
|
|
839
|
-
!= tracker.slots[SLOT_SILENCE_TIMEOUT].initial_value
|
|
840
|
-
):
|
|
841
|
-
tracker.update(
|
|
842
|
-
SlotSet(
|
|
843
|
-
SLOT_SILENCE_TIMEOUT,
|
|
844
|
-
tracker.slots[SLOT_SILENCE_TIMEOUT].initial_value,
|
|
845
|
-
)
|
|
846
|
-
)
|
|
831
|
+
|
|
832
|
+
self._initialise_consecutive_silence_timeout_slots(parse_data, tracker)
|
|
847
833
|
|
|
848
834
|
return parse_data
|
|
849
835
|
|
|
@@ -1068,7 +1054,14 @@ class MessageProcessor:
|
|
|
1068
1054
|
|
|
1069
1055
|
@staticmethod
|
|
1070
1056
|
def _should_handle_message(tracker: DialogueStateTracker) -> bool:
|
|
1071
|
-
return not tracker.is_paused() or (
|
|
1057
|
+
return not tracker.is_paused() or MessageProcessor._last_user_intent_is_restart(
|
|
1058
|
+
tracker
|
|
1059
|
+
)
|
|
1060
|
+
|
|
1061
|
+
@staticmethod
|
|
1062
|
+
def _last_user_intent_is_restart(tracker: DialogueStateTracker) -> bool:
|
|
1063
|
+
"""Check if the last user intent is a restart intent."""
|
|
1064
|
+
return (
|
|
1072
1065
|
tracker.latest_message is not None
|
|
1073
1066
|
and tracker.latest_message.intent.get(INTENT_NAME_KEY)
|
|
1074
1067
|
== USER_INTENT_RESTART
|
|
@@ -1615,3 +1608,31 @@ class MessageProcessor:
|
|
|
1615
1608
|
)
|
|
1616
1609
|
|
|
1617
1610
|
return tracker, validate_frames
|
|
1611
|
+
|
|
1612
|
+
@staticmethod
|
|
1613
|
+
def _initialise_consecutive_silence_timeout_slots(
|
|
1614
|
+
parse_data: Dict[str, Any],
|
|
1615
|
+
tracker: DialogueStateTracker,
|
|
1616
|
+
) -> None:
|
|
1617
|
+
# resetting timeouts variables whenever something that is not a timeout occurs
|
|
1618
|
+
if (
|
|
1619
|
+
parse_data.get(INTENT, {}).get(INTENT_NAME_KEY)
|
|
1620
|
+
!= USER_INTENT_SILENCE_TIMEOUT
|
|
1621
|
+
and tracker
|
|
1622
|
+
):
|
|
1623
|
+
if (
|
|
1624
|
+
SLOT_CONSECUTIVE_SILENCE_TIMEOUTS in tracker.slots
|
|
1625
|
+
and tracker.slots[SLOT_CONSECUTIVE_SILENCE_TIMEOUTS].value != 0.0
|
|
1626
|
+
):
|
|
1627
|
+
tracker.update(SlotSet(SLOT_CONSECUTIVE_SILENCE_TIMEOUTS, 0.0))
|
|
1628
|
+
if (
|
|
1629
|
+
SILENCE_TIMEOUT_SLOT in tracker.slots
|
|
1630
|
+
and tracker.slots[SILENCE_TIMEOUT_SLOT].value
|
|
1631
|
+
!= tracker.slots[SILENCE_TIMEOUT_SLOT].initial_value
|
|
1632
|
+
):
|
|
1633
|
+
tracker.update(
|
|
1634
|
+
SlotSet(
|
|
1635
|
+
SILENCE_TIMEOUT_SLOT,
|
|
1636
|
+
tracker.slots[SILENCE_TIMEOUT_SLOT].initial_value,
|
|
1637
|
+
)
|
|
1638
|
+
)
|
rasa/core/run.py
CHANGED
|
@@ -30,12 +30,11 @@ from rasa import server, telemetry
|
|
|
30
30
|
from rasa.constants import ENV_SANIC_BACKLOG
|
|
31
31
|
from rasa.core import agent, channels, constants
|
|
32
32
|
from rasa.core.agent import Agent
|
|
33
|
+
from rasa.core.available_endpoints import AvailableEndpoints
|
|
33
34
|
from rasa.core.channels import console
|
|
34
35
|
from rasa.core.channels.channel import InputChannel
|
|
35
36
|
from rasa.core.channels.development_inspector import DevelopmentInspectProxy
|
|
36
37
|
from rasa.core.persistor import StorageType
|
|
37
|
-
from rasa.core.utils import AvailableEndpoints
|
|
38
|
-
from rasa.plugin import plugin_manager
|
|
39
38
|
from rasa.shared.exceptions import RasaException
|
|
40
39
|
from rasa.shared.utils.yaml import read_config_file
|
|
41
40
|
from rasa.utils import licensing
|
|
@@ -45,7 +44,7 @@ logger = logging.getLogger() # get the root logger
|
|
|
45
44
|
|
|
46
45
|
def create_http_input_channels(
|
|
47
46
|
channel: Optional[Text], credentials_file: Optional[Text]
|
|
48
|
-
) -> List[
|
|
47
|
+
) -> List[InputChannel]:
|
|
49
48
|
"""Instantiate the chosen input channel."""
|
|
50
49
|
if credentials_file:
|
|
51
50
|
all_credentials = read_config_file(credentials_file)
|
|
@@ -59,22 +58,45 @@ def create_http_input_channels(
|
|
|
59
58
|
"To connect to all given channels, omit the '--connector' "
|
|
60
59
|
"argument.".format(channel)
|
|
61
60
|
)
|
|
62
|
-
return [
|
|
61
|
+
return [
|
|
62
|
+
_create_single_channel(
|
|
63
|
+
channel,
|
|
64
|
+
all_credentials.get(channel),
|
|
65
|
+
)
|
|
66
|
+
]
|
|
63
67
|
else:
|
|
64
68
|
return [_create_single_channel(c, k) for c, k in all_credentials.items()]
|
|
65
69
|
|
|
66
70
|
|
|
67
|
-
def _create_single_channel(
|
|
71
|
+
def _create_single_channel(
|
|
72
|
+
channel: Text,
|
|
73
|
+
credentials: Optional[Dict[Text, Any]],
|
|
74
|
+
) -> Any:
|
|
75
|
+
"""Create a single input channel based on the channel name and credentials.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
channel: The name of the input channel to create.
|
|
79
|
+
credentials: The credentials for the input channel.
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
An instance of the input channel class.
|
|
83
|
+
|
|
84
|
+
Raises:
|
|
85
|
+
RasaException: If the channel class cannot be found or instantiated.
|
|
86
|
+
"""
|
|
68
87
|
from rasa.core.channels import BUILTIN_CHANNELS
|
|
69
88
|
|
|
70
89
|
if channel in BUILTIN_CHANNELS:
|
|
71
|
-
|
|
90
|
+
channel_class = BUILTIN_CHANNELS[channel]
|
|
91
|
+
|
|
92
|
+
return channel_class.from_credentials(credentials)
|
|
72
93
|
else:
|
|
73
94
|
# try to load channel based on class name
|
|
74
95
|
try:
|
|
75
96
|
input_channel_class = rasa.shared.utils.common.class_from_module_path(
|
|
76
97
|
channel
|
|
77
98
|
)
|
|
99
|
+
|
|
78
100
|
return input_channel_class.from_credentials(credentials)
|
|
79
101
|
except (AttributeError, ImportError):
|
|
80
102
|
raise RasaException(
|
|
@@ -108,7 +130,7 @@ def _is_apple_silicon_system() -> bool:
|
|
|
108
130
|
|
|
109
131
|
|
|
110
132
|
def configure_app(
|
|
111
|
-
input_channels: Optional[List[
|
|
133
|
+
input_channels: Optional[List[InputChannel]] = None,
|
|
112
134
|
cors: Optional[Union[Text, List[Text], None]] = None,
|
|
113
135
|
auth_token: Optional[Text] = None,
|
|
114
136
|
enable_api: bool = True,
|
|
@@ -190,10 +212,6 @@ def configure_app(
|
|
|
190
212
|
logger.info("Killing Sanic server now.")
|
|
191
213
|
running_app.stop() # kill the sanic server
|
|
192
214
|
|
|
193
|
-
@app.after_server_stop
|
|
194
|
-
async def after_server_stop(running_app: Sanic) -> None:
|
|
195
|
-
plugin_manager().hook.after_server_stop()
|
|
196
|
-
|
|
197
215
|
if server_listeners:
|
|
198
216
|
for listener, event in server_listeners:
|
|
199
217
|
app.register_listener(listener, event)
|
|
@@ -346,3 +364,7 @@ async def close_resources(app: Sanic, _: AbstractEventLoop) -> None:
|
|
|
346
364
|
event_broker = current_agent.tracker_store.event_broker
|
|
347
365
|
if event_broker:
|
|
348
366
|
await event_broker.close()
|
|
367
|
+
|
|
368
|
+
privacy_manager = current_agent.privacy_manager
|
|
369
|
+
if privacy_manager:
|
|
370
|
+
privacy_manager.stop()
|
|
File without changes
|
|
@@ -3,7 +3,7 @@ from typing import Iterable, Optional, Text
|
|
|
3
3
|
|
|
4
4
|
from rasa.core.brokers.broker import EventBroker
|
|
5
5
|
from rasa.core.secrets_manager.secret_manager import EndpointResolver
|
|
6
|
-
from rasa.core.tracker_store import TrackerStore, create_tracker_store
|
|
6
|
+
from rasa.core.tracker_stores.tracker_store import TrackerStore, create_tracker_store
|
|
7
7
|
from rasa.shared.core.domain import Domain
|
|
8
8
|
from rasa.shared.core.trackers import DialogueStateTracker
|
|
9
9
|
from rasa.utils.endpoints import EndpointConfig
|
|
@@ -92,6 +92,29 @@ class AuthRetryTrackerStore(TrackerStore):
|
|
|
92
92
|
)
|
|
93
93
|
return None
|
|
94
94
|
|
|
95
|
+
async def retrieve_full_tracker(
|
|
96
|
+
self, sender_id: Text
|
|
97
|
+
) -> Optional["DialogueStateTracker"]:
|
|
98
|
+
"""Retries retrieving the full tracker if it fails."""
|
|
99
|
+
# add + 1 to retries because the retries are additional to the first attempt
|
|
100
|
+
for _ in range(self.retries + 1):
|
|
101
|
+
try:
|
|
102
|
+
return await self._tracker_store.retrieve_full_tracker(sender_id)
|
|
103
|
+
except Exception as e:
|
|
104
|
+
logger.warning(
|
|
105
|
+
f"Failed to retrieve full tracker for {sender_id}. Retrying...",
|
|
106
|
+
exc_info=e,
|
|
107
|
+
)
|
|
108
|
+
self._tracker_store = self.recreate_tracker_store(
|
|
109
|
+
self.domain, self.event_broker
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
logger.error(
|
|
113
|
+
f"Failed to retrieve full tracker for {sender_id} "
|
|
114
|
+
f"after {self.retries} retries."
|
|
115
|
+
)
|
|
116
|
+
return None
|
|
117
|
+
|
|
95
118
|
async def save(self, tracker: "DialogueStateTracker") -> None:
|
|
96
119
|
"""Retries saving the tracker if it fails."""
|
|
97
120
|
# add + 1 to retries because the retries are additional to the first attempt
|
|
@@ -119,3 +142,45 @@ class AuthRetryTrackerStore(TrackerStore):
|
|
|
119
142
|
"""Recreate tracker store with updated credentials."""
|
|
120
143
|
endpoint_config = EndpointResolver.update_config(self.endpoint_config)
|
|
121
144
|
return create_tracker_store(endpoint_config, domain, event_broker)
|
|
145
|
+
|
|
146
|
+
async def delete(self, sender_id: str) -> None:
|
|
147
|
+
"""Retries deleting the tracker for the given sender_id."""
|
|
148
|
+
# add + 1 to retries because the retries are additional to the first attempt
|
|
149
|
+
for _ in range(self.retries + 1):
|
|
150
|
+
try:
|
|
151
|
+
await self._tracker_store.delete(sender_id)
|
|
152
|
+
break
|
|
153
|
+
except Exception as e:
|
|
154
|
+
logger.warning(
|
|
155
|
+
f"Failed to delete tracker for {sender_id}. Retrying...",
|
|
156
|
+
exc_info=e,
|
|
157
|
+
)
|
|
158
|
+
self._tracker_store = self.recreate_tracker_store(
|
|
159
|
+
self.domain, self.event_broker
|
|
160
|
+
)
|
|
161
|
+
else:
|
|
162
|
+
logger.error(
|
|
163
|
+
f"Failed to delete tracker for {sender_id} "
|
|
164
|
+
f"after {self.retries} retries."
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
async def update(self, tracker: DialogueStateTracker) -> None:
|
|
168
|
+
"""Retries replacing the tracker if it fails."""
|
|
169
|
+
# add + 1 to retries because the retries are additional to the first attempt
|
|
170
|
+
for _ in range(self.retries + 1):
|
|
171
|
+
try:
|
|
172
|
+
await self._tracker_store.update(tracker)
|
|
173
|
+
break
|
|
174
|
+
except Exception as e:
|
|
175
|
+
logger.warning(
|
|
176
|
+
f"Failed to replace tracker for {tracker.sender_id}. Retrying...",
|
|
177
|
+
exc_info=e,
|
|
178
|
+
)
|
|
179
|
+
self._tracker_store = self.recreate_tracker_store(
|
|
180
|
+
self.domain, self.event_broker
|
|
181
|
+
)
|
|
182
|
+
else:
|
|
183
|
+
logger.error(
|
|
184
|
+
f"Failed to replace tracker for {tracker.sender_id} "
|
|
185
|
+
f"after {self.retries} retries."
|
|
186
|
+
)
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from typing import TYPE_CHECKING, Any, Dict, Iterable, Optional, Text
|
|
5
|
+
|
|
6
|
+
import structlog
|
|
7
|
+
from boto3.dynamodb.conditions import Key
|
|
8
|
+
|
|
9
|
+
import rasa.utils
|
|
10
|
+
from rasa.constants import DEFAULT_SANIC_WORKERS, ENV_SANIC_WORKERS
|
|
11
|
+
from rasa.core.tracker_stores.tracker_store import (
|
|
12
|
+
SerializedTrackerAsDict,
|
|
13
|
+
TrackerStore,
|
|
14
|
+
)
|
|
15
|
+
from rasa.shared.core.domain import Domain
|
|
16
|
+
from rasa.shared.core.trackers import DialogueStateTracker
|
|
17
|
+
from rasa.shared.exceptions import RasaException
|
|
18
|
+
from rasa.utils.endpoints import EndpointConfig
|
|
19
|
+
|
|
20
|
+
structlogger = structlog.get_logger(__name__)
|
|
21
|
+
|
|
22
|
+
if TYPE_CHECKING:
|
|
23
|
+
import boto3.resources.factory.dynamodb.Table
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class DynamoTrackerStore(TrackerStore, SerializedTrackerAsDict):
|
|
27
|
+
"""Stores conversation history in DynamoDB."""
|
|
28
|
+
|
|
29
|
+
def __init__(
|
|
30
|
+
self,
|
|
31
|
+
domain: Domain,
|
|
32
|
+
table_name: Text = "states",
|
|
33
|
+
region: Text = "us-east-1",
|
|
34
|
+
event_broker: Optional[EndpointConfig] = None,
|
|
35
|
+
**kwargs: Dict[Text, Any],
|
|
36
|
+
) -> None:
|
|
37
|
+
"""Initialize `DynamoTrackerStore`.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
domain: Domain associated with this tracker store.
|
|
41
|
+
table_name: The name of the DynamoDB table, does not need to be present a
|
|
42
|
+
priori.
|
|
43
|
+
region: The name of the region associated with the client.
|
|
44
|
+
A client is associated with a single region.
|
|
45
|
+
event_broker: An event broker used to publish events.
|
|
46
|
+
kwargs: Additional kwargs.
|
|
47
|
+
"""
|
|
48
|
+
import boto3
|
|
49
|
+
|
|
50
|
+
self.client = boto3.client("dynamodb", region_name=region)
|
|
51
|
+
self.region = region
|
|
52
|
+
self.table_name = table_name
|
|
53
|
+
self.db = self.get_or_create_table(table_name)
|
|
54
|
+
super().__init__(domain, event_broker, **kwargs)
|
|
55
|
+
|
|
56
|
+
def get_or_create_table(
|
|
57
|
+
self, table_name: Text
|
|
58
|
+
) -> "boto3.resources.factory.dynamodb.Table":
|
|
59
|
+
"""Returns table or creates one if the table name is not in the table list."""
|
|
60
|
+
import boto3
|
|
61
|
+
|
|
62
|
+
dynamo = boto3.resource("dynamodb", region_name=self.region)
|
|
63
|
+
try:
|
|
64
|
+
self.client.describe_table(TableName=table_name)
|
|
65
|
+
except self.client.exceptions.ResourceNotFoundException:
|
|
66
|
+
sanic_workers_count = int(
|
|
67
|
+
os.environ.get(ENV_SANIC_WORKERS, DEFAULT_SANIC_WORKERS)
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
if sanic_workers_count > 1:
|
|
71
|
+
structlogger.error(
|
|
72
|
+
"dynamo_tracker_store.table_creation_not_supported_in_multi_worker_mode",
|
|
73
|
+
event_info=(
|
|
74
|
+
"DynamoDB table creation is not "
|
|
75
|
+
"supported in multi-worker mode. "
|
|
76
|
+
"Table should already exist.",
|
|
77
|
+
),
|
|
78
|
+
)
|
|
79
|
+
raise RasaException(
|
|
80
|
+
"DynamoDB table creation is not supported in "
|
|
81
|
+
"case of multiple sanic workers. To create the table either "
|
|
82
|
+
"run Rasa with a single worker or create the table manually."
|
|
83
|
+
"Here are the defaults which can be used to "
|
|
84
|
+
"create the table manually: "
|
|
85
|
+
f"Table name: {table_name}, Primary key: sender_id, "
|
|
86
|
+
f"key type `HASH`, attribute type `S` (String), "
|
|
87
|
+
"Provisioned throughput: Read capacity units: 5, "
|
|
88
|
+
"Write capacity units: 5"
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
table = dynamo.create_table(
|
|
92
|
+
TableName=self.table_name,
|
|
93
|
+
KeySchema=[{"AttributeName": "sender_id", "KeyType": "HASH"}],
|
|
94
|
+
AttributeDefinitions=[
|
|
95
|
+
{"AttributeName": "sender_id", "AttributeType": "S"}
|
|
96
|
+
],
|
|
97
|
+
ProvisionedThroughput={"ReadCapacityUnits": 5, "WriteCapacityUnits": 5},
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
# Wait until the table exists.
|
|
101
|
+
table.meta.client.get_waiter("table_exists").wait(TableName=table_name)
|
|
102
|
+
else:
|
|
103
|
+
table = dynamo.Table(table_name)
|
|
104
|
+
|
|
105
|
+
return table
|
|
106
|
+
|
|
107
|
+
async def save(self, tracker: DialogueStateTracker) -> None:
|
|
108
|
+
"""Saves the current conversation state."""
|
|
109
|
+
await self.stream_events(tracker)
|
|
110
|
+
serialized = self.serialise_tracker(tracker)
|
|
111
|
+
|
|
112
|
+
full_tracker = await self.retrieve_full_tracker(tracker.sender_id)
|
|
113
|
+
if full_tracker is None:
|
|
114
|
+
self.db.put_item(Item=serialized)
|
|
115
|
+
return None
|
|
116
|
+
|
|
117
|
+
# return the latest events since the last user message
|
|
118
|
+
new_tracker = DialogueStateTracker.from_dict(
|
|
119
|
+
serialized["sender_id"], events_as_dict=serialized["events"]
|
|
120
|
+
)
|
|
121
|
+
new_events = new_tracker.get_last_turn_events()
|
|
122
|
+
new_serialized_events = [event.as_dict() for event in new_events]
|
|
123
|
+
|
|
124
|
+
# we need to save the full tracker if it is a new tracker
|
|
125
|
+
# without events following a user message
|
|
126
|
+
if not new_serialized_events:
|
|
127
|
+
self.db.put_item(Item=serialized)
|
|
128
|
+
return None
|
|
129
|
+
|
|
130
|
+
# append new events to the existing tracker
|
|
131
|
+
self.db.update_item(
|
|
132
|
+
Key={"sender_id": tracker.sender_id},
|
|
133
|
+
UpdateExpression="SET events = list_append(if_not_exists(events, :empty_list), :events)", # noqa: E501
|
|
134
|
+
ExpressionAttributeValues={
|
|
135
|
+
":events": new_serialized_events,
|
|
136
|
+
":empty_list": [],
|
|
137
|
+
},
|
|
138
|
+
ReturnValues="UPDATED_NEW",
|
|
139
|
+
)
|
|
140
|
+
return None
|
|
141
|
+
|
|
142
|
+
async def delete(self, sender_id: Text) -> None:
|
|
143
|
+
"""Delete tracker for the given sender_id."""
|
|
144
|
+
if not await self.exists(sender_id):
|
|
145
|
+
structlogger.info(
|
|
146
|
+
"dynamo_tracker_store.delete.no_tracker_for_sender_id",
|
|
147
|
+
event_info=f"Could not find tracker for conversation ID '{sender_id}'.",
|
|
148
|
+
)
|
|
149
|
+
return None
|
|
150
|
+
|
|
151
|
+
self.db.delete_item(
|
|
152
|
+
Key={"sender_id": sender_id},
|
|
153
|
+
ConditionExpression="attribute_exists(sender_id)",
|
|
154
|
+
)
|
|
155
|
+
structlogger.info(
|
|
156
|
+
"dynamo_tracker_store.delete.deleted_tracker",
|
|
157
|
+
sender_id=sender_id,
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
@staticmethod
|
|
161
|
+
def serialise_tracker(
|
|
162
|
+
tracker: "DialogueStateTracker",
|
|
163
|
+
) -> Dict:
|
|
164
|
+
"""Serializes the tracker, returns object with decimal types.
|
|
165
|
+
|
|
166
|
+
DynamoDB cannot store `float`s, so we'll convert them to `Decimal`s.
|
|
167
|
+
"""
|
|
168
|
+
return rasa.utils.json_utils.replace_floats_with_decimals(
|
|
169
|
+
SerializedTrackerAsDict.serialise_tracker(tracker)
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
async def retrieve(self, sender_id: Text) -> Optional[DialogueStateTracker]:
|
|
173
|
+
"""Retrieve dialogues for a sender_id in reverse-chronological order.
|
|
174
|
+
|
|
175
|
+
Based on the session_date sort key.
|
|
176
|
+
"""
|
|
177
|
+
return await self._retrieve(sender_id, fetch_all_sessions=False)
|
|
178
|
+
|
|
179
|
+
async def retrieve_full_tracker(
|
|
180
|
+
self, sender_id: Text
|
|
181
|
+
) -> Optional[DialogueStateTracker]:
|
|
182
|
+
"""Retrieves tracker for all conversation sessions.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
sender_id: Conversation ID to fetch the tracker for.
|
|
186
|
+
"""
|
|
187
|
+
return await self._retrieve(sender_id, fetch_all_sessions=True)
|
|
188
|
+
|
|
189
|
+
async def _retrieve(
|
|
190
|
+
self, sender_id: Text, fetch_all_sessions: bool
|
|
191
|
+
) -> Optional[DialogueStateTracker]:
|
|
192
|
+
"""Returns tracker matching sender_id.
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
sender_id: Conversation ID to fetch the tracker for.
|
|
196
|
+
fetch_all_sessions: Whether to fetch all sessions or only the last one.
|
|
197
|
+
"""
|
|
198
|
+
dialogues = self.db.query(
|
|
199
|
+
KeyConditionExpression=Key("sender_id").eq(sender_id),
|
|
200
|
+
ScanIndexForward=False,
|
|
201
|
+
)["Items"]
|
|
202
|
+
|
|
203
|
+
if not dialogues:
|
|
204
|
+
return None
|
|
205
|
+
|
|
206
|
+
events_with_floats = []
|
|
207
|
+
for dialogue in dialogues:
|
|
208
|
+
if dialogue.get("events"):
|
|
209
|
+
events = rasa.utils.json_utils.replace_decimals_with_floats(
|
|
210
|
+
dialogue["events"]
|
|
211
|
+
)
|
|
212
|
+
events_with_floats.extend(events)
|
|
213
|
+
|
|
214
|
+
if self.domain is None:
|
|
215
|
+
slots = []
|
|
216
|
+
else:
|
|
217
|
+
slots = self.domain.slots
|
|
218
|
+
|
|
219
|
+
tracker = DialogueStateTracker.from_dict(sender_id, events_with_floats, slots)
|
|
220
|
+
|
|
221
|
+
if fetch_all_sessions:
|
|
222
|
+
return tracker
|
|
223
|
+
|
|
224
|
+
# only return the last session
|
|
225
|
+
multiple_tracker_sessions = (
|
|
226
|
+
rasa.shared.core.trackers.get_trackers_for_conversation_sessions(tracker)
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
if len(multiple_tracker_sessions) <= 1:
|
|
230
|
+
return tracker
|
|
231
|
+
|
|
232
|
+
return multiple_tracker_sessions[-1]
|
|
233
|
+
|
|
234
|
+
async def keys(self) -> Iterable[Text]:
|
|
235
|
+
"""Returns sender_ids of the `DynamoTrackerStore`."""
|
|
236
|
+
response = self.db.scan(ProjectionExpression="sender_id")
|
|
237
|
+
sender_ids = [i["sender_id"] for i in response["Items"]]
|
|
238
|
+
|
|
239
|
+
while response.get("LastEvaluatedKey"):
|
|
240
|
+
response = self.db.scan(
|
|
241
|
+
ProjectionExpression="sender_id",
|
|
242
|
+
ExclusiveStartKey=response["LastEvaluatedKey"],
|
|
243
|
+
)
|
|
244
|
+
sender_ids.extend([i["sender_id"] for i in response["Items"]])
|
|
245
|
+
|
|
246
|
+
return sender_ids
|
|
247
|
+
|
|
248
|
+
async def update(self, tracker: DialogueStateTracker) -> None:
|
|
249
|
+
"""Overwrites the tracker for the given sender_id."""
|
|
250
|
+
serialized = self.serialise_tracker(tracker)
|
|
251
|
+
self.db.put_item(Item=serialized)
|
|
252
|
+
|
|
253
|
+
structlogger.info(
|
|
254
|
+
"dynamo_tracker_store.replace.replaced_tracker",
|
|
255
|
+
sender_id=tracker.sender_id,
|
|
256
|
+
)
|