rasa-pro 3.12.18.dev1__py3-none-any.whl → 3.13.0a1.dev1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of rasa-pro might be problematic. Click here for more details.
- 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 +645 -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.dev1.dist-info}/METADATA +12 -14
- {rasa_pro-3.12.18.dev1.dist-info → rasa_pro-3.13.0a1.dev1.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.dev1.dist-info}/NOTICE +0 -0
- {rasa_pro-3.12.18.dev1.dist-info → rasa_pro-3.13.0a1.dev1.dist-info}/WHEEL +0 -0
- {rasa_pro-3.12.18.dev1.dist-info → rasa_pro-3.13.0a1.dev1.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,555 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import contextlib
|
|
4
|
+
import itertools
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
from time import sleep
|
|
8
|
+
from typing import (
|
|
9
|
+
TYPE_CHECKING,
|
|
10
|
+
Any,
|
|
11
|
+
Dict,
|
|
12
|
+
Generator,
|
|
13
|
+
Iterable,
|
|
14
|
+
Iterator,
|
|
15
|
+
Optional,
|
|
16
|
+
Text,
|
|
17
|
+
Union,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
import sqlalchemy as sa
|
|
21
|
+
import structlog
|
|
22
|
+
|
|
23
|
+
import rasa.shared
|
|
24
|
+
from rasa.core.brokers.broker import EventBroker
|
|
25
|
+
from rasa.core.constants import (
|
|
26
|
+
POSTGRESQL_MAX_OVERFLOW,
|
|
27
|
+
POSTGRESQL_POOL_SIZE,
|
|
28
|
+
POSTGRESQL_SCHEMA,
|
|
29
|
+
)
|
|
30
|
+
from rasa.core.tracker_stores.tracker_store import (
|
|
31
|
+
SerializedTrackerAsText,
|
|
32
|
+
TrackerStore,
|
|
33
|
+
validate_port,
|
|
34
|
+
)
|
|
35
|
+
from rasa.shared.core.domain import Domain
|
|
36
|
+
from rasa.shared.core.events import SessionStarted
|
|
37
|
+
from rasa.shared.core.trackers import DialogueStateTracker
|
|
38
|
+
from rasa.shared.exceptions import RasaException
|
|
39
|
+
from rasa.shared.nlu.constants import INTENT_NAME_KEY
|
|
40
|
+
|
|
41
|
+
if TYPE_CHECKING:
|
|
42
|
+
from sqlalchemy import Sequence
|
|
43
|
+
from sqlalchemy.engine.base import Engine
|
|
44
|
+
from sqlalchemy.engine.url import URL
|
|
45
|
+
from sqlalchemy.orm import Query, Session
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
structlogger = structlog.get_logger(__name__)
|
|
49
|
+
|
|
50
|
+
# default values of PostgreSQL pool size and max overflow
|
|
51
|
+
POSTGRESQL_DEFAULT_MAX_OVERFLOW = 100
|
|
52
|
+
POSTGRESQL_DEFAULT_POOL_SIZE = 50
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _create_sequence(table_name: Text) -> "Sequence":
|
|
56
|
+
"""Creates a sequence object for a specific table name.
|
|
57
|
+
|
|
58
|
+
If using Oracle you will need to create a sequence in your database,
|
|
59
|
+
as described here: https://rasa.com/docs/rasa-pro/production/tracker-stores#sqltrackerstore
|
|
60
|
+
Args:
|
|
61
|
+
table_name: The name of the table, which gets a Sequence assigned
|
|
62
|
+
|
|
63
|
+
Returns: A `Sequence` object
|
|
64
|
+
"""
|
|
65
|
+
from sqlalchemy.orm import declarative_base
|
|
66
|
+
|
|
67
|
+
sequence_name = f"{table_name}_seq"
|
|
68
|
+
Base = declarative_base()
|
|
69
|
+
return sa.Sequence(sequence_name, metadata=Base.metadata, optional=True)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def is_postgresql_url(url: Union[Text, "URL"]) -> bool:
|
|
73
|
+
"""Determine whether `url` configures a PostgreSQL connection.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
url: SQL connection URL.
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
`True` if `url` is a PostgreSQL connection URL.
|
|
80
|
+
"""
|
|
81
|
+
if isinstance(url, str):
|
|
82
|
+
return "postgresql" in url
|
|
83
|
+
|
|
84
|
+
return url.drivername == "postgresql"
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def create_engine_kwargs(url: Union[Text, "URL"]) -> Dict[Text, Any]:
|
|
88
|
+
"""Get `sqlalchemy.create_engine()` kwargs.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
url: SQL connection URL.
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
kwargs to be passed into `sqlalchemy.create_engine()`.
|
|
95
|
+
"""
|
|
96
|
+
if not is_postgresql_url(url):
|
|
97
|
+
return {}
|
|
98
|
+
|
|
99
|
+
kwargs: Dict[Text, Any] = {}
|
|
100
|
+
|
|
101
|
+
schema_name = os.environ.get(POSTGRESQL_SCHEMA)
|
|
102
|
+
|
|
103
|
+
if schema_name:
|
|
104
|
+
structlogger.debug(
|
|
105
|
+
"postgresql_tracker_store.schema_name",
|
|
106
|
+
event_inf=f"Using PostgreSQL schema '{schema_name}'.",
|
|
107
|
+
)
|
|
108
|
+
kwargs["connect_args"] = {"options": f"-csearch_path={schema_name}"}
|
|
109
|
+
|
|
110
|
+
# pool_size and max_overflow can be set to control the number of
|
|
111
|
+
# connections that are kept in the connection pool. Not available
|
|
112
|
+
# for SQLite, and only tested for PostgreSQL. See
|
|
113
|
+
# https://docs.sqlalchemy.org/en/13/core/pooling.html#sqlalchemy.pool.QueuePool
|
|
114
|
+
kwargs["pool_size"] = int(
|
|
115
|
+
os.environ.get(POSTGRESQL_POOL_SIZE, POSTGRESQL_DEFAULT_POOL_SIZE)
|
|
116
|
+
)
|
|
117
|
+
kwargs["max_overflow"] = int(
|
|
118
|
+
os.environ.get(POSTGRESQL_MAX_OVERFLOW, POSTGRESQL_DEFAULT_MAX_OVERFLOW)
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
return kwargs
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def ensure_schema_exists(session: "Session") -> None:
|
|
125
|
+
"""Ensure that the requested PostgreSQL schema exists in the database.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
session: Session used to inspect the database.
|
|
129
|
+
|
|
130
|
+
Raises:
|
|
131
|
+
`ValueError` if the requested schema does not exist.
|
|
132
|
+
RasaException if no engine can be obtained from session.
|
|
133
|
+
"""
|
|
134
|
+
schema_name = os.environ.get(POSTGRESQL_SCHEMA)
|
|
135
|
+
|
|
136
|
+
if not schema_name:
|
|
137
|
+
return
|
|
138
|
+
|
|
139
|
+
engine = session.get_bind()
|
|
140
|
+
|
|
141
|
+
if not isinstance(engine, sa.engine.base.Engine):
|
|
142
|
+
# The "bind" is usually an instance of Engine, except in the case
|
|
143
|
+
# where the session has been explicitly bound directly to a connection.
|
|
144
|
+
raise RasaException("Cannot ensure schema exists as no engine exists.")
|
|
145
|
+
|
|
146
|
+
if is_postgresql_url(engine.url):
|
|
147
|
+
query = sa.exists(
|
|
148
|
+
sa.select(sa.text("schema_name"))
|
|
149
|
+
.select_from(sa.text("information_schema.schemata"))
|
|
150
|
+
.where(sa.text(f"schema_name = '{schema_name}'"))
|
|
151
|
+
)
|
|
152
|
+
if not session.query(query).scalar():
|
|
153
|
+
raise ValueError(schema_name)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
class SQLTrackerStore(TrackerStore, SerializedTrackerAsText):
|
|
157
|
+
"""Store which can save and retrieve trackers from an SQL database."""
|
|
158
|
+
|
|
159
|
+
from sqlalchemy.orm import DeclarativeBase
|
|
160
|
+
|
|
161
|
+
class Base(DeclarativeBase):
|
|
162
|
+
"""Base class for all tracker store tables."""
|
|
163
|
+
|
|
164
|
+
pass
|
|
165
|
+
|
|
166
|
+
class SQLEvent(Base):
|
|
167
|
+
"""Represents an event in the SQL Tracker Store."""
|
|
168
|
+
|
|
169
|
+
__tablename__ = "events"
|
|
170
|
+
|
|
171
|
+
# `create_sequence` is needed to create a sequence for databases that
|
|
172
|
+
# don't autoincrement Integer primary keys (e.g. Oracle)
|
|
173
|
+
id = sa.Column(sa.Integer, _create_sequence(__tablename__), primary_key=True)
|
|
174
|
+
sender_id = sa.Column(sa.String(255), nullable=False, index=True)
|
|
175
|
+
type_name = sa.Column(sa.String(255), nullable=False)
|
|
176
|
+
timestamp = sa.Column(sa.Float)
|
|
177
|
+
intent_name = sa.Column(sa.String(255))
|
|
178
|
+
action_name = sa.Column(sa.String(255))
|
|
179
|
+
data = sa.Column(sa.Text)
|
|
180
|
+
|
|
181
|
+
def __init__(
|
|
182
|
+
self,
|
|
183
|
+
domain: Optional[Domain] = None,
|
|
184
|
+
dialect: Text = "sqlite",
|
|
185
|
+
host: Optional[Text] = None,
|
|
186
|
+
port: Optional[int] = None,
|
|
187
|
+
db: Text = "rasa.db",
|
|
188
|
+
username: Optional[Text] = None,
|
|
189
|
+
password: Optional[Text] = None,
|
|
190
|
+
event_broker: Optional[EventBroker] = None,
|
|
191
|
+
login_db: Optional[Text] = None,
|
|
192
|
+
query: Optional[Dict] = None,
|
|
193
|
+
**kwargs: Dict[Text, Any],
|
|
194
|
+
) -> None:
|
|
195
|
+
import sqlalchemy.exc
|
|
196
|
+
|
|
197
|
+
port = validate_port(port)
|
|
198
|
+
|
|
199
|
+
engine_url = self.get_db_url(
|
|
200
|
+
dialect, host, port, db, username, password, login_db, query
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
self.engine = sa.create_engine(engine_url, **create_engine_kwargs(engine_url))
|
|
204
|
+
|
|
205
|
+
structlogger.debug(
|
|
206
|
+
"sql_tracker_store.connect_to_sql_database",
|
|
207
|
+
event_info=f"Attempting to connect to database via '{self.engine.url!r}'.",
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
# Database might take a while to come up
|
|
211
|
+
while True:
|
|
212
|
+
try:
|
|
213
|
+
# if `login_db` has been provided, use current channel with
|
|
214
|
+
# that database to create working database `db`
|
|
215
|
+
if login_db:
|
|
216
|
+
self._create_database_and_update_engine(db, engine_url)
|
|
217
|
+
|
|
218
|
+
try:
|
|
219
|
+
self.Base.metadata.create_all(self.engine)
|
|
220
|
+
except (
|
|
221
|
+
sqlalchemy.exc.OperationalError,
|
|
222
|
+
sqlalchemy.exc.ProgrammingError,
|
|
223
|
+
) as e:
|
|
224
|
+
# Several Rasa services started in parallel may attempt to
|
|
225
|
+
# create tables at the same time. That is okay so long as
|
|
226
|
+
# the first services finishes the table creation.
|
|
227
|
+
structlogger.error(
|
|
228
|
+
"sql_tracker_store.create_tables_failed",
|
|
229
|
+
event_info="Could not create tables",
|
|
230
|
+
exec_info=e,
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
self.sessionmaker = sa.orm.session.sessionmaker(bind=self.engine)
|
|
234
|
+
break
|
|
235
|
+
except (
|
|
236
|
+
sqlalchemy.exc.OperationalError,
|
|
237
|
+
sqlalchemy.exc.IntegrityError,
|
|
238
|
+
) as error:
|
|
239
|
+
structlogger.warning(
|
|
240
|
+
"sql_tracker_store.initialisation_error",
|
|
241
|
+
event_info="Failed to establish a connection to the SQL database. ",
|
|
242
|
+
exc_info=error,
|
|
243
|
+
)
|
|
244
|
+
sleep(5)
|
|
245
|
+
|
|
246
|
+
structlogger.debug(
|
|
247
|
+
"sql_tracker_store.connected_to_sql_database",
|
|
248
|
+
event_info=f"Connection to SQL database '{db}' successful.",
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
super().__init__(domain, event_broker, **kwargs)
|
|
252
|
+
|
|
253
|
+
@staticmethod
|
|
254
|
+
def get_db_url(
|
|
255
|
+
dialect: Text = "sqlite",
|
|
256
|
+
host: Optional[Text] = None,
|
|
257
|
+
port: Optional[int] = None,
|
|
258
|
+
db: Text = "rasa.db",
|
|
259
|
+
username: Optional[Text] = None,
|
|
260
|
+
password: Optional[Text] = None,
|
|
261
|
+
login_db: Optional[Text] = None,
|
|
262
|
+
query: Optional[Dict] = None,
|
|
263
|
+
) -> Union[Text, "URL"]:
|
|
264
|
+
"""Build an SQLAlchemy `URL` object.
|
|
265
|
+
|
|
266
|
+
The URL object represents the parameters needed to connect to an
|
|
267
|
+
SQL database.
|
|
268
|
+
|
|
269
|
+
Args:
|
|
270
|
+
dialect: SQL database type.
|
|
271
|
+
host: Database network host.
|
|
272
|
+
port: Database network port.
|
|
273
|
+
db: Database name.
|
|
274
|
+
username: User name to use when connecting to the database.
|
|
275
|
+
password: Password for database user.
|
|
276
|
+
login_db: Alternative database name to which initially connect, and create
|
|
277
|
+
the database specified by `db` (PostgreSQL only).
|
|
278
|
+
query: Dictionary of options to be passed to the dialect and/or the
|
|
279
|
+
DBAPI upon connect.
|
|
280
|
+
|
|
281
|
+
Returns:
|
|
282
|
+
URL ready to be used with an SQLAlchemy `Engine` object.
|
|
283
|
+
"""
|
|
284
|
+
from urllib import parse
|
|
285
|
+
|
|
286
|
+
# Users might specify a url in the host
|
|
287
|
+
if host and "://" in host:
|
|
288
|
+
# assumes this is a complete database host name including
|
|
289
|
+
# e.g. `postgres://...`
|
|
290
|
+
return host
|
|
291
|
+
elif host:
|
|
292
|
+
# add fake scheme to properly parse components
|
|
293
|
+
parsed = parse.urlsplit(f"scheme://{host}")
|
|
294
|
+
|
|
295
|
+
# users might include the port in the url
|
|
296
|
+
port = parsed.port or port
|
|
297
|
+
host = parsed.hostname or host
|
|
298
|
+
|
|
299
|
+
if not query:
|
|
300
|
+
# query needs to be set in order to create a URL
|
|
301
|
+
query = {}
|
|
302
|
+
|
|
303
|
+
return sa.engine.url.URL(
|
|
304
|
+
dialect,
|
|
305
|
+
username,
|
|
306
|
+
password,
|
|
307
|
+
host,
|
|
308
|
+
port,
|
|
309
|
+
database=login_db if login_db else db,
|
|
310
|
+
query=query,
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
def _create_database_and_update_engine(self, db: Text, engine_url: "URL") -> None:
|
|
314
|
+
"""Creates database `db` and updates engine accordingly."""
|
|
315
|
+
from sqlalchemy import create_engine
|
|
316
|
+
|
|
317
|
+
if self.engine.dialect.name != "postgresql":
|
|
318
|
+
rasa.shared.utils.io.raise_warning(
|
|
319
|
+
"The parameter 'login_db' can only be used with a postgres database."
|
|
320
|
+
)
|
|
321
|
+
return
|
|
322
|
+
|
|
323
|
+
self._create_database(self.engine, db)
|
|
324
|
+
self.engine.dispose()
|
|
325
|
+
engine_url = sa.engine.url.URL(
|
|
326
|
+
drivername=engine_url.drivername,
|
|
327
|
+
username=engine_url.username,
|
|
328
|
+
password=engine_url.password,
|
|
329
|
+
host=engine_url.host,
|
|
330
|
+
port=engine_url.port,
|
|
331
|
+
database=db,
|
|
332
|
+
query=engine_url.query,
|
|
333
|
+
)
|
|
334
|
+
self.engine = create_engine(engine_url)
|
|
335
|
+
|
|
336
|
+
@staticmethod
|
|
337
|
+
def _create_database(engine: "Engine", database_name: Text) -> None:
|
|
338
|
+
"""Create database `db` on `engine` if it does not exist."""
|
|
339
|
+
import sqlalchemy.exc
|
|
340
|
+
|
|
341
|
+
with engine.connect() as connection:
|
|
342
|
+
connection.execution_options(isolation_level="AUTOCOMMIT")
|
|
343
|
+
matching_rows = connection.execute(
|
|
344
|
+
sa.text(
|
|
345
|
+
f"SELECT 1 FROM pg_catalog.pg_database "
|
|
346
|
+
f"WHERE datname = '{database_name}'"
|
|
347
|
+
)
|
|
348
|
+
).rowcount
|
|
349
|
+
|
|
350
|
+
if not matching_rows:
|
|
351
|
+
try:
|
|
352
|
+
connection.execute(sa.text(f"CREATE DATABASE {database_name}"))
|
|
353
|
+
except (
|
|
354
|
+
sqlalchemy.exc.ProgrammingError,
|
|
355
|
+
sqlalchemy.exc.IntegrityError,
|
|
356
|
+
) as e:
|
|
357
|
+
structlogger.error(
|
|
358
|
+
"sql_tracker_store.create_database_failed",
|
|
359
|
+
event_info=f"Could not create database '{database_name}'",
|
|
360
|
+
exec_info=e,
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
@contextlib.contextmanager
|
|
364
|
+
def session_scope(self) -> Generator["Session", None, None]:
|
|
365
|
+
"""Provide a transactional scope around a series of operations."""
|
|
366
|
+
session = self.sessionmaker()
|
|
367
|
+
try:
|
|
368
|
+
ensure_schema_exists(session)
|
|
369
|
+
yield session
|
|
370
|
+
except ValueError as e:
|
|
371
|
+
rasa.shared.utils.cli.print_error_and_exit(
|
|
372
|
+
f"Requested PostgreSQL schema '{e}' was not found in the database. To "
|
|
373
|
+
f"continue, please create the schema by running 'CREATE DATABASE {e};' "
|
|
374
|
+
f"or unset the '{POSTGRESQL_SCHEMA}' environment variable in order to "
|
|
375
|
+
f"use the default schema. Exiting application."
|
|
376
|
+
)
|
|
377
|
+
finally:
|
|
378
|
+
session.close()
|
|
379
|
+
|
|
380
|
+
async def keys(self) -> Iterable[Text]:
|
|
381
|
+
"""Returns sender_ids of the SQLTrackerStore."""
|
|
382
|
+
with self.session_scope() as session:
|
|
383
|
+
sender_ids = session.query(self.SQLEvent.sender_id).distinct().all()
|
|
384
|
+
return [sender_id for (sender_id,) in sender_ids]
|
|
385
|
+
|
|
386
|
+
async def delete(self, sender_id: Text) -> None:
|
|
387
|
+
"""Delete tracker for the given sender_id."""
|
|
388
|
+
if not await self.exists(sender_id):
|
|
389
|
+
structlogger.info(
|
|
390
|
+
"sql_tracker_store.delete.no_tracker_for_sender_id",
|
|
391
|
+
event_info=f"Could not find tracker for conversation ID '{sender_id}'.",
|
|
392
|
+
)
|
|
393
|
+
return None
|
|
394
|
+
|
|
395
|
+
with self.session_scope() as session:
|
|
396
|
+
statement = sa.delete(self.SQLEvent).where(
|
|
397
|
+
self.SQLEvent.sender_id == sender_id
|
|
398
|
+
)
|
|
399
|
+
result = session.execute(statement)
|
|
400
|
+
session.commit()
|
|
401
|
+
|
|
402
|
+
structlogger.info(
|
|
403
|
+
"sql_tracker_store.delete.deleted_tracker",
|
|
404
|
+
sender_id=sender_id,
|
|
405
|
+
num_rows=result.rowcount,
|
|
406
|
+
)
|
|
407
|
+
|
|
408
|
+
async def retrieve(self, sender_id: Text) -> Optional[DialogueStateTracker]:
|
|
409
|
+
"""Retrieves tracker for the latest conversation session."""
|
|
410
|
+
return await self._retrieve(sender_id, fetch_events_from_all_sessions=False)
|
|
411
|
+
|
|
412
|
+
async def retrieve_full_tracker(
|
|
413
|
+
self, conversation_id: Text
|
|
414
|
+
) -> Optional[DialogueStateTracker]:
|
|
415
|
+
"""Fetching all tracker events across conversation sessions."""
|
|
416
|
+
return await self._retrieve(
|
|
417
|
+
conversation_id, fetch_events_from_all_sessions=True
|
|
418
|
+
)
|
|
419
|
+
|
|
420
|
+
async def count_conversations(self, after_timestamp: float = 0.0) -> int:
|
|
421
|
+
"""Returns the number of conversations that have occurred after a timestamp.
|
|
422
|
+
|
|
423
|
+
By default, this method returns the number of conversations that
|
|
424
|
+
have occurred after the Unix epoch (i.e. timestamp 0).
|
|
425
|
+
"""
|
|
426
|
+
with self.session_scope() as session:
|
|
427
|
+
query = (
|
|
428
|
+
session.query(self.SQLEvent.sender_id)
|
|
429
|
+
.distinct()
|
|
430
|
+
.filter(self.SQLEvent.timestamp >= after_timestamp)
|
|
431
|
+
)
|
|
432
|
+
return query.count()
|
|
433
|
+
|
|
434
|
+
async def _retrieve(
|
|
435
|
+
self, sender_id: Text, fetch_events_from_all_sessions: bool
|
|
436
|
+
) -> Optional[DialogueStateTracker]:
|
|
437
|
+
with self.session_scope() as session:
|
|
438
|
+
serialised_events = self._event_query(
|
|
439
|
+
session,
|
|
440
|
+
sender_id,
|
|
441
|
+
fetch_events_from_all_sessions=fetch_events_from_all_sessions,
|
|
442
|
+
).all()
|
|
443
|
+
|
|
444
|
+
events = [json.loads(event.data) for event in serialised_events]
|
|
445
|
+
|
|
446
|
+
if self.domain and len(events) > 0:
|
|
447
|
+
structlogger.debug(
|
|
448
|
+
"sql_tracker_store.recreating_tracker",
|
|
449
|
+
event_info=f"Recreating tracker from sender id '{sender_id}'",
|
|
450
|
+
)
|
|
451
|
+
return DialogueStateTracker.from_dict(
|
|
452
|
+
sender_id, events, self.domain.slots
|
|
453
|
+
)
|
|
454
|
+
else:
|
|
455
|
+
structlogger.debug(
|
|
456
|
+
"sql_tracker_store._retrieve.no_tracker_for_sender_id",
|
|
457
|
+
event_info=(
|
|
458
|
+
f"Can't retrieve tracker matching "
|
|
459
|
+
f"sender id '{sender_id}' from SQL storage. "
|
|
460
|
+
f"Returning `None` instead.",
|
|
461
|
+
),
|
|
462
|
+
)
|
|
463
|
+
return None
|
|
464
|
+
|
|
465
|
+
def _event_query(
|
|
466
|
+
self, session: "Session", sender_id: Text, fetch_events_from_all_sessions: bool
|
|
467
|
+
) -> "Query":
|
|
468
|
+
"""Provide the query to retrieve the conversation events for a specific sender.
|
|
469
|
+
|
|
470
|
+
The events are ordered by ID to ensure correct sequence of events.
|
|
471
|
+
As `timestamp` is not guaranteed to be unique and low-precision (float), it
|
|
472
|
+
cannot be used to order the events.
|
|
473
|
+
|
|
474
|
+
Args:
|
|
475
|
+
session: Current database session.
|
|
476
|
+
sender_id: Sender id whose conversation events should be retrieved.
|
|
477
|
+
fetch_events_from_all_sessions: Whether to fetch events from all
|
|
478
|
+
conversation sessions. If `False`, only fetch events from the
|
|
479
|
+
latest conversation session.
|
|
480
|
+
|
|
481
|
+
Returns:
|
|
482
|
+
Query to get the conversation events.
|
|
483
|
+
"""
|
|
484
|
+
# Subquery to find the timestamp of the latest `SessionStarted` event
|
|
485
|
+
session_start_sub_query = (
|
|
486
|
+
session.query(sa.func.max(self.SQLEvent.timestamp).label("session_start"))
|
|
487
|
+
.filter(
|
|
488
|
+
self.SQLEvent.sender_id == sender_id,
|
|
489
|
+
self.SQLEvent.type_name == SessionStarted.type_name,
|
|
490
|
+
)
|
|
491
|
+
.subquery()
|
|
492
|
+
)
|
|
493
|
+
|
|
494
|
+
event_query = session.query(self.SQLEvent).filter(
|
|
495
|
+
self.SQLEvent.sender_id == sender_id
|
|
496
|
+
)
|
|
497
|
+
if not fetch_events_from_all_sessions:
|
|
498
|
+
event_query = event_query.filter(
|
|
499
|
+
# Find events after the latest `SessionStarted` event or return all
|
|
500
|
+
# events
|
|
501
|
+
sa.or_(
|
|
502
|
+
self.SQLEvent.timestamp >= session_start_sub_query.c.session_start,
|
|
503
|
+
session_start_sub_query.c.session_start.is_(None),
|
|
504
|
+
)
|
|
505
|
+
)
|
|
506
|
+
|
|
507
|
+
return event_query.order_by(self.SQLEvent.id)
|
|
508
|
+
|
|
509
|
+
async def save(self, tracker: DialogueStateTracker) -> None:
|
|
510
|
+
"""Update database with events from the current conversation."""
|
|
511
|
+
await self.stream_events(tracker)
|
|
512
|
+
|
|
513
|
+
with self.session_scope() as session:
|
|
514
|
+
# only store recent events
|
|
515
|
+
events = self._additional_events(session, tracker)
|
|
516
|
+
|
|
517
|
+
for event in events:
|
|
518
|
+
data = event.as_dict()
|
|
519
|
+
intent = (
|
|
520
|
+
data.get("parse_data", {}).get("intent", {}).get(INTENT_NAME_KEY)
|
|
521
|
+
)
|
|
522
|
+
action = data.get("name")
|
|
523
|
+
timestamp = data.get("timestamp")
|
|
524
|
+
|
|
525
|
+
# noinspection PyArgumentList
|
|
526
|
+
session.add(
|
|
527
|
+
self.SQLEvent(
|
|
528
|
+
sender_id=tracker.sender_id,
|
|
529
|
+
type_name=event.type_name,
|
|
530
|
+
timestamp=timestamp,
|
|
531
|
+
intent_name=intent,
|
|
532
|
+
action_name=action,
|
|
533
|
+
data=json.dumps(data),
|
|
534
|
+
)
|
|
535
|
+
)
|
|
536
|
+
session.commit()
|
|
537
|
+
|
|
538
|
+
structlogger.debug(
|
|
539
|
+
"sql_tracker_store.save_tracker",
|
|
540
|
+
event_info=(
|
|
541
|
+
f"Tracker with sender_id '{tracker.sender_id}' stored to database",
|
|
542
|
+
),
|
|
543
|
+
)
|
|
544
|
+
|
|
545
|
+
def _additional_events(
|
|
546
|
+
self, session: "Session", tracker: DialogueStateTracker
|
|
547
|
+
) -> Iterator:
|
|
548
|
+
"""Return events from the tracker which aren't currently stored."""
|
|
549
|
+
number_of_events_since_last_session = self._event_query(
|
|
550
|
+
session, tracker.sender_id, fetch_events_from_all_sessions=False
|
|
551
|
+
).count()
|
|
552
|
+
|
|
553
|
+
return itertools.islice(
|
|
554
|
+
tracker.events, number_of_events_since_last_session, len(tracker.events)
|
|
555
|
+
)
|