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
|
@@ -104,30 +104,27 @@ class AudiocodesVoiceInputChannel(VoiceInputChannel):
|
|
|
104
104
|
|
|
105
105
|
def __init__(
|
|
106
106
|
self,
|
|
107
|
-
token: Optional[Text],
|
|
108
107
|
server_url: str,
|
|
109
108
|
asr_config: Dict,
|
|
110
109
|
tts_config: Dict,
|
|
111
|
-
|
|
110
|
+
token: Optional[Text] = None,
|
|
112
111
|
):
|
|
113
112
|
mark_as_beta_feature("Audiocodes (audiocodes_stream) Channel")
|
|
114
|
-
super().__init__(
|
|
113
|
+
super().__init__(
|
|
114
|
+
server_url=server_url,
|
|
115
|
+
asr_config=asr_config,
|
|
116
|
+
tts_config=tts_config,
|
|
117
|
+
)
|
|
115
118
|
self.token = token
|
|
116
119
|
|
|
117
120
|
@classmethod
|
|
118
121
|
def from_credentials(
|
|
119
|
-
cls,
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
return
|
|
125
|
-
token=credentials.get("token"),
|
|
126
|
-
server_url=credentials["server_url"],
|
|
127
|
-
asr_config=credentials["asr"],
|
|
128
|
-
tts_config=credentials["tts"],
|
|
129
|
-
monitor_silence=credentials.get("monitor_silence", False),
|
|
130
|
-
)
|
|
122
|
+
cls,
|
|
123
|
+
credentials: Optional[Dict[str, Any]],
|
|
124
|
+
) -> "AudiocodesVoiceInputChannel":
|
|
125
|
+
channel = super().from_credentials(credentials)
|
|
126
|
+
channel.token = credentials.get("token") # type: ignore[attr-defined, union-attr]
|
|
127
|
+
return channel # type: ignore[return-value]
|
|
131
128
|
|
|
132
129
|
def channel_bytes_to_rasa_audio_bytes(self, input_bytes: bytes) -> RasaAudioBytes:
|
|
133
130
|
return RasaAudioBytes(base64.b64decode(input_bytes))
|
|
@@ -292,7 +289,7 @@ class AudiocodesVoiceInputChannel(VoiceInputChannel):
|
|
|
292
289
|
def blueprint(
|
|
293
290
|
self, on_new_message: Callable[[UserMessage], Awaitable[Any]]
|
|
294
291
|
) -> Blueprint:
|
|
295
|
-
"""Defines a Sanic
|
|
292
|
+
"""Defines a Sanic blueprint"""
|
|
296
293
|
blueprint = Blueprint("audiocodes_stream", __name__)
|
|
297
294
|
|
|
298
295
|
@blueprint.route("/", methods=["GET"])
|
|
@@ -97,7 +97,7 @@ class BrowserAudioInputChannel(VoiceInputChannel):
|
|
|
97
97
|
def blueprint(
|
|
98
98
|
self, on_new_message: Callable[[UserMessage], Awaitable[Any]]
|
|
99
99
|
) -> Blueprint:
|
|
100
|
-
"""Defines a Sanic
|
|
100
|
+
"""Defines a Sanic blueprint"""
|
|
101
101
|
blueprint = Blueprint("browser_audio", __name__)
|
|
102
102
|
|
|
103
103
|
@blueprint.route("/", methods=["GET"])
|
|
@@ -29,6 +29,7 @@ from rasa.core.channels.voice_stream.voice_channel import (
|
|
|
29
29
|
VoiceInputChannel,
|
|
30
30
|
VoiceOutputChannel,
|
|
31
31
|
)
|
|
32
|
+
from rasa.shared.exceptions import InvalidConfigException
|
|
32
33
|
|
|
33
34
|
"""
|
|
34
35
|
Genesys throws a rate limit error with too many audio messages.
|
|
@@ -91,30 +92,48 @@ class GenesysInputChannel(VoiceInputChannel):
|
|
|
91
92
|
return "genesys"
|
|
92
93
|
|
|
93
94
|
def __init__(
|
|
94
|
-
self,
|
|
95
|
+
self,
|
|
96
|
+
server_url: str,
|
|
97
|
+
asr_config: Dict,
|
|
98
|
+
tts_config: Dict,
|
|
99
|
+
api_key: Optional[Text] = None,
|
|
100
|
+
client_secret: Optional[Text] = None,
|
|
95
101
|
) -> None:
|
|
96
|
-
super().__init__(
|
|
102
|
+
super().__init__(server_url, asr_config, tts_config)
|
|
97
103
|
self.api_key = api_key
|
|
98
104
|
self.client_secret = client_secret
|
|
99
105
|
|
|
100
106
|
@classmethod
|
|
101
107
|
def from_credentials(
|
|
102
|
-
cls,
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
108
|
+
cls,
|
|
109
|
+
credentials: Optional[Dict[str, Any]],
|
|
110
|
+
) -> "GenesysInputChannel":
|
|
111
|
+
"""Create a channel from credentials dictionary.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
credentials: Dictionary containing the required credentials:
|
|
115
|
+
- server_url: URL where the server is hosted
|
|
116
|
+
- asr: ASR engine configuration
|
|
117
|
+
- tts: TTS engine configuration
|
|
118
|
+
- api_key: Required API key for Genesys authentication
|
|
119
|
+
- client_secret: Optional client secret for signature verification
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
GenesysInputChannel instance
|
|
123
|
+
"""
|
|
124
|
+
channel = super().from_credentials(credentials)
|
|
125
|
+
|
|
126
|
+
# Check required Genesys-specific credentials
|
|
127
|
+
if not credentials.get("api_key"): # type: ignore[union-attr]
|
|
128
|
+
raise InvalidConfigException(
|
|
129
|
+
"No API key given for Genesys voice channel (api_key)."
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
# Update channel with Genesys-specific credentials
|
|
133
|
+
channel.api_key = credentials["api_key"] # type: ignore[index,attr-defined]
|
|
134
|
+
channel.client_secret = credentials.get("client_secret") # type: ignore[union-attr,attr-defined]
|
|
135
|
+
|
|
136
|
+
return channel # type: ignore[return-value]
|
|
118
137
|
|
|
119
138
|
def _ensure_channel_data_initialized(self) -> None:
|
|
120
139
|
"""Initialize Genesys-specific channel data if not already present.
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import audioop
|
|
2
|
+
import json
|
|
3
|
+
import uuid
|
|
4
|
+
from typing import Any, Awaitable, Callable, Dict, Optional, Text, Tuple
|
|
5
|
+
|
|
6
|
+
import structlog
|
|
7
|
+
from sanic import ( # type: ignore[attr-defined]
|
|
8
|
+
Blueprint,
|
|
9
|
+
HTTPResponse,
|
|
10
|
+
Request,
|
|
11
|
+
Websocket,
|
|
12
|
+
response,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
from rasa.core.channels import UserMessage, requires_basic_auth
|
|
16
|
+
from rasa.core.channels.voice_ready.utils import (
|
|
17
|
+
CallParameters,
|
|
18
|
+
validate_username_password_credentials,
|
|
19
|
+
)
|
|
20
|
+
from rasa.core.channels.voice_stream.audio_bytes import RasaAudioBytes
|
|
21
|
+
from rasa.core.channels.voice_stream.call_state import call_state
|
|
22
|
+
from rasa.core.channels.voice_stream.tts.tts_engine import TTSEngine
|
|
23
|
+
from rasa.core.channels.voice_stream.voice_channel import (
|
|
24
|
+
ContinueConversationAction,
|
|
25
|
+
EndConversationAction,
|
|
26
|
+
NewAudioAction,
|
|
27
|
+
VoiceChannelAction,
|
|
28
|
+
VoiceInputChannel,
|
|
29
|
+
VoiceOutputChannel,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
logger = structlog.get_logger()
|
|
33
|
+
|
|
34
|
+
JAMBONZ_STREAMS_WEBSOCKET_PATH = "webhooks/jambonz_streams/websocket"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def map_call_params(data: Dict[Text, str]) -> CallParameters:
|
|
38
|
+
"""Map the twilio stream parameters to the CallParameters dataclass."""
|
|
39
|
+
call_sid = data.get("callSid", "None")
|
|
40
|
+
from_number = data.get("from", "Unknown")
|
|
41
|
+
to_number = data.get("to")
|
|
42
|
+
return CallParameters(
|
|
43
|
+
call_id=call_sid,
|
|
44
|
+
user_phone=from_number,
|
|
45
|
+
bot_phone=to_number,
|
|
46
|
+
stream_id=call_sid,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class JambonzStreamOutputChannel(VoiceOutputChannel):
|
|
51
|
+
@classmethod
|
|
52
|
+
def name(cls) -> str:
|
|
53
|
+
return "jambonz_stream"
|
|
54
|
+
|
|
55
|
+
async def send_audio_bytes(
|
|
56
|
+
self, recipient_id: str, audio_bytes: RasaAudioBytes
|
|
57
|
+
) -> None:
|
|
58
|
+
"""Overridden to send binary websocket messages for Jambonz.
|
|
59
|
+
|
|
60
|
+
Converts 8kHz μ-law to 8kHz L16 PCM for Jambonz streaming.
|
|
61
|
+
"""
|
|
62
|
+
pcm = audioop.ulaw2lin(audio_bytes, 2)
|
|
63
|
+
await self.voice_websocket.send(pcm)
|
|
64
|
+
|
|
65
|
+
def create_marker_message(self, recipient_id: str) -> Tuple[str, str]:
|
|
66
|
+
"""Create a marker message to track audio stream position."""
|
|
67
|
+
marker_id = uuid.uuid4().hex
|
|
68
|
+
return json.dumps({"type": "mark", "data": {"name": marker_id}}), marker_id
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class JambonzStreamInputChannel(VoiceInputChannel):
|
|
72
|
+
@classmethod
|
|
73
|
+
def name(cls) -> str:
|
|
74
|
+
return "jambonz_stream"
|
|
75
|
+
|
|
76
|
+
def __init__(
|
|
77
|
+
self,
|
|
78
|
+
server_url: str,
|
|
79
|
+
asr_config: Dict,
|
|
80
|
+
tts_config: Dict,
|
|
81
|
+
username: Optional[Text] = None,
|
|
82
|
+
password: Optional[Text] = None,
|
|
83
|
+
) -> None:
|
|
84
|
+
"""Initialize the channel.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
username: Optional username for basic auth
|
|
88
|
+
password: Optional password for basic auth
|
|
89
|
+
"""
|
|
90
|
+
super().__init__(server_url, asr_config, tts_config)
|
|
91
|
+
self.username = username
|
|
92
|
+
self.password = password
|
|
93
|
+
|
|
94
|
+
@classmethod
|
|
95
|
+
def from_credentials(
|
|
96
|
+
cls, credentials: Optional[Dict[Text, Any]]
|
|
97
|
+
) -> "JambonzStreamInputChannel":
|
|
98
|
+
"""Create a channel from credentials dictionary.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
credentials: Dictionary containing the required credentials:
|
|
102
|
+
- server_url: URL where the server is hosted
|
|
103
|
+
- asr: ASR engine configuration
|
|
104
|
+
- tts: TTS engine configuration
|
|
105
|
+
- username: Optional username for basic auth
|
|
106
|
+
- password: Optional password for basic auth
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
JambonzStreamInputChannel instance
|
|
110
|
+
"""
|
|
111
|
+
# Get common credentials from parent
|
|
112
|
+
channel = super().from_credentials(credentials)
|
|
113
|
+
|
|
114
|
+
# Check optional basic auth credentials
|
|
115
|
+
username = credentials.get("username") # type: ignore[union-attr]
|
|
116
|
+
password = credentials.get("password") # type: ignore[union-attr]
|
|
117
|
+
validate_username_password_credentials(username, password, "Jambonz Stream")
|
|
118
|
+
|
|
119
|
+
# Update channel with auth credentials
|
|
120
|
+
channel.username = username # type: ignore[attr-defined]
|
|
121
|
+
channel.password = password # type: ignore[attr-defined]
|
|
122
|
+
|
|
123
|
+
return channel # type: ignore[return-value]
|
|
124
|
+
|
|
125
|
+
def _websocket_stream_url(self) -> str:
|
|
126
|
+
"""Returns the websocket stream URL."""
|
|
127
|
+
# depending on the config value, the url might contain http as a
|
|
128
|
+
# protocol or not - we'll make sure both work
|
|
129
|
+
if self.server_url.startswith("http"):
|
|
130
|
+
base_url = self.server_url.replace("http", "ws")
|
|
131
|
+
else:
|
|
132
|
+
base_url = f"wss://{self.server_url}"
|
|
133
|
+
return f"{base_url}/{JAMBONZ_STREAMS_WEBSOCKET_PATH}"
|
|
134
|
+
|
|
135
|
+
def channel_bytes_to_rasa_audio_bytes(self, input_bytes: bytes) -> RasaAudioBytes:
|
|
136
|
+
"""Convert Jambonz audio bytes (L16 PCM) to Rasa audio bytes (μ-law)."""
|
|
137
|
+
ulaw = audioop.lin2ulaw(input_bytes, 2)
|
|
138
|
+
return RasaAudioBytes(ulaw)
|
|
139
|
+
|
|
140
|
+
async def collect_call_parameters(
|
|
141
|
+
self, channel_websocket: Websocket
|
|
142
|
+
) -> Optional[CallParameters]:
|
|
143
|
+
# Wait for initial metadata message
|
|
144
|
+
message = await channel_websocket.recv()
|
|
145
|
+
logger.debug("jambonz.collect_call_parameters", message=message)
|
|
146
|
+
metadata = json.loads(message)
|
|
147
|
+
return map_call_params(metadata)
|
|
148
|
+
|
|
149
|
+
def map_input_message(self, message: Any, ws: Websocket) -> VoiceChannelAction:
|
|
150
|
+
# Handle binary audio frames
|
|
151
|
+
if isinstance(message, bytes):
|
|
152
|
+
channel_bytes = message
|
|
153
|
+
audio_bytes = self.channel_bytes_to_rasa_audio_bytes(channel_bytes)
|
|
154
|
+
return NewAudioAction(audio_bytes)
|
|
155
|
+
|
|
156
|
+
# Handle JSON messages
|
|
157
|
+
data = json.loads(message)
|
|
158
|
+
if data["type"] == "mark":
|
|
159
|
+
if data["data"]["name"] == call_state.latest_bot_audio_id:
|
|
160
|
+
# Just finished streaming last audio bytes
|
|
161
|
+
call_state.is_bot_speaking = False # type: ignore[attr-defined]
|
|
162
|
+
if call_state.should_hangup:
|
|
163
|
+
logger.debug(
|
|
164
|
+
"jambonz.hangup", marker=call_state.latest_bot_audio_id
|
|
165
|
+
)
|
|
166
|
+
return EndConversationAction()
|
|
167
|
+
else:
|
|
168
|
+
call_state.is_bot_speaking = True # type: ignore[attr-defined]
|
|
169
|
+
elif data["event"] == "dtmf":
|
|
170
|
+
# TODO: handle DTMF input
|
|
171
|
+
logger.debug("jambonz.dtmf.received", dtmf=data["dtmf"])
|
|
172
|
+
else:
|
|
173
|
+
logger.warning("jambonz.unexpected_message", message=data)
|
|
174
|
+
|
|
175
|
+
return ContinueConversationAction()
|
|
176
|
+
|
|
177
|
+
def create_output_channel(
|
|
178
|
+
self, voice_websocket: Websocket, tts_engine: TTSEngine
|
|
179
|
+
) -> VoiceOutputChannel:
|
|
180
|
+
return JambonzStreamOutputChannel(
|
|
181
|
+
voice_websocket,
|
|
182
|
+
tts_engine,
|
|
183
|
+
self.tts_cache,
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
def blueprint(
|
|
187
|
+
self, on_new_message: Callable[[UserMessage], Awaitable[Any]]
|
|
188
|
+
) -> Blueprint:
|
|
189
|
+
blueprint = Blueprint("jambonz_stream", __name__)
|
|
190
|
+
|
|
191
|
+
@blueprint.route("/", methods=["GET"])
|
|
192
|
+
async def health(_: Request) -> HTTPResponse:
|
|
193
|
+
return response.json({"status": "ok"})
|
|
194
|
+
|
|
195
|
+
@blueprint.route("/call_status", methods=["POST"])
|
|
196
|
+
@requires_basic_auth(self.username, self.password)
|
|
197
|
+
async def call_status(request: Request) -> HTTPResponse:
|
|
198
|
+
"""Handle call status updates from Jambonz."""
|
|
199
|
+
data = request.json
|
|
200
|
+
logger.debug("jambonz.call_status.received", data=data)
|
|
201
|
+
return response.json({"status": "ok"})
|
|
202
|
+
|
|
203
|
+
@blueprint.route("/webhook", methods=["POST"])
|
|
204
|
+
@requires_basic_auth(self.username, self.password)
|
|
205
|
+
async def webhook(request: Request) -> HTTPResponse:
|
|
206
|
+
"""Handle incoming webhook requests from Jambonz."""
|
|
207
|
+
data = request.json
|
|
208
|
+
logger.debug("jambonz.webhook.received", data=data)
|
|
209
|
+
return response.json(
|
|
210
|
+
[
|
|
211
|
+
{
|
|
212
|
+
"verb": "listen",
|
|
213
|
+
"url": self._websocket_stream_url(),
|
|
214
|
+
"sampleRate": 8000,
|
|
215
|
+
"passDtmf": True,
|
|
216
|
+
"bidirectionalAudio": {
|
|
217
|
+
"enabled": True,
|
|
218
|
+
"streaming": True,
|
|
219
|
+
"sampleRate": 8000,
|
|
220
|
+
},
|
|
221
|
+
}
|
|
222
|
+
]
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
@blueprint.websocket("/websocket", subprotocols=["audio.jambonz.org"]) # type: ignore[misc]
|
|
226
|
+
async def handle_message(request: Request, ws: Websocket) -> None:
|
|
227
|
+
try:
|
|
228
|
+
await self.run_audio_streaming(on_new_message, ws)
|
|
229
|
+
except Exception as e:
|
|
230
|
+
logger.error("jambonz.handle_message.error", error=e)
|
|
231
|
+
|
|
232
|
+
return blueprint
|
|
@@ -14,12 +14,15 @@ from sanic import ( # type: ignore[attr-defined]
|
|
|
14
14
|
response,
|
|
15
15
|
)
|
|
16
16
|
|
|
17
|
-
from rasa.core.channels import
|
|
17
|
+
from rasa.core.channels import UserMessage
|
|
18
18
|
from rasa.core.channels.channel import (
|
|
19
19
|
create_auth_requested_response_provider,
|
|
20
20
|
requires_basic_auth,
|
|
21
21
|
)
|
|
22
|
-
from rasa.core.channels.voice_ready.utils import
|
|
22
|
+
from rasa.core.channels.voice_ready.utils import (
|
|
23
|
+
CallParameters,
|
|
24
|
+
validate_username_password_credentials,
|
|
25
|
+
)
|
|
23
26
|
from rasa.core.channels.voice_stream.audio_bytes import RasaAudioBytes
|
|
24
27
|
from rasa.core.channels.voice_stream.call_state import call_state
|
|
25
28
|
from rasa.core.channels.voice_stream.tts.tts_engine import TTSEngine
|
|
@@ -31,7 +34,6 @@ from rasa.core.channels.voice_stream.voice_channel import (
|
|
|
31
34
|
VoiceInputChannel,
|
|
32
35
|
VoiceOutputChannel,
|
|
33
36
|
)
|
|
34
|
-
from rasa.shared.exceptions import RasaException
|
|
35
37
|
|
|
36
38
|
if TYPE_CHECKING:
|
|
37
39
|
from twilio.twiml.voice_response import VoiceResponse
|
|
@@ -102,31 +104,32 @@ class TwilioMediaStreamsInputChannel(VoiceInputChannel):
|
|
|
102
104
|
server_url: str,
|
|
103
105
|
asr_config: Dict,
|
|
104
106
|
tts_config: Dict,
|
|
105
|
-
monitor_silence: bool = False,
|
|
106
107
|
username: Optional[Text] = None,
|
|
107
108
|
password: Optional[Text] = None,
|
|
108
109
|
):
|
|
109
|
-
super().__init__(
|
|
110
|
+
super().__init__(
|
|
111
|
+
server_url=server_url,
|
|
112
|
+
asr_config=asr_config,
|
|
113
|
+
tts_config=tts_config,
|
|
114
|
+
)
|
|
110
115
|
self.username = username
|
|
111
116
|
self.password = password
|
|
112
117
|
|
|
113
118
|
@classmethod
|
|
114
|
-
def from_credentials(
|
|
119
|
+
def from_credentials(
|
|
120
|
+
cls,
|
|
121
|
+
credentials: Optional[Dict[str, Any]],
|
|
122
|
+
) -> VoiceInputChannel:
|
|
115
123
|
credentials = credentials or {}
|
|
116
124
|
|
|
117
125
|
username = credentials.get("username")
|
|
118
126
|
password = credentials.get("password")
|
|
119
|
-
|
|
120
|
-
raise RasaException(
|
|
121
|
-
"In TwilioMediaStreams channel, either both username and password "
|
|
122
|
-
"or neither should be provided. "
|
|
123
|
-
)
|
|
127
|
+
validate_username_password_credentials(username, password, "TwilioMediaStreams")
|
|
124
128
|
|
|
125
129
|
return cls(
|
|
126
130
|
credentials["server_url"],
|
|
127
131
|
credentials["asr"],
|
|
128
132
|
credentials["tts"],
|
|
129
|
-
credentials.get("monitor_silence", False),
|
|
130
133
|
username=username,
|
|
131
134
|
password=password,
|
|
132
135
|
)
|
|
@@ -31,9 +31,11 @@ from rasa.core.channels.voice_stream.tts.azure import AzureTTS
|
|
|
31
31
|
from rasa.core.channels.voice_stream.tts.cartesia import CartesiaTTS
|
|
32
32
|
from rasa.core.channels.voice_stream.tts.tts_cache import TTSCache
|
|
33
33
|
from rasa.core.channels.voice_stream.tts.tts_engine import TTSEngine, TTSError
|
|
34
|
-
from rasa.core.channels.voice_stream.util import
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
from rasa.core.channels.voice_stream.util import (
|
|
35
|
+
generate_silence,
|
|
36
|
+
)
|
|
37
|
+
from rasa.shared.core.constants import SILENCE_TIMEOUT_SLOT
|
|
38
|
+
from rasa.shared.exceptions import InvalidConfigException
|
|
37
39
|
from rasa.shared.utils.common import (
|
|
38
40
|
class_from_module_path,
|
|
39
41
|
mark_as_beta_feature,
|
|
@@ -42,6 +44,11 @@ from rasa.utils.io import remove_emojis
|
|
|
42
44
|
|
|
43
45
|
logger = structlog.get_logger(__name__)
|
|
44
46
|
|
|
47
|
+
# define constants for the voice channel
|
|
48
|
+
USER_CONVERSATION_SESSION_END = "/session_end"
|
|
49
|
+
USER_CONVERSATION_SESSION_START = "/session_start"
|
|
50
|
+
USER_CONVERSATION_SILENCE_TIMEOUT = "/silence_timeout"
|
|
51
|
+
|
|
45
52
|
|
|
46
53
|
@dataclass
|
|
47
54
|
class VoiceChannelAction:
|
|
@@ -64,6 +71,14 @@ class ContinueConversationAction(VoiceChannelAction):
|
|
|
64
71
|
|
|
65
72
|
|
|
66
73
|
def asr_engine_from_config(asr_config: Dict) -> ASREngine:
|
|
74
|
+
if not asr_config:
|
|
75
|
+
raise ValueError("ASR configuration dictionary cannot be empty")
|
|
76
|
+
|
|
77
|
+
if "name" not in asr_config:
|
|
78
|
+
raise ValueError(
|
|
79
|
+
"ASR configuration must contain 'name' key specifying the engine type"
|
|
80
|
+
)
|
|
81
|
+
|
|
67
82
|
name = str(asr_config["name"])
|
|
68
83
|
asr_config = copy.copy(asr_config)
|
|
69
84
|
asr_config.pop("name")
|
|
@@ -77,12 +92,12 @@ def asr_engine_from_config(asr_config: Dict) -> ASREngine:
|
|
|
77
92
|
asr_engine_class = class_from_module_path(name)
|
|
78
93
|
return asr_engine_class.from_config_dict(asr_config)
|
|
79
94
|
except NameError:
|
|
80
|
-
|
|
95
|
+
raise InvalidConfigException(
|
|
81
96
|
f"Failed to initialize ASR Engine with type '{name}'. "
|
|
82
97
|
f"Please make sure the method `from_config_dict`is implemented."
|
|
83
98
|
)
|
|
84
99
|
except TypeError as e:
|
|
85
|
-
|
|
100
|
+
raise InvalidConfigException(
|
|
86
101
|
f"Failed to initialize ASR Engine with type '{name}'. "
|
|
87
102
|
f"Invalid configuration provided. "
|
|
88
103
|
f"Error: {e}"
|
|
@@ -90,6 +105,14 @@ def asr_engine_from_config(asr_config: Dict) -> ASREngine:
|
|
|
90
105
|
|
|
91
106
|
|
|
92
107
|
def tts_engine_from_config(tts_config: Dict) -> TTSEngine:
|
|
108
|
+
if not tts_config:
|
|
109
|
+
raise ValueError("TTS configuration dictionary cannot be empty")
|
|
110
|
+
|
|
111
|
+
if "name" not in tts_config:
|
|
112
|
+
raise ValueError(
|
|
113
|
+
"TTS configuration must contain 'name' key specifying the engine type"
|
|
114
|
+
)
|
|
115
|
+
|
|
93
116
|
name = str(tts_config["name"])
|
|
94
117
|
tts_config = copy.copy(tts_config)
|
|
95
118
|
tts_config.pop("name")
|
|
@@ -103,13 +126,13 @@ def tts_engine_from_config(tts_config: Dict) -> TTSEngine:
|
|
|
103
126
|
tts_engine_class = class_from_module_path(name)
|
|
104
127
|
return tts_engine_class.from_config_dict(tts_config)
|
|
105
128
|
except NameError:
|
|
106
|
-
|
|
129
|
+
raise InvalidConfigException(
|
|
107
130
|
f"Failed to initialize TTS Engine with type '{name}'. "
|
|
108
131
|
f"Please make sure the method `from_config_dict`is implemented."
|
|
109
132
|
)
|
|
110
133
|
except TypeError as e:
|
|
111
|
-
|
|
112
|
-
f"Failed to initialize
|
|
134
|
+
raise InvalidConfigException(
|
|
135
|
+
f"Failed to initialize TTS Engine with type '{name}'. "
|
|
113
136
|
f"Invalid configuration provided. "
|
|
114
137
|
f"Error: {e}"
|
|
115
138
|
)
|
|
@@ -166,8 +189,12 @@ class VoiceOutputChannel(OutputChannel):
|
|
|
166
189
|
def update_silence_timeout(self) -> None:
|
|
167
190
|
"""Updates the silence timeout for the session."""
|
|
168
191
|
if self.tracker_state:
|
|
169
|
-
call_state.silence_timeout =
|
|
170
|
-
|
|
192
|
+
call_state.silence_timeout = self.tracker_state["slots"][ # type: ignore[attr-defined]
|
|
193
|
+
SILENCE_TIMEOUT_SLOT
|
|
194
|
+
]
|
|
195
|
+
logger.debug(
|
|
196
|
+
"voice_channel.silence_timeout_updated",
|
|
197
|
+
silence_timeout=call_state.silence_timeout,
|
|
171
198
|
)
|
|
172
199
|
|
|
173
200
|
async def send_text_with_buttons(
|
|
@@ -189,6 +216,7 @@ class VoiceOutputChannel(OutputChannel):
|
|
|
189
216
|
collected_audio_bytes = RasaAudioBytes(b"")
|
|
190
217
|
seconds_marker = -1
|
|
191
218
|
last_sent_offset = 0
|
|
219
|
+
logger.debug("voice_channel.sending_audio", text=text)
|
|
192
220
|
|
|
193
221
|
# Send start marker before first chunk
|
|
194
222
|
try:
|
|
@@ -274,18 +302,21 @@ class VoiceOutputChannel(OutputChannel):
|
|
|
274
302
|
|
|
275
303
|
|
|
276
304
|
class VoiceInputChannel(InputChannel):
|
|
305
|
+
# All children of this class require a voice license to be used.
|
|
306
|
+
requires_voice_license = True
|
|
307
|
+
|
|
277
308
|
def __init__(
|
|
278
309
|
self,
|
|
279
310
|
server_url: str,
|
|
280
311
|
asr_config: Dict,
|
|
281
312
|
tts_config: Dict,
|
|
282
|
-
monitor_silence: bool = False,
|
|
283
313
|
):
|
|
284
|
-
|
|
314
|
+
if self.requires_voice_license:
|
|
315
|
+
validate_voice_license_scope()
|
|
316
|
+
|
|
285
317
|
self.server_url = server_url
|
|
286
318
|
self.asr_config = asr_config
|
|
287
319
|
self.tts_config = tts_config
|
|
288
|
-
self.monitor_silence = monitor_silence
|
|
289
320
|
self.tts_cache = TTSCache(tts_config.get("cache_size", 1000))
|
|
290
321
|
|
|
291
322
|
logger.info(
|
|
@@ -303,8 +334,6 @@ class VoiceInputChannel(InputChannel):
|
|
|
303
334
|
timeout = call_state.silence_timeout
|
|
304
335
|
if not timeout:
|
|
305
336
|
return
|
|
306
|
-
if not self.monitor_silence:
|
|
307
|
-
return
|
|
308
337
|
logger.debug("voice_channel.silence_timeout_watch_started", timeout=timeout)
|
|
309
338
|
await asyncio.sleep(timeout)
|
|
310
339
|
await asr_event_queue.put(UserSilence())
|
|
@@ -319,13 +348,28 @@ class VoiceInputChannel(InputChannel):
|
|
|
319
348
|
call_state.silence_timeout_watcher = None # type: ignore[attr-defined]
|
|
320
349
|
|
|
321
350
|
@classmethod
|
|
322
|
-
def from_credentials(
|
|
323
|
-
|
|
351
|
+
def from_credentials(
|
|
352
|
+
cls,
|
|
353
|
+
credentials: Optional[Dict[str, Any]],
|
|
354
|
+
) -> InputChannel:
|
|
355
|
+
if not credentials:
|
|
356
|
+
cls.raise_missing_credentials_exception()
|
|
357
|
+
|
|
358
|
+
if not credentials.get("server_url"):
|
|
359
|
+
raise InvalidConfigException("No server_url provided in credentials.")
|
|
360
|
+
if not credentials.get("asr"):
|
|
361
|
+
raise InvalidConfigException(
|
|
362
|
+
"No ASR configuration provided in credentials."
|
|
363
|
+
)
|
|
364
|
+
if not credentials.get("tts"):
|
|
365
|
+
raise InvalidConfigException(
|
|
366
|
+
"No TTS configuration provided in credentials."
|
|
367
|
+
)
|
|
368
|
+
|
|
324
369
|
return cls(
|
|
325
|
-
credentials["server_url"],
|
|
326
|
-
credentials["asr"],
|
|
327
|
-
credentials["tts"],
|
|
328
|
-
credentials.get("monitor_silence", False),
|
|
370
|
+
server_url=credentials["server_url"],
|
|
371
|
+
asr_config=credentials["asr"],
|
|
372
|
+
tts_config=credentials["tts"],
|
|
329
373
|
)
|
|
330
374
|
|
|
331
375
|
def channel_bytes_to_rasa_audio_bytes(self, input_bytes: bytes) -> RasaAudioBytes:
|
|
@@ -345,7 +389,7 @@ class VoiceInputChannel(InputChannel):
|
|
|
345
389
|
) -> None:
|
|
346
390
|
output_channel = self.create_output_channel(channel_websocket, tts_engine)
|
|
347
391
|
message = UserMessage(
|
|
348
|
-
text=
|
|
392
|
+
text=USER_CONVERSATION_SESSION_START,
|
|
349
393
|
output_channel=output_channel,
|
|
350
394
|
sender_id=self.get_sender_id(call_parameters),
|
|
351
395
|
input_channel=self.name(),
|
|
@@ -382,17 +426,17 @@ class VoiceInputChannel(InputChannel):
|
|
|
382
426
|
|
|
383
427
|
async def consume_audio_bytes() -> None:
|
|
384
428
|
async for message in channel_websocket:
|
|
385
|
-
|
|
429
|
+
was_bot_speaking_before = call_state.is_bot_speaking
|
|
386
430
|
channel_action = self.map_input_message(message, channel_websocket)
|
|
387
431
|
is_bot_speaking_after = call_state.is_bot_speaking
|
|
388
432
|
|
|
389
|
-
if not
|
|
433
|
+
if not was_bot_speaking_before and is_bot_speaking_after:
|
|
390
434
|
logger.debug("voice_channel.bot_started_speaking")
|
|
391
435
|
# relevant when the bot speaks multiple messages in one turn
|
|
392
436
|
self._cancel_silence_timeout_watcher()
|
|
393
437
|
|
|
394
438
|
# we just stopped speaking, starting a watcher for silence timeout
|
|
395
|
-
if
|
|
439
|
+
if was_bot_speaking_before and not is_bot_speaking_after:
|
|
396
440
|
logger.debug("voice_channel.bot_stopped_speaking")
|
|
397
441
|
self._cancel_silence_timeout_watcher()
|
|
398
442
|
call_state.silence_timeout_watcher = ( # type: ignore[attr-defined]
|
|
@@ -476,7 +520,7 @@ class VoiceInputChannel(InputChannel):
|
|
|
476
520
|
elif isinstance(e, UserSilence):
|
|
477
521
|
output_channel = self.create_output_channel(voice_websocket, tts_engine)
|
|
478
522
|
message = UserMessage(
|
|
479
|
-
text=
|
|
523
|
+
text=USER_CONVERSATION_SILENCE_TIMEOUT,
|
|
480
524
|
output_channel=output_channel,
|
|
481
525
|
sender_id=self.get_sender_id(call_parameters),
|
|
482
526
|
input_channel=self.name(),
|
|
@@ -494,7 +538,7 @@ class VoiceInputChannel(InputChannel):
|
|
|
494
538
|
"""Handle disconnection from the channel."""
|
|
495
539
|
output_channel = self.create_output_channel(channel_websocket, tts_engine)
|
|
496
540
|
message = UserMessage(
|
|
497
|
-
text=
|
|
541
|
+
text=USER_CONVERSATION_SESSION_END,
|
|
498
542
|
output_channel=output_channel,
|
|
499
543
|
sender_id=self.get_sender_id(call_parameters),
|
|
500
544
|
input_channel=self.name(),
|