rasa-pro 3.12.22__py3-none-any.whl → 3.13.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of rasa-pro might be problematic. Click here for more details.
- rasa/__main__.py +3 -4
- rasa/api.py +1 -1
- rasa/cli/dialogue_understanding_test.py +1 -1
- rasa/cli/e2e_test.py +1 -8
- rasa/cli/evaluate.py +2 -2
- rasa/cli/export.py +5 -3
- rasa/cli/inspect.py +7 -0
- rasa/cli/llm_fine_tuning.py +1 -1
- rasa/cli/project_templates/default/config.yml +5 -32
- rasa/cli/project_templates/{calm → default}/e2e_tests/cancelations/user_cancels_during_a_correction.yml +1 -1
- rasa/cli/project_templates/{calm → default}/e2e_tests/cancelations/user_changes_mind_on_a_whim.yml +1 -1
- rasa/cli/project_templates/{calm → default}/e2e_tests/corrections/user_corrects_contact_handle.yml +1 -1
- rasa/cli/project_templates/{calm → default}/e2e_tests/corrections/user_corrects_contact_name.yml +1 -1
- rasa/cli/project_templates/{calm → default}/e2e_tests/happy_paths/user_adds_contact_to_their_list.yml +1 -1
- rasa/cli/project_templates/{calm → default}/e2e_tests/happy_paths/user_lists_contacts.yml +1 -1
- rasa/cli/project_templates/{calm → default}/e2e_tests/happy_paths/user_removes_contact.yml +1 -1
- rasa/cli/project_templates/{calm → default}/e2e_tests/happy_paths/user_removes_contact_from_list.yml +1 -1
- rasa/cli/project_templates/default/endpoints.yml +18 -2
- rasa/cli/project_templates/defaults.py +133 -0
- rasa/cli/project_templates/tutorial/config.yml +1 -1
- rasa/cli/project_templates/tutorial/endpoints.yml +1 -1
- rasa/cli/run.py +1 -1
- rasa/cli/scaffold.py +2 -3
- rasa/cli/shell.py +6 -1
- rasa/cli/studio/download.py +0 -22
- rasa/cli/studio/link.py +36 -0
- rasa/cli/studio/pull.py +79 -0
- rasa/cli/studio/push.py +78 -0
- rasa/cli/studio/studio.py +12 -0
- rasa/cli/studio/train.py +1 -5
- rasa/cli/studio/upload.py +6 -4
- rasa/cli/train.py +5 -1
- rasa/cli/utils.py +1 -1
- rasa/cli/x.py +1 -1
- rasa/constants.py +2 -0
- rasa/core/__init__.py +0 -16
- rasa/core/actions/action.py +43 -29
- rasa/core/actions/action_repeat_bot_messages.py +18 -22
- rasa/core/actions/action_run_slot_rejections.py +1 -2
- rasa/core/agent.py +24 -3
- rasa/core/available_endpoints.py +146 -0
- rasa/core/brokers/kafka.py +4 -0
- rasa/core/brokers/pika.py +5 -2
- rasa/core/brokers/sql.py +1 -1
- rasa/core/channels/__init__.py +3 -0
- rasa/core/channels/botframework.py +2 -2
- rasa/core/channels/channel.py +2 -2
- rasa/core/channels/development_inspector.py +1 -1
- rasa/core/channels/facebook.py +1 -4
- rasa/core/channels/hangouts.py +8 -5
- rasa/core/channels/inspector/.eslintrc.cjs +12 -6
- rasa/core/channels/inspector/.prettierrc +5 -0
- rasa/core/channels/inspector/README.md +11 -5
- rasa/core/channels/inspector/dist/assets/{arc-9f75cc3b.js → arc-371401b1.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{blockDiagram-38ab4fdb-7f34db23.js → blockDiagram-38ab4fdb-3f126156.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{c4Diagram-3d4e48cf-948bab2c.js → c4Diagram-3d4e48cf-12f22eb7.js} +1 -1
- rasa/core/channels/inspector/dist/assets/channel-f1efda17.js +1 -0
- rasa/core/channels/inspector/dist/assets/{classDiagram-70f12bd4-53b0dd0e.js → classDiagram-70f12bd4-03b1d386.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{classDiagram-v2-f2320105-fdf789e7.js → classDiagram-v2-f2320105-84f69d63.js} +1 -1
- rasa/core/channels/inspector/dist/assets/clone-fdf164e2.js +1 -0
- rasa/core/channels/inspector/dist/assets/{createText-2e5e7dd3-87c4ece5.js → createText-2e5e7dd3-ca47fd38.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{edges-e0da2a9e-5a8b0749.js → edges-e0da2a9e-f837ca8a.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{erDiagram-9861fffd-66da90e2.js → erDiagram-9861fffd-8717ac54.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{flowDb-956e92f1-10044f05.js → flowDb-956e92f1-94f38b83.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{flowDiagram-66a62f08-f338f66a.js → flowDiagram-66a62f08-b616f9fb.js} +1 -1
- rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-7d7a1629.js +1 -0
- rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-4a651766-b13140aa.js → flowchart-elk-definition-4a651766-f5d24bb8.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{ganttDiagram-c361ad54-f2b4a55a.js → ganttDiagram-c361ad54-b43ba8d9.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-72cf32ee-dedc298d.js → gitGraphDiagram-72cf32ee-c3aafaa5.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{graph-4ede11ff.js → graph-0d0a2c10.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{index-3862675e-65549d37.js → index-3862675e-58ea0305.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{index-3a23e736.js → index-cce6f8a1.js} +123 -123
- rasa/core/channels/inspector/dist/assets/{infoDiagram-f8f76790-65439671.js → infoDiagram-f8f76790-b8f60461.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{journeyDiagram-49397b02-56d03d98.js → journeyDiagram-49397b02-95be5545.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{layout-dd48f7f4.js → layout-da885b9b.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{line-1569ad2c.js → line-f1c817d3.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{linear-48bf4935.js → linear-d42801e6.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{mindmap-definition-fc14e90a-688504c1.js → mindmap-definition-fc14e90a-a38923a6.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{pieDiagram-8a3498a8-78b6d7e6.js → pieDiagram-8a3498a8-ca6e71e9.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{quadrantDiagram-120e2f19-048b84b3.js → quadrantDiagram-120e2f19-b290dae9.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{requirementDiagram-deff3bca-dd67f107.js → requirementDiagram-deff3bca-03f02ceb.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{sankeyDiagram-04a897e0-8128436e.js → sankeyDiagram-04a897e0-c49eee40.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{sequenceDiagram-704730f1-1a0d1461.js → sequenceDiagram-704730f1-b2cd6a3d.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{stateDiagram-587899a1-46d388ed.js → stateDiagram-587899a1-e53a2028.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-d93cdb3a-ea42951a.js → stateDiagram-v2-d93cdb3a-e1982a03.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-6aaf32cf-7427ed0c.js → styles-6aaf32cf-d0226ca5.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-9a916d00-ff5e5a16.js → styles-9a916d00-0e21dc00.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-c10674c1-7b3680cf.js → styles-c10674c1-9588494e.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{svgDrawCommon-08f97a94-f860f2ad.js → svgDrawCommon-08f97a94-be478d4f.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{timeline-definition-85554ec2-2eebf0c8.js → timeline-definition-85554ec2-74631749.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{xychartDiagram-e933f94c-5d7f4e96.js → xychartDiagram-e933f94c-a043552f.js} +1 -1
- rasa/core/channels/inspector/dist/index.html +1 -1
- rasa/core/channels/inspector/package.json +3 -1
- rasa/core/channels/inspector/src/App.tsx +91 -90
- rasa/core/channels/inspector/src/components/Chat.tsx +45 -41
- rasa/core/channels/inspector/src/components/DiagramFlow.tsx +40 -40
- rasa/core/channels/inspector/src/components/DialogueInformation.tsx +57 -57
- rasa/core/channels/inspector/src/components/DialogueStack.tsx +36 -27
- rasa/core/channels/inspector/src/components/ExpandIcon.tsx +4 -4
- rasa/core/channels/inspector/src/components/FullscreenButton.tsx +7 -7
- rasa/core/channels/inspector/src/components/LoadingSpinner.tsx +28 -12
- rasa/core/channels/inspector/src/components/NoActiveFlow.tsx +9 -9
- rasa/core/channels/inspector/src/components/RasaLogo.tsx +5 -5
- rasa/core/channels/inspector/src/components/RecruitmentPanel.tsx +55 -60
- rasa/core/channels/inspector/src/components/SaraDiagrams.tsx +5 -5
- rasa/core/channels/inspector/src/components/Slots.tsx +22 -22
- rasa/core/channels/inspector/src/components/Welcome.tsx +28 -31
- rasa/core/channels/inspector/src/helpers/audio/audiostream.ts +245 -0
- rasa/core/channels/inspector/src/helpers/audio/microphone-processor.js +12 -0
- rasa/core/channels/inspector/src/helpers/audio/playback-processor.js +36 -0
- rasa/core/channels/inspector/src/helpers/conversation.ts +7 -7
- rasa/core/channels/inspector/src/helpers/formatters.test.ts +181 -181
- rasa/core/channels/inspector/src/helpers/formatters.ts +111 -111
- rasa/core/channels/inspector/src/helpers/utils.ts +78 -61
- rasa/core/channels/inspector/src/main.tsx +8 -8
- rasa/core/channels/inspector/src/theme/Button/Button.ts +8 -8
- rasa/core/channels/inspector/src/theme/Heading/Heading.ts +7 -7
- rasa/core/channels/inspector/src/theme/Input/Input.ts +9 -9
- rasa/core/channels/inspector/src/theme/Link/Link.ts +6 -6
- rasa/core/channels/inspector/src/theme/Modal/Modal.ts +13 -13
- rasa/core/channels/inspector/src/theme/Table/Table.tsx +10 -10
- rasa/core/channels/inspector/src/theme/Tooltip/Tooltip.ts +5 -5
- rasa/core/channels/inspector/src/theme/base/breakpoints.ts +7 -7
- rasa/core/channels/inspector/src/theme/base/colors.ts +64 -64
- rasa/core/channels/inspector/src/theme/base/fonts/fontFaces.css +21 -18
- rasa/core/channels/inspector/src/theme/base/radii.ts +8 -8
- rasa/core/channels/inspector/src/theme/base/shadows.ts +5 -5
- rasa/core/channels/inspector/src/theme/base/sizes.ts +5 -5
- rasa/core/channels/inspector/src/theme/base/space.ts +12 -12
- rasa/core/channels/inspector/src/theme/base/styles.ts +5 -5
- rasa/core/channels/inspector/src/theme/base/typography.ts +12 -12
- rasa/core/channels/inspector/src/theme/base/zIndices.ts +3 -3
- rasa/core/channels/inspector/src/theme/index.ts +38 -38
- rasa/core/channels/inspector/src/types.ts +56 -50
- rasa/core/channels/inspector/yarn.lock +5 -0
- rasa/core/channels/mattermost.py +1 -1
- rasa/core/channels/rasa_chat.py +2 -4
- rasa/core/channels/rest.py +5 -4
- rasa/core/channels/socketio.py +56 -41
- rasa/core/channels/studio_chat.py +329 -68
- rasa/core/channels/vier_cvg.py +1 -2
- rasa/core/channels/voice_ready/audiocodes.py +4 -11
- rasa/core/channels/voice_ready/jambonz.py +5 -6
- rasa/core/channels/voice_ready/twilio_voice.py +13 -12
- rasa/core/channels/voice_ready/utils.py +22 -0
- rasa/core/channels/voice_stream/audiocodes.py +13 -16
- rasa/core/channels/voice_stream/browser_audio.py +1 -1
- rasa/core/channels/voice_stream/genesys.py +37 -18
- rasa/core/channels/voice_stream/jambonz.py +232 -0
- rasa/core/channels/voice_stream/tts/__init__.py +8 -0
- rasa/core/channels/voice_stream/twilio_media_streams.py +15 -12
- rasa/core/channels/voice_stream/voice_channel.py +71 -27
- rasa/core/concurrent_lock_store.py +24 -10
- rasa/core/evaluation/marker_tracker_loader.py +1 -1
- rasa/core/exporter.py +37 -1
- rasa/core/http_interpreter.py +3 -7
- rasa/core/information_retrieval/faiss.py +18 -11
- rasa/core/information_retrieval/ingestion/faq_parser.py +158 -0
- rasa/core/jobs.py +2 -1
- rasa/core/lock_store.py +151 -60
- rasa/core/nlg/contextual_response_rephraser.py +17 -7
- rasa/core/nlg/generator.py +5 -22
- rasa/core/nlg/interpolator.py +2 -3
- rasa/core/nlg/response.py +6 -43
- rasa/core/nlg/summarize.py +1 -1
- rasa/core/nlg/translate.py +0 -8
- rasa/core/policies/enterprise_search_policy.py +305 -189
- rasa/core/policies/enterprise_search_policy_config.py +241 -0
- rasa/core/policies/enterprise_search_prompt_with_relevancy_check_and_citation_template.jinja2 +67 -0
- rasa/core/policies/flow_policy.py +1 -1
- rasa/core/policies/flows/flow_executor.py +102 -17
- rasa/core/policies/intentless_policy.py +56 -17
- rasa/core/processor.py +70 -49
- rasa/core/run.py +33 -11
- rasa/core/tracker_stores/__init__.py +0 -0
- rasa/core/{auth_retry_tracker_store.py → tracker_stores/auth_retry_tracker_store.py} +66 -1
- rasa/core/tracker_stores/dynamo_tracker_store.py +256 -0
- rasa/core/tracker_stores/mongo_tracker_store.py +223 -0
- rasa/core/tracker_stores/redis_tracker_store.py +252 -0
- rasa/core/tracker_stores/sql_tracker_store.py +582 -0
- rasa/core/tracker_stores/tracker_store.py +839 -0
- rasa/core/training/interactive.py +1 -1
- rasa/core/utils.py +24 -95
- rasa/dialogue_understanding/coexistence/intent_based_router.py +2 -1
- rasa/dialogue_understanding/coexistence/llm_based_router.py +13 -11
- rasa/dialogue_understanding/commands/can_not_handle_command.py +2 -0
- rasa/dialogue_understanding/commands/cancel_flow_command.py +3 -1
- rasa/dialogue_understanding/commands/chit_chat_answer_command.py +2 -0
- rasa/dialogue_understanding/commands/clarify_command.py +6 -2
- rasa/dialogue_understanding/commands/command_syntax_manager.py +1 -0
- rasa/dialogue_understanding/commands/correct_slots_command.py +5 -6
- rasa/dialogue_understanding/commands/error_command.py +1 -1
- rasa/dialogue_understanding/commands/human_handoff_command.py +3 -3
- rasa/dialogue_understanding/commands/knowledge_answer_command.py +2 -0
- rasa/dialogue_understanding/commands/repeat_bot_messages_command.py +2 -0
- rasa/dialogue_understanding/commands/set_slot_command.py +8 -4
- rasa/dialogue_understanding/commands/skip_question_command.py +3 -3
- rasa/dialogue_understanding/commands/start_flow_command.py +7 -3
- rasa/dialogue_understanding/generator/__init__.py +7 -1
- rasa/dialogue_understanding/generator/command_generator.py +4 -2
- rasa/dialogue_understanding/generator/command_parser.py +2 -2
- rasa/dialogue_understanding/generator/command_parser_validator.py +63 -0
- rasa/dialogue_understanding/generator/llm_based_command_generator.py +1 -2
- rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +3 -2
- rasa/dialogue_understanding/generator/nlu_command_adapter.py +2 -2
- rasa/dialogue_understanding/generator/prompt_templates/command_prompt_template.jinja2 +0 -2
- rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_claude_3_5_sonnet_20240620_template.jinja2 +1 -0
- rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_gpt_4o_2024_11_20_template.jinja2 +1 -0
- rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v3_claude_3_5_sonnet_20240620_template.jinja2 +79 -0
- rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v3_gpt_4o_2024_11_20_template.jinja2 +79 -0
- rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +26 -461
- rasa/dialogue_understanding/generator/single_step/search_ready_llm_command_generator.py +147 -0
- rasa/dialogue_understanding/generator/single_step/single_step_based_llm_command_generator.py +461 -0
- rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +20 -64
- rasa/dialogue_understanding/patterns/cancel.py +1 -2
- rasa/dialogue_understanding/patterns/clarify.py +1 -1
- rasa/dialogue_understanding/patterns/correction.py +2 -2
- rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +42 -27
- rasa/dialogue_understanding/patterns/domain_for_patterns.py +190 -0
- rasa/dialogue_understanding/processor/command_processor.py +6 -7
- rasa/dialogue_understanding_test/command_metric_calculation.py +7 -40
- rasa/dialogue_understanding_test/command_metrics.py +38 -0
- rasa/dialogue_understanding_test/du_test_case.py +58 -25
- rasa/dialogue_understanding_test/du_test_result.py +228 -132
- rasa/dialogue_understanding_test/du_test_runner.py +11 -2
- rasa/dialogue_understanding_test/du_test_schema.yml +3 -3
- rasa/dialogue_understanding_test/io.py +35 -8
- rasa/e2e_test/constants.py +1 -1
- rasa/e2e_test/e2e_test_runner.py +1 -1
- rasa/e2e_test/e2e_test_schema.yml +3 -3
- rasa/engine/constants.py +1 -1
- rasa/engine/graph.py +2 -2
- rasa/engine/recipes/default_recipe.py +1 -1
- rasa/engine/validation.py +3 -2
- rasa/hooks.py +2 -30
- rasa/llm_fine_tuning/paraphrasing/conversation_rephraser.py +2 -6
- rasa/model_manager/model_api.py +89 -1
- rasa/model_manager/runner_service.py +20 -4
- rasa/model_manager/socket_bridge.py +0 -7
- rasa/model_manager/trainer_service.py +10 -4
- rasa/plugin.py +2 -15
- rasa/privacy/__init__.py +0 -0
- rasa/privacy/constants.py +83 -0
- rasa/privacy/event_broker_utils.py +77 -0
- rasa/privacy/privacy_config.py +281 -0
- rasa/privacy/privacy_config_schema.json +86 -0
- rasa/privacy/privacy_filter.py +393 -0
- rasa/privacy/privacy_manager.py +594 -0
- rasa/server.py +23 -2
- rasa/shared/constants.py +17 -0
- rasa/shared/core/command_payload_reader.py +1 -5
- rasa/shared/core/constants.py +4 -3
- rasa/shared/core/domain.py +172 -11
- rasa/shared/core/events.py +100 -6
- rasa/shared/core/flows/flow.py +30 -5
- rasa/shared/core/flows/flow_step.py +19 -3
- rasa/shared/core/flows/flow_step_links.py +15 -0
- rasa/shared/core/flows/flow_step_sequence.py +6 -0
- rasa/shared/core/flows/flows_yaml_schema.json +3 -0
- rasa/shared/core/flows/nlu_trigger.py +13 -0
- rasa/shared/core/flows/steps/action.py +7 -4
- rasa/shared/core/flows/steps/call.py +11 -4
- rasa/shared/core/flows/steps/collect.py +71 -6
- rasa/shared/core/flows/steps/internal.py +6 -1
- rasa/shared/core/flows/steps/link.py +7 -4
- rasa/shared/core/flows/steps/no_operation.py +7 -4
- rasa/shared/core/flows/steps/set_slots.py +8 -4
- rasa/shared/core/flows/validation.py +25 -5
- rasa/shared/core/flows/yaml_flows_io.py +106 -5
- rasa/shared/core/slots.py +29 -1
- rasa/shared/core/trackers.py +21 -10
- rasa/shared/core/training_data/story_reader/yaml_story_reader.py +1 -4
- rasa/shared/importers/importer.py +8 -0
- rasa/shared/providers/_configs/azure_openai_client_config.py +2 -2
- rasa/shared/providers/_configs/default_litellm_client_config.py +1 -1
- rasa/shared/providers/_configs/huggingface_local_embedding_client_config.py +1 -1
- rasa/shared/providers/_configs/openai_client_config.py +1 -1
- rasa/shared/providers/_configs/rasa_llm_client_config.py +1 -1
- rasa/shared/providers/_configs/self_hosted_llm_client_config.py +1 -1
- rasa/shared/providers/_configs/utils.py +0 -99
- rasa/shared/providers/llm/default_litellm_llm_client.py +2 -2
- rasa/shared/utils/common.py +43 -1
- rasa/shared/utils/configs.py +110 -0
- rasa/shared/utils/constants.py +0 -3
- rasa/shared/utils/llm.py +245 -8
- rasa/shared/utils/pykwalify_extensions.py +0 -9
- rasa/shared/utils/yaml.py +32 -0
- rasa/studio/constants.py +1 -0
- rasa/studio/data_handler.py +33 -12
- rasa/studio/download.py +117 -435
- rasa/studio/link.py +211 -0
- rasa/studio/prompts.py +221 -0
- rasa/studio/pull/__init__.py +0 -0
- rasa/studio/pull/data.py +222 -0
- rasa/studio/pull/domains.py +60 -0
- rasa/studio/pull/pull.py +239 -0
- rasa/studio/push.py +138 -0
- rasa/studio/results_logger.py +6 -1
- rasa/studio/train.py +1 -1
- rasa/studio/upload.py +243 -72
- rasa/studio/utils.py +33 -0
- rasa/telemetry.py +83 -26
- rasa/tracing/config.py +4 -5
- rasa/tracing/constants.py +19 -1
- rasa/tracing/instrumentation/attribute_extractors.py +68 -16
- rasa/tracing/instrumentation/instrumentation.py +54 -3
- rasa/tracing/instrumentation/metrics.py +98 -15
- rasa/tracing/metric_instrument_provider.py +75 -3
- rasa/utils/common.py +43 -22
- rasa/utils/endpoints.py +22 -1
- rasa/utils/licensing.py +2 -3
- rasa/utils/log_utils.py +1 -45
- rasa/validator.py +2 -8
- rasa/version.py +1 -1
- {rasa_pro-3.12.22.dist-info → rasa_pro-3.13.0.dist-info}/METADATA +11 -12
- {rasa_pro-3.12.22.dist-info → rasa_pro-3.13.0.dist-info}/RECORD +333 -309
- rasa/anonymization/__init__.py +0 -2
- rasa/anonymization/anonymisation_rule_yaml_reader.py +0 -91
- rasa/anonymization/anonymization_pipeline.py +0 -286
- rasa/anonymization/anonymization_rule_executor.py +0 -266
- rasa/anonymization/anonymization_rule_orchestrator.py +0 -119
- rasa/anonymization/schemas/config.yml +0 -47
- rasa/anonymization/utils.py +0 -118
- rasa/cli/project_templates/calm/config.yml +0 -10
- rasa/cli/project_templates/calm/credentials.yml +0 -33
- rasa/cli/project_templates/calm/endpoints.yml +0 -58
- rasa/cli/project_templates/default/actions/actions.py +0 -27
- rasa/cli/project_templates/default/data/nlu.yml +0 -91
- rasa/cli/project_templates/default/data/rules.yml +0 -13
- rasa/cli/project_templates/default/data/stories.yml +0 -30
- rasa/cli/project_templates/default/domain.yml +0 -34
- rasa/cli/project_templates/default/tests/test_stories.yml +0 -91
- rasa/core/channels/inspector/dist/assets/channel-dfa68278.js +0 -1
- rasa/core/channels/inspector/dist/assets/clone-edb7f119.js +0 -1
- rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-65e7c670.js +0 -1
- rasa/core/channels/inspector/src/helpers/audiostream.ts +0 -191
- rasa/core/tracker_store.py +0 -1792
- /rasa/cli/project_templates/{calm → default}/actions/action_template.py +0 -0
- /rasa/cli/project_templates/{calm → default}/actions/add_contact.py +0 -0
- /rasa/cli/project_templates/{calm → default}/actions/db.py +0 -0
- /rasa/cli/project_templates/{calm → default}/actions/list_contacts.py +0 -0
- /rasa/cli/project_templates/{calm → default}/actions/remove_contact.py +0 -0
- /rasa/cli/project_templates/{calm → default}/data/flows/add_contact.yml +0 -0
- /rasa/cli/project_templates/{calm → default}/data/flows/list_contacts.yml +0 -0
- /rasa/cli/project_templates/{calm → default}/data/flows/remove_contact.yml +0 -0
- /rasa/cli/project_templates/{calm → default}/db/contacts.json +0 -0
- /rasa/cli/project_templates/{calm → default}/domain/add_contact.yml +0 -0
- /rasa/cli/project_templates/{calm → default}/domain/list_contacts.yml +0 -0
- /rasa/cli/project_templates/{calm → default}/domain/remove_contact.yml +0 -0
- /rasa/cli/project_templates/{calm → default}/domain/shared.yml +0 -0
- /rasa/{cli/project_templates/calm/actions → core/information_retrieval/ingestion}/__init__.py +0 -0
- {rasa_pro-3.12.22.dist-info → rasa_pro-3.13.0.dist-info}/NOTICE +0 -0
- {rasa_pro-3.12.22.dist-info → rasa_pro-3.13.0.dist-info}/WHEEL +0 -0
- {rasa_pro-3.12.22.dist-info → rasa_pro-3.13.0.dist-info}/entry_points.txt +0 -0
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
import audioop
|
|
3
|
+
import base64
|
|
2
4
|
import json
|
|
5
|
+
import uuid
|
|
3
6
|
from functools import partial
|
|
4
7
|
from typing import (
|
|
5
8
|
TYPE_CHECKING,
|
|
@@ -10,13 +13,25 @@ from typing import (
|
|
|
10
13
|
List,
|
|
11
14
|
Optional,
|
|
12
15
|
Text,
|
|
16
|
+
Tuple,
|
|
13
17
|
)
|
|
14
18
|
|
|
15
19
|
import structlog
|
|
16
|
-
from sanic import Sanic
|
|
17
20
|
|
|
21
|
+
from rasa.core.channels import UserMessage
|
|
18
22
|
from rasa.core.channels.socketio import SocketBlueprint, SocketIOInput
|
|
19
|
-
from rasa.core.
|
|
23
|
+
from rasa.core.channels.voice_ready.utils import CallParameters
|
|
24
|
+
from rasa.core.channels.voice_stream.audio_bytes import RasaAudioBytes
|
|
25
|
+
from rasa.core.channels.voice_stream.call_state import call_state
|
|
26
|
+
from rasa.core.channels.voice_stream.tts import TTSEngine
|
|
27
|
+
from rasa.core.channels.voice_stream.voice_channel import (
|
|
28
|
+
ContinueConversationAction,
|
|
29
|
+
EndConversationAction,
|
|
30
|
+
NewAudioAction,
|
|
31
|
+
VoiceChannelAction,
|
|
32
|
+
VoiceInputChannel,
|
|
33
|
+
VoiceOutputChannel,
|
|
34
|
+
)
|
|
20
35
|
from rasa.hooks import hookimpl
|
|
21
36
|
from rasa.plugin import plugin_manager
|
|
22
37
|
from rasa.shared.core.constants import ACTION_LISTEN_NAME
|
|
@@ -24,7 +39,10 @@ from rasa.shared.core.events import ActionExecuted
|
|
|
24
39
|
from rasa.shared.core.trackers import EventVerbosity
|
|
25
40
|
|
|
26
41
|
if TYPE_CHECKING:
|
|
27
|
-
from
|
|
42
|
+
from sanic import Sanic, Websocket # type: ignore[attr-defined]
|
|
43
|
+
from socketio import AsyncServer
|
|
44
|
+
|
|
45
|
+
from rasa.core.channels.channel import InputChannel, UserMessage
|
|
28
46
|
from rasa.shared.core.trackers import DialogueStateTracker
|
|
29
47
|
|
|
30
48
|
|
|
@@ -84,7 +102,9 @@ class StudioTrackerUpdatePlugin:
|
|
|
84
102
|
|
|
85
103
|
def handle_tracker_update(self, tracker: "DialogueStateTracker") -> None:
|
|
86
104
|
"""Handles a tracker update when triggered by a hook."""
|
|
87
|
-
structlogger.info(
|
|
105
|
+
structlogger.info(
|
|
106
|
+
"studio_chat.after_tracker_update", sender_id=tracker.sender_id
|
|
107
|
+
)
|
|
88
108
|
# directly create a dump to avoid the tracker getting modified by another
|
|
89
109
|
# function before it gets published (since the publishing is scheduled
|
|
90
110
|
# as an async task)
|
|
@@ -101,32 +121,83 @@ class StudioTrackerUpdatePlugin:
|
|
|
101
121
|
self._cancel_tasks()
|
|
102
122
|
|
|
103
123
|
|
|
104
|
-
class StudioChatInput(SocketIOInput):
|
|
124
|
+
class StudioChatInput(SocketIOInput, VoiceInputChannel):
|
|
105
125
|
"""Input channel for the communication between Rasa Studio and Rasa Pro."""
|
|
106
126
|
|
|
127
|
+
requires_voice_license = False
|
|
128
|
+
|
|
107
129
|
@classmethod
|
|
108
130
|
def name(cls) -> Text:
|
|
109
131
|
return "studio_chat"
|
|
110
132
|
|
|
111
133
|
def __init__(
|
|
112
134
|
self,
|
|
113
|
-
|
|
114
|
-
|
|
135
|
+
server_url: str,
|
|
136
|
+
asr_config: Dict,
|
|
137
|
+
tts_config: Dict,
|
|
138
|
+
user_message_evt: Text = "user_uttered",
|
|
139
|
+
bot_message_evt: Text = "bot_uttered",
|
|
140
|
+
namespace: Optional[Text] = None,
|
|
141
|
+
session_persistence: bool = False,
|
|
142
|
+
socketio_path: Optional[Text] = "/socket.io",
|
|
143
|
+
jwt_key: Optional[Text] = None,
|
|
144
|
+
jwt_method: Optional[Text] = "HS256",
|
|
145
|
+
metadata_key: Optional[Text] = "metadata",
|
|
115
146
|
) -> None:
|
|
116
|
-
"""Creates a
|
|
147
|
+
"""Creates a `StudioChatInput` object."""
|
|
117
148
|
from rasa.core.agent import Agent
|
|
118
149
|
|
|
119
|
-
super().__init__(*args, **kwargs)
|
|
120
150
|
self.agent: Optional[Agent] = None
|
|
121
151
|
|
|
152
|
+
# Initialize the SocketIO input channel
|
|
153
|
+
SocketIOInput.__init__(
|
|
154
|
+
self,
|
|
155
|
+
user_message_evt=user_message_evt,
|
|
156
|
+
bot_message_evt=bot_message_evt,
|
|
157
|
+
namespace=namespace,
|
|
158
|
+
session_persistence=session_persistence,
|
|
159
|
+
socketio_path=socketio_path,
|
|
160
|
+
jwt_key=jwt_key,
|
|
161
|
+
jwt_method=jwt_method,
|
|
162
|
+
metadata_key=metadata_key,
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
# Initialize the Voice Input Channel
|
|
166
|
+
VoiceInputChannel.__init__(
|
|
167
|
+
self,
|
|
168
|
+
server_url=server_url,
|
|
169
|
+
asr_config=asr_config,
|
|
170
|
+
tts_config=tts_config,
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
# Dictionaries to manage active connections and background tasks
|
|
174
|
+
# `active_connections` holds the active voice sessions
|
|
175
|
+
# `background_tasks` holds the asyncio tasks for voice streaming
|
|
176
|
+
self.active_connections: Dict[str, SocketIOVoiceWebsocketAdapter] = {}
|
|
177
|
+
self.background_tasks: Dict[str, asyncio.Task] = {}
|
|
178
|
+
|
|
122
179
|
self._register_tracker_update_hook()
|
|
123
180
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
181
|
+
@classmethod
|
|
182
|
+
def from_credentials(cls, credentials: Optional[Dict[Text, Any]]) -> "InputChannel":
|
|
183
|
+
"""Creates a StudioChatInput channel from credentials."""
|
|
184
|
+
credentials = credentials or {}
|
|
185
|
+
|
|
186
|
+
return cls(
|
|
187
|
+
# Voice specific parameters
|
|
188
|
+
server_url=credentials.get("server_url", ""),
|
|
189
|
+
asr_config=credentials.get("asr", {}),
|
|
190
|
+
tts_config=credentials.get("tts", {}),
|
|
191
|
+
# SocketIO parameters
|
|
192
|
+
user_message_evt=credentials.get("user_message_evt", "user_uttered"),
|
|
193
|
+
bot_message_evt=credentials.get("bot_message_evt", "bot_uttered"),
|
|
194
|
+
namespace=credentials.get("namespace"),
|
|
195
|
+
session_persistence=credentials.get("session_persistence", False),
|
|
196
|
+
socketio_path=credentials.get("socketio_path", "/socket.io"),
|
|
197
|
+
jwt_key=credentials.get("jwt_key"),
|
|
198
|
+
jwt_method=credentials.get("jwt_method", "HS256"),
|
|
199
|
+
metadata_key=credentials.get("metadata_key", "metadata"),
|
|
200
|
+
)
|
|
130
201
|
|
|
131
202
|
def _register_tracker_update_hook(self) -> None:
|
|
132
203
|
plugin_manager().register(StudioTrackerUpdatePlugin(self))
|
|
@@ -137,7 +208,10 @@ class StudioChatInput(SocketIOInput):
|
|
|
137
208
|
|
|
138
209
|
async def publish_tracker_update(self, sender_id: str, tracker_dump: Dict) -> None:
|
|
139
210
|
"""Publishes a tracker update notification to the websocket."""
|
|
140
|
-
|
|
211
|
+
if not self.sio:
|
|
212
|
+
structlogger.error("studio_chat.on_tracker_updated.sio_not_initialized")
|
|
213
|
+
return
|
|
214
|
+
await self.sio.emit("tracker", tracker_dump, room=sender_id)
|
|
141
215
|
|
|
142
216
|
async def on_message_proxy(
|
|
143
217
|
self,
|
|
@@ -150,15 +224,8 @@ class StudioChatInput(SocketIOInput):
|
|
|
150
224
|
"""
|
|
151
225
|
await on_new_message(message)
|
|
152
226
|
|
|
153
|
-
if not self.agent
|
|
227
|
+
if not self.agent:
|
|
154
228
|
structlogger.error("studio_chat.on_message_proxy.agent_not_initialized")
|
|
155
|
-
await self.emit_error(
|
|
156
|
-
"The Rasa Pro model could not be loaded. "
|
|
157
|
-
"Please check the training and deployment logs "
|
|
158
|
-
"for more information.",
|
|
159
|
-
message.sender_id,
|
|
160
|
-
AgentNotReady("The Rasa Pro model could not be loaded."),
|
|
161
|
-
)
|
|
162
229
|
return
|
|
163
230
|
|
|
164
231
|
tracker = await self.agent.tracker_store.retrieve(message.sender_id)
|
|
@@ -168,17 +235,6 @@ class StudioChatInput(SocketIOInput):
|
|
|
168
235
|
|
|
169
236
|
await self.on_tracker_updated(tracker)
|
|
170
237
|
|
|
171
|
-
async def emit_error(self, message: str, room: str, e: Exception) -> None:
|
|
172
|
-
await self.emit(
|
|
173
|
-
"error",
|
|
174
|
-
{
|
|
175
|
-
"message": message,
|
|
176
|
-
"error": str(e),
|
|
177
|
-
"exception": str(type(e).__name__),
|
|
178
|
-
},
|
|
179
|
-
room=room,
|
|
180
|
-
)
|
|
181
|
-
|
|
182
238
|
async def handle_tracker_update(self, sid: str, data: Dict) -> None:
|
|
183
239
|
from rasa.shared.core.trackers import DialogueStateTracker
|
|
184
240
|
|
|
@@ -195,44 +251,134 @@ class StudioChatInput(SocketIOInput):
|
|
|
195
251
|
structlogger.error("studio_chat.sio.domain_not_initialized")
|
|
196
252
|
return None
|
|
197
253
|
|
|
198
|
-
tracker: Optional[DialogueStateTracker] = None
|
|
199
|
-
|
|
200
254
|
async with self.agent.lock_store.lock(data["sender_id"]):
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
)
|
|
255
|
+
tracker = DialogueStateTracker.from_dict(
|
|
256
|
+
data["sender_id"], data["events"], domain.slots
|
|
257
|
+
)
|
|
205
258
|
|
|
206
|
-
|
|
259
|
+
# will override an existing tracker with the same id!
|
|
260
|
+
await self.agent.tracker_store.save(tracker)
|
|
261
|
+
|
|
262
|
+
processor = self.agent.processor
|
|
263
|
+
if processor and does_need_action_prediction(tracker):
|
|
264
|
+
output_channel = self.get_output_channel()
|
|
265
|
+
|
|
266
|
+
await processor._run_prediction_loop(output_channel, tracker)
|
|
207
267
|
await self.agent.tracker_store.save(tracker)
|
|
208
268
|
|
|
209
|
-
processor = self.agent.processor
|
|
210
|
-
if processor and does_need_action_prediction(tracker):
|
|
211
|
-
output_channel = self.get_output_channel()
|
|
212
|
-
|
|
213
|
-
await processor._run_prediction_loop(output_channel, tracker)
|
|
214
|
-
await processor.run_anonymization_pipeline(tracker)
|
|
215
|
-
await self.agent.tracker_store.save(tracker)
|
|
216
|
-
except Exception as e:
|
|
217
|
-
structlogger.error(
|
|
218
|
-
"studio_chat.sio.handle_tracker_update.error",
|
|
219
|
-
error=e,
|
|
220
|
-
sender_id=data["sender_id"],
|
|
221
|
-
)
|
|
222
|
-
await self.emit_error(
|
|
223
|
-
"An error occurred while updating the conversation.",
|
|
224
|
-
data["sender_id"],
|
|
225
|
-
e,
|
|
226
|
-
)
|
|
227
|
-
|
|
228
|
-
if not tracker:
|
|
229
|
-
# in case the tracker couldn't be updated, we retrieve the prior
|
|
230
|
-
# version and use that to populate the update
|
|
231
|
-
tracker = await self.agent.tracker_store.get_or_create_tracker(
|
|
232
|
-
data["sender_id"]
|
|
233
|
-
)
|
|
234
269
|
await self.on_tracker_updated(tracker)
|
|
235
270
|
|
|
271
|
+
def channel_bytes_to_rasa_audio_bytes(self, input_bytes: bytes) -> RasaAudioBytes:
|
|
272
|
+
"""Voice method to convert channel bytes to RasaAudioBytes."""
|
|
273
|
+
return RasaAudioBytes(audioop.lin2ulaw(input_bytes, 4))
|
|
274
|
+
|
|
275
|
+
async def collect_call_parameters(
|
|
276
|
+
self, channel_websocket: "Websocket"
|
|
277
|
+
) -> Optional[CallParameters]:
|
|
278
|
+
"""Voice method to collect call parameters"""
|
|
279
|
+
session_id = channel_websocket.session_id
|
|
280
|
+
return CallParameters(session_id, "local", "local", stream_id=session_id)
|
|
281
|
+
|
|
282
|
+
def map_input_message(
|
|
283
|
+
self,
|
|
284
|
+
message: Any,
|
|
285
|
+
ws: "Websocket",
|
|
286
|
+
) -> VoiceChannelAction:
|
|
287
|
+
"""Voice method to map websocket messages to actions."""
|
|
288
|
+
if "audio" in message:
|
|
289
|
+
channel_bytes = base64.b64decode(message["audio"])
|
|
290
|
+
audio_bytes = self.channel_bytes_to_rasa_audio_bytes(channel_bytes)
|
|
291
|
+
return NewAudioAction(audio_bytes)
|
|
292
|
+
elif "marker" in message:
|
|
293
|
+
if message["marker"] == call_state.latest_bot_audio_id:
|
|
294
|
+
# Just finished streaming last audio bytes
|
|
295
|
+
call_state.is_bot_speaking = False # type: ignore[attr-defined]
|
|
296
|
+
if call_state.should_hangup:
|
|
297
|
+
structlogger.debug(
|
|
298
|
+
"studio_chat.hangup", marker=call_state.latest_bot_audio_id
|
|
299
|
+
)
|
|
300
|
+
return EndConversationAction()
|
|
301
|
+
else:
|
|
302
|
+
call_state.is_bot_speaking = True # type: ignore[attr-defined]
|
|
303
|
+
return ContinueConversationAction()
|
|
304
|
+
|
|
305
|
+
def create_output_channel(
|
|
306
|
+
self, voice_websocket: "Websocket", tts_engine: TTSEngine
|
|
307
|
+
) -> VoiceOutputChannel:
|
|
308
|
+
"""Create a voice output channel"""
|
|
309
|
+
return StudioVoiceOutputChannel(
|
|
310
|
+
voice_websocket,
|
|
311
|
+
tts_engine,
|
|
312
|
+
self.tts_cache,
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
def _start_voice_session(
|
|
316
|
+
self,
|
|
317
|
+
session_id: str,
|
|
318
|
+
sid: str,
|
|
319
|
+
on_new_message: Callable[[UserMessage], Awaitable[Any]],
|
|
320
|
+
) -> None:
|
|
321
|
+
"""Create SocketIO WebSocket Adaptor & start async task for voice streaming."""
|
|
322
|
+
if sid in self.active_connections:
|
|
323
|
+
structlogger.warning(
|
|
324
|
+
"studio_chat._start_voice_session.session_already_active",
|
|
325
|
+
session_id=sid,
|
|
326
|
+
)
|
|
327
|
+
return
|
|
328
|
+
|
|
329
|
+
structlogger.info(
|
|
330
|
+
"studio_chat._start_voice_session.starting_session", session_id=sid
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
# Create a websocket adapter for this connection
|
|
334
|
+
ws_adapter = SocketIOVoiceWebsocketAdapter(
|
|
335
|
+
sio=self.sio,
|
|
336
|
+
session_id=session_id,
|
|
337
|
+
sid=sid,
|
|
338
|
+
bot_message_evt=self.bot_message_evt,
|
|
339
|
+
)
|
|
340
|
+
self.active_connections[sid] = ws_adapter
|
|
341
|
+
|
|
342
|
+
# Start voice streaming in an async task
|
|
343
|
+
task = asyncio.create_task(
|
|
344
|
+
self._handle_voice_streaming(on_new_message, ws_adapter, sid)
|
|
345
|
+
)
|
|
346
|
+
self.background_tasks[sid] = task
|
|
347
|
+
task.add_done_callback(lambda _: self._cleanup_tasks_for_sid(sid))
|
|
348
|
+
|
|
349
|
+
async def _handle_voice_streaming(
|
|
350
|
+
self,
|
|
351
|
+
on_new_message: Callable[[UserMessage], Awaitable[Any]],
|
|
352
|
+
ws_adapter: "Websocket",
|
|
353
|
+
sid: str,
|
|
354
|
+
) -> None:
|
|
355
|
+
"""Handle voice streaming for a Socket.IO connection."""
|
|
356
|
+
try:
|
|
357
|
+
await self.run_audio_streaming(on_new_message, ws_adapter)
|
|
358
|
+
except Exception as e:
|
|
359
|
+
structlogger.exception(
|
|
360
|
+
"studio_voice.voice_streaming.error",
|
|
361
|
+
error=str(e),
|
|
362
|
+
sid=sid,
|
|
363
|
+
)
|
|
364
|
+
if sid in self.active_connections:
|
|
365
|
+
del self.active_connections[sid]
|
|
366
|
+
|
|
367
|
+
def _cleanup_tasks_for_sid(self, sid: str) -> None:
|
|
368
|
+
if sid in self.background_tasks:
|
|
369
|
+
task = self.background_tasks.pop(sid)
|
|
370
|
+
task.cancel()
|
|
371
|
+
if sid in self.active_connections:
|
|
372
|
+
del self.active_connections[sid]
|
|
373
|
+
|
|
374
|
+
@hookimpl # type: ignore[misc]
|
|
375
|
+
def after_server_stop(self) -> None:
|
|
376
|
+
"""Cleanup background tasks and active connections when the server stops."""
|
|
377
|
+
structlogger.info("studio_chat.after_server_stop.cleanup")
|
|
378
|
+
self.active_connections.clear()
|
|
379
|
+
for task in self.background_tasks.values():
|
|
380
|
+
task.cancel()
|
|
381
|
+
|
|
236
382
|
def blueprint(
|
|
237
383
|
self, on_new_message: Callable[["UserMessage"], Awaitable[Any]]
|
|
238
384
|
) -> SocketBlueprint:
|
|
@@ -245,11 +391,126 @@ class StudioChatInput(SocketIOInput):
|
|
|
245
391
|
return socket_blueprint
|
|
246
392
|
|
|
247
393
|
@socket_blueprint.listener("after_server_start") # type: ignore[misc]
|
|
248
|
-
async def after_server_start(
|
|
394
|
+
async def after_server_start(
|
|
395
|
+
app: "Sanic", _: asyncio.AbstractEventLoop
|
|
396
|
+
) -> None:
|
|
249
397
|
self.agent = app.ctx.agent
|
|
250
398
|
|
|
399
|
+
@self.sio.on("disconnect", namespace=self.namespace)
|
|
400
|
+
async def disconnect(sid: Text) -> None:
|
|
401
|
+
structlogger.debug("studio_chat.sio.disconnect", sid=sid)
|
|
402
|
+
self._cleanup_tasks_for_sid(sid)
|
|
403
|
+
|
|
404
|
+
@self.sio.on("session_request", namespace=self.namespace)
|
|
405
|
+
async def session_request(sid: Text, data: Optional[Dict]) -> None:
|
|
406
|
+
"""Overrides the base SocketIOInput session_request handler.
|
|
407
|
+
|
|
408
|
+
Args:
|
|
409
|
+
sid: ID of the session (from SocketIO).
|
|
410
|
+
data:
|
|
411
|
+
- session_id: Studio Chat channel is used with a Bridge Architecture
|
|
412
|
+
(Model Service's Socket Bridge), so we use session_id to remain
|
|
413
|
+
consistent across the bridge. Session ID becomes the sender_id
|
|
414
|
+
for the UserMessage.
|
|
415
|
+
- is_voice: Boolean indicating if its a voice session.
|
|
416
|
+
"""
|
|
417
|
+
# Call parent session_request handler first
|
|
418
|
+
await self.handle_session_request(sid, data)
|
|
419
|
+
|
|
420
|
+
# start a voice session if requested
|
|
421
|
+
if data and data.get("is_voice", False):
|
|
422
|
+
self._start_voice_session(data["session_id"], sid, on_new_message)
|
|
423
|
+
|
|
424
|
+
@self.sio.on(self.user_message_evt, namespace=self.namespace)
|
|
425
|
+
async def handle_message(sid: Text, data: Dict) -> None:
|
|
426
|
+
"""Overrides the base SocketIOInput handle_message handler."""
|
|
427
|
+
# Handle voice messages
|
|
428
|
+
if "audio" in data or "marker" in data:
|
|
429
|
+
if sid in self.active_connections:
|
|
430
|
+
# Route audio messages to the voice adapter queue
|
|
431
|
+
ws = self.active_connections[sid]
|
|
432
|
+
ws.put_message(data)
|
|
433
|
+
return
|
|
434
|
+
|
|
435
|
+
# Handle text messages
|
|
436
|
+
await self.handle_user_message(sid, data, on_new_message)
|
|
437
|
+
|
|
251
438
|
@self.sio.on("update_tracker", namespace=self.namespace)
|
|
252
439
|
async def on_update_tracker(sid: Text, data: Dict) -> None:
|
|
253
440
|
await self.handle_tracker_update(sid, data)
|
|
254
441
|
|
|
255
442
|
return socket_blueprint
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
class StudioVoiceOutputChannel(VoiceOutputChannel):
|
|
446
|
+
@classmethod
|
|
447
|
+
def name(cls) -> str:
|
|
448
|
+
return "studio_chat"
|
|
449
|
+
|
|
450
|
+
def rasa_audio_bytes_to_channel_bytes(
|
|
451
|
+
self, rasa_audio_bytes: RasaAudioBytes
|
|
452
|
+
) -> bytes:
|
|
453
|
+
return audioop.ulaw2lin(rasa_audio_bytes, 4)
|
|
454
|
+
|
|
455
|
+
def channel_bytes_to_message(self, recipient_id: str, channel_bytes: bytes) -> str:
|
|
456
|
+
return json.dumps({"audio": base64.b64encode(channel_bytes).decode("utf-8")})
|
|
457
|
+
|
|
458
|
+
def create_marker_message(self, recipient_id: str) -> Tuple[str, str]:
|
|
459
|
+
message_id = uuid.uuid4().hex
|
|
460
|
+
return json.dumps({"marker": message_id}), message_id
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
class SocketIOVoiceWebsocketAdapter:
|
|
464
|
+
"""Adapter to make Socket.IO work like a Sanic WebSocket for voice channels."""
|
|
465
|
+
|
|
466
|
+
def __init__(
|
|
467
|
+
self, sio: "AsyncServer", session_id: str, sid: str, bot_message_evt: str
|
|
468
|
+
) -> None:
|
|
469
|
+
self.sio = sio
|
|
470
|
+
self.bot_message_evt = bot_message_evt
|
|
471
|
+
self._closed = False
|
|
472
|
+
self._receive_queue: asyncio.Queue[Any] = asyncio.Queue()
|
|
473
|
+
|
|
474
|
+
# the messages need to be emitted on room=sid
|
|
475
|
+
self.sid = sid
|
|
476
|
+
|
|
477
|
+
# used by collect_call_parameters
|
|
478
|
+
# ultimately, this becomes the sender_id
|
|
479
|
+
self.session_id = session_id
|
|
480
|
+
|
|
481
|
+
@property
|
|
482
|
+
def closed(self) -> bool:
|
|
483
|
+
return self._closed
|
|
484
|
+
|
|
485
|
+
async def send(self, data: Any) -> None:
|
|
486
|
+
"""Send data to the client."""
|
|
487
|
+
if not self.closed:
|
|
488
|
+
await self.sio.emit(self.bot_message_evt, data, room=self.sid)
|
|
489
|
+
|
|
490
|
+
async def recv(self) -> Any:
|
|
491
|
+
"""Receive data from the client."""
|
|
492
|
+
if self.closed:
|
|
493
|
+
raise ConnectionError("WebSocket is closed")
|
|
494
|
+
return await self._receive_queue.get()
|
|
495
|
+
|
|
496
|
+
def put_message(self, message: Any) -> None:
|
|
497
|
+
"""Put message in the internal receive queue."""
|
|
498
|
+
self._receive_queue.put_nowait(message)
|
|
499
|
+
|
|
500
|
+
async def close(self, code: int = 1000, reason: str = "") -> None:
|
|
501
|
+
"""Close the connection."""
|
|
502
|
+
self._closed = True
|
|
503
|
+
# at this point, the client should have disconnected
|
|
504
|
+
|
|
505
|
+
def __aiter__(self) -> "SocketIOVoiceWebsocketAdapter":
|
|
506
|
+
"""Allow the adapter to be used in an async for loop."""
|
|
507
|
+
return self
|
|
508
|
+
|
|
509
|
+
async def __anext__(self) -> Any:
|
|
510
|
+
if self.closed:
|
|
511
|
+
raise StopAsyncIteration
|
|
512
|
+
try:
|
|
513
|
+
message = await self.recv()
|
|
514
|
+
return message
|
|
515
|
+
except Exception:
|
|
516
|
+
raise StopAsyncIteration
|
rasa/core/channels/vier_cvg.py
CHANGED
|
@@ -129,9 +129,8 @@ class CVGOutput(OutputChannel):
|
|
|
129
129
|
)
|
|
130
130
|
|
|
131
131
|
logger.info(
|
|
132
|
-
"Creating incoming UserMessage: {
|
|
132
|
+
"Creating incoming UserMessage: {output_channel=%s, sender_id=%s, metadata=%s}" # noqa: E501
|
|
133
133
|
% (
|
|
134
|
-
user_message.text,
|
|
135
134
|
user_message.output_channel,
|
|
136
135
|
user_message.sender_id,
|
|
137
136
|
user_message.metadata,
|
|
@@ -149,7 +149,7 @@ class Conversation:
|
|
|
149
149
|
structlogger.warning(
|
|
150
150
|
"audiocodes.handle.activities.empty_input_channel_name",
|
|
151
151
|
event_info=(
|
|
152
|
-
"Audiocodes input channel name is empty "
|
|
152
|
+
f"Audiocodes input channel name is empty "
|
|
153
153
|
f"for conversation {self.conversation_id}"
|
|
154
154
|
),
|
|
155
155
|
)
|
|
@@ -177,7 +177,7 @@ class Conversation:
|
|
|
177
177
|
else:
|
|
178
178
|
structlogger.warning(
|
|
179
179
|
"audiocodes.handle.activities.unknown_activity_type",
|
|
180
|
-
|
|
180
|
+
activity_type=activity["type"],
|
|
181
181
|
)
|
|
182
182
|
continue
|
|
183
183
|
|
|
@@ -193,16 +193,9 @@ class Conversation:
|
|
|
193
193
|
try:
|
|
194
194
|
await on_new_message(user_msg)
|
|
195
195
|
except Exception as e: # skipcq: PYL-W0703
|
|
196
|
-
if isinstance(user_msg.text, dict):
|
|
197
|
-
anonymized_info = json.dumps(user_msg.text)
|
|
198
|
-
elif isinstance(user_msg.text, str):
|
|
199
|
-
anonymized_info = user_msg.text
|
|
200
|
-
else:
|
|
201
|
-
anonymized_info = INFO_UNKNOWN
|
|
202
|
-
|
|
203
196
|
structlogger.exception(
|
|
204
197
|
"audiocodes.handle.activities.failure",
|
|
205
|
-
|
|
198
|
+
sender_id=self.conversation_id,
|
|
206
199
|
error=e,
|
|
207
200
|
exc_info=True,
|
|
208
201
|
)
|
|
@@ -495,8 +488,8 @@ class AudiocodesInput(InputChannel):
|
|
|
495
488
|
await on_new_message(
|
|
496
489
|
UserMessage(
|
|
497
490
|
text=f"{INTENT_MESSAGE_PREFIX}session_end",
|
|
498
|
-
input_channel=self.name(),
|
|
499
491
|
output_channel=None,
|
|
492
|
+
input_channel=self.name(),
|
|
500
493
|
sender_id=conversation_id,
|
|
501
494
|
metadata=reason,
|
|
502
495
|
)
|
|
@@ -17,7 +17,10 @@ from rasa.core.channels.voice_ready.jambonz_protocol import (
|
|
|
17
17
|
send_ws_text_message,
|
|
18
18
|
websocket_message_handler,
|
|
19
19
|
)
|
|
20
|
-
from rasa.core.channels.voice_ready.utils import
|
|
20
|
+
from rasa.core.channels.voice_ready.utils import (
|
|
21
|
+
validate_username_password_credentials,
|
|
22
|
+
validate_voice_license_scope,
|
|
23
|
+
)
|
|
21
24
|
from rasa.shared.exceptions import RasaException
|
|
22
25
|
from rasa.shared.utils.common import mark_as_beta_feature
|
|
23
26
|
from rasa.utils.io import remove_emojis
|
|
@@ -41,11 +44,7 @@ class JambonzVoiceReadyInput(InputChannel):
|
|
|
41
44
|
|
|
42
45
|
username = credentials.get("username")
|
|
43
46
|
password = credentials.get("password")
|
|
44
|
-
|
|
45
|
-
raise RasaException(
|
|
46
|
-
"In Jambonz channel, either both username and password "
|
|
47
|
-
"or neither should be provided. "
|
|
48
|
-
)
|
|
47
|
+
validate_username_password_credentials(username, password, "Jambonz")
|
|
49
48
|
|
|
50
49
|
return cls(username, password)
|
|
51
50
|
|
|
@@ -16,9 +16,12 @@ from rasa.core.channels.channel import (
|
|
|
16
16
|
create_auth_requested_response_provider,
|
|
17
17
|
requires_basic_auth,
|
|
18
18
|
)
|
|
19
|
-
from rasa.core.channels.voice_ready.utils import
|
|
19
|
+
from rasa.core.channels.voice_ready.utils import (
|
|
20
|
+
CallParameters,
|
|
21
|
+
validate_username_password_credentials,
|
|
22
|
+
)
|
|
20
23
|
from rasa.shared.core.events import BotUttered
|
|
21
|
-
from rasa.shared.exceptions import InvalidConfigException
|
|
24
|
+
from rasa.shared.exceptions import InvalidConfigException
|
|
22
25
|
|
|
23
26
|
logger = structlog.get_logger(__name__)
|
|
24
27
|
|
|
@@ -127,11 +130,7 @@ class TwilioVoiceInput(InputChannel):
|
|
|
127
130
|
|
|
128
131
|
username = credentials.get("username")
|
|
129
132
|
password = credentials.get("password")
|
|
130
|
-
|
|
131
|
-
raise RasaException(
|
|
132
|
-
"In TwilioVoice channel, either both username and password "
|
|
133
|
-
"or neither should be provided. "
|
|
134
|
-
)
|
|
133
|
+
validate_username_password_credentials(username, password, "TwilioVoice")
|
|
135
134
|
|
|
136
135
|
return cls(
|
|
137
136
|
credentials.get(
|
|
@@ -180,8 +179,9 @@ class TwilioVoiceInput(InputChannel):
|
|
|
180
179
|
if self.assistant_voice not in self.SUPPORTED_VOICES:
|
|
181
180
|
self._raise_invalid_voice_exception()
|
|
182
181
|
|
|
183
|
-
|
|
184
|
-
self.
|
|
182
|
+
validate_username_password_credentials(
|
|
183
|
+
self.username, self.password, "TwilioVoice"
|
|
184
|
+
)
|
|
185
185
|
|
|
186
186
|
try:
|
|
187
187
|
int(self.speech_timeout)
|
|
@@ -389,9 +389,10 @@ class TwilioVoiceInput(InputChannel):
|
|
|
389
389
|
return voice_response
|
|
390
390
|
|
|
391
391
|
def _raise_invalid_credentials_exception(self) -> None:
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
392
|
+
# This method is now redundant since we use the shared validation function
|
|
393
|
+
# but keeping it for backward compatibility if any external code calls it
|
|
394
|
+
validate_username_password_credentials(
|
|
395
|
+
self.username, self.password, "TwilioVoice"
|
|
395
396
|
)
|
|
396
397
|
|
|
397
398
|
|
|
@@ -3,9 +3,31 @@ from typing import Optional
|
|
|
3
3
|
|
|
4
4
|
import structlog
|
|
5
5
|
|
|
6
|
+
from rasa.shared.exceptions import InvalidConfigException
|
|
7
|
+
|
|
6
8
|
structlogger = structlog.get_logger()
|
|
7
9
|
|
|
8
10
|
|
|
11
|
+
def validate_username_password_credentials(
|
|
12
|
+
username: Optional[str], password: Optional[str], channel_name: str
|
|
13
|
+
) -> None:
|
|
14
|
+
"""Validate that username and password are both provided or both None.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
username: The username credential, or None
|
|
18
|
+
password: The password credential, or None
|
|
19
|
+
channel_name: The name of the channel for error message
|
|
20
|
+
|
|
21
|
+
Raises:
|
|
22
|
+
InvalidConfigException: If only one of username/password is provided
|
|
23
|
+
"""
|
|
24
|
+
if (not username) != (not password):
|
|
25
|
+
raise InvalidConfigException(
|
|
26
|
+
f"In {channel_name} channel, either both username and password "
|
|
27
|
+
"or neither should be provided."
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
9
31
|
def validate_voice_license_scope() -> None:
|
|
10
32
|
from rasa.utils.licensing import (
|
|
11
33
|
PRODUCT_AREA,
|