rasa-pro 3.11.5__py3-none-any.whl → 3.12.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.
- README.md +10 -13
- rasa/__main__.py +7 -7
- rasa/anonymization/anonymisation_rule_yaml_reader.py +1 -1
- rasa/anonymization/anonymization_pipeline.py +3 -3
- rasa/anonymization/anonymization_rule_executor.py +17 -11
- rasa/anonymization/anonymization_rule_orchestrator.py +2 -3
- rasa/cli/arguments/data.py +2 -2
- rasa/cli/arguments/default_arguments.py +1 -1
- rasa/cli/arguments/evaluate.py +2 -1
- rasa/cli/arguments/interactive.py +1 -1
- rasa/cli/arguments/run.py +1 -1
- rasa/cli/arguments/test.py +7 -5
- rasa/cli/arguments/train.py +3 -3
- rasa/cli/arguments/visualize.py +2 -2
- rasa/cli/arguments/x.py +1 -0
- rasa/cli/data.py +20 -3
- rasa/cli/dialogue_understanding_test.py +386 -0
- rasa/cli/evaluate.py +1 -1
- rasa/cli/export.py +6 -6
- rasa/cli/inspect.py +20 -1
- rasa/cli/interactive.py +4 -5
- rasa/cli/llm_fine_tuning.py +51 -16
- rasa/cli/markers.py +1 -2
- rasa/cli/project_templates/calm/actions/add_contact.py +1 -1
- rasa/cli/project_templates/calm/config.yml +2 -2
- rasa/cli/project_templates/calm/domain/list_contacts.yml +1 -2
- rasa/cli/project_templates/calm/domain/remove_contact.yml +1 -2
- rasa/cli/project_templates/calm/domain/shared.yml +1 -4
- rasa/cli/project_templates/calm/endpoints.yml +2 -2
- rasa/cli/project_templates/tutorial/actions/actions.py +3 -2
- rasa/cli/shell.py +5 -6
- rasa/cli/studio/download.py +1 -2
- rasa/cli/studio/studio.py +2 -3
- rasa/cli/studio/train.py +0 -1
- rasa/cli/telemetry.py +2 -2
- rasa/cli/test.py +11 -11
- rasa/cli/train.py +3 -0
- rasa/cli/utils.py +25 -5
- rasa/constants.py +0 -1
- rasa/core/__init__.py +0 -1
- rasa/core/actions/action.py +135 -208
- rasa/core/actions/action_handle_digressions.py +164 -0
- rasa/core/actions/action_hangup.py +1 -1
- rasa/core/actions/action_repeat_bot_messages.py +2 -2
- rasa/core/actions/action_run_slot_rejections.py +18 -6
- rasa/core/actions/action_trigger_chitchat.py +1 -1
- rasa/core/actions/action_trigger_flow.py +5 -5
- rasa/core/actions/action_trigger_search.py +1 -1
- rasa/core/actions/custom_action_executor.py +1 -1
- rasa/core/actions/direct_custom_actions_executor.py +1 -0
- rasa/core/actions/forms.py +22 -15
- rasa/core/actions/http_custom_action_executor.py +8 -1
- rasa/core/actions/loops.py +3 -3
- rasa/core/actions/two_stage_fallback.py +13 -13
- rasa/core/auth_retry_tracker_store.py +1 -2
- rasa/core/brokers/broker.py +2 -1
- rasa/core/brokers/file.py +1 -1
- rasa/core/brokers/kafka.py +8 -8
- rasa/core/brokers/pika.py +8 -9
- rasa/core/brokers/sql.py +4 -3
- rasa/core/channels/__init__.py +7 -0
- rasa/core/channels/botframework.py +2 -2
- rasa/core/channels/callback.py +4 -4
- rasa/core/channels/channel.py +11 -11
- rasa/core/channels/console.py +0 -1
- rasa/core/channels/development_inspector.py +80 -24
- rasa/core/channels/facebook.py +5 -5
- rasa/core/channels/hangouts.py +7 -8
- rasa/core/channels/inspector/dist/assets/{arc-f0f8bd46.js → arc-9f1365dc.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{blockDiagram-38ab4fdb-7162c77d.js → blockDiagram-38ab4fdb-e0f81b12.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{c4Diagram-3d4e48cf-b1d0d098.js → c4Diagram-3d4e48cf-9deaee1c.js} +1 -1
- rasa/core/channels/inspector/dist/assets/channel-44956714.js +1 -0
- rasa/core/channels/inspector/dist/assets/{classDiagram-70f12bd4-807a1b27.js → classDiagram-70f12bd4-20450a96.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{classDiagram-v2-f2320105-5238dcdb.js → classDiagram-v2-f2320105-749d2abf.js} +1 -1
- rasa/core/channels/inspector/dist/assets/clone-a9475142.js +1 -0
- rasa/core/channels/inspector/dist/assets/{createText-2e5e7dd3-75dfaa67.js → createText-2e5e7dd3-bef0b38c.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{edges-e0da2a9e-df20501d.js → edges-e0da2a9e-943801a7.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{erDiagram-9861fffd-13cf4797.js → erDiagram-9861fffd-d523a948.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{flowDb-956e92f1-a4991264.js → flowDb-956e92f1-54e4cf19.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{flowDiagram-66a62f08-ccecf773.js → flowDiagram-66a62f08-48bfbbe8.js} +1 -1
- rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-43fa749a.js +1 -0
- rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-4a651766-b5801783.js → flowchart-elk-definition-4a651766-17c30827.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{ganttDiagram-c361ad54-161e079a.js → ganttDiagram-c361ad54-43086f2d.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-72cf32ee-f38e86a4.js → gitGraphDiagram-72cf32ee-5c8b693e.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{graph-be6ef5d8.js → graph-41a90d26.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{index-3862675e-d9ce8994.js → index-3862675e-b43eeae9.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{index-7794b245.js → index-e8affe45.js} +155 -155
- rasa/core/channels/inspector/dist/assets/{infoDiagram-f8f76790-5000a3dc.js → infoDiagram-f8f76790-0b20676b.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{journeyDiagram-49397b02-8ef0a17a.js → journeyDiagram-49397b02-39bce7b5.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{layout-d649bc98.js → layout-dc8eeea4.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{line-95add810.js → line-c4d2e756.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{linear-f6025094.js → linear-86f6f2d9.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{mindmap-definition-fc14e90a-2e8531c4.js → mindmap-definition-fc14e90a-4216f771.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{pieDiagram-8a3498a8-918adfdb.js → pieDiagram-8a3498a8-1a0cfa96.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{quadrantDiagram-120e2f19-cbd01797.js → quadrantDiagram-120e2f19-f91e67cf.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{requirementDiagram-deff3bca-6a8b877b.js → requirementDiagram-deff3bca-d4046bed.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{sankeyDiagram-04a897e0-c377c3fe.js → sankeyDiagram-04a897e0-2cf6d1d7.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{sequenceDiagram-704730f1-ab9e9b7f.js → sequenceDiagram-704730f1-751ac4f5.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{stateDiagram-587899a1-5e6ae67d.js → stateDiagram-587899a1-f734f4d4.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-d93cdb3a-40643476.js → stateDiagram-v2-d93cdb3a-91c65710.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-6aaf32cf-afb8d108.js → styles-6aaf32cf-e0cff7be.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-9a916d00-7edc9423.js → styles-9a916d00-c8029e5d.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-c10674c1-c1d8f7e9.js → styles-c10674c1-114f312a.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{svgDrawCommon-08f97a94-f494b2ef.js → svgDrawCommon-08f97a94-b7b9dc00.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{timeline-definition-85554ec2-11c7cdd0.js → timeline-definition-85554ec2-9536d189.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{xychartDiagram-e933f94c-3f191ec1.js → xychartDiagram-e933f94c-bf3b0f36.js} +1 -1
- rasa/core/channels/inspector/dist/index.html +1 -1
- rasa/core/channels/inspector/package.json +1 -0
- rasa/core/channels/inspector/src/App.tsx +15 -2
- rasa/core/channels/inspector/src/components/RasaLogo.tsx +31 -0
- rasa/core/channels/inspector/src/components/RecruitmentPanel.tsx +68 -0
- rasa/core/channels/inspector/src/components/Welcome.tsx +19 -13
- rasa/core/channels/inspector/yarn.lock +5 -0
- rasa/core/channels/mattermost.py +4 -4
- rasa/core/channels/rasa_chat.py +4 -4
- rasa/core/channels/rest.py +11 -12
- rasa/core/channels/rocketchat.py +4 -3
- rasa/core/channels/slack.py +6 -5
- rasa/core/channels/socketio.py +8 -28
- rasa/core/channels/studio_chat.py +212 -0
- rasa/core/channels/telegram.py +105 -55
- rasa/core/channels/twilio.py +3 -3
- rasa/core/channels/vier_cvg.py +2 -2
- rasa/core/channels/voice_ready/audiocodes.py +9 -9
- rasa/core/channels/voice_ready/jambonz.py +5 -5
- rasa/core/channels/voice_ready/jambonz_protocol.py +3 -4
- rasa/core/channels/voice_ready/twilio_voice.py +9 -8
- rasa/core/channels/voice_ready/utils.py +2 -2
- rasa/core/channels/voice_stream/asr/asr_engine.py +12 -6
- rasa/core/channels/voice_stream/asr/asr_event.py +5 -0
- rasa/core/channels/voice_stream/asr/azure.py +16 -3
- rasa/core/channels/voice_stream/asr/deepgram.py +76 -19
- rasa/core/channels/voice_stream/audiocodes.py +292 -0
- rasa/core/channels/voice_stream/browser_audio.py +14 -7
- rasa/core/channels/voice_stream/call_state.py +6 -2
- rasa/core/channels/voice_stream/genesys.py +320 -0
- rasa/core/channels/voice_stream/tts/azure.py +13 -5
- rasa/core/channels/voice_stream/tts/cartesia.py +34 -14
- rasa/core/channels/voice_stream/tts/tts_cache.py +3 -2
- rasa/core/channels/voice_stream/tts/tts_engine.py +1 -1
- rasa/core/channels/voice_stream/twilio_media_streams.py +12 -8
- rasa/core/channels/voice_stream/util.py +1 -1
- rasa/core/channels/voice_stream/voice_channel.py +100 -56
- rasa/core/channels/webexteams.py +3 -4
- rasa/core/constants.py +2 -0
- rasa/core/evaluation/marker.py +7 -6
- rasa/core/evaluation/marker_base.py +15 -16
- rasa/core/evaluation/marker_stats.py +3 -4
- rasa/core/evaluation/marker_tracker_loader.py +5 -4
- rasa/core/exporter.py +4 -4
- rasa/core/featurizers/precomputation.py +8 -8
- rasa/core/featurizers/single_state_featurizer.py +7 -7
- rasa/core/featurizers/tracker_featurizers.py +13 -13
- rasa/core/http_interpreter.py +3 -4
- rasa/core/information_retrieval/__init__.py +1 -1
- rasa/core/information_retrieval/faiss.py +4 -4
- rasa/core/information_retrieval/information_retrieval.py +2 -2
- rasa/core/information_retrieval/milvus.py +3 -3
- rasa/core/information_retrieval/qdrant.py +3 -3
- rasa/core/jobs.py +1 -0
- rasa/core/lock.py +2 -3
- rasa/core/lock_store.py +3 -3
- rasa/core/migrate.py +12 -9
- rasa/core/nlg/__init__.py +1 -1
- rasa/core/nlg/callback.py +2 -3
- rasa/core/nlg/contextual_response_rephraser.py +82 -14
- rasa/core/nlg/generator.py +85 -17
- rasa/core/nlg/interpolator.py +4 -3
- rasa/core/nlg/response.py +9 -7
- rasa/core/nlg/summarize.py +1 -0
- rasa/core/nlg/translate.py +55 -0
- rasa/core/persistor.py +3 -3
- rasa/core/policies/ensemble.py +10 -9
- rasa/core/policies/enterprise_search_policy.py +87 -21
- rasa/core/policies/enterprise_search_prompt_with_citation_template.jinja2 +1 -1
- rasa/core/policies/flow_policy.py +13 -14
- rasa/core/policies/flows/flow_executor.py +85 -55
- rasa/core/policies/intentless_policy.py +6 -7
- rasa/core/policies/memoization.py +22 -20
- rasa/core/policies/policy.py +24 -22
- rasa/core/policies/rule_policy.py +37 -36
- rasa/core/policies/ted_policy.py +87 -85
- rasa/core/policies/unexpected_intent_policy.py +77 -75
- rasa/core/processor.py +167 -74
- rasa/core/run.py +5 -4
- rasa/core/secrets_manager/endpoints.py +2 -3
- rasa/core/secrets_manager/factory.py +2 -3
- rasa/core/secrets_manager/secret_manager.py +2 -3
- rasa/core/secrets_manager/vault.py +2 -2
- rasa/core/test.py +30 -30
- rasa/core/tracker_store.py +138 -49
- rasa/core/train.py +1 -1
- rasa/core/training/__init__.py +2 -2
- rasa/core/training/converters/responses_prefix_converter.py +1 -2
- rasa/core/training/interactive.py +13 -13
- rasa/core/training/story_conflict.py +4 -5
- rasa/core/training/training.py +3 -5
- rasa/core/utils.py +5 -5
- rasa/core/visualize.py +1 -1
- rasa/dialogue_understanding/coexistence/intent_based_router.py +2 -2
- rasa/dialogue_understanding/coexistence/llm_based_router.py +5 -5
- rasa/dialogue_understanding/commands/__init__.py +22 -22
- rasa/dialogue_understanding/commands/can_not_handle_command.py +38 -1
- rasa/dialogue_understanding/commands/cancel_flow_command.py +96 -9
- rasa/dialogue_understanding/commands/change_flow_command.py +36 -2
- rasa/dialogue_understanding/commands/chit_chat_answer_command.py +36 -4
- rasa/dialogue_understanding/commands/clarify_command.py +46 -4
- rasa/dialogue_understanding/commands/command.py +3 -2
- rasa/dialogue_understanding/commands/command_syntax_manager.py +55 -0
- rasa/dialogue_understanding/commands/correct_slots_command.py +14 -5
- rasa/dialogue_understanding/commands/error_command.py +1 -1
- rasa/dialogue_understanding/commands/free_form_answer_command.py +2 -1
- rasa/dialogue_understanding/commands/handle_code_change_command.py +2 -2
- rasa/dialogue_understanding/commands/handle_digressions_command.py +144 -0
- rasa/dialogue_understanding/commands/human_handoff_command.py +34 -4
- rasa/dialogue_understanding/commands/knowledge_answer_command.py +36 -4
- rasa/dialogue_understanding/commands/noop_command.py +2 -1
- rasa/dialogue_understanding/commands/prompt_command.py +94 -0
- rasa/dialogue_understanding/commands/repeat_bot_messages_command.py +34 -4
- rasa/dialogue_understanding/commands/restart_command.py +2 -5
- rasa/dialogue_understanding/commands/session_end_command.py +3 -5
- rasa/dialogue_understanding/commands/session_start_command.py +3 -5
- rasa/dialogue_understanding/commands/set_slot_command.py +55 -16
- rasa/dialogue_understanding/commands/skip_question_command.py +34 -4
- rasa/dialogue_understanding/commands/start_flow_command.py +78 -2
- rasa/dialogue_understanding/commands/user_silence_command.py +3 -5
- rasa/dialogue_understanding/commands/utils.py +126 -43
- rasa/dialogue_understanding/constants.py +2 -0
- rasa/dialogue_understanding/generator/__init__.py +2 -0
- rasa/dialogue_understanding/generator/command_generator.py +120 -79
- rasa/dialogue_understanding/generator/command_parser.py +245 -0
- rasa/dialogue_understanding/generator/constants.py +12 -4
- rasa/dialogue_understanding/generator/flow_retrieval.py +7 -7
- rasa/dialogue_understanding/generator/llm_based_command_generator.py +187 -59
- rasa/dialogue_understanding/generator/llm_command_generator.py +6 -3
- rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +106 -110
- rasa/dialogue_understanding/generator/nlu_command_adapter.py +53 -11
- rasa/dialogue_understanding/generator/prompt_templates/__init__.py +0 -0
- rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_claude_3_5_sonnet_20240620_template.jinja2 +58 -0
- rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_gpt_4o_2024_11_20_template.jinja2 +57 -0
- rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +574 -0
- rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +41 -386
- rasa/dialogue_understanding/generator/utils.py +76 -0
- rasa/dialogue_understanding/patterns/cancel.py +2 -1
- rasa/dialogue_understanding/patterns/cannot_handle.py +1 -0
- rasa/dialogue_understanding/patterns/chitchat.py +1 -1
- rasa/dialogue_understanding/patterns/clarify.py +2 -1
- rasa/dialogue_understanding/patterns/code_change.py +2 -0
- rasa/dialogue_understanding/patterns/collect_information.py +7 -4
- rasa/dialogue_understanding/patterns/completed.py +1 -1
- rasa/dialogue_understanding/patterns/continue_interrupted.py +1 -1
- rasa/dialogue_understanding/patterns/correction.py +17 -3
- rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +78 -2
- rasa/dialogue_understanding/patterns/handle_digressions.py +81 -0
- rasa/dialogue_understanding/patterns/human_handoff.py +1 -1
- rasa/dialogue_understanding/patterns/internal_error.py +1 -0
- rasa/dialogue_understanding/patterns/search.py +1 -1
- rasa/dialogue_understanding/patterns/session_start.py +1 -1
- rasa/dialogue_understanding/patterns/skip_question.py +1 -0
- rasa/dialogue_understanding/patterns/user_silence.py +1 -1
- rasa/dialogue_understanding/patterns/validate_slot.py +65 -0
- rasa/dialogue_understanding/processor/command_processor.py +193 -43
- rasa/dialogue_understanding/processor/command_processor_component.py +1 -1
- rasa/dialogue_understanding/stack/dialogue_stack.py +4 -3
- rasa/dialogue_understanding/stack/frames/__init__.py +2 -2
- rasa/dialogue_understanding/stack/frames/chit_chat_frame.py +4 -1
- rasa/dialogue_understanding/stack/frames/dialogue_stack_frame.py +2 -3
- rasa/dialogue_understanding/stack/frames/flow_stack_frame.py +5 -2
- rasa/dialogue_understanding/stack/frames/search_frame.py +4 -1
- rasa/dialogue_understanding/stack/utils.py +56 -10
- rasa/dialogue_understanding/utils.py +164 -0
- rasa/dialogue_understanding_test/README.md +429 -0
- rasa/dialogue_understanding_test/__init__.py +0 -0
- rasa/dialogue_understanding_test/command_comparison.py +60 -0
- rasa/dialogue_understanding_test/command_metric_calculation.py +122 -0
- rasa/dialogue_understanding_test/constants.py +22 -0
- rasa/dialogue_understanding_test/du_test_case.py +448 -0
- rasa/dialogue_understanding_test/du_test_result.py +390 -0
- rasa/dialogue_understanding_test/du_test_runner.py +322 -0
- rasa/dialogue_understanding_test/du_test_schema.yml +161 -0
- rasa/dialogue_understanding_test/io.py +443 -0
- rasa/dialogue_understanding_test/test_case_simulation/__init__.py +0 -0
- rasa/dialogue_understanding_test/test_case_simulation/exception.py +28 -0
- rasa/dialogue_understanding_test/test_case_simulation/test_case_tracker_simulator.py +336 -0
- rasa/dialogue_understanding_test/utils.py +70 -0
- rasa/dialogue_understanding_test/validation.py +77 -0
- rasa/e2e_test/aggregate_test_stats_calculator.py +1 -1
- rasa/e2e_test/assertions.py +202 -175
- rasa/e2e_test/assertions_schema.yml +6 -0
- rasa/e2e_test/constants.py +16 -1
- rasa/e2e_test/e2e_config.py +102 -41
- rasa/e2e_test/e2e_config_schema.yml +28 -10
- rasa/e2e_test/e2e_test_case.py +5 -5
- rasa/e2e_test/e2e_test_converter.py +2 -3
- rasa/e2e_test/e2e_test_coverage_report.py +6 -6
- rasa/e2e_test/e2e_test_result.py +1 -1
- rasa/e2e_test/e2e_test_runner.py +143 -38
- rasa/e2e_test/llm_judge_prompts/answer_relevance_prompt_template.jinja2 +93 -0
- rasa/e2e_test/llm_judge_prompts/groundedness_prompt_template.jinja2 +169 -0
- rasa/e2e_test/stub_custom_action.py +1 -1
- rasa/e2e_test/utils/generative_assertions.py +243 -0
- rasa/e2e_test/utils/io.py +123 -93
- rasa/e2e_test/utils/validation.py +101 -3
- rasa/engine/caching.py +5 -7
- rasa/engine/constants.py +1 -1
- rasa/engine/graph.py +3 -2
- rasa/engine/language.py +182 -0
- rasa/engine/recipes/config_files/default_config.yml +4 -0
- rasa/engine/recipes/default_components.py +13 -15
- rasa/engine/recipes/default_recipe.py +65 -49
- rasa/engine/recipes/graph_recipe.py +10 -7
- rasa/engine/recipes/recipe.py +2 -2
- rasa/engine/runner/dask.py +2 -2
- rasa/engine/runner/interface.py +1 -0
- rasa/engine/storage/local_model_storage.py +6 -4
- rasa/engine/storage/resource.py +2 -1
- rasa/engine/storage/storage.py +8 -3
- rasa/engine/training/components.py +2 -1
- rasa/engine/training/fingerprinting.py +4 -2
- rasa/engine/training/graph_trainer.py +4 -4
- rasa/engine/training/hooks.py +2 -2
- rasa/engine/validation.py +36 -33
- rasa/exceptions.py +3 -2
- rasa/graph_components/converters/nlu_message_converter.py +3 -3
- rasa/graph_components/providers/domain_for_core_training_provider.py +3 -3
- rasa/graph_components/providers/domain_provider.py +3 -2
- rasa/graph_components/providers/flows_provider.py +2 -3
- rasa/graph_components/providers/forms_provider.py +4 -4
- rasa/graph_components/providers/nlu_training_data_provider.py +5 -3
- rasa/graph_components/providers/responses_provider.py +4 -4
- rasa/graph_components/providers/rule_only_provider.py +3 -2
- rasa/graph_components/providers/story_graph_provider.py +8 -8
- rasa/graph_components/providers/training_tracker_provider.py +3 -2
- rasa/graph_components/validators/default_recipe_validator.py +16 -16
- rasa/graph_components/validators/finetuning_validator.py +10 -8
- rasa/hooks.py +19 -14
- rasa/jupyter.py +2 -2
- rasa/llm_fine_tuning/annotation_module.py +4 -4
- rasa/llm_fine_tuning/conversations.py +5 -33
- rasa/llm_fine_tuning/llm_data_preparation_module.py +6 -4
- rasa/llm_fine_tuning/paraphrasing/conversation_rephraser.py +4 -4
- rasa/llm_fine_tuning/paraphrasing/rephrase_validator.py +18 -13
- rasa/llm_fine_tuning/paraphrasing_module.py +6 -2
- rasa/llm_fine_tuning/storage.py +3 -3
- rasa/llm_fine_tuning/train_test_split_module.py +27 -27
- rasa/llm_fine_tuning/utils.py +7 -0
- rasa/markers/marker.py +2 -3
- rasa/markers/marker_base.py +1 -2
- rasa/markers/upload.py +2 -2
- rasa/markers/validate.py +2 -3
- rasa/model.py +3 -5
- rasa/model_manager/config.py +1 -1
- rasa/model_manager/model_api.py +5 -4
- rasa/model_manager/runner_service.py +13 -10
- rasa/model_manager/socket_bridge.py +15 -9
- rasa/model_manager/studio_jwt_auth.py +1 -0
- rasa/model_manager/trainer_service.py +9 -7
- rasa/model_manager/utils.py +1 -1
- rasa/model_manager/warm_rasa_process.py +14 -9
- rasa/model_service.py +5 -6
- rasa/model_testing.py +13 -15
- rasa/model_training.py +29 -29
- rasa/nlu/classifiers/diet_classifier.py +72 -73
- rasa/nlu/classifiers/fallback_classifier.py +9 -8
- rasa/nlu/classifiers/keyword_intent_classifier.py +7 -6
- rasa/nlu/classifiers/logistic_regression_classifier.py +3 -3
- rasa/nlu/classifiers/mitie_intent_classifier.py +5 -4
- rasa/nlu/classifiers/regex_message_handler.py +3 -2
- rasa/nlu/classifiers/sklearn_intent_classifier.py +2 -2
- rasa/nlu/convert.py +2 -2
- rasa/nlu/emulators/dialogflow.py +3 -3
- rasa/nlu/emulators/luis.py +5 -5
- rasa/nlu/emulators/no_emulator.py +1 -0
- rasa/nlu/emulators/wit.py +4 -4
- rasa/nlu/extractors/crf_entity_extractor.py +11 -11
- rasa/nlu/extractors/duckling_entity_extractor.py +7 -6
- rasa/nlu/extractors/entity_synonyms.py +10 -9
- rasa/nlu/extractors/extractor.py +16 -16
- rasa/nlu/extractors/mitie_entity_extractor.py +10 -9
- rasa/nlu/extractors/regex_entity_extractor.py +11 -10
- rasa/nlu/extractors/spacy_entity_extractor.py +2 -2
- rasa/nlu/featurizers/dense_featurizer/convert_featurizer.py +15 -14
- rasa/nlu/featurizers/dense_featurizer/dense_featurizer.py +2 -1
- rasa/nlu/featurizers/dense_featurizer/lm_featurizer.py +10 -9
- rasa/nlu/featurizers/dense_featurizer/mitie_featurizer.py +9 -7
- rasa/nlu/featurizers/dense_featurizer/spacy_featurizer.py +13 -12
- rasa/nlu/featurizers/featurizer.py +5 -4
- rasa/nlu/featurizers/sparse_featurizer/count_vectors_featurizer.py +6 -6
- rasa/nlu/featurizers/sparse_featurizer/lexical_syntactic_featurizer.py +4 -4
- rasa/nlu/featurizers/sparse_featurizer/regex_featurizer.py +4 -4
- rasa/nlu/featurizers/sparse_featurizer/sparse_featurizer.py +2 -0
- rasa/nlu/model.py +0 -1
- rasa/nlu/selectors/response_selector.py +67 -68
- rasa/nlu/test.py +38 -38
- rasa/nlu/tokenizers/jieba_tokenizer.py +1 -2
- rasa/nlu/tokenizers/mitie_tokenizer.py +2 -2
- rasa/nlu/tokenizers/spacy_tokenizer.py +3 -3
- rasa/nlu/tokenizers/tokenizer.py +6 -7
- rasa/nlu/tokenizers/whitespace_tokenizer.py +1 -1
- rasa/nlu/utils/bilou_utils.py +7 -7
- rasa/nlu/utils/hugging_face/registry.py +22 -22
- rasa/nlu/utils/hugging_face/transformers_pre_post_processors.py +2 -1
- rasa/nlu/utils/mitie_utils.py +2 -1
- rasa/nlu/utils/pattern_utils.py +1 -1
- rasa/nlu/utils/spacy_utils.py +3 -3
- rasa/plugin.py +12 -1
- rasa/server.py +3 -2
- rasa/shared/constants.py +45 -18
- rasa/shared/core/command_payload_reader.py +15 -7
- rasa/shared/core/constants.py +34 -4
- rasa/shared/core/conversation.py +1 -2
- rasa/shared/core/domain.py +19 -20
- rasa/shared/core/events.py +60 -39
- rasa/shared/core/flows/__init__.py +0 -1
- rasa/shared/core/flows/constants.py +11 -0
- rasa/shared/core/flows/flow.py +107 -26
- rasa/shared/core/flows/flow_step.py +4 -3
- rasa/shared/core/flows/flow_step_links.py +1 -2
- rasa/shared/core/flows/flow_step_sequence.py +1 -1
- rasa/shared/core/flows/flows_list.py +3 -3
- rasa/shared/core/flows/flows_yaml_schema.json +69 -3
- rasa/shared/core/flows/nlu_trigger.py +1 -1
- rasa/shared/core/flows/steps/__init__.py +2 -2
- rasa/shared/core/flows/steps/action.py +1 -1
- rasa/shared/core/flows/steps/call.py +1 -1
- rasa/shared/core/flows/steps/collect.py +22 -40
- rasa/shared/core/flows/steps/internal.py +1 -1
- rasa/shared/core/flows/steps/link.py +1 -1
- rasa/shared/core/flows/steps/no_operation.py +2 -2
- rasa/shared/core/flows/steps/set_slots.py +1 -1
- rasa/shared/core/flows/utils.py +44 -4
- rasa/shared/core/flows/validation.py +4 -6
- rasa/shared/core/generator.py +20 -21
- rasa/shared/core/slot_mappings.py +360 -121
- rasa/shared/core/slots.py +163 -6
- rasa/shared/core/trackers.py +108 -33
- rasa/shared/core/training_data/loading.py +1 -1
- rasa/shared/core/training_data/story_reader/story_reader.py +3 -3
- rasa/shared/core/training_data/story_reader/story_step_builder.py +4 -4
- rasa/shared/core/training_data/story_reader/yaml_story_reader.py +29 -31
- rasa/shared/core/training_data/story_writer/yaml_story_writer.py +22 -24
- rasa/shared/core/training_data/structures.py +11 -12
- rasa/shared/core/training_data/visualization.py +10 -10
- rasa/shared/data.py +6 -6
- rasa/shared/engine/caching.py +0 -1
- rasa/shared/exceptions.py +2 -2
- rasa/shared/importers/importer.py +58 -2
- rasa/shared/importers/rasa.py +5 -6
- rasa/shared/importers/utils.py +1 -1
- rasa/shared/nlu/constants.py +9 -0
- rasa/shared/nlu/training_data/entities_parser.py +6 -6
- rasa/shared/nlu/training_data/features.py +3 -3
- rasa/shared/nlu/training_data/formats/__init__.py +1 -1
- rasa/shared/nlu/training_data/formats/dialogflow.py +4 -5
- rasa/shared/nlu/training_data/formats/luis.py +7 -8
- rasa/shared/nlu/training_data/formats/rasa.py +4 -5
- rasa/shared/nlu/training_data/formats/rasa_yaml.py +17 -16
- rasa/shared/nlu/training_data/formats/readerwriter.py +8 -11
- rasa/shared/nlu/training_data/formats/wit.py +3 -4
- rasa/shared/nlu/training_data/loading.py +4 -4
- rasa/shared/nlu/training_data/lookup_tables_parser.py +1 -1
- rasa/shared/nlu/training_data/message.py +13 -14
- rasa/shared/nlu/training_data/schemas/data_schema.py +1 -1
- rasa/shared/nlu/training_data/schemas/responses.yml +19 -11
- rasa/shared/nlu/training_data/synonyms_parser.py +3 -3
- rasa/shared/nlu/training_data/training_data.py +12 -13
- rasa/shared/nlu/training_data/util.py +11 -10
- rasa/shared/providers/_configs/azure_entra_id_config.py +541 -0
- rasa/shared/providers/_configs/azure_openai_client_config.py +150 -15
- rasa/shared/providers/_configs/client_config.py +3 -1
- rasa/shared/providers/_configs/default_litellm_client_config.py +9 -7
- rasa/shared/providers/_configs/huggingface_local_embedding_client_config.py +13 -11
- rasa/shared/providers/_configs/litellm_router_client_config.py +12 -10
- rasa/shared/providers/_configs/model_group_config.py +8 -5
- rasa/shared/providers/_configs/oauth_config.py +33 -0
- rasa/shared/providers/_configs/openai_client_config.py +14 -12
- rasa/shared/providers/_configs/rasa_llm_client_config.py +5 -3
- rasa/shared/providers/_configs/self_hosted_llm_client_config.py +12 -11
- rasa/shared/providers/_configs/utils.py +1 -0
- rasa/shared/providers/_ssl_verification_utils.py +5 -6
- rasa/shared/providers/_utils.py +5 -5
- rasa/shared/providers/constants.py +6 -0
- rasa/shared/providers/embedding/_base_litellm_embedding_client.py +1 -1
- rasa/shared/providers/embedding/azure_openai_embedding_client.py +32 -7
- rasa/shared/providers/embedding/embedding_client.py +1 -1
- rasa/shared/providers/embedding/litellm_router_embedding_client.py +5 -2
- rasa/shared/providers/llm/_base_litellm_client.py +43 -18
- rasa/shared/providers/llm/azure_openai_llm_client.py +90 -34
- rasa/shared/providers/llm/default_litellm_llm_client.py +4 -2
- rasa/shared/providers/llm/litellm_router_llm_client.py +32 -9
- rasa/shared/providers/llm/llm_client.py +24 -8
- rasa/shared/providers/llm/llm_response.py +61 -2
- rasa/shared/providers/llm/openai_llm_client.py +11 -5
- rasa/shared/providers/llm/rasa_llm_client.py +17 -14
- rasa/shared/providers/llm/self_hosted_llm_client.py +35 -15
- rasa/shared/providers/mappings.py +18 -19
- rasa/shared/providers/router/_base_litellm_router_client.py +48 -15
- rasa/shared/providers/router/router_client.py +3 -1
- rasa/shared/utils/cli.py +1 -1
- rasa/shared/utils/common.py +15 -1
- rasa/shared/utils/constants.py +3 -0
- rasa/shared/utils/health_check/embeddings_health_check_mixin.py +1 -1
- rasa/shared/utils/health_check/health_check.py +3 -3
- rasa/shared/utils/health_check/llm_health_check_mixin.py +1 -1
- rasa/shared/utils/io.py +1 -1
- rasa/shared/utils/llm.py +100 -18
- rasa/shared/utils/pykwalify_extensions.py +25 -1
- rasa/shared/utils/schemas/domain.yml +26 -1
- rasa/shared/utils/schemas/events.py +1 -1
- rasa/shared/utils/yaml.py +24 -20
- rasa/studio/auth.py +3 -3
- rasa/studio/config.py +1 -2
- rasa/studio/data_handler.py +3 -3
- rasa/studio/download.py +1 -1
- rasa/studio/results_logger.py +3 -3
- rasa/studio/upload.py +21 -5
- rasa/telemetry.py +127 -48
- rasa/tracing/config.py +5 -3
- rasa/tracing/constants.py +12 -0
- rasa/tracing/instrumentation/attribute_extractors.py +92 -14
- rasa/tracing/instrumentation/instrumentation.py +61 -5
- rasa/tracing/instrumentation/intentless_policy_instrumentation.py +1 -1
- rasa/tracing/instrumentation/metrics.py +52 -11
- rasa/tracing/metric_instrument_provider.py +54 -14
- rasa/utils/common.py +12 -24
- rasa/utils/endpoints.py +1 -1
- rasa/utils/io.py +7 -7
- rasa/utils/licensing.py +3 -4
- rasa/utils/log_utils.py +7 -6
- rasa/utils/ml_utils.py +1 -0
- rasa/utils/plotting.py +3 -3
- rasa/utils/sanic_error_handler.py +1 -1
- rasa/utils/tensorflow/callback.py +2 -2
- rasa/utils/tensorflow/crf.py +2 -2
- rasa/utils/tensorflow/data_generator.py +5 -5
- rasa/utils/tensorflow/environment.py +3 -3
- rasa/utils/tensorflow/feature_array.py +2 -3
- rasa/utils/tensorflow/layers.py +18 -12
- rasa/utils/tensorflow/layers_utils.py +2 -1
- rasa/utils/tensorflow/metrics.py +2 -2
- rasa/utils/tensorflow/model_data.py +7 -7
- rasa/utils/tensorflow/model_data_utils.py +10 -9
- rasa/utils/tensorflow/models.py +31 -32
- rasa/utils/tensorflow/rasa_layers.py +20 -19
- rasa/utils/tensorflow/types.py +2 -1
- rasa/utils/train_utils.py +23 -21
- rasa/utils/url_tools.py +1 -1
- rasa/validator.py +594 -115
- rasa/version.py +1 -1
- {rasa_pro-3.11.5.dist-info → rasa_pro-3.12.0.dist-info}/METADATA +23 -26
- rasa_pro-3.12.0.dist-info/RECORD +829 -0
- rasa/core/channels/inspector/dist/assets/channel-e265ea59.js +0 -1
- rasa/core/channels/inspector/dist/assets/clone-21f8a43d.js +0 -1
- rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-5c8ce12d.js +0 -1
- rasa_pro-3.11.5.dist-info/RECORD +0 -785
- /rasa/dialogue_understanding/generator/{single_step → prompt_templates}/command_prompt_template.jinja2 +0 -0
- {rasa_pro-3.11.5.dist-info → rasa_pro-3.12.0.dist-info}/NOTICE +0 -0
- {rasa_pro-3.11.5.dist-info → rasa_pro-3.12.0.dist-info}/WHEEL +0 -0
- {rasa_pro-3.11.5.dist-info → rasa_pro-3.12.0.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import json
|
|
3
|
+
from typing import Any, Awaitable, Callable, Dict, Optional, Text
|
|
4
|
+
|
|
5
|
+
import structlog
|
|
6
|
+
from sanic import ( # type: ignore[attr-defined]
|
|
7
|
+
Blueprint,
|
|
8
|
+
HTTPResponse,
|
|
9
|
+
Request,
|
|
10
|
+
Websocket,
|
|
11
|
+
response,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
from rasa.core.channels import UserMessage
|
|
15
|
+
from rasa.core.channels.voice_ready.utils import CallParameters
|
|
16
|
+
from rasa.core.channels.voice_stream.audio_bytes import RasaAudioBytes
|
|
17
|
+
from rasa.core.channels.voice_stream.call_state import (
|
|
18
|
+
call_state,
|
|
19
|
+
)
|
|
20
|
+
from rasa.core.channels.voice_stream.tts.tts_engine import TTSEngine
|
|
21
|
+
from rasa.core.channels.voice_stream.voice_channel import (
|
|
22
|
+
ContinueConversationAction,
|
|
23
|
+
EndConversationAction,
|
|
24
|
+
NewAudioAction,
|
|
25
|
+
VoiceChannelAction,
|
|
26
|
+
VoiceInputChannel,
|
|
27
|
+
VoiceOutputChannel,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
"""
|
|
31
|
+
Genesys throws a rate limit error with too many audio messages.
|
|
32
|
+
To avoid this, we buffer the audio messages and send them in chunks.
|
|
33
|
+
|
|
34
|
+
- global.inbound.binary.average.rate.per.second: 5
|
|
35
|
+
The allowed average rate per second of inbound binary data
|
|
36
|
+
|
|
37
|
+
- global.inbound.binary.max: 25
|
|
38
|
+
The maximum number of inbound binary data messages
|
|
39
|
+
that can be sent instantaneously
|
|
40
|
+
|
|
41
|
+
https://developer.genesys.cloud/organization/organization/limits#audiohook
|
|
42
|
+
|
|
43
|
+
The maximum binary message size is not mentioned
|
|
44
|
+
in the documentation but observed in their example app
|
|
45
|
+
https://github.com/GenesysCloudBlueprints/audioconnector-server-reference-implementation
|
|
46
|
+
"""
|
|
47
|
+
MAXIMUM_BINARY_MESSAGE_SIZE = 64000 # 64KB
|
|
48
|
+
logger = structlog.get_logger(__name__)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def map_call_params(data: Dict[Text, Any]) -> CallParameters:
|
|
52
|
+
"""Map the twilio stream parameters to the CallParameters dataclass."""
|
|
53
|
+
parameters = data["parameters"]
|
|
54
|
+
participant = parameters["participant"]
|
|
55
|
+
# sent as {"ani": "tel:+491604697810"}
|
|
56
|
+
ani = participant.get("ani", "")
|
|
57
|
+
user_phone = ani.split(":")[-1] if ani else ""
|
|
58
|
+
|
|
59
|
+
return CallParameters(
|
|
60
|
+
call_id=parameters.get("conversationId", ""),
|
|
61
|
+
user_phone=user_phone,
|
|
62
|
+
bot_phone=participant.get("dnis", ""),
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class GenesysOutputChannel(VoiceOutputChannel):
|
|
67
|
+
@classmethod
|
|
68
|
+
def name(cls) -> str:
|
|
69
|
+
return "genesys"
|
|
70
|
+
|
|
71
|
+
async def send_audio_bytes(
|
|
72
|
+
self, recipient_id: str, audio_bytes: RasaAudioBytes
|
|
73
|
+
) -> None:
|
|
74
|
+
await self.voice_websocket.send(audio_bytes)
|
|
75
|
+
|
|
76
|
+
async def send_marker_message(self, recipient_id: str) -> None:
|
|
77
|
+
"""
|
|
78
|
+
Send a message that marks positions in the audio stream.
|
|
79
|
+
Genesys does not support this feature, so we do nothing here.
|
|
80
|
+
"""
|
|
81
|
+
pass
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class GenesysInputChannel(VoiceInputChannel):
|
|
85
|
+
@classmethod
|
|
86
|
+
def name(cls) -> str:
|
|
87
|
+
return "genesys"
|
|
88
|
+
|
|
89
|
+
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
90
|
+
super().__init__(*args, **kwargs)
|
|
91
|
+
|
|
92
|
+
def _ensure_channel_data_initialized(self) -> None:
|
|
93
|
+
"""Initialize Genesys-specific channel data if not already present.
|
|
94
|
+
|
|
95
|
+
Genesys requires the server and client each maintain a
|
|
96
|
+
monotonically increasing message sequence number.
|
|
97
|
+
"""
|
|
98
|
+
if "server_sequence_number" not in call_state.channel_data:
|
|
99
|
+
call_state.channel_data["server_sequence_number"] = 0
|
|
100
|
+
if "client_sequence_number" not in call_state.channel_data:
|
|
101
|
+
call_state.channel_data["client_sequence_number"] = 0
|
|
102
|
+
|
|
103
|
+
def _get_next_sequence(self) -> int:
|
|
104
|
+
"""
|
|
105
|
+
Get the next message sequence number
|
|
106
|
+
Rasa == Server
|
|
107
|
+
Genesys == Client
|
|
108
|
+
|
|
109
|
+
Genesys requires the server and client each maintain a
|
|
110
|
+
monotonically increasing message sequence number.
|
|
111
|
+
"""
|
|
112
|
+
self._ensure_channel_data_initialized()
|
|
113
|
+
call_state.channel_data["server_sequence_number"] += 1
|
|
114
|
+
return call_state.channel_data["server_sequence_number"]
|
|
115
|
+
|
|
116
|
+
def _get_last_client_sequence(self) -> int:
|
|
117
|
+
"""Get the last client(Genesys) sequence number."""
|
|
118
|
+
self._ensure_channel_data_initialized()
|
|
119
|
+
return call_state.channel_data["client_sequence_number"]
|
|
120
|
+
|
|
121
|
+
def _update_client_sequence(self, seq: int) -> None:
|
|
122
|
+
"""Update the client(Genesys) sequence number."""
|
|
123
|
+
self._ensure_channel_data_initialized()
|
|
124
|
+
|
|
125
|
+
if seq - call_state.channel_data["client_sequence_number"] != 1:
|
|
126
|
+
logger.warning(
|
|
127
|
+
"genesys.update_client_sequence.sequence_gap",
|
|
128
|
+
received_seq=seq,
|
|
129
|
+
last_seq=call_state.channel_data["client_sequence_number"],
|
|
130
|
+
)
|
|
131
|
+
call_state.channel_data["client_sequence_number"] = seq
|
|
132
|
+
|
|
133
|
+
def channel_bytes_to_rasa_audio_bytes(self, input_bytes: bytes) -> RasaAudioBytes:
|
|
134
|
+
return RasaAudioBytes(input_bytes)
|
|
135
|
+
|
|
136
|
+
async def collect_call_parameters(
|
|
137
|
+
self, channel_websocket: Websocket
|
|
138
|
+
) -> Optional[CallParameters]:
|
|
139
|
+
"""Call Parameters are collected during the open event."""
|
|
140
|
+
async for message in channel_websocket:
|
|
141
|
+
data = json.loads(message)
|
|
142
|
+
self._update_client_sequence(data["seq"])
|
|
143
|
+
if data.get("type") == "open":
|
|
144
|
+
call_params = await self.handle_open(channel_websocket, data)
|
|
145
|
+
return call_params
|
|
146
|
+
else:
|
|
147
|
+
logger.error("genesys.receive.unexpected_initial_message", message=data)
|
|
148
|
+
|
|
149
|
+
return None
|
|
150
|
+
|
|
151
|
+
def map_input_message(
|
|
152
|
+
self,
|
|
153
|
+
message: Any,
|
|
154
|
+
ws: Websocket,
|
|
155
|
+
) -> VoiceChannelAction:
|
|
156
|
+
# if message is binary, it's audio
|
|
157
|
+
if isinstance(message, bytes):
|
|
158
|
+
return NewAudioAction(self.channel_bytes_to_rasa_audio_bytes(message))
|
|
159
|
+
else:
|
|
160
|
+
# process text message
|
|
161
|
+
data = json.loads(message)
|
|
162
|
+
self._update_client_sequence(data["seq"])
|
|
163
|
+
msg_type = data.get("type")
|
|
164
|
+
if msg_type == "close":
|
|
165
|
+
logger.info("genesys.handle_close", message=data)
|
|
166
|
+
self.handle_close(ws, data)
|
|
167
|
+
return EndConversationAction()
|
|
168
|
+
elif msg_type == "ping":
|
|
169
|
+
logger.info("genesys.handle_ping", message=data)
|
|
170
|
+
self.handle_ping(ws, data)
|
|
171
|
+
elif msg_type == "playback_started":
|
|
172
|
+
logger.debug("genesys.handle_playback_started", message=data)
|
|
173
|
+
call_state.is_bot_speaking = True # type: ignore[attr-defined]
|
|
174
|
+
elif msg_type == "playback_completed":
|
|
175
|
+
logger.debug("genesys.handle_playback_completed", message=data)
|
|
176
|
+
call_state.is_bot_speaking = False # type: ignore[attr-defined]
|
|
177
|
+
if call_state.should_hangup:
|
|
178
|
+
logger.info("genesys.hangup")
|
|
179
|
+
self.disconnect(ws, data)
|
|
180
|
+
# the conversation should continue until
|
|
181
|
+
# we receive a close message from Genesys
|
|
182
|
+
elif msg_type == "dtmf":
|
|
183
|
+
logger.info("genesys.handle_dtmf", message=data)
|
|
184
|
+
elif msg_type == "error":
|
|
185
|
+
logger.warning("genesys.handle_error", message=data)
|
|
186
|
+
else:
|
|
187
|
+
logger.warning("genesys.map_input_message.unknown_type", message=data)
|
|
188
|
+
|
|
189
|
+
return ContinueConversationAction()
|
|
190
|
+
|
|
191
|
+
def create_output_channel(
|
|
192
|
+
self, voice_websocket: Websocket, tts_engine: TTSEngine
|
|
193
|
+
) -> VoiceOutputChannel:
|
|
194
|
+
return GenesysOutputChannel(
|
|
195
|
+
voice_websocket,
|
|
196
|
+
tts_engine,
|
|
197
|
+
self.tts_cache,
|
|
198
|
+
min_buffer_size=MAXIMUM_BINARY_MESSAGE_SIZE // 2,
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
async def handle_open(self, ws: Websocket, message: dict) -> CallParameters:
|
|
202
|
+
"""Handle initial open transaction from Genesys."""
|
|
203
|
+
call_parameters = map_call_params(message)
|
|
204
|
+
params = message["parameters"]
|
|
205
|
+
media_options = params.get("media", [])
|
|
206
|
+
|
|
207
|
+
# Send opened response
|
|
208
|
+
if media_options:
|
|
209
|
+
logger.info("genesys.handle_open", media_parameter=media_options[0])
|
|
210
|
+
response = {
|
|
211
|
+
"version": "2",
|
|
212
|
+
"type": "opened",
|
|
213
|
+
"seq": self._get_next_sequence(),
|
|
214
|
+
"clientseq": self._get_last_client_sequence(),
|
|
215
|
+
"id": message.get("id"),
|
|
216
|
+
"parameters": {"startPaused": False, "media": [media_options[0]]},
|
|
217
|
+
}
|
|
218
|
+
logger.debug("genesys.handle_open.opened", response=response)
|
|
219
|
+
await ws.send(json.dumps(response))
|
|
220
|
+
else:
|
|
221
|
+
logger.warning(
|
|
222
|
+
"genesys.handle_open.no_media_formats", client_message=message
|
|
223
|
+
)
|
|
224
|
+
return call_parameters
|
|
225
|
+
|
|
226
|
+
def handle_ping(self, ws: Websocket, message: dict) -> None:
|
|
227
|
+
"""Handle ping message from Genesys."""
|
|
228
|
+
response = {
|
|
229
|
+
"version": "2",
|
|
230
|
+
"type": "pong",
|
|
231
|
+
"seq": self._get_next_sequence(),
|
|
232
|
+
"clientseq": message.get("seq"),
|
|
233
|
+
"id": message.get("id"),
|
|
234
|
+
"parameters": {},
|
|
235
|
+
}
|
|
236
|
+
logger.debug("genesys.handle_ping.pong", response=response)
|
|
237
|
+
_schedule_ws_task(ws.send(json.dumps(response)))
|
|
238
|
+
|
|
239
|
+
def handle_close(self, ws: Websocket, message: dict) -> None:
|
|
240
|
+
"""Handle close message from Genesys."""
|
|
241
|
+
response = {
|
|
242
|
+
"version": "2",
|
|
243
|
+
"type": "closed",
|
|
244
|
+
"seq": self._get_next_sequence(),
|
|
245
|
+
"clientseq": self._get_last_client_sequence(),
|
|
246
|
+
"id": message.get("id"),
|
|
247
|
+
"parameters": message.get("parameters", {}),
|
|
248
|
+
}
|
|
249
|
+
logger.debug("genesys.handle_close.closed", response=response)
|
|
250
|
+
|
|
251
|
+
_schedule_ws_task(ws.send(json.dumps(response)))
|
|
252
|
+
|
|
253
|
+
def disconnect(self, ws: Websocket, data: dict) -> None:
|
|
254
|
+
"""
|
|
255
|
+
Send disconnect message to Genesys.
|
|
256
|
+
|
|
257
|
+
https://developer.genesys.cloud/devapps/audiohook/protocol-reference#disconnect
|
|
258
|
+
It should be used to hangup the call.
|
|
259
|
+
Genesys will respond with a "close" message to us
|
|
260
|
+
that is handled by the handle_close method.
|
|
261
|
+
"""
|
|
262
|
+
message = {
|
|
263
|
+
"version": "2",
|
|
264
|
+
"type": "disconnect",
|
|
265
|
+
"seq": self._get_next_sequence(),
|
|
266
|
+
"clientseq": self._get_last_client_sequence(),
|
|
267
|
+
"id": data.get("id"),
|
|
268
|
+
"parameters": {
|
|
269
|
+
"reason": "completed",
|
|
270
|
+
# arbitrary values can be sent here
|
|
271
|
+
},
|
|
272
|
+
}
|
|
273
|
+
logger.debug("genesys.disconnect", message=message)
|
|
274
|
+
_schedule_ws_task(ws.send(json.dumps(message)))
|
|
275
|
+
|
|
276
|
+
def blueprint(
|
|
277
|
+
self, on_new_message: Callable[[UserMessage], Awaitable[Any]]
|
|
278
|
+
) -> Blueprint:
|
|
279
|
+
"""Defines a Sanic blueprint for the voice input channel."""
|
|
280
|
+
blueprint = Blueprint("genesys", __name__)
|
|
281
|
+
|
|
282
|
+
@blueprint.route("/", methods=["GET"])
|
|
283
|
+
async def health(_: Request) -> HTTPResponse:
|
|
284
|
+
return response.json({"status": "ok"})
|
|
285
|
+
|
|
286
|
+
@blueprint.websocket("/websocket") # type: ignore[misc]
|
|
287
|
+
async def receive(request: Request, ws: Websocket) -> None:
|
|
288
|
+
logger.debug(
|
|
289
|
+
"genesys.receive",
|
|
290
|
+
audiohook_session_id=request.headers.get("audiohook-session-id"),
|
|
291
|
+
)
|
|
292
|
+
# validate required headers
|
|
293
|
+
required_headers = [
|
|
294
|
+
"audiohook-organization-id",
|
|
295
|
+
"audiohook-correlation-id",
|
|
296
|
+
"audiohook-session-id",
|
|
297
|
+
"x-api-key",
|
|
298
|
+
]
|
|
299
|
+
|
|
300
|
+
for header in required_headers:
|
|
301
|
+
if header not in request.headers:
|
|
302
|
+
await ws.close(1008, f"Missing required header: {header}")
|
|
303
|
+
return
|
|
304
|
+
|
|
305
|
+
# TODO: validate API key header
|
|
306
|
+
# process audio streaming
|
|
307
|
+
logger.info("genesys.receive", message="Starting audio streaming")
|
|
308
|
+
await self.run_audio_streaming(on_new_message, ws)
|
|
309
|
+
|
|
310
|
+
return blueprint
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def _schedule_ws_task(coro: Awaitable[Any]) -> None:
|
|
314
|
+
"""Helper function to schedule a coroutine in the event loop.
|
|
315
|
+
|
|
316
|
+
Args:
|
|
317
|
+
coro: The coroutine to schedule
|
|
318
|
+
"""
|
|
319
|
+
loop = asyncio.get_running_loop()
|
|
320
|
+
loop.call_soon_threadsafe(lambda: loop.create_task(coro))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import os
|
|
2
|
-
from typing import AsyncIterator, Dict, Optional
|
|
3
2
|
from dataclasses import dataclass
|
|
3
|
+
from typing import AsyncIterator, Dict, Optional
|
|
4
4
|
|
|
5
5
|
import aiohttp
|
|
6
6
|
import structlog
|
|
@@ -15,13 +15,13 @@ from rasa.core.channels.voice_stream.tts.tts_engine import (
|
|
|
15
15
|
from rasa.shared.constants import AZURE_SPEECH_API_KEY_ENV_VAR
|
|
16
16
|
from rasa.shared.exceptions import ConnectionException
|
|
17
17
|
|
|
18
|
-
|
|
19
18
|
structlogger = structlog.get_logger()
|
|
20
19
|
|
|
21
20
|
|
|
22
21
|
@dataclass
|
|
23
22
|
class AzureTTSConfig(TTSEngineConfig):
|
|
24
23
|
speech_region: Optional[str] = None
|
|
24
|
+
endpoint: Optional[str] = None
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
class AzureTTS(TTSEngine[AzureTTSConfig]):
|
|
@@ -77,12 +77,19 @@ class AzureTTS(TTSEngine[AzureTTSConfig]):
|
|
|
77
77
|
|
|
78
78
|
@staticmethod
|
|
79
79
|
def get_tts_endpoint(config: AzureTTSConfig) -> str:
|
|
80
|
-
|
|
80
|
+
if config.endpoint is not None:
|
|
81
|
+
return config.endpoint
|
|
82
|
+
else:
|
|
83
|
+
return (
|
|
84
|
+
f"https://{config.speech_region}.tts.speech.microsoft.com/"
|
|
85
|
+
f"cognitiveservices/v1"
|
|
86
|
+
)
|
|
81
87
|
|
|
82
88
|
@staticmethod
|
|
83
89
|
def create_request_body(text: str, conf: AzureTTSConfig) -> str:
|
|
84
90
|
return f"""
|
|
85
|
-
<speak version='1.0' xml:lang='{conf.language}'
|
|
91
|
+
<speak version='1.0' xml:lang='{conf.language}' xmlns:mstts='http://www.w3.org/2001/mstts'
|
|
92
|
+
xmlns='http://www.w3.org/2001/10/synthesis'>
|
|
86
93
|
<voice xml:lang='{conf.language}' name='{conf.voice}'>
|
|
87
94
|
{text}
|
|
88
95
|
</voice>
|
|
@@ -98,7 +105,8 @@ class AzureTTS(TTSEngine[AzureTTSConfig]):
|
|
|
98
105
|
language="en-US",
|
|
99
106
|
voice="en-US-JennyNeural",
|
|
100
107
|
timeout=10,
|
|
101
|
-
speech_region="
|
|
108
|
+
speech_region="eastus",
|
|
109
|
+
endpoint=None,
|
|
102
110
|
)
|
|
103
111
|
|
|
104
112
|
@classmethod
|
|
@@ -1,16 +1,19 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import json
|
|
3
|
+
import os
|
|
1
4
|
from dataclasses import dataclass
|
|
2
5
|
from typing import AsyncIterator, Dict, Optional
|
|
3
|
-
|
|
6
|
+
|
|
4
7
|
import aiohttp
|
|
5
8
|
import structlog
|
|
6
9
|
from aiohttp import ClientConnectorError, ClientTimeout
|
|
7
10
|
|
|
11
|
+
from rasa.core.channels.voice_stream.audio_bytes import HERTZ, RasaAudioBytes
|
|
8
12
|
from rasa.core.channels.voice_stream.tts.tts_engine import (
|
|
13
|
+
TTSEngine,
|
|
9
14
|
TTSEngineConfig,
|
|
15
|
+
TTSError,
|
|
10
16
|
)
|
|
11
|
-
|
|
12
|
-
from rasa.core.channels.voice_stream.audio_bytes import HERTZ, RasaAudioBytes
|
|
13
|
-
from rasa.core.channels.voice_stream.tts.tts_engine import TTSEngine, TTSError
|
|
14
17
|
from rasa.shared.constants import CARTESIA_API_KEY_ENV_VAR
|
|
15
18
|
from rasa.shared.exceptions import ConnectionException
|
|
16
19
|
|
|
@@ -21,6 +24,7 @@ structlogger = structlog.get_logger()
|
|
|
21
24
|
class CartesiaTTSConfig(TTSEngineConfig):
|
|
22
25
|
model_id: Optional[str] = None
|
|
23
26
|
version: Optional[str] = None
|
|
27
|
+
endpoint: Optional[str] = None
|
|
24
28
|
|
|
25
29
|
|
|
26
30
|
class CartesiaTTS(TTSEngine[CartesiaTTSConfig]):
|
|
@@ -35,11 +39,6 @@ class CartesiaTTS(TTSEngine[CartesiaTTSConfig]):
|
|
|
35
39
|
if self.__class__.session is None or self.__class__.session.closed:
|
|
36
40
|
self.__class__.session = aiohttp.ClientSession(timeout=timeout)
|
|
37
41
|
|
|
38
|
-
@staticmethod
|
|
39
|
-
def get_tts_endpoint() -> str:
|
|
40
|
-
"""Create the endpoint string for cartesia."""
|
|
41
|
-
return "https://api.cartesia.ai/tts/bytes"
|
|
42
|
-
|
|
43
42
|
@staticmethod
|
|
44
43
|
def get_request_body(text: str, config: CartesiaTTSConfig) -> Dict:
|
|
45
44
|
"""Create the request body for cartesia."""
|
|
@@ -76,7 +75,7 @@ class CartesiaTTS(TTSEngine[CartesiaTTSConfig]):
|
|
|
76
75
|
config = self.config.merge(config)
|
|
77
76
|
payload = self.get_request_body(text, config)
|
|
78
77
|
headers = self.get_request_headers(config)
|
|
79
|
-
url = self.
|
|
78
|
+
url = self.config.endpoint
|
|
80
79
|
if self.session is None:
|
|
81
80
|
raise ConnectionException("Client session is not initialized")
|
|
82
81
|
try:
|
|
@@ -84,16 +83,36 @@ class CartesiaTTS(TTSEngine[CartesiaTTSConfig]):
|
|
|
84
83
|
url, headers=headers, json=payload, chunked=True
|
|
85
84
|
) as response:
|
|
86
85
|
if 200 <= response.status < 300:
|
|
87
|
-
async for
|
|
88
|
-
|
|
86
|
+
async for chunk in response.content:
|
|
87
|
+
# we are looking for chunks in the response that look like
|
|
88
|
+
# b"data: {..., data: <base64 encoded audio bytes> ...}"
|
|
89
|
+
# and extract the audio bytes from that
|
|
90
|
+
if chunk.startswith(b"data: "):
|
|
91
|
+
json_bytes = chunk[5:-1]
|
|
92
|
+
json_data = json.loads(json_bytes.decode())
|
|
93
|
+
if "data" in json_data:
|
|
94
|
+
base64_encoded_bytes = json_data["data"]
|
|
95
|
+
channel_bytes = base64.b64decode(base64_encoded_bytes)
|
|
96
|
+
yield self.engine_bytes_to_rasa_audio_bytes(
|
|
97
|
+
channel_bytes
|
|
98
|
+
)
|
|
89
99
|
return
|
|
100
|
+
elif response.status == 401:
|
|
101
|
+
structlogger.error(
|
|
102
|
+
"cartesia.synthesize.rest.unauthorized",
|
|
103
|
+
status_code=response.status,
|
|
104
|
+
)
|
|
105
|
+
raise TTSError(
|
|
106
|
+
"Unauthorized. Please make sure you have the correct API key."
|
|
107
|
+
)
|
|
90
108
|
else:
|
|
109
|
+
response_text = await response.text()
|
|
91
110
|
structlogger.error(
|
|
92
111
|
"cartesia.synthesize.rest.failed",
|
|
93
112
|
status_code=response.status,
|
|
94
|
-
msg=
|
|
113
|
+
msg=response_text,
|
|
95
114
|
)
|
|
96
|
-
raise TTSError(f"TTS failed: {
|
|
115
|
+
raise TTSError(f"TTS failed: {response_text}")
|
|
97
116
|
except ClientConnectorError as e:
|
|
98
117
|
raise TTSError(e)
|
|
99
118
|
except TimeoutError as e:
|
|
@@ -111,6 +130,7 @@ class CartesiaTTS(TTSEngine[CartesiaTTSConfig]):
|
|
|
111
130
|
timeout=10,
|
|
112
131
|
model_id="sonic-english",
|
|
113
132
|
version="2024-06-10",
|
|
133
|
+
endpoint="https://api.cartesia.ai/tts/sse",
|
|
114
134
|
)
|
|
115
135
|
|
|
116
136
|
@classmethod
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
from typing import Optional
|
|
2
|
-
from collections import OrderedDict
|
|
3
1
|
import logging
|
|
2
|
+
from collections import OrderedDict
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
4
5
|
from rasa.core.channels.voice_stream.audio_bytes import RasaAudioBytes
|
|
5
6
|
|
|
6
7
|
logger = logging.getLogger(__name__)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
from typing import AsyncIterator, Dict, Generic, Optional, Tuple, Type, TypeVar
|
|
2
1
|
from dataclasses import dataclass
|
|
2
|
+
from typing import AsyncIterator, Dict, Generic, Optional, Tuple, Type, TypeVar
|
|
3
3
|
|
|
4
4
|
from rasa.core.channels.voice_stream.audio_bytes import RasaAudioBytes
|
|
5
5
|
from rasa.core.channels.voice_stream.util import MergeableConfig
|
|
@@ -1,24 +1,27 @@
|
|
|
1
1
|
import base64
|
|
2
2
|
import json
|
|
3
3
|
import uuid
|
|
4
|
-
|
|
5
|
-
import structlog
|
|
6
4
|
from typing import Any, Awaitable, Callable, Dict, Optional, Text, Tuple
|
|
7
5
|
|
|
8
|
-
|
|
9
|
-
from sanic import
|
|
10
|
-
|
|
6
|
+
import structlog
|
|
7
|
+
from sanic import ( # type: ignore[attr-defined]
|
|
8
|
+
Blueprint,
|
|
9
|
+
HTTPResponse,
|
|
10
|
+
Request,
|
|
11
|
+
Websocket,
|
|
12
|
+
response,
|
|
13
|
+
)
|
|
11
14
|
|
|
12
15
|
from rasa.core.channels import UserMessage
|
|
13
16
|
from rasa.core.channels.voice_ready.utils import CallParameters
|
|
17
|
+
from rasa.core.channels.voice_stream.audio_bytes import RasaAudioBytes
|
|
14
18
|
from rasa.core.channels.voice_stream.call_state import call_state
|
|
15
19
|
from rasa.core.channels.voice_stream.tts.tts_engine import TTSEngine
|
|
16
|
-
from rasa.core.channels.voice_stream.audio_bytes import RasaAudioBytes
|
|
17
20
|
from rasa.core.channels.voice_stream.voice_channel import (
|
|
21
|
+
ContinueConversationAction,
|
|
18
22
|
EndConversationAction,
|
|
19
23
|
NewAudioAction,
|
|
20
24
|
VoiceChannelAction,
|
|
21
|
-
ContinueConversationAction,
|
|
22
25
|
VoiceInputChannel,
|
|
23
26
|
VoiceOutputChannel,
|
|
24
27
|
)
|
|
@@ -95,6 +98,7 @@ class TwilioMediaStreamsInputChannel(VoiceInputChannel):
|
|
|
95
98
|
def map_input_message(
|
|
96
99
|
self,
|
|
97
100
|
message: Any,
|
|
101
|
+
ws: Websocket,
|
|
98
102
|
) -> VoiceChannelAction:
|
|
99
103
|
data = json.loads(message)
|
|
100
104
|
if data["event"] == "media":
|
|
@@ -139,7 +143,7 @@ class TwilioMediaStreamsInputChannel(VoiceInputChannel):
|
|
|
139
143
|
def blueprint(
|
|
140
144
|
self, on_new_message: Callable[[UserMessage], Awaitable[Any]]
|
|
141
145
|
) -> Blueprint:
|
|
142
|
-
"""Defines a Sanic
|
|
146
|
+
"""Defines a Sanic blueprint for the voice input channel."""
|
|
143
147
|
blueprint = Blueprint("twilio_media_streams", __name__)
|
|
144
148
|
|
|
145
149
|
@blueprint.route("/", methods=["GET"])
|