rasa-pro 3.12.18.dev1__py3-none-any.whl → 3.13.0a1.dev2__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/__init__.py +0 -6
- rasa/__main__.py +3 -4
- rasa/api.py +1 -1
- rasa/builder/create_openai_vector_store.py +69 -0
- rasa/builder/llm-helper-schema.json +69 -0
- rasa/builder/prompt_to_bot.py +650 -0
- rasa/builder/scrape_rasa_docs.py +97 -0
- rasa/builder/skill_to_bot_prompt.jinja +158 -0
- rasa/cli/dialogue_understanding_test.py +1 -1
- rasa/cli/e2e_test.py +1 -1
- rasa/cli/evaluate.py +2 -2
- rasa/cli/export.py +3 -3
- 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/run.py +1 -1
- rasa/cli/scaffold.py +2 -3
- rasa/cli/studio/download.py +1 -1
- rasa/cli/studio/link.py +53 -0
- rasa/cli/studio/pull.py +78 -0
- rasa/cli/studio/push.py +78 -0
- rasa/cli/studio/studio.py +12 -0
- rasa/cli/studio/upload.py +5 -3
- rasa/cli/train.py +1 -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 +42 -31
- rasa/core/actions/action_repeat_bot_messages.py +18 -22
- rasa/core/actions/action_run_slot_rejections.py +1 -2
- rasa/core/agent.py +18 -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/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-02053cc1.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{blockDiagram-38ab4fdb-7f34db23.js → blockDiagram-38ab4fdb-008b6289.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{c4Diagram-3d4e48cf-948bab2c.js → c4Diagram-3d4e48cf-fb2597be.js} +1 -1
- rasa/core/channels/inspector/dist/assets/channel-078dada8.js +1 -0
- rasa/core/channels/inspector/dist/assets/{classDiagram-70f12bd4-53b0dd0e.js → classDiagram-70f12bd4-7f847e00.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{classDiagram-v2-f2320105-fdf789e7.js → classDiagram-v2-f2320105-ba1d689b.js} +1 -1
- rasa/core/channels/inspector/dist/assets/clone-5b4516de.js +1 -0
- rasa/core/channels/inspector/dist/assets/{createText-2e5e7dd3-87c4ece5.js → createText-2e5e7dd3-dd8e67c4.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{edges-e0da2a9e-5a8b0749.js → edges-e0da2a9e-10784939.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{erDiagram-9861fffd-66da90e2.js → erDiagram-9861fffd-24947ae6.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{flowDb-956e92f1-10044f05.js → flowDb-956e92f1-a9ced505.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{flowDiagram-66a62f08-f338f66a.js → flowDiagram-66a62f08-afda9c7c.js} +1 -1
- rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-f9613071.js +1 -0
- rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-4a651766-b13140aa.js → flowchart-elk-definition-4a651766-6ef530b8.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{ganttDiagram-c361ad54-f2b4a55a.js → ganttDiagram-c361ad54-0c7dd39a.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-72cf32ee-dedc298d.js → gitGraphDiagram-72cf32ee-b57239d6.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{graph-4ede11ff.js → graph-9ed57cec.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{index-3862675e-65549d37.js → index-3862675e-233090de.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{index-3a23e736.js → index-72184470.js} +123 -123
- rasa/core/channels/inspector/dist/assets/{infoDiagram-f8f76790-65439671.js → infoDiagram-f8f76790-aa116649.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{journeyDiagram-49397b02-56d03d98.js → journeyDiagram-49397b02-e51877cc.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{layout-dd48f7f4.js → layout-3ca3798c.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{line-1569ad2c.js → line-26ee10d3.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{linear-48bf4935.js → linear-aedded32.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{mindmap-definition-fc14e90a-688504c1.js → mindmap-definition-fc14e90a-d8957261.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{pieDiagram-8a3498a8-78b6d7e6.js → pieDiagram-8a3498a8-d771f885.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{quadrantDiagram-120e2f19-048b84b3.js → quadrantDiagram-120e2f19-09fdf50c.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{requirementDiagram-deff3bca-dd67f107.js → requirementDiagram-deff3bca-9f0af02e.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{sankeyDiagram-04a897e0-8128436e.js → sankeyDiagram-04a897e0-84415b37.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{sequenceDiagram-704730f1-1a0d1461.js → sequenceDiagram-704730f1-8dec4055.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{stateDiagram-587899a1-46d388ed.js → stateDiagram-587899a1-c5431d07.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-d93cdb3a-ea42951a.js → stateDiagram-v2-d93cdb3a-274e77d9.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-6aaf32cf-7427ed0c.js → styles-6aaf32cf-e364a1d7.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-9a916d00-ff5e5a16.js → styles-9a916d00-0dae36f6.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-c10674c1-7b3680cf.js → styles-c10674c1-c4641675.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{svgDrawCommon-08f97a94-f860f2ad.js → svgDrawCommon-08f97a94-831fe9a1.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{timeline-definition-85554ec2-2eebf0c8.js → timeline-definition-85554ec2-c3304b3a.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{xychartDiagram-e933f94c-5d7f4e96.js → xychartDiagram-e933f94c-da799369.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 +337 -71
- rasa/core/channels/vier_cvg.py +1 -2
- rasa/core/channels/voice_ready/audiocodes.py +4 -11
- rasa/core/channels/voice_stream/audiocodes.py +8 -5
- rasa/core/channels/voice_stream/browser_audio.py +1 -1
- rasa/core/channels/voice_stream/genesys.py +2 -2
- rasa/core/channels/voice_stream/tts/__init__.py +8 -0
- rasa/core/channels/voice_stream/twilio_media_streams.py +10 -5
- rasa/core/channels/voice_stream/voice_channel.py +65 -23
- rasa/core/concurrent_lock_store.py +24 -10
- rasa/core/evaluation/marker_tracker_loader.py +1 -1
- rasa/core/exporter.py +1 -1
- rasa/core/http_interpreter.py +3 -7
- rasa/core/information_retrieval/faiss.py +18 -11
- rasa/core/information_retrieval/ingestion/__init__.py +0 -0
- 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 +262 -62
- rasa/core/policies/enterprise_search_prompt_with_relevancy_check_and_citation_template.jinja2 +63 -0
- rasa/core/policies/flow_policy.py +1 -1
- rasa/core/policies/flows/flow_executor.py +96 -17
- rasa/core/policies/intentless_policy.py +57 -20
- rasa/core/processor.py +114 -54
- 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} +5 -1
- rasa/core/tracker_stores/dynamo_tracker_store.py +218 -0
- rasa/core/tracker_stores/mongo_tracker_store.py +206 -0
- rasa/core/tracker_stores/redis_tracker_store.py +219 -0
- rasa/core/tracker_stores/sql_tracker_store.py +555 -0
- rasa/core/tracker_stores/tracker_store.py +805 -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 +10 -6
- rasa/dialogue_understanding/commands/can_not_handle_command.py +2 -0
- rasa/dialogue_understanding/commands/cancel_flow_command.py +5 -1
- rasa/dialogue_understanding/commands/chit_chat_answer_command.py +2 -0
- rasa/dialogue_understanding/commands/clarify_command.py +4 -0
- rasa/dialogue_understanding/commands/command_syntax_manager.py +1 -0
- rasa/dialogue_understanding/commands/correct_slots_command.py +1 -3
- rasa/dialogue_understanding/commands/human_handoff_command.py +2 -0
- 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 +10 -0
- rasa/dialogue_understanding/commands/skip_question_command.py +2 -0
- rasa/dialogue_understanding/commands/start_flow_command.py +4 -0
- rasa/dialogue_understanding/commands/utils.py +26 -2
- 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 +5 -17
- rasa/dialogue_understanding/generator/llm_command_generator.py +1 -3
- rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +4 -44
- rasa/dialogue_understanding/generator/nlu_command_adapter.py +2 -2
- rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v3_gpt_4o_2024_11_20_template.jinja2 +78 -0
- rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +26 -474
- 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 +477 -0
- rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +11 -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 +37 -25
- rasa/dialogue_understanding/patterns/domain_for_patterns.py +190 -0
- rasa/dialogue_understanding/processor/command_processor.py +6 -7
- rasa/dialogue_understanding/stack/frames/flow_stack_frame.py +17 -4
- rasa/dialogue_understanding/stack/utils.py +3 -1
- rasa/dialogue_understanding/utils.py +68 -12
- 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/io.py +35 -8
- rasa/e2e_test/e2e_test_runner.py +1 -1
- 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 -85
- rasa/llm_fine_tuning/paraphrasing/conversation_rephraser.py +1 -5
- rasa/llm_fine_tuning/utils.py +2 -4
- rasa/model_manager/model_api.py +90 -2
- rasa/model_manager/socket_bridge.py +0 -7
- rasa/model_manager/trainer_service.py +15 -12
- 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 +340 -0
- rasa/privacy/privacy_manager.py +576 -0
- rasa/server.py +23 -2
- rasa/shared/constants.py +13 -4
- 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 +35 -8
- rasa/shared/core/flows/flow_step.py +26 -4
- 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 +16 -3
- rasa/shared/core/flows/yaml_flows_io.py +106 -5
- rasa/shared/core/slots.py +33 -1
- rasa/shared/core/trackers.py +4 -10
- rasa/shared/core/training_data/story_reader/yaml_story_reader.py +1 -4
- rasa/shared/importers/importer.py +14 -0
- rasa/shared/importers/static.py +63 -0
- rasa/shared/providers/constants.py +0 -9
- rasa/shared/providers/llm/_base_litellm_client.py +4 -14
- rasa/shared/providers/llm/default_litellm_llm_client.py +2 -2
- rasa/shared/providers/llm/litellm_router_llm_client.py +7 -17
- rasa/shared/providers/llm/llm_client.py +15 -24
- rasa/shared/providers/llm/self_hosted_llm_client.py +2 -10
- rasa/shared/utils/common.py +43 -1
- rasa/shared/utils/llm.py +155 -3
- rasa/shared/utils/yaml.py +32 -0
- rasa/studio/data_handler.py +3 -3
- rasa/studio/download/__init__.py +0 -0
- rasa/studio/download/domains.py +49 -0
- rasa/studio/download/download.py +416 -0
- rasa/studio/download/flows.py +351 -0
- rasa/studio/link.py +200 -0
- rasa/studio/pull.py +94 -0
- rasa/studio/push.py +131 -0
- rasa/studio/results_logger.py +6 -1
- rasa/studio/upload.py +185 -71
- rasa/telemetry.py +83 -26
- rasa/tracing/config.py +4 -5
- rasa/tracing/constants.py +19 -1
- rasa/tracing/instrumentation/attribute_extractors.py +49 -11
- 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 +37 -27
- rasa/utils/endpoints.py +22 -1
- rasa/utils/licensing.py +2 -3
- rasa/utils/log_utils.py +1 -45
- rasa/validator.py +9 -11
- rasa/version.py +1 -1
- {rasa_pro-3.12.18.dev1.dist-info → rasa_pro-3.13.0a1.dev2.dist-info}/METADATA +12 -14
- {rasa_pro-3.12.18.dev1.dist-info → rasa_pro-3.13.0a1.dev2.dist-info}/RECORD +318 -294
- 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/monkey_patches.py +0 -91
- rasa/studio/download.py +0 -489
- /rasa/{cli/project_templates/calm/actions → builder}/__init__.py +0 -0
- /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_pro-3.12.18.dev1.dist-info → rasa_pro-3.13.0a1.dev2.dist-info}/NOTICE +0 -0
- {rasa_pro-3.12.18.dev1.dist-info → rasa_pro-3.13.0a1.dev2.dist-info}/WHEEL +0 -0
- {rasa_pro-3.12.18.dev1.dist-info → rasa_pro-3.13.0a1.dev2.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,576 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import copy
|
|
5
|
+
import datetime
|
|
6
|
+
import os
|
|
7
|
+
import queue
|
|
8
|
+
import time
|
|
9
|
+
from typing import TYPE_CHECKING, Any, Callable, List, Optional, Tuple
|
|
10
|
+
|
|
11
|
+
import structlog
|
|
12
|
+
from apscheduler.schedulers.background import BackgroundScheduler
|
|
13
|
+
|
|
14
|
+
import rasa.shared.core.trackers
|
|
15
|
+
from rasa.core.tracker_stores.tracker_store import TrackerStore
|
|
16
|
+
from rasa.privacy.constants import (
|
|
17
|
+
TEXT_KEY,
|
|
18
|
+
USER_CHAT_INACTIVITY_IN_MINUTES_ENV_VAR_NAME,
|
|
19
|
+
)
|
|
20
|
+
from rasa.privacy.event_broker_utils import create_event_brokers
|
|
21
|
+
from rasa.privacy.privacy_config import (
|
|
22
|
+
PrivacyConfig,
|
|
23
|
+
PrivacyPolicy,
|
|
24
|
+
validate_sensitive_slots,
|
|
25
|
+
)
|
|
26
|
+
from rasa.privacy.privacy_filter import PrivacyFilter
|
|
27
|
+
from rasa.shared.core.events import Event, SlotSet, UserUttered, split_events
|
|
28
|
+
from rasa.shared.core.trackers import DialogueStateTracker
|
|
29
|
+
|
|
30
|
+
if TYPE_CHECKING:
|
|
31
|
+
from asyncio import AbstractEventLoop
|
|
32
|
+
|
|
33
|
+
from rasa.core.available_endpoints import AvailableEndpoints
|
|
34
|
+
from rasa.core.brokers.broker import EventBroker
|
|
35
|
+
from rasa.shared.core.domain import Domain
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
structlogger = structlog.get_logger(__name__)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def wrap_async(func: Callable) -> Callable:
|
|
42
|
+
"""Wraps a function to be used as an async job in the background scheduler."""
|
|
43
|
+
|
|
44
|
+
def wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
45
|
+
return asyncio.run(func(*args, **kwargs))
|
|
46
|
+
|
|
47
|
+
return wrapper
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class BackgroundPrivacyManager:
|
|
51
|
+
"""Manages privacy-related tasks in the background.
|
|
52
|
+
|
|
53
|
+
This class handles the anonymization and deletion of sensitive information
|
|
54
|
+
in dialogue state trackers, as well as the streaming of anonymized events
|
|
55
|
+
to event brokers. It uses background schedulers to periodically run these
|
|
56
|
+
tasks and processes trackers from a queue to ensure that sensitive information
|
|
57
|
+
is handled in a timely manner.
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
TRACKER_QUEUE_PROCESSING_TIMEOUT_IN_SECONDS = 2.0
|
|
61
|
+
|
|
62
|
+
def __init__(
|
|
63
|
+
self,
|
|
64
|
+
endpoints: Optional["AvailableEndpoints"],
|
|
65
|
+
event_loop: Optional["AbstractEventLoop"] = None,
|
|
66
|
+
):
|
|
67
|
+
self.config = (
|
|
68
|
+
PrivacyConfig.from_dict(endpoints.privacy)
|
|
69
|
+
if endpoints and endpoints.privacy
|
|
70
|
+
else None
|
|
71
|
+
)
|
|
72
|
+
self.privacy_filter = (
|
|
73
|
+
PrivacyFilter(self.config.anonymization_rules) if self.config else None
|
|
74
|
+
)
|
|
75
|
+
self.user_chat_inactivity_in_minutes = int(
|
|
76
|
+
os.getenv(USER_CHAT_INACTIVITY_IN_MINUTES_ENV_VAR_NAME, 30)
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# we recreate the tracker store here to ensure
|
|
80
|
+
# that this instance has no event brokers
|
|
81
|
+
# that could publish events during the tracker store
|
|
82
|
+
# background jobs
|
|
83
|
+
self.tracker_store = (
|
|
84
|
+
TrackerStore.create(endpoints.tracker_store)
|
|
85
|
+
if endpoints
|
|
86
|
+
else TrackerStore.create(None)
|
|
87
|
+
)
|
|
88
|
+
self.event_brokers: List["EventBroker"] = []
|
|
89
|
+
self.event_loop = event_loop
|
|
90
|
+
|
|
91
|
+
# Order of the initialisation is important
|
|
92
|
+
# The tracker queue must be created before the scheduler
|
|
93
|
+
# The can_consume_tracker_queue must be set to True before the scheduler starts
|
|
94
|
+
self.tracker_queue: queue.Queue = queue.Queue()
|
|
95
|
+
|
|
96
|
+
# This flag is used to stop the scheduler
|
|
97
|
+
self.can_consume_from_tracker_queue = True
|
|
98
|
+
self.background_scheduler = BackgroundScheduler()
|
|
99
|
+
self.background_scheduler.add_job(
|
|
100
|
+
self._consumer_queue, max_instances=1, id="event_broker_job"
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
self.previous_fire_time_deletion = datetime.datetime.now(
|
|
104
|
+
tz=datetime.timezone.utc
|
|
105
|
+
)
|
|
106
|
+
self._configure_background_scheduler()
|
|
107
|
+
self.background_scheduler.start()
|
|
108
|
+
|
|
109
|
+
async def initialize(
|
|
110
|
+
self, endpoints: Optional["AvailableEndpoints"]
|
|
111
|
+
) -> BackgroundPrivacyManager:
|
|
112
|
+
"""Initialize async attributes of the BackgroundPrivacyManager."""
|
|
113
|
+
event_broker_endpoints = endpoints.event_broker if endpoints else None
|
|
114
|
+
self.event_brokers = (
|
|
115
|
+
await create_event_brokers(event_broker_endpoints, self.event_loop)
|
|
116
|
+
if event_broker_endpoints
|
|
117
|
+
else []
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
return self
|
|
121
|
+
|
|
122
|
+
@classmethod
|
|
123
|
+
async def create_instance(
|
|
124
|
+
cls,
|
|
125
|
+
endpoints: Optional["AvailableEndpoints"],
|
|
126
|
+
event_loop: Optional["AbstractEventLoop"] = None,
|
|
127
|
+
) -> BackgroundPrivacyManager:
|
|
128
|
+
"""Create an instance of BackgroundPrivacyManager."""
|
|
129
|
+
instance = cls(endpoints, event_loop)
|
|
130
|
+
return await instance.initialize(endpoints)
|
|
131
|
+
|
|
132
|
+
def stop(self) -> None:
|
|
133
|
+
structlogger.debug("rasa.privacy_manager.stop_schedulers")
|
|
134
|
+
self.can_consume_from_tracker_queue = False
|
|
135
|
+
self.background_scheduler.shutdown(wait=False)
|
|
136
|
+
|
|
137
|
+
def run(self, tracker: "DialogueStateTracker") -> None:
|
|
138
|
+
self.tracker_queue.put(tracker)
|
|
139
|
+
|
|
140
|
+
def process(
|
|
141
|
+
self, tracker: "DialogueStateTracker", process_all: bool = False
|
|
142
|
+
) -> None:
|
|
143
|
+
"""Process the tracker to identify and anonymize sensitive information.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
tracker: The tracker to process.
|
|
147
|
+
process_all: If True, process all events in the tracker.
|
|
148
|
+
"""
|
|
149
|
+
events = self.process_events(tracker, process_all=process_all)
|
|
150
|
+
events_to_stream = events if events else tracker.events
|
|
151
|
+
self.stream_events(events_to_stream, tracker.sender_id)
|
|
152
|
+
|
|
153
|
+
def process_events(
|
|
154
|
+
self, tracker: DialogueStateTracker, process_all: bool = False
|
|
155
|
+
) -> List[Event]:
|
|
156
|
+
"""Anonymize tracker events."""
|
|
157
|
+
if (latest_message := self._get_latest_user_message(tracker)) is None:
|
|
158
|
+
return []
|
|
159
|
+
|
|
160
|
+
processed_events = list(tracker.events)
|
|
161
|
+
prior_sensitive_slot_events: List[Event] = []
|
|
162
|
+
|
|
163
|
+
if not process_all:
|
|
164
|
+
additional_splitting_conditions = {
|
|
165
|
+
TEXT_KEY: latest_message.text,
|
|
166
|
+
"timestamp": latest_message.timestamp,
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
resulting_events = split_events(
|
|
170
|
+
processed_events,
|
|
171
|
+
UserUttered,
|
|
172
|
+
additional_splitting_conditions=additional_splitting_conditions,
|
|
173
|
+
include_splitting_event=True,
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
processed_events = resulting_events[1]
|
|
177
|
+
prior_events = resulting_events[0]
|
|
178
|
+
prior_tracker = DialogueStateTracker.from_events(
|
|
179
|
+
sender_id=tracker.sender_id, evts=prior_events
|
|
180
|
+
)
|
|
181
|
+
prior_sensitive_slot_events = [
|
|
182
|
+
event
|
|
183
|
+
for event in prior_tracker.applied_events()
|
|
184
|
+
if isinstance(event, SlotSet)
|
|
185
|
+
and event.key in self.config.anonymization_rules # type: ignore[union-attr]
|
|
186
|
+
]
|
|
187
|
+
|
|
188
|
+
return self.privacy_filter.anonymize( # type: ignore[union-attr]
|
|
189
|
+
processed_events, prior_sensitive_slot_events
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
def stream_events(
|
|
193
|
+
self,
|
|
194
|
+
anonymized_events: List[Event],
|
|
195
|
+
sender_id: str,
|
|
196
|
+
) -> None:
|
|
197
|
+
"""Stream anonymized events to the event broker."""
|
|
198
|
+
if not self.event_brokers:
|
|
199
|
+
structlogger.debug(
|
|
200
|
+
"rasa.privacy_manager.no_event_broker_configured",
|
|
201
|
+
)
|
|
202
|
+
return None
|
|
203
|
+
|
|
204
|
+
for event in anonymized_events:
|
|
205
|
+
body = {"sender_id": sender_id}
|
|
206
|
+
body.update(event.as_dict())
|
|
207
|
+
for broker in self.event_brokers:
|
|
208
|
+
broker.publish(body)
|
|
209
|
+
|
|
210
|
+
return None
|
|
211
|
+
|
|
212
|
+
def validate_sensitive_slots_in_domain(self, domain: "Domain") -> None:
|
|
213
|
+
"""Validate the sensitive slots defined in the privacy config against the domain.""" # noqa: E501
|
|
214
|
+
if not self.config:
|
|
215
|
+
structlogger.debug(
|
|
216
|
+
"rasa.privacy_manager.no_sensitive_slots_configured",
|
|
217
|
+
)
|
|
218
|
+
return None
|
|
219
|
+
|
|
220
|
+
# we need to set the domain in the tracker store
|
|
221
|
+
# to prevent errors being raised about slots not found in the domain
|
|
222
|
+
# during the background jobs
|
|
223
|
+
self.tracker_store.domain = domain
|
|
224
|
+
sensitive_slots = list(self.config.anonymization_rules.keys())
|
|
225
|
+
return validate_sensitive_slots(sensitive_slots, domain)
|
|
226
|
+
|
|
227
|
+
def _consumer_queue(self) -> None:
|
|
228
|
+
while self.can_consume_from_tracker_queue:
|
|
229
|
+
try:
|
|
230
|
+
# Wait for 2 seconds for an event to be added to the queue
|
|
231
|
+
# If no event is added to the queue, continue
|
|
232
|
+
# This is done to avoid the scheduler to be stuck in the while loop
|
|
233
|
+
# when we want to stop the scheduler
|
|
234
|
+
tracker = self.tracker_queue.get(
|
|
235
|
+
timeout=self.TRACKER_QUEUE_PROCESSING_TIMEOUT_IN_SECONDS
|
|
236
|
+
)
|
|
237
|
+
self.process(tracker)
|
|
238
|
+
self.tracker_queue.task_done()
|
|
239
|
+
except queue.Empty:
|
|
240
|
+
continue
|
|
241
|
+
|
|
242
|
+
def _get_latest_user_message(
|
|
243
|
+
self, tracker: DialogueStateTracker
|
|
244
|
+
) -> Optional[UserUttered]:
|
|
245
|
+
"""Check if a tracker should be processed."""
|
|
246
|
+
if self.privacy_filter is None:
|
|
247
|
+
structlogger.debug(
|
|
248
|
+
"rasa.privacy_manager.no_privacy_rules_configured",
|
|
249
|
+
)
|
|
250
|
+
return None
|
|
251
|
+
|
|
252
|
+
latest_message = tracker.latest_message
|
|
253
|
+
|
|
254
|
+
if latest_message is None or not latest_message.text:
|
|
255
|
+
structlogger.debug(
|
|
256
|
+
"rasa.privacy_manager.no_user_message.skipping_processing",
|
|
257
|
+
)
|
|
258
|
+
return None
|
|
259
|
+
|
|
260
|
+
return latest_message
|
|
261
|
+
|
|
262
|
+
@staticmethod
|
|
263
|
+
def _has_session_been_anonymized(events: List[Event]) -> bool:
|
|
264
|
+
"""Check if the session has already been anonymized."""
|
|
265
|
+
if not events:
|
|
266
|
+
return False
|
|
267
|
+
for event in reversed(events):
|
|
268
|
+
if (
|
|
269
|
+
hasattr(event, "anonymized_at")
|
|
270
|
+
and getattr(event, "anonymized_at") is not None
|
|
271
|
+
):
|
|
272
|
+
return True
|
|
273
|
+
|
|
274
|
+
return False
|
|
275
|
+
|
|
276
|
+
async def _run_tracker_store_anonymization(self) -> None:
|
|
277
|
+
"""Anonymize eligible tracker sessions in the tracker store."""
|
|
278
|
+
structlogger.info(
|
|
279
|
+
"rasa.privacy_manager.starting_tracker_store_anonymization",
|
|
280
|
+
triggered_by="anonymization_cron_job",
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
keys = await self.tracker_store.keys()
|
|
284
|
+
keys_copy = copy.deepcopy(list(keys))
|
|
285
|
+
|
|
286
|
+
for key in keys_copy:
|
|
287
|
+
full_tracker = await self.tracker_store.retrieve_full_tracker(key)
|
|
288
|
+
|
|
289
|
+
if not full_tracker:
|
|
290
|
+
structlogger.debug(
|
|
291
|
+
"rasa.privacy_manager.no_tracker_found_for_sender_id",
|
|
292
|
+
sender_id=key,
|
|
293
|
+
)
|
|
294
|
+
continue
|
|
295
|
+
|
|
296
|
+
processed_events, already_anonymized_events, uneligible_events = (
|
|
297
|
+
self._get_processed_events_after_anonymization(full_tracker)
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
if not processed_events:
|
|
301
|
+
structlogger.debug(
|
|
302
|
+
"rasa.privacy_manager.no_events_to_anonymize_for_tracker",
|
|
303
|
+
sender_id=key,
|
|
304
|
+
)
|
|
305
|
+
continue
|
|
306
|
+
|
|
307
|
+
all_events = (
|
|
308
|
+
already_anonymized_events + processed_events + uneligible_events
|
|
309
|
+
)
|
|
310
|
+
updated_tracker = DialogueStateTracker.from_events(
|
|
311
|
+
sender_id=key,
|
|
312
|
+
evts=all_events,
|
|
313
|
+
slots=full_tracker.slots.values(),
|
|
314
|
+
)
|
|
315
|
+
await self.tracker_store.delete(sender_id=key)
|
|
316
|
+
await self.tracker_store.save(updated_tracker)
|
|
317
|
+
|
|
318
|
+
structlogger.info(
|
|
319
|
+
"rasa.privacy_manager.saved_tracker_after_anonymization",
|
|
320
|
+
sender_id=key,
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
async def _run_tracker_store_deletion(self) -> None:
|
|
324
|
+
"""Delete eligible tracker sessions from the tracker store."""
|
|
325
|
+
structlogger.info(
|
|
326
|
+
"rasa.privacy_manager.starting_tracker_store_deletion",
|
|
327
|
+
triggered_by="deletion_cron_job",
|
|
328
|
+
)
|
|
329
|
+
keys = await self.tracker_store.keys()
|
|
330
|
+
|
|
331
|
+
# Make a copy of the keys to avoid modifying the list while iterating
|
|
332
|
+
keys_copy = copy.deepcopy(list(keys))
|
|
333
|
+
|
|
334
|
+
for key in keys_copy:
|
|
335
|
+
full_tracker = await self.tracker_store.retrieve_full_tracker(key)
|
|
336
|
+
|
|
337
|
+
if not full_tracker:
|
|
338
|
+
structlogger.debug(
|
|
339
|
+
"rasa.privacy_manager.no_tracker_found_for_sender_id",
|
|
340
|
+
key=key,
|
|
341
|
+
)
|
|
342
|
+
continue
|
|
343
|
+
|
|
344
|
+
events_to_be_retained = self._get_events_to_be_retained_after_deletion(
|
|
345
|
+
full_tracker
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
await self.tracker_store.delete(sender_id=key)
|
|
349
|
+
|
|
350
|
+
if not events_to_be_retained:
|
|
351
|
+
continue
|
|
352
|
+
|
|
353
|
+
tracker = DialogueStateTracker.from_events(
|
|
354
|
+
sender_id=key,
|
|
355
|
+
evts=events_to_be_retained,
|
|
356
|
+
slots=full_tracker.slots.values(),
|
|
357
|
+
)
|
|
358
|
+
await self.tracker_store.save(tracker)
|
|
359
|
+
|
|
360
|
+
structlogger.info(
|
|
361
|
+
"rasa.privacy_manager.save_tracker_after_deletion",
|
|
362
|
+
key=key,
|
|
363
|
+
event_info="Saved tracker with events not scheduled "
|
|
364
|
+
"for deletion yet.",
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
async def _run_tracker_store_background_jobs_sequentially(self) -> None:
|
|
368
|
+
"""Run the tracker store background jobs.
|
|
369
|
+
|
|
370
|
+
If both anonymization and deletion policies are configured,
|
|
371
|
+
we need to ensure that the background job timings do not
|
|
372
|
+
overlap to prevent race conditions when accessing the
|
|
373
|
+
tracker store.
|
|
374
|
+
|
|
375
|
+
The scheduler will run the anonymization job first,
|
|
376
|
+
and then the deletion job if the current time is past
|
|
377
|
+
the next scheduled time for deletion.
|
|
378
|
+
"""
|
|
379
|
+
await self._run_tracker_store_anonymization()
|
|
380
|
+
|
|
381
|
+
now = datetime.datetime.now(tz=datetime.timezone.utc)
|
|
382
|
+
next_fire_time = (
|
|
383
|
+
self.config.tracker_store_settings.deletion_policy.cron.get_next_fire_time( # type: ignore[union-attr]
|
|
384
|
+
self.previous_fire_time_deletion,
|
|
385
|
+
now=now,
|
|
386
|
+
)
|
|
387
|
+
)
|
|
388
|
+
|
|
389
|
+
if next_fire_time and now >= next_fire_time:
|
|
390
|
+
await self._run_tracker_store_deletion()
|
|
391
|
+
self.previous_fire_time_deletion = next_fire_time
|
|
392
|
+
|
|
393
|
+
return None
|
|
394
|
+
|
|
395
|
+
def _add_anonymization_job(self) -> None:
|
|
396
|
+
wrapped_anonymization = wrap_async(self._run_tracker_store_anonymization)
|
|
397
|
+
self.background_scheduler.add_job(
|
|
398
|
+
wrapped_anonymization,
|
|
399
|
+
trigger=self.config.tracker_store_settings.anonymization_policy.cron, # type: ignore[union-attr]
|
|
400
|
+
max_instances=1,
|
|
401
|
+
id="anonymization_cron_job",
|
|
402
|
+
)
|
|
403
|
+
|
|
404
|
+
def _add_deletion_job(self) -> None:
|
|
405
|
+
wrapped_deletion = wrap_async(self._run_tracker_store_deletion)
|
|
406
|
+
self.background_scheduler.add_job(
|
|
407
|
+
wrapped_deletion,
|
|
408
|
+
trigger=self.config.tracker_store_settings.deletion_policy.cron, # type: ignore[union-attr]
|
|
409
|
+
max_instances=1,
|
|
410
|
+
id="deletion_cron_job",
|
|
411
|
+
)
|
|
412
|
+
|
|
413
|
+
def _add_sequential_job(self) -> None:
|
|
414
|
+
sequential_dispatcher = wrap_async(
|
|
415
|
+
self._run_tracker_store_background_jobs_sequentially
|
|
416
|
+
)
|
|
417
|
+
self.background_scheduler.add_job(
|
|
418
|
+
sequential_dispatcher,
|
|
419
|
+
trigger=self.config.tracker_store_settings.anonymization_policy.cron, # type: ignore[union-attr]
|
|
420
|
+
max_instances=1,
|
|
421
|
+
id="anonymization_and_deletion_cron_job",
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
def _configure_background_scheduler(self) -> None:
|
|
425
|
+
"""Configure the background scheduler."""
|
|
426
|
+
tracker_store_settings_configured = (
|
|
427
|
+
self.config is not None and self.config.tracker_store_settings is not None
|
|
428
|
+
)
|
|
429
|
+
anonymization_policy = (
|
|
430
|
+
self.config.tracker_store_settings.anonymization_policy # type: ignore[union-attr]
|
|
431
|
+
if tracker_store_settings_configured
|
|
432
|
+
else None
|
|
433
|
+
)
|
|
434
|
+
deletion_policy = (
|
|
435
|
+
self.config.tracker_store_settings.deletion_policy # type: ignore[union-attr]
|
|
436
|
+
if tracker_store_settings_configured
|
|
437
|
+
else None
|
|
438
|
+
)
|
|
439
|
+
|
|
440
|
+
if (
|
|
441
|
+
tracker_store_settings_configured
|
|
442
|
+
and anonymization_policy is not None
|
|
443
|
+
and deletion_policy is not None
|
|
444
|
+
):
|
|
445
|
+
next_fire_time_anonymization = get_next_fire_time(anonymization_policy)
|
|
446
|
+
next_fire_time_deletion = get_next_fire_time(deletion_policy)
|
|
447
|
+
|
|
448
|
+
# If both anonymization and deletion policies are configured
|
|
449
|
+
# to start on the same date,
|
|
450
|
+
# we need to run them sequentially to avoid race conditions
|
|
451
|
+
if (
|
|
452
|
+
next_fire_time_anonymization is not None
|
|
453
|
+
and next_fire_time_deletion is not None
|
|
454
|
+
and next_fire_time_anonymization.date()
|
|
455
|
+
== next_fire_time_deletion.date()
|
|
456
|
+
):
|
|
457
|
+
self._add_sequential_job()
|
|
458
|
+
else:
|
|
459
|
+
self._add_anonymization_job()
|
|
460
|
+
self._add_deletion_job()
|
|
461
|
+
|
|
462
|
+
elif tracker_store_settings_configured and anonymization_policy is not None:
|
|
463
|
+
self._add_anonymization_job()
|
|
464
|
+
|
|
465
|
+
elif tracker_store_settings_configured and deletion_policy is not None:
|
|
466
|
+
self._add_deletion_job()
|
|
467
|
+
|
|
468
|
+
def _get_processed_events_after_anonymization(
|
|
469
|
+
self,
|
|
470
|
+
full_tracker: DialogueStateTracker,
|
|
471
|
+
) -> Tuple[List[Event], List[Event], List[Event]]:
|
|
472
|
+
"""Get processed events after anonymization job."""
|
|
473
|
+
multiple_tracker_sessions = (
|
|
474
|
+
rasa.shared.core.trackers.get_trackers_for_conversation_sessions(
|
|
475
|
+
full_tracker
|
|
476
|
+
)
|
|
477
|
+
)
|
|
478
|
+
|
|
479
|
+
processed_events = []
|
|
480
|
+
already_anonymized_events = []
|
|
481
|
+
uneligible_events = []
|
|
482
|
+
|
|
483
|
+
for session in multiple_tracker_sessions:
|
|
484
|
+
has_session_been_anonymized = self._has_session_been_anonymized(
|
|
485
|
+
list(session.events)
|
|
486
|
+
)
|
|
487
|
+
|
|
488
|
+
if has_session_been_anonymized:
|
|
489
|
+
structlogger.debug(
|
|
490
|
+
"rasa.privacy_manager.session_already_anonymized",
|
|
491
|
+
session_id=session.sender_id,
|
|
492
|
+
)
|
|
493
|
+
already_anonymized_events.extend(list(session.events))
|
|
494
|
+
continue
|
|
495
|
+
|
|
496
|
+
current_time = time.time()
|
|
497
|
+
|
|
498
|
+
last_event_timestamp = (
|
|
499
|
+
str(datetime.datetime.fromtimestamp(session.events[-1].timestamp))
|
|
500
|
+
if session.events
|
|
501
|
+
else "N/A"
|
|
502
|
+
)
|
|
503
|
+
|
|
504
|
+
if session.events and current_time - session.events[-1].timestamp > (
|
|
505
|
+
self.user_chat_inactivity_in_minutes * 60
|
|
506
|
+
+ self.config.tracker_store_settings.anonymization_policy.min_after_session_end # type: ignore[union-attr] # noqa: E501
|
|
507
|
+
* 60
|
|
508
|
+
):
|
|
509
|
+
structlogger.info(
|
|
510
|
+
"rasa.privacy_manager.anonymizing_tracker_session",
|
|
511
|
+
key=session.sender_id,
|
|
512
|
+
last_event_timestamp=last_event_timestamp,
|
|
513
|
+
triggered_by="anonymization_cron_job",
|
|
514
|
+
)
|
|
515
|
+
tracker = DialogueStateTracker.from_events(
|
|
516
|
+
session.sender_id, session.events
|
|
517
|
+
)
|
|
518
|
+
events = self.process_events(tracker, process_all=True)
|
|
519
|
+
processed_events.extend(events)
|
|
520
|
+
else:
|
|
521
|
+
# If the session is not valid for anonymization,
|
|
522
|
+
# we still want to write them back to the tracker store
|
|
523
|
+
events = list(session.events)
|
|
524
|
+
uneligible_events.extend(events)
|
|
525
|
+
structlogger.debug(
|
|
526
|
+
"rasa.privacy_manager.session_not_valid_for_anonymization",
|
|
527
|
+
key=session.sender_id,
|
|
528
|
+
session_id=session.sender_id,
|
|
529
|
+
last_event_timestamp=last_event_timestamp,
|
|
530
|
+
)
|
|
531
|
+
return processed_events, already_anonymized_events, uneligible_events
|
|
532
|
+
|
|
533
|
+
def _get_events_to_be_retained_after_deletion(
|
|
534
|
+
self, full_tracker: DialogueStateTracker
|
|
535
|
+
) -> List[Event]:
|
|
536
|
+
"""Get the events to be retained after deletion."""
|
|
537
|
+
multiple_tracker_sessions = (
|
|
538
|
+
rasa.shared.core.trackers.get_trackers_for_conversation_sessions(
|
|
539
|
+
full_tracker
|
|
540
|
+
)
|
|
541
|
+
)
|
|
542
|
+
events_to_be_retained: List[Event] = []
|
|
543
|
+
for session in multiple_tracker_sessions:
|
|
544
|
+
current_time = time.time()
|
|
545
|
+
if session.events and (
|
|
546
|
+
current_time - session.events[-1].timestamp
|
|
547
|
+
<= (
|
|
548
|
+
self.user_chat_inactivity_in_minutes * 60
|
|
549
|
+
+ self.config.tracker_store_settings.deletion_policy.min_after_session_end # type: ignore[union-attr] # noqa: E501
|
|
550
|
+
* 60
|
|
551
|
+
)
|
|
552
|
+
):
|
|
553
|
+
events_to_be_retained.extend(session.events)
|
|
554
|
+
else:
|
|
555
|
+
last_event_timestamp = (
|
|
556
|
+
str(datetime.datetime.fromtimestamp(session.events[-1].timestamp))
|
|
557
|
+
if session.events
|
|
558
|
+
else "N/A"
|
|
559
|
+
)
|
|
560
|
+
|
|
561
|
+
structlogger.info(
|
|
562
|
+
"rasa.privacy_manager.tracker_session_scheduled_for_deletion",
|
|
563
|
+
key=full_tracker.sender_id,
|
|
564
|
+
last_event_timestamp=last_event_timestamp,
|
|
565
|
+
triggered_by="deletion_cron_job",
|
|
566
|
+
)
|
|
567
|
+
|
|
568
|
+
return events_to_be_retained
|
|
569
|
+
|
|
570
|
+
|
|
571
|
+
def get_next_fire_time(
|
|
572
|
+
privacy_policy: PrivacyPolicy,
|
|
573
|
+
) -> Optional[datetime.datetime]:
|
|
574
|
+
"""Get the next fire time for the privacy policy."""
|
|
575
|
+
now = datetime.datetime.now(tz=datetime.timezone.utc)
|
|
576
|
+
return privacy_policy.cron.get_next_fire_time(None, now=now)
|
rasa/server.py
CHANGED
|
@@ -44,6 +44,7 @@ import rasa.utils.endpoints
|
|
|
44
44
|
import rasa.utils.io
|
|
45
45
|
from rasa.constants import MINIMUM_COMPATIBLE_VERSION
|
|
46
46
|
from rasa.core.agent import Agent
|
|
47
|
+
from rasa.core.available_endpoints import AvailableEndpoints
|
|
47
48
|
from rasa.core.channels.channel import (
|
|
48
49
|
CollectingOutputChannel,
|
|
49
50
|
OutputChannel,
|
|
@@ -52,7 +53,6 @@ from rasa.core.channels.channel import (
|
|
|
52
53
|
from rasa.core.constants import DEFAULT_RESPONSE_TIMEOUT
|
|
53
54
|
from rasa.core.persistor import parse_remote_storage
|
|
54
55
|
from rasa.core.test import test
|
|
55
|
-
from rasa.core.utils import AvailableEndpoints
|
|
56
56
|
from rasa.nlu.emulators.emulator import Emulator
|
|
57
57
|
from rasa.nlu.emulators.no_emulator import NoEmulator
|
|
58
58
|
from rasa.nlu.test import CVEvaluationResult
|
|
@@ -75,7 +75,9 @@ from rasa.shared.core.training_data.story_writer.yaml_story_writer import (
|
|
|
75
75
|
YAMLStoryWriter,
|
|
76
76
|
)
|
|
77
77
|
from rasa.shared.exceptions import RasaException, YamlException
|
|
78
|
-
from rasa.shared.importers.importer import
|
|
78
|
+
from rasa.shared.importers.importer import (
|
|
79
|
+
TrainingDataImporter,
|
|
80
|
+
)
|
|
79
81
|
from rasa.shared.nlu.training_data.formats import RasaYAMLReader
|
|
80
82
|
from rasa.shared.utils.schemas.events import EVENTS_SCHEMA
|
|
81
83
|
from rasa.shared.utils.yaml import validate_training_data
|
|
@@ -797,6 +799,25 @@ def create_app(
|
|
|
797
799
|
f"An unexpected error occurred. Error: {e}",
|
|
798
800
|
)
|
|
799
801
|
|
|
802
|
+
@app.delete("/conversations/<conversation_id:path>/tracker")
|
|
803
|
+
@requires_auth(app, auth_token)
|
|
804
|
+
@ensure_loaded_agent(app)
|
|
805
|
+
@ensure_conversation_exists()
|
|
806
|
+
async def delete_tracker(request: Request, conversation_id: Text) -> HTTPResponse:
|
|
807
|
+
"""Delete a conversation's tracker."""
|
|
808
|
+
try:
|
|
809
|
+
async with app.ctx.agent.lock_store.lock(conversation_id):
|
|
810
|
+
await app.ctx.agent.tracker_store.delete(conversation_id)
|
|
811
|
+
logger.info(f"Tracker for conversation '{conversation_id}' deleted.")
|
|
812
|
+
return response.empty(status=HTTPStatus.NO_CONTENT)
|
|
813
|
+
except Exception as e:
|
|
814
|
+
logger.debug(traceback.format_exc())
|
|
815
|
+
raise ErrorResponse(
|
|
816
|
+
HTTPStatus.INTERNAL_SERVER_ERROR,
|
|
817
|
+
"ConversationError",
|
|
818
|
+
f"An unexpected error occurred. Error: {e}",
|
|
819
|
+
)
|
|
820
|
+
|
|
800
821
|
@app.post("/conversations/<conversation_id:path>/tracker/events")
|
|
801
822
|
@requires_auth(app, auth_token)
|
|
802
823
|
@ensure_loaded_agent(app)
|
rasa/shared/constants.py
CHANGED
|
@@ -295,6 +295,7 @@ CONTEXT = "context"
|
|
|
295
295
|
|
|
296
296
|
RASA_PATTERN_INTERNAL_ERROR = "pattern_internal_error"
|
|
297
297
|
RASA_PATTERN_HUMAN_HANDOFF = "pattern_human_handoff"
|
|
298
|
+
RASA_PATTERN_CHITCHAT = "pattern_chitchat"
|
|
298
299
|
|
|
299
300
|
RASA_INTERNAL_ERROR_PREFIX = "rasa_internal_error_"
|
|
300
301
|
RASA_PATTERN_INTERNAL_ERROR_DEFAULT = RASA_INTERNAL_ERROR_PREFIX + "default"
|
|
@@ -314,6 +315,9 @@ RASA_PATTERN_CANNOT_HANDLE_NOT_SUPPORTED = (
|
|
|
314
315
|
RASA_PATTERN_CANNOT_HANDLE_INVALID_INTENT = (
|
|
315
316
|
RASA_PATTERN_CANNOT_HANDLE_PREFIX + "invalid_intent"
|
|
316
317
|
)
|
|
318
|
+
RASA_PATTERN_CANNOT_HANDLE_NO_RELEVANT_ANSWER = (
|
|
319
|
+
RASA_PATTERN_CANNOT_HANDLE_PREFIX + "no_relevant_answer"
|
|
320
|
+
)
|
|
317
321
|
|
|
318
322
|
ROUTE_TO_CALM_SLOT = "route_session_to_calm"
|
|
319
323
|
|
|
@@ -343,7 +347,12 @@ ROLE_SYSTEM = "system"
|
|
|
343
347
|
REFILL_UTTER = "refill_utter"
|
|
344
348
|
REJECTIONS = "rejections"
|
|
345
349
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
+
# Constants for extractive search FAQ parsing (QA pairs from input documents)
|
|
351
|
+
FAQ_DOCUMENT_METADATA_TITLE = "title"
|
|
352
|
+
FAQ_DOCUMENT_METADATA_ANSWER = "answer"
|
|
353
|
+
FAQ_DOCUMENT_METADATA_TYPE = "type"
|
|
354
|
+
DOCUMENT_TYPE_FAQ = "faq"
|
|
355
|
+
FAQ_INPUT_DATA_QUESTION_LINE_PREFIX = "Q:"
|
|
356
|
+
FAQ_INPUT_DATA_ANSWER_LINE_PREFIX = "A:"
|
|
357
|
+
FAQ_DOCUMENT_ENTRY_SEPARATOR = "\n\n"
|
|
358
|
+
FAQ_DOCUMENT_LINE_SEPARATOR = "\n"
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import copy
|
|
2
1
|
import re
|
|
3
2
|
from typing import List, Optional
|
|
4
3
|
|
|
@@ -35,9 +34,7 @@ class CommandPayloadReader:
|
|
|
35
34
|
|
|
36
35
|
matches = CommandPayloadReader.find_matches(user_text)
|
|
37
36
|
if not matches:
|
|
38
|
-
structlogger.warning(
|
|
39
|
-
"message.parsing.failed", user_text=copy.deepcopy(user_text)
|
|
40
|
-
)
|
|
37
|
+
structlogger.warning("message.parsing.failed")
|
|
41
38
|
return message
|
|
42
39
|
|
|
43
40
|
return CommandPayloadReader.extract_commands_from_pattern_matches(
|
|
@@ -110,7 +107,6 @@ class CommandPayloadReader:
|
|
|
110
107
|
if user_text.count("=") > MAX_NUMBER_OF_SLOTS:
|
|
111
108
|
structlogger.warning(
|
|
112
109
|
"too.many.slots",
|
|
113
|
-
user_text=copy.deepcopy(user_text),
|
|
114
110
|
slot_limit=10,
|
|
115
111
|
)
|
|
116
112
|
return True
|