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
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
2
|
-
from typing import Any, Dict, Optional
|
|
3
1
|
import json
|
|
4
2
|
import os
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Any, Dict, Optional
|
|
5
|
+
from urllib.parse import urlencode
|
|
5
6
|
|
|
7
|
+
import structlog
|
|
6
8
|
import websockets
|
|
9
|
+
import websockets.exceptions
|
|
7
10
|
from websockets.legacy.client import WebSocketClientProtocol
|
|
8
11
|
|
|
9
12
|
from rasa.core.channels.voice_stream.asr.asr_engine import ASREngine, ASREngineConfig
|
|
@@ -15,15 +18,20 @@ from rasa.core.channels.voice_stream.asr.asr_event import (
|
|
|
15
18
|
from rasa.core.channels.voice_stream.audio_bytes import HERTZ, RasaAudioBytes
|
|
16
19
|
from rasa.shared.constants import DEEPGRAM_API_KEY_ENV_VAR
|
|
17
20
|
|
|
21
|
+
logger = structlog.get_logger(__name__)
|
|
22
|
+
|
|
18
23
|
|
|
19
24
|
@dataclass
|
|
20
25
|
class DeepgramASRConfig(ASREngineConfig):
|
|
21
26
|
endpoint: Optional[str] = None
|
|
22
|
-
# number of
|
|
27
|
+
# number of milliseconds of silence to determine end of speech
|
|
23
28
|
endpointing: Optional[int] = None
|
|
24
29
|
language: Optional[str] = None
|
|
25
30
|
model: Optional[str] = None
|
|
26
31
|
smart_format: Optional[bool] = None
|
|
32
|
+
# number of milliseconds of no new transcript to determine end of speech
|
|
33
|
+
# should be at least 1000 according to docs
|
|
34
|
+
utterance_end_ms: Optional[int] = None
|
|
27
35
|
|
|
28
36
|
|
|
29
37
|
class DeepgramASR(ASREngine[DeepgramASRConfig]):
|
|
@@ -37,22 +45,47 @@ class DeepgramASR(ASREngine[DeepgramASRConfig]):
|
|
|
37
45
|
"""Connect to the ASR system."""
|
|
38
46
|
deepgram_api_key = os.environ[DEEPGRAM_API_KEY_ENV_VAR]
|
|
39
47
|
extra_headers = {"Authorization": f"Token {deepgram_api_key}"}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
48
|
+
try:
|
|
49
|
+
return await websockets.connect( # type: ignore
|
|
50
|
+
self._get_api_url_with_query_params(),
|
|
51
|
+
extra_headers=extra_headers,
|
|
52
|
+
)
|
|
53
|
+
except websockets.exceptions.InvalidStatusCode as e:
|
|
54
|
+
if e.status_code == 401:
|
|
55
|
+
error_msg = "Please make sure your Deepgram API key is correct."
|
|
56
|
+
else:
|
|
57
|
+
error_msg = "Connection to Deepgram failed."
|
|
58
|
+
logger.error(
|
|
59
|
+
"deepgram.connection.failed",
|
|
60
|
+
status_code=e.status_code,
|
|
61
|
+
error=error_msg,
|
|
62
|
+
)
|
|
63
|
+
raise
|
|
64
|
+
|
|
65
|
+
def _get_api_url_with_query_params(self) -> str:
|
|
66
|
+
"""Combine api url and query params."""
|
|
67
|
+
return self._get_api_url() + self._get_query_params()
|
|
46
68
|
|
|
47
69
|
def _get_api_url(self) -> str:
|
|
70
|
+
"""Get the api url with the configured endpoint."""
|
|
48
71
|
return f"wss://{self.config.endpoint}/v1/listen?"
|
|
49
72
|
|
|
50
73
|
def _get_query_params(self) -> str:
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
74
|
+
"""Get the configured query parameters for the api."""
|
|
75
|
+
query_params = {
|
|
76
|
+
"encoding": "mulaw",
|
|
77
|
+
"sample_rate": HERTZ,
|
|
78
|
+
"endpointing": self.config.endpointing,
|
|
79
|
+
"vad_events": "true",
|
|
80
|
+
"language": self.config.language,
|
|
81
|
+
"interim_results": "true",
|
|
82
|
+
"model": self.config.model,
|
|
83
|
+
"smart_format": str(self.config.smart_format).lower(),
|
|
84
|
+
}
|
|
85
|
+
if self.config.utterance_end_ms and self.config.utterance_end_ms > 0:
|
|
86
|
+
query_params["utterance_end_ms"] = self.config.utterance_end_ms
|
|
87
|
+
|
|
88
|
+
return urlencode(query_params)
|
|
56
89
|
|
|
57
90
|
async def signal_audio_done(self) -> None:
|
|
58
91
|
"""Signal to the ASR Api that you are done sending data."""
|
|
@@ -67,24 +100,48 @@ class DeepgramASR(ASREngine[DeepgramASRConfig]):
|
|
|
67
100
|
def engine_event_to_asr_event(self, e: Any) -> Optional[ASREvent]:
|
|
68
101
|
"""Translate an engine event to a common ASREvent."""
|
|
69
102
|
data = json.loads(e)
|
|
70
|
-
|
|
71
|
-
|
|
103
|
+
data_type = data["type"]
|
|
104
|
+
if data_type == "Results":
|
|
105
|
+
transcript_data = data["channel"]["alternatives"][0]
|
|
106
|
+
transcript = transcript_data["transcript"]
|
|
72
107
|
if data["is_final"]:
|
|
73
108
|
if data.get("speech_final"):
|
|
74
|
-
full_transcript = self.
|
|
109
|
+
full_transcript = self.concatenate_transcripts(
|
|
110
|
+
self.accumulated_transcript, transcript
|
|
111
|
+
)
|
|
75
112
|
self.accumulated_transcript = ""
|
|
76
113
|
if full_transcript:
|
|
77
114
|
return NewTranscript(full_transcript)
|
|
78
115
|
else:
|
|
79
|
-
self.accumulated_transcript
|
|
116
|
+
self.accumulated_transcript = self.concatenate_transcripts(
|
|
117
|
+
self.accumulated_transcript, transcript
|
|
118
|
+
)
|
|
80
119
|
elif transcript:
|
|
81
120
|
return UserIsSpeaking()
|
|
121
|
+
# event that comes after utterance_end_ms of no new transcript
|
|
122
|
+
elif data_type == "UtteranceEnd":
|
|
123
|
+
if self.accumulated_transcript:
|
|
124
|
+
transcript = self.accumulated_transcript
|
|
125
|
+
self.accumulated_transcript = ""
|
|
126
|
+
return NewTranscript(transcript)
|
|
82
127
|
return None
|
|
83
128
|
|
|
84
129
|
@staticmethod
|
|
85
130
|
def get_default_config() -> DeepgramASRConfig:
|
|
86
|
-
return DeepgramASRConfig(
|
|
131
|
+
return DeepgramASRConfig(
|
|
132
|
+
endpoint="api.deepgram.com",
|
|
133
|
+
endpointing=400,
|
|
134
|
+
language="en",
|
|
135
|
+
model="nova-2-general",
|
|
136
|
+
smart_format=True,
|
|
137
|
+
utterance_end_ms=1000,
|
|
138
|
+
)
|
|
87
139
|
|
|
88
140
|
@classmethod
|
|
89
141
|
def from_config_dict(cls, config: Dict) -> "DeepgramASR":
|
|
90
142
|
return DeepgramASR(DeepgramASRConfig.from_dict(config))
|
|
143
|
+
|
|
144
|
+
@staticmethod
|
|
145
|
+
def concatenate_transcripts(t1: str, t2: str) -> str:
|
|
146
|
+
"""Concatenate two transcripts making sure there is a space between them."""
|
|
147
|
+
return (t1.strip() + " " + t2.strip()).strip()
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import base64
|
|
3
|
+
import json
|
|
4
|
+
from typing import Any, Awaitable, Callable, Dict, Optional, Text
|
|
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
|
|
16
|
+
from rasa.core.channels.voice_ready.audiocodes import map_call_params
|
|
17
|
+
from rasa.core.channels.voice_ready.utils import CallParameters
|
|
18
|
+
from rasa.core.channels.voice_stream.audio_bytes import RasaAudioBytes
|
|
19
|
+
from rasa.core.channels.voice_stream.call_state import (
|
|
20
|
+
call_state,
|
|
21
|
+
)
|
|
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
|
+
from rasa.shared.utils.common import mark_as_beta_feature
|
|
32
|
+
|
|
33
|
+
logger = structlog.get_logger(__name__)
|
|
34
|
+
PREFERRED_AUDIO_FORMAT = "raw/mulaw"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class AudiocodesVoiceOutputChannel(VoiceOutputChannel):
|
|
38
|
+
@classmethod
|
|
39
|
+
def name(cls) -> str:
|
|
40
|
+
return "audiocodes_stream"
|
|
41
|
+
|
|
42
|
+
def _ensure_stream_id(self) -> None:
|
|
43
|
+
"""Audiocodes requires a stream ID with playStream messages."""
|
|
44
|
+
if "stream_id" not in call_state.channel_data:
|
|
45
|
+
call_state.channel_data["stream_id"] = 0
|
|
46
|
+
|
|
47
|
+
def _increment_stream_id(self) -> None:
|
|
48
|
+
self._ensure_stream_id()
|
|
49
|
+
call_state.channel_data["stream_id"] += 1
|
|
50
|
+
|
|
51
|
+
def _get_stream_id(self) -> str:
|
|
52
|
+
self._ensure_stream_id()
|
|
53
|
+
return str(call_state.channel_data["stream_id"])
|
|
54
|
+
|
|
55
|
+
def rasa_audio_bytes_to_channel_bytes(
|
|
56
|
+
self, rasa_audio_bytes: RasaAudioBytes
|
|
57
|
+
) -> bytes:
|
|
58
|
+
return base64.b64encode(rasa_audio_bytes)
|
|
59
|
+
|
|
60
|
+
def channel_bytes_to_message(self, recipient_id: str, channel_bytes: bytes) -> str:
|
|
61
|
+
media_message = json.dumps(
|
|
62
|
+
{
|
|
63
|
+
"type": "playStream.chunk",
|
|
64
|
+
"streamId": self._get_stream_id(),
|
|
65
|
+
"audioChunk": channel_bytes.decode("utf-8"),
|
|
66
|
+
}
|
|
67
|
+
)
|
|
68
|
+
return media_message
|
|
69
|
+
|
|
70
|
+
async def send_start_marker(self, recipient_id: str) -> None:
|
|
71
|
+
"""Send playStream.start before first audio chunk."""
|
|
72
|
+
self._increment_stream_id()
|
|
73
|
+
media_message = json.dumps(
|
|
74
|
+
{
|
|
75
|
+
"type": "playStream.start",
|
|
76
|
+
"streamId": self._get_stream_id(),
|
|
77
|
+
"mediaFormat": PREFERRED_AUDIO_FORMAT,
|
|
78
|
+
}
|
|
79
|
+
)
|
|
80
|
+
logger.debug("Sending start marker", stream_id=self._get_stream_id())
|
|
81
|
+
await self.voice_websocket.send(media_message)
|
|
82
|
+
|
|
83
|
+
async def send_intermediate_marker(self, recipient_id: str) -> None:
|
|
84
|
+
"""Audiocodes doesn't need intermediate markers, so do nothing."""
|
|
85
|
+
pass
|
|
86
|
+
|
|
87
|
+
async def send_end_marker(self, recipient_id: str) -> None:
|
|
88
|
+
"""Send playStream.stop after last audio chunk."""
|
|
89
|
+
media_message = json.dumps(
|
|
90
|
+
{
|
|
91
|
+
"type": "playStream.stop",
|
|
92
|
+
"streamId": self._get_stream_id(),
|
|
93
|
+
}
|
|
94
|
+
)
|
|
95
|
+
logger.debug("Sending end marker", stream_id=self._get_stream_id())
|
|
96
|
+
await self.voice_websocket.send(media_message)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class AudiocodesVoiceInputChannel(VoiceInputChannel):
|
|
100
|
+
@classmethod
|
|
101
|
+
def name(cls) -> str:
|
|
102
|
+
return "audiocodes_stream"
|
|
103
|
+
|
|
104
|
+
def __init__(
|
|
105
|
+
self,
|
|
106
|
+
server_url: str,
|
|
107
|
+
asr_config: Dict,
|
|
108
|
+
tts_config: Dict,
|
|
109
|
+
monitor_silence: bool = False,
|
|
110
|
+
):
|
|
111
|
+
mark_as_beta_feature("Audiocodes (audiocodes_stream) Channel")
|
|
112
|
+
super().__init__(server_url, asr_config, tts_config, monitor_silence)
|
|
113
|
+
|
|
114
|
+
def channel_bytes_to_rasa_audio_bytes(self, input_bytes: bytes) -> RasaAudioBytes:
|
|
115
|
+
return RasaAudioBytes(base64.b64decode(input_bytes))
|
|
116
|
+
|
|
117
|
+
async def collect_call_parameters(
|
|
118
|
+
self, channel_websocket: Websocket
|
|
119
|
+
) -> Optional[CallParameters]:
|
|
120
|
+
async for message in channel_websocket:
|
|
121
|
+
data = json.loads(message)
|
|
122
|
+
if data["type"] == "session.initiate":
|
|
123
|
+
# contains info about mediaformats
|
|
124
|
+
logger.info(
|
|
125
|
+
"audiocodes_stream.collect_call_parameters.session.initiate",
|
|
126
|
+
data=data,
|
|
127
|
+
)
|
|
128
|
+
self._send_accepted(channel_websocket, data)
|
|
129
|
+
elif data["type"] == "activities":
|
|
130
|
+
activities = data["activities"]
|
|
131
|
+
for activity in activities:
|
|
132
|
+
logger.debug(
|
|
133
|
+
"audiocodes_stream.collect_call_parameters.activity",
|
|
134
|
+
data=activity,
|
|
135
|
+
)
|
|
136
|
+
if activity["name"] == "start":
|
|
137
|
+
return map_call_params(activity["parameters"])
|
|
138
|
+
else:
|
|
139
|
+
logger.warning("audiocodes_stream.unknown_message", data=data)
|
|
140
|
+
return None
|
|
141
|
+
|
|
142
|
+
def map_input_message(
|
|
143
|
+
self,
|
|
144
|
+
message: Any,
|
|
145
|
+
ws: Websocket,
|
|
146
|
+
) -> VoiceChannelAction:
|
|
147
|
+
data = json.loads(message)
|
|
148
|
+
if data["type"] == "activities":
|
|
149
|
+
activities = data["activities"]
|
|
150
|
+
for activity in activities:
|
|
151
|
+
logger.debug("audiocodes_stream.activity", data=activity)
|
|
152
|
+
if activity["name"] == "start":
|
|
153
|
+
# already handled in collect_call_parameters
|
|
154
|
+
pass
|
|
155
|
+
elif activity["name"] == "dtmf":
|
|
156
|
+
# TODO: handle DTMF input
|
|
157
|
+
pass
|
|
158
|
+
elif activity["name"] == "playFinished":
|
|
159
|
+
logger.debug("audiocodes_stream.playFinished", data=activity)
|
|
160
|
+
if call_state.should_hangup:
|
|
161
|
+
logger.info("audiocodes.hangup")
|
|
162
|
+
self._send_hangup(ws, data)
|
|
163
|
+
# the conversation should continue until
|
|
164
|
+
# we receive a end message from audiocodes
|
|
165
|
+
pass
|
|
166
|
+
else:
|
|
167
|
+
logger.warning("audiocodes_stream.unknown_activity", data=activity)
|
|
168
|
+
elif data["type"] == "userStream.start":
|
|
169
|
+
logger.debug("audiocodes_stream.userStream.start", data=data)
|
|
170
|
+
self._send_recognition_started(ws, data)
|
|
171
|
+
elif data["type"] == "userStream.chunk":
|
|
172
|
+
audio_bytes = self.channel_bytes_to_rasa_audio_bytes(data["audioChunk"])
|
|
173
|
+
return NewAudioAction(audio_bytes)
|
|
174
|
+
elif data["type"] == "userStream.stop":
|
|
175
|
+
logger.debug("audiocodes_stream.stop_recognition", data=data)
|
|
176
|
+
self._send_recognition_ended(ws, data)
|
|
177
|
+
elif data["type"] == "session.resume":
|
|
178
|
+
logger.debug("audiocodes_stream.resume", data=data)
|
|
179
|
+
self._send_accepted(ws, data)
|
|
180
|
+
elif data["type"] == "session.end":
|
|
181
|
+
logger.debug("audiocodes_stream.end", data=data)
|
|
182
|
+
return EndConversationAction()
|
|
183
|
+
elif data["type"] == "connection.validate":
|
|
184
|
+
# not part of call flow; only sent when integration is created
|
|
185
|
+
self._send_validated(ws, data)
|
|
186
|
+
else:
|
|
187
|
+
logger.warning("audiocodes_stream.unknown_message", data=data)
|
|
188
|
+
|
|
189
|
+
return ContinueConversationAction()
|
|
190
|
+
|
|
191
|
+
def _send_accepted(self, ws: Websocket, data: Dict[Text, Any]) -> None:
|
|
192
|
+
supported_formats = data.get("supportedMediaFormats", [])
|
|
193
|
+
preferred_format = PREFERRED_AUDIO_FORMAT
|
|
194
|
+
|
|
195
|
+
if preferred_format not in supported_formats:
|
|
196
|
+
logger.warning(
|
|
197
|
+
"audiocodes_stream.format_not_supported",
|
|
198
|
+
supported_formats=supported_formats,
|
|
199
|
+
preferred_format=preferred_format,
|
|
200
|
+
)
|
|
201
|
+
raise
|
|
202
|
+
|
|
203
|
+
payload = {
|
|
204
|
+
"type": "session.accepted",
|
|
205
|
+
"mediaFormat": PREFERRED_AUDIO_FORMAT,
|
|
206
|
+
}
|
|
207
|
+
_schedule_async_task(ws.send(json.dumps(payload)))
|
|
208
|
+
|
|
209
|
+
def _send_recognition_started(self, ws: Websocket, data: Dict[Text, Any]) -> None:
|
|
210
|
+
payload = {"type": "userStream.started"}
|
|
211
|
+
_schedule_async_task(ws.send(json.dumps(payload)))
|
|
212
|
+
|
|
213
|
+
def _send_recognition_ended(self, ws: Websocket, data: Dict[Text, Any]) -> None:
|
|
214
|
+
payload = {"type": "userStream.stopped"}
|
|
215
|
+
_schedule_async_task(ws.send(json.dumps(payload)))
|
|
216
|
+
|
|
217
|
+
def _send_hypothesis(self, ws: Websocket, data: Dict[Text, Any]) -> None:
|
|
218
|
+
"""
|
|
219
|
+
TODO: The hypothesis message is sent by the bot to provide partial
|
|
220
|
+
recognition results. Using this message is recommended,
|
|
221
|
+
as VAIC relies on it for performing barge-in.
|
|
222
|
+
"""
|
|
223
|
+
pass
|
|
224
|
+
|
|
225
|
+
def _send_recognition(self, ws: Websocket, data: Dict[Text, Any]) -> None:
|
|
226
|
+
"""
|
|
227
|
+
TODO: The recognition message is sent by the bot to provide
|
|
228
|
+
the final recognition result. Using this message is recommended
|
|
229
|
+
mainly for logging purposes.
|
|
230
|
+
"""
|
|
231
|
+
pass
|
|
232
|
+
|
|
233
|
+
def _send_hangup(self, ws: Websocket, data: Dict[Text, Any]) -> None:
|
|
234
|
+
payload = {
|
|
235
|
+
"conversationId": data["conversationId"],
|
|
236
|
+
"type": "activities",
|
|
237
|
+
"activities": [{"type": "event", "name": "hangup"}],
|
|
238
|
+
}
|
|
239
|
+
_schedule_async_task(ws.send(json.dumps(payload)))
|
|
240
|
+
|
|
241
|
+
def _send_validated(self, ws: Websocket, data: Dict[Text, Any]) -> None:
|
|
242
|
+
payload = {
|
|
243
|
+
"type": "connection.validated",
|
|
244
|
+
"success": True,
|
|
245
|
+
}
|
|
246
|
+
_schedule_async_task(ws.send(json.dumps(payload)))
|
|
247
|
+
|
|
248
|
+
def create_output_channel(
|
|
249
|
+
self, voice_websocket: Websocket, tts_engine: TTSEngine
|
|
250
|
+
) -> VoiceOutputChannel:
|
|
251
|
+
return AudiocodesVoiceOutputChannel(
|
|
252
|
+
voice_websocket,
|
|
253
|
+
tts_engine,
|
|
254
|
+
self.tts_cache,
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
def blueprint(
|
|
258
|
+
self, on_new_message: Callable[[UserMessage], Awaitable[Any]]
|
|
259
|
+
) -> Blueprint:
|
|
260
|
+
"""Defines a Sanic bluelogger.debug."""
|
|
261
|
+
blueprint = Blueprint("audiocodes_stream", __name__)
|
|
262
|
+
|
|
263
|
+
@blueprint.route("/", methods=["GET"])
|
|
264
|
+
async def health(_: Request) -> HTTPResponse:
|
|
265
|
+
return response.json({"status": "ok"})
|
|
266
|
+
|
|
267
|
+
@blueprint.websocket("/websocket") # type: ignore
|
|
268
|
+
async def receive(request: Request, ws: Websocket) -> None:
|
|
269
|
+
# TODO: validate API key header
|
|
270
|
+
logger.info("audiocodes.receive", message="Starting audio streaming")
|
|
271
|
+
try:
|
|
272
|
+
await self.run_audio_streaming(on_new_message, ws)
|
|
273
|
+
except Exception as e:
|
|
274
|
+
logger.exception(
|
|
275
|
+
"audiocodes.receive",
|
|
276
|
+
message="Error during audio streaming",
|
|
277
|
+
error=e,
|
|
278
|
+
)
|
|
279
|
+
# return 500 error
|
|
280
|
+
raise
|
|
281
|
+
|
|
282
|
+
return blueprint
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def _schedule_async_task(coro: Awaitable[Any]) -> None:
|
|
286
|
+
"""Helper function to schedule a coroutine in the event loop.
|
|
287
|
+
|
|
288
|
+
Args:
|
|
289
|
+
coro: The coroutine to schedule
|
|
290
|
+
"""
|
|
291
|
+
loop = asyncio.get_running_loop()
|
|
292
|
+
loop.call_soon_threadsafe(lambda: loop.create_task(coro))
|
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
import audioop
|
|
2
2
|
import base64
|
|
3
3
|
import json
|
|
4
|
-
|
|
5
|
-
import structlog
|
|
6
4
|
import uuid
|
|
7
5
|
from typing import Any, Awaitable, Callable, Optional, Tuple
|
|
8
6
|
|
|
9
|
-
|
|
10
|
-
from sanic import
|
|
11
|
-
|
|
7
|
+
import structlog
|
|
8
|
+
from sanic import ( # type: ignore[attr-defined]
|
|
9
|
+
Blueprint,
|
|
10
|
+
HTTPResponse,
|
|
11
|
+
Request,
|
|
12
|
+
Websocket,
|
|
13
|
+
response,
|
|
14
|
+
)
|
|
12
15
|
|
|
13
16
|
from rasa.core.channels import UserMessage
|
|
14
17
|
from rasa.core.channels.voice_ready.utils import CallParameters
|
|
18
|
+
from rasa.core.channels.voice_stream.audio_bytes import RasaAudioBytes
|
|
15
19
|
from rasa.core.channels.voice_stream.call_state import call_state
|
|
16
20
|
from rasa.core.channels.voice_stream.tts.tts_engine import TTSEngine
|
|
17
|
-
from rasa.core.channels.voice_stream.audio_bytes import RasaAudioBytes
|
|
18
21
|
from rasa.core.channels.voice_stream.voice_channel import (
|
|
19
22
|
ContinueConversationAction,
|
|
20
23
|
EndConversationAction,
|
|
@@ -62,6 +65,7 @@ class BrowserAudioInputChannel(VoiceInputChannel):
|
|
|
62
65
|
def map_input_message(
|
|
63
66
|
self,
|
|
64
67
|
message: Any,
|
|
68
|
+
ws: Websocket,
|
|
65
69
|
) -> VoiceChannelAction:
|
|
66
70
|
data = json.loads(message)
|
|
67
71
|
if "audio" in data:
|
|
@@ -102,6 +106,9 @@ class BrowserAudioInputChannel(VoiceInputChannel):
|
|
|
102
106
|
|
|
103
107
|
@blueprint.websocket("/websocket") # type: ignore
|
|
104
108
|
async def handle_message(request: Request, ws: Websocket) -> None:
|
|
105
|
-
|
|
109
|
+
try:
|
|
110
|
+
await self.run_audio_streaming(on_new_message, ws)
|
|
111
|
+
except Exception as e:
|
|
112
|
+
logger.error("browser_audio.handle_message.error", error=e)
|
|
106
113
|
|
|
107
114
|
return blueprint
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
from contextvars import ContextVar
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from typing import Any, Dict, Optional
|
|
5
|
+
|
|
3
6
|
from werkzeug.local import LocalProxy
|
|
4
|
-
from dataclasses import dataclass
|
|
5
|
-
from typing import Optional
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
# Per voice session data
|
|
@@ -18,6 +19,9 @@ class CallState:
|
|
|
18
19
|
should_hangup: bool = False
|
|
19
20
|
connection_failed: bool = False
|
|
20
21
|
|
|
22
|
+
# Generic field for channel-specific state data
|
|
23
|
+
channel_data: Dict[str, Any] = field(default_factory=dict)
|
|
24
|
+
|
|
21
25
|
|
|
22
26
|
_call_state: ContextVar[CallState] = ContextVar("call_state")
|
|
23
27
|
call_state = LocalProxy(_call_state)
|