rasa-pro 3.14.0.dev20250922__py3-none-any.whl → 3.14.0rc1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of rasa-pro might be problematic. Click here for more details.
- rasa/__main__.py +15 -3
- rasa/agents/__init__.py +0 -0
- rasa/agents/agent_factory.py +122 -0
- rasa/agents/agent_manager.py +211 -0
- rasa/agents/constants.py +43 -0
- rasa/agents/core/__init__.py +0 -0
- rasa/agents/core/agent_protocol.py +107 -0
- rasa/agents/core/types.py +81 -0
- rasa/agents/exceptions.py +38 -0
- rasa/agents/protocol/__init__.py +5 -0
- rasa/agents/protocol/a2a/__init__.py +0 -0
- rasa/agents/protocol/a2a/a2a_agent.py +879 -0
- rasa/agents/protocol/mcp/__init__.py +0 -0
- rasa/agents/protocol/mcp/mcp_base_agent.py +726 -0
- rasa/agents/protocol/mcp/mcp_open_agent.py +327 -0
- rasa/agents/protocol/mcp/mcp_task_agent.py +522 -0
- rasa/agents/schemas/__init__.py +13 -0
- rasa/agents/schemas/agent_input.py +38 -0
- rasa/agents/schemas/agent_output.py +26 -0
- rasa/agents/schemas/agent_tool_result.py +65 -0
- rasa/agents/schemas/agent_tool_schema.py +186 -0
- rasa/agents/templates/__init__.py +0 -0
- rasa/agents/templates/mcp_open_agent_prompt_template.jinja2 +20 -0
- rasa/agents/templates/mcp_task_agent_prompt_template.jinja2 +22 -0
- rasa/agents/utils.py +206 -0
- rasa/agents/validation.py +485 -0
- rasa/api.py +24 -9
- rasa/builder/config.py +6 -2
- rasa/builder/guardrails/{lakera.py → clients.py} +55 -5
- rasa/builder/guardrails/constants.py +3 -0
- rasa/builder/guardrails/models.py +45 -10
- rasa/builder/guardrails/policy_checker.py +324 -0
- rasa/builder/guardrails/utils.py +42 -276
- rasa/builder/llm_service.py +32 -5
- rasa/builder/models.py +1 -0
- rasa/builder/project_generator.py +6 -1
- rasa/builder/service.py +16 -13
- rasa/builder/training_service.py +18 -24
- rasa/builder/validation_service.py +1 -1
- rasa/cli/arguments/default_arguments.py +12 -0
- rasa/cli/arguments/run.py +2 -0
- rasa/cli/arguments/train.py +2 -0
- rasa/cli/data.py +10 -8
- rasa/cli/dialogue_understanding_test.py +10 -7
- rasa/cli/e2e_test.py +9 -6
- rasa/cli/evaluate.py +4 -2
- rasa/cli/export.py +5 -2
- rasa/cli/inspect.py +8 -4
- rasa/cli/interactive.py +5 -4
- rasa/cli/llm_fine_tuning.py +11 -6
- rasa/cli/project_templates/tutorial/credentials.yml +10 -0
- rasa/cli/run.py +12 -10
- rasa/cli/scaffold.py +4 -4
- rasa/cli/shell.py +9 -5
- rasa/cli/studio/studio.py +1 -1
- rasa/cli/test.py +34 -14
- rasa/cli/train.py +41 -28
- rasa/cli/utils.py +1 -393
- rasa/cli/validation/__init__.py +0 -0
- rasa/cli/validation/bot_config.py +223 -0
- rasa/cli/validation/config_path_validation.py +257 -0
- rasa/cli/x.py +8 -4
- rasa/constants.py +7 -1
- rasa/core/actions/action.py +51 -10
- rasa/core/actions/grpc_custom_action_executor.py +1 -1
- rasa/core/agent.py +19 -2
- rasa/core/available_agents.py +229 -0
- rasa/core/channels/__init__.py +82 -35
- rasa/core/channels/development_inspector.py +3 -3
- rasa/core/channels/inspector/README.md +25 -13
- rasa/core/channels/inspector/dist/assets/{arc-35222594.js → arc-6177260a.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{blockDiagram-38ab4fdb-a0efbfd3.js → blockDiagram-38ab4fdb-b054f038.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{c4Diagram-3d4e48cf-0584c0f2.js → c4Diagram-3d4e48cf-f25427d5.js} +1 -1
- rasa/core/channels/inspector/dist/assets/channel-bf9cbb34.js +1 -0
- rasa/core/channels/inspector/dist/assets/{classDiagram-70f12bd4-39f40dbe.js → classDiagram-70f12bd4-c7a2af53.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{classDiagram-v2-f2320105-1ad755f3.js → classDiagram-v2-f2320105-58db65c0.js} +1 -1
- rasa/core/channels/inspector/dist/assets/clone-8f9083bb.js +1 -0
- rasa/core/channels/inspector/dist/assets/{createText-2e5e7dd3-b0f4f0fe.js → createText-2e5e7dd3-088372e2.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{edges-e0da2a9e-9039bff9.js → edges-e0da2a9e-58676240.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{erDiagram-9861fffd-65c9b127.js → erDiagram-9861fffd-0c14d7c6.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{flowDb-956e92f1-4f08b38e.js → flowDb-956e92f1-ea63f85c.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{flowDiagram-66a62f08-e95c362a.js → flowDiagram-66a62f08-a2af48cd.js} +1 -1
- rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-9ecd5b59.js +1 -0
- rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-4a651766-703c3015.js → flowchart-elk-definition-4a651766-6937abe7.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{ganttDiagram-c361ad54-699328ea.js → ganttDiagram-c361ad54-7473f357.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-72cf32ee-04cf4b05.js → gitGraphDiagram-72cf32ee-d0c9405e.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{graph-ee94449e.js → graph-0a6f8466.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{index-3862675e-940162b4.js → index-3862675e-7610671a.js} +1 -1
- rasa/core/channels/inspector/dist/assets/index-74e01d94.js +1354 -0
- rasa/core/channels/inspector/dist/assets/{infoDiagram-f8f76790-c79c2866.js → infoDiagram-f8f76790-be397dc7.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{journeyDiagram-49397b02-84489d30.js → journeyDiagram-49397b02-4cefbf62.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{layout-a9aa9858.js → layout-e7fbc2bf.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{line-eb73cf26.js → line-a8aa457c.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{linear-b3399f9a.js → linear-3351e0d2.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{mindmap-definition-fc14e90a-b095bf1a.js → mindmap-definition-fc14e90a-b8cbf605.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{pieDiagram-8a3498a8-07644b66.js → pieDiagram-8a3498a8-f327f774.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{quadrantDiagram-120e2f19-573a3f9c.js → quadrantDiagram-120e2f19-2854c591.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{requirementDiagram-deff3bca-d457e1e1.js → requirementDiagram-deff3bca-964985d5.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{sankeyDiagram-04a897e0-9d26e1a2.js → sankeyDiagram-04a897e0-edeb4f33.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{sequenceDiagram-704730f1-3a9cde10.js → sequenceDiagram-704730f1-fcf70125.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{stateDiagram-587899a1-4f3e8cec.js → stateDiagram-587899a1-0e770395.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-d93cdb3a-e617e5bf.js → stateDiagram-v2-d93cdb3a-af8dcd22.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-6aaf32cf-eab30d2f.js → styles-6aaf32cf-36a9e70d.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-9a916d00-09994be2.js → styles-9a916d00-884a8b5b.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{styles-c10674c1-b7110364.js → styles-c10674c1-dc097813.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{svgDrawCommon-08f97a94-3ebc92ad.js → svgDrawCommon-08f97a94-5a2c7eed.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{timeline-definition-85554ec2-7d13d2f2.js → timeline-definition-85554ec2-e89c4f6e.js} +1 -1
- rasa/core/channels/inspector/dist/assets/{xychartDiagram-e933f94c-488385e1.js → xychartDiagram-e933f94c-afb6fe56.js} +1 -1
- rasa/core/channels/inspector/dist/index.html +1 -1
- rasa/core/channels/inspector/package.json +18 -18
- rasa/core/channels/inspector/src/App.tsx +29 -4
- rasa/core/channels/inspector/src/components/DialogueAgentStack.tsx +108 -0
- rasa/core/channels/inspector/src/components/{DialogueStack.tsx → DialogueHistoryStack.tsx} +4 -2
- rasa/core/channels/inspector/src/helpers/audio/audiostream.ts +7 -4
- rasa/core/channels/inspector/src/helpers/formatters.test.ts +4 -0
- rasa/core/channels/inspector/src/helpers/formatters.ts +24 -3
- rasa/core/channels/inspector/src/helpers/utils.test.ts +127 -0
- rasa/core/channels/inspector/src/helpers/utils.ts +66 -1
- rasa/core/channels/inspector/src/theme/base/styles.ts +19 -1
- rasa/core/channels/inspector/src/types.ts +21 -0
- rasa/core/channels/inspector/yarn.lock +336 -189
- rasa/core/channels/studio_chat.py +6 -6
- rasa/core/channels/telegram.py +4 -9
- rasa/core/channels/voice_stream/genesys.py +1 -1
- rasa/core/channels/voice_stream/tts/deepgram.py +140 -0
- rasa/core/channels/voice_stream/twilio_media_streams.py +5 -1
- rasa/core/channels/voice_stream/voice_channel.py +3 -0
- rasa/core/config/__init__.py +0 -0
- rasa/core/{available_endpoints.py → config/available_endpoints.py} +51 -16
- rasa/core/config/configuration.py +260 -0
- rasa/core/config/credentials.py +19 -0
- rasa/core/config/message_procesing_config.py +34 -0
- rasa/core/constants.py +4 -0
- rasa/core/policies/enterprise_search_policy.py +5 -3
- rasa/core/policies/flow_policy.py +4 -4
- rasa/core/policies/flows/agent_executor.py +632 -0
- rasa/core/policies/flows/flow_executor.py +136 -75
- rasa/core/policies/flows/mcp_tool_executor.py +298 -0
- rasa/core/policies/intentless_policy.py +1 -1
- rasa/core/policies/ted_policy.py +20 -12
- rasa/core/policies/unexpected_intent_policy.py +6 -0
- rasa/core/processor.py +68 -44
- rasa/core/run.py +37 -8
- rasa/core/test.py +4 -0
- rasa/core/tracker_stores/tracker_store.py +3 -7
- rasa/core/train.py +1 -1
- rasa/core/training/interactive.py +20 -18
- rasa/core/training/story_conflict.py +5 -5
- rasa/core/utils.py +22 -23
- rasa/dialogue_understanding/commands/__init__.py +8 -0
- rasa/dialogue_understanding/commands/cancel_flow_command.py +19 -5
- rasa/dialogue_understanding/commands/chit_chat_answer_command.py +21 -2
- rasa/dialogue_understanding/commands/clarify_command.py +20 -2
- rasa/dialogue_understanding/commands/continue_agent_command.py +91 -0
- rasa/dialogue_understanding/commands/knowledge_answer_command.py +21 -2
- rasa/dialogue_understanding/commands/restart_agent_command.py +162 -0
- rasa/dialogue_understanding/commands/start_flow_command.py +68 -7
- rasa/dialogue_understanding/commands/utils.py +124 -2
- rasa/dialogue_understanding/generator/command_parser.py +4 -0
- rasa/dialogue_understanding/generator/llm_based_command_generator.py +50 -12
- rasa/dialogue_understanding/generator/llm_command_generator.py +1 -1
- rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +1 -1
- rasa/dialogue_understanding/generator/prompt_templates/agent_command_prompt_v2_claude_3_5_sonnet_20240620_template.jinja2 +66 -0
- rasa/dialogue_understanding/generator/prompt_templates/agent_command_prompt_v2_gpt_4o_2024_11_20_template.jinja2 +66 -0
- rasa/dialogue_understanding/generator/prompt_templates/agent_command_prompt_v3_claude_3_5_sonnet_20240620_template.jinja2 +89 -0
- rasa/dialogue_understanding/generator/prompt_templates/agent_command_prompt_v3_gpt_4o_2024_11_20_template.jinja2 +88 -0
- rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +42 -7
- rasa/dialogue_understanding/generator/single_step/search_ready_llm_command_generator.py +40 -3
- rasa/dialogue_understanding/generator/single_step/single_step_based_llm_command_generator.py +20 -3
- rasa/dialogue_understanding/patterns/cancel.py +27 -6
- rasa/dialogue_understanding/patterns/clarify.py +3 -14
- rasa/dialogue_understanding/patterns/continue_interrupted.py +239 -6
- rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +46 -8
- rasa/dialogue_understanding/processor/command_processor.py +136 -15
- rasa/dialogue_understanding/stack/dialogue_stack.py +98 -2
- rasa/dialogue_understanding/stack/frames/flow_stack_frame.py +57 -0
- rasa/dialogue_understanding/stack/utils.py +57 -3
- rasa/dialogue_understanding/utils.py +24 -4
- rasa/dialogue_understanding_test/du_test_runner.py +8 -3
- rasa/e2e_test/e2e_test_runner.py +13 -3
- rasa/engine/caching.py +2 -2
- rasa/engine/constants.py +1 -1
- rasa/engine/recipes/default_components.py +138 -49
- rasa/engine/recipes/default_recipe.py +108 -11
- rasa/engine/runner/dask.py +8 -5
- rasa/engine/validation.py +19 -6
- rasa/graph_components/validators/default_recipe_validator.py +86 -28
- rasa/hooks.py +5 -5
- rasa/llm_fine_tuning/utils.py +2 -2
- rasa/model_training.py +60 -47
- rasa/nlu/classifiers/diet_classifier.py +198 -98
- rasa/nlu/classifiers/logistic_regression_classifier.py +1 -4
- rasa/nlu/classifiers/mitie_intent_classifier.py +3 -0
- rasa/nlu/classifiers/sklearn_intent_classifier.py +1 -3
- rasa/nlu/extractors/crf_entity_extractor.py +9 -10
- rasa/nlu/extractors/mitie_entity_extractor.py +3 -0
- rasa/nlu/extractors/spacy_entity_extractor.py +3 -0
- rasa/nlu/featurizers/dense_featurizer/convert_featurizer.py +4 -0
- rasa/nlu/featurizers/dense_featurizer/lm_featurizer.py +5 -0
- rasa/nlu/featurizers/dense_featurizer/mitie_featurizer.py +2 -0
- rasa/nlu/featurizers/dense_featurizer/spacy_featurizer.py +3 -0
- rasa/nlu/featurizers/sparse_featurizer/count_vectors_featurizer.py +4 -2
- rasa/nlu/featurizers/sparse_featurizer/lexical_syntactic_featurizer.py +4 -0
- rasa/nlu/selectors/response_selector.py +10 -2
- rasa/nlu/tokenizers/jieba_tokenizer.py +3 -4
- rasa/nlu/tokenizers/mitie_tokenizer.py +3 -2
- rasa/nlu/tokenizers/spacy_tokenizer.py +3 -2
- rasa/nlu/utils/mitie_utils.py +3 -0
- rasa/nlu/utils/spacy_utils.py +3 -2
- rasa/plugin.py +8 -8
- rasa/privacy/privacy_manager.py +12 -3
- rasa/server.py +15 -3
- rasa/shared/agents/__init__.py +0 -0
- rasa/shared/agents/auth/__init__.py +0 -0
- rasa/shared/agents/auth/agent_auth_factory.py +105 -0
- rasa/shared/agents/auth/agent_auth_manager.py +92 -0
- rasa/shared/agents/auth/auth_strategy/__init__.py +19 -0
- rasa/shared/agents/auth/auth_strategy/agent_auth_strategy.py +52 -0
- rasa/shared/agents/auth/auth_strategy/api_key_auth_strategy.py +42 -0
- rasa/shared/agents/auth/auth_strategy/bearer_token_auth_strategy.py +28 -0
- rasa/shared/agents/auth/auth_strategy/oauth2_auth_strategy.py +167 -0
- rasa/shared/agents/auth/constants.py +12 -0
- rasa/shared/agents/auth/types.py +12 -0
- rasa/shared/agents/utils.py +35 -0
- rasa/shared/constants.py +8 -0
- rasa/shared/core/constants.py +16 -1
- rasa/shared/core/domain.py +0 -7
- rasa/shared/core/events.py +327 -0
- rasa/shared/core/flows/constants.py +5 -0
- rasa/shared/core/flows/flows_list.py +21 -5
- rasa/shared/core/flows/flows_yaml_schema.json +119 -184
- rasa/shared/core/flows/steps/call.py +49 -5
- rasa/shared/core/flows/steps/collect.py +98 -13
- rasa/shared/core/flows/validation.py +372 -8
- rasa/shared/core/flows/yaml_flows_io.py +3 -2
- rasa/shared/core/slots.py +2 -2
- rasa/shared/core/trackers.py +5 -2
- rasa/shared/exceptions.py +16 -0
- rasa/shared/importers/rasa.py +1 -1
- rasa/shared/importers/utils.py +9 -3
- rasa/shared/providers/llm/_base_litellm_client.py +41 -9
- rasa/shared/providers/llm/litellm_router_llm_client.py +8 -4
- rasa/shared/providers/llm/llm_client.py +7 -3
- rasa/shared/providers/llm/llm_response.py +66 -0
- rasa/shared/providers/llm/self_hosted_llm_client.py +8 -4
- rasa/shared/utils/common.py +24 -0
- rasa/shared/utils/health_check/health_check.py +7 -3
- rasa/shared/utils/llm.py +39 -16
- rasa/shared/utils/mcp/__init__.py +0 -0
- rasa/shared/utils/mcp/server_connection.py +247 -0
- rasa/shared/utils/mcp/utils.py +20 -0
- rasa/shared/utils/schemas/events.py +42 -0
- rasa/shared/utils/yaml.py +3 -1
- rasa/studio/pull/pull.py +3 -2
- rasa/studio/train.py +8 -7
- rasa/studio/upload.py +3 -6
- rasa/telemetry.py +69 -5
- rasa/tracing/config.py +45 -12
- rasa/tracing/constants.py +14 -0
- rasa/tracing/instrumentation/attribute_extractors.py +142 -9
- rasa/tracing/instrumentation/instrumentation.py +626 -21
- rasa/tracing/instrumentation/intentless_policy_instrumentation.py +4 -4
- rasa/tracing/instrumentation/metrics.py +32 -0
- rasa/tracing/metric_instrument_provider.py +68 -0
- rasa/utils/common.py +92 -1
- rasa/utils/endpoints.py +11 -2
- rasa/utils/log_utils.py +96 -5
- rasa/utils/ml_utils.py +1 -1
- rasa/utils/tensorflow/__init__.py +7 -0
- rasa/utils/tensorflow/callback.py +136 -101
- rasa/utils/tensorflow/crf.py +1 -1
- rasa/utils/tensorflow/data_generator.py +21 -8
- rasa/utils/tensorflow/layers.py +21 -11
- rasa/utils/tensorflow/metrics.py +7 -3
- rasa/utils/tensorflow/models.py +56 -8
- rasa/utils/tensorflow/rasa_layers.py +8 -6
- rasa/utils/tensorflow/transformer.py +2 -3
- rasa/utils/train_utils.py +54 -24
- rasa/validator.py +5 -5
- rasa/version.py +1 -1
- {rasa_pro-3.14.0.dev20250922.dist-info → rasa_pro-3.14.0rc1.dist-info}/METADATA +46 -41
- {rasa_pro-3.14.0.dev20250922.dist-info → rasa_pro-3.14.0rc1.dist-info}/RECORD +285 -226
- rasa/builder/scrape_rasa_docs.py +0 -97
- rasa/core/channels/inspector/dist/assets/channel-8e08bed9.js +0 -1
- rasa/core/channels/inspector/dist/assets/clone-78c82dea.js +0 -1
- rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-2b08f601.js +0 -1
- rasa/core/channels/inspector/dist/assets/index-c941dcb3.js +0 -1336
- {rasa_pro-3.14.0.dev20250922.dist-info → rasa_pro-3.14.0rc1.dist-info}/NOTICE +0 -0
- {rasa_pro-3.14.0.dev20250922.dist-info → rasa_pro-3.14.0rc1.dist-info}/WHEEL +0 -0
- {rasa_pro-3.14.0.dev20250922.dist-info → rasa_pro-3.14.0rc1.dist-info}/entry_points.txt +0 -0
|
@@ -93,12 +93,12 @@ class StudioTrackerUpdatePlugin:
|
|
|
93
93
|
"""Remove tasks that have already completed."""
|
|
94
94
|
self.tasks = [task for task in self.tasks if not task.done()]
|
|
95
95
|
|
|
96
|
-
@hookimpl
|
|
96
|
+
@hookimpl
|
|
97
97
|
def after_new_user_message(self, tracker: "DialogueStateTracker") -> None:
|
|
98
98
|
"""Triggers a tracker update notification after a new user message."""
|
|
99
99
|
self.handle_tracker_update(tracker)
|
|
100
100
|
|
|
101
|
-
@hookimpl
|
|
101
|
+
@hookimpl
|
|
102
102
|
def after_action_executed(self, tracker: "DialogueStateTracker") -> None:
|
|
103
103
|
"""Triggers a tracker update notification after an action is executed."""
|
|
104
104
|
self.handle_tracker_update(tracker)
|
|
@@ -118,7 +118,7 @@ class StudioTrackerUpdatePlugin:
|
|
|
118
118
|
self.tasks.append(task)
|
|
119
119
|
self._cleanup_tasks()
|
|
120
120
|
|
|
121
|
-
@hookimpl
|
|
121
|
+
@hookimpl
|
|
122
122
|
def after_server_stop(self) -> None:
|
|
123
123
|
"""Cancels all remaining tasks when the server stops."""
|
|
124
124
|
self._cancel_tasks()
|
|
@@ -438,7 +438,7 @@ class StudioChatInput(SocketIOInput, VoiceInputChannel):
|
|
|
438
438
|
if sid in self.active_connections:
|
|
439
439
|
del self.active_connections[sid]
|
|
440
440
|
|
|
441
|
-
@hookimpl
|
|
441
|
+
@hookimpl
|
|
442
442
|
def after_server_stop(self) -> None:
|
|
443
443
|
"""Cleanup background tasks and active connections when the server stops."""
|
|
444
444
|
structlogger.info("studio_chat.after_server_stop.cleanup")
|
|
@@ -533,7 +533,7 @@ class StudioVoiceOutputChannel(VoiceOutputChannel):
|
|
|
533
533
|
|
|
534
534
|
def create_marker_message(self, recipient_id: str) -> Tuple[str, str]:
|
|
535
535
|
message_id = uuid.uuid4().hex
|
|
536
|
-
marker_data = {"marker": message_id}
|
|
536
|
+
marker_data: Dict[str, Any] = {"marker": message_id}
|
|
537
537
|
|
|
538
538
|
# Include comprehensive latency information if available
|
|
539
539
|
latency_data = {
|
|
@@ -548,7 +548,7 @@ class StudioVoiceOutputChannel(VoiceOutputChannel):
|
|
|
548
548
|
|
|
549
549
|
# Add latency data to marker if any metrics are available
|
|
550
550
|
if latency_data:
|
|
551
|
-
marker_data["latency"] = latency_data
|
|
551
|
+
marker_data["latency"] = latency_data
|
|
552
552
|
|
|
553
553
|
return json.dumps(marker_data), message_id
|
|
554
554
|
|
rasa/core/channels/telegram.py
CHANGED
|
@@ -4,6 +4,9 @@ import typing
|
|
|
4
4
|
from copy import deepcopy
|
|
5
5
|
from typing import Any, Awaitable, Callable, Dict, List, Optional, Text
|
|
6
6
|
|
|
7
|
+
# Import aiogram at module level to raise error if not installed
|
|
8
|
+
from aiogram import Bot
|
|
9
|
+
from aiogram.types import Message, Update
|
|
7
10
|
from sanic import Blueprint, response
|
|
8
11
|
from sanic.request import Request
|
|
9
12
|
from sanic.response import HTTPResponse
|
|
@@ -28,15 +31,7 @@ class TelegramOutput(OutputChannel):
|
|
|
28
31
|
return "telegram"
|
|
29
32
|
|
|
30
33
|
def __init__(self, access_token: Optional[Text]) -> None:
|
|
31
|
-
|
|
32
|
-
from aiogram import Bot
|
|
33
|
-
|
|
34
|
-
self.bot = Bot(access_token)
|
|
35
|
-
except ImportError:
|
|
36
|
-
raise ImportError(
|
|
37
|
-
"To use the Telegram channel, please install the aiogram package "
|
|
38
|
-
"with 'pip install aiogram'"
|
|
39
|
-
)
|
|
34
|
+
self.bot = Bot(access_token)
|
|
40
35
|
|
|
41
36
|
async def send_text_message(
|
|
42
37
|
self, recipient_id: Text, text: Text, **kwargs: Any
|
|
@@ -275,7 +275,7 @@ class GenesysInputChannel(VoiceInputChannel):
|
|
|
275
275
|
|
|
276
276
|
def handle_ping(self, ws: Websocket, message: dict) -> None:
|
|
277
277
|
"""Handle ping message from Genesys."""
|
|
278
|
-
response = {
|
|
278
|
+
response: Dict[str, Any] = {
|
|
279
279
|
"version": "2",
|
|
280
280
|
"type": "pong",
|
|
281
281
|
"seq": self._get_next_sequence(),
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import AsyncIterator, Dict, Optional
|
|
4
|
+
from urllib.parse import urlencode
|
|
5
|
+
|
|
6
|
+
import aiohttp
|
|
7
|
+
import orjson
|
|
8
|
+
import structlog
|
|
9
|
+
from aiohttp import ClientConnectorError, ClientTimeout, WSMsgType
|
|
10
|
+
|
|
11
|
+
from rasa.core.channels.voice_stream.audio_bytes import RasaAudioBytes
|
|
12
|
+
from rasa.core.channels.voice_stream.tts.tts_engine import (
|
|
13
|
+
TTSEngine,
|
|
14
|
+
TTSEngineConfig,
|
|
15
|
+
TTSError,
|
|
16
|
+
)
|
|
17
|
+
from rasa.shared.constants import DEEPGRAM_API_KEY_ENV_VAR
|
|
18
|
+
from rasa.shared.exceptions import ConnectionException
|
|
19
|
+
|
|
20
|
+
structlogger = structlog.get_logger()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass
|
|
24
|
+
class DeepgramTTSConfig(TTSEngineConfig):
|
|
25
|
+
model_id: Optional[str] = None
|
|
26
|
+
endpoint: Optional[str] = None
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class DeepgramTTS(TTSEngine[DeepgramTTSConfig]):
|
|
30
|
+
session: Optional[aiohttp.ClientSession] = None
|
|
31
|
+
required_env_vars = (DEEPGRAM_API_KEY_ENV_VAR,)
|
|
32
|
+
ws: Optional[aiohttp.ClientWebSocketResponse] = None
|
|
33
|
+
|
|
34
|
+
def __init__(self, config: Optional[DeepgramTTSConfig] = None):
|
|
35
|
+
super().__init__(config)
|
|
36
|
+
timeout = ClientTimeout(total=self.config.timeout)
|
|
37
|
+
# Have to create this class-shared session lazily at run time otherwise
|
|
38
|
+
# the async event loop doesn't work
|
|
39
|
+
if self.__class__.session is None or self.__class__.session.closed:
|
|
40
|
+
self.__class__.session = aiohttp.ClientSession(timeout=timeout)
|
|
41
|
+
|
|
42
|
+
@staticmethod
|
|
43
|
+
def get_request_headers(config: DeepgramTTSConfig) -> dict[str, str]:
|
|
44
|
+
deepgram_api_key = os.environ[DEEPGRAM_API_KEY_ENV_VAR]
|
|
45
|
+
return {
|
|
46
|
+
"Authorization": f"Token {deepgram_api_key!s}",
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async def close_connection(self) -> None:
|
|
50
|
+
"""Close WebSocket connection if it exists."""
|
|
51
|
+
if self.ws and not self.ws.closed:
|
|
52
|
+
await self.ws.close()
|
|
53
|
+
self.ws = None
|
|
54
|
+
|
|
55
|
+
def get_websocket_url(self, config: DeepgramTTSConfig) -> str:
|
|
56
|
+
"""Build WebSocket URL with query parameters."""
|
|
57
|
+
base_url = config.endpoint
|
|
58
|
+
query_params = {
|
|
59
|
+
"model": config.model_id,
|
|
60
|
+
"encoding": "mulaw",
|
|
61
|
+
"sample_rate": "8000",
|
|
62
|
+
}
|
|
63
|
+
return f"{base_url}?{urlencode(query_params)}"
|
|
64
|
+
|
|
65
|
+
async def synthesize(
|
|
66
|
+
self, text: str, config: Optional[DeepgramTTSConfig] = None
|
|
67
|
+
) -> AsyncIterator[RasaAudioBytes]:
|
|
68
|
+
"""Generate speech from text using Deepgram WebSocket TTS API."""
|
|
69
|
+
config = self.config.merge(config)
|
|
70
|
+
headers = self.get_request_headers(config)
|
|
71
|
+
ws_url = self.get_websocket_url(config)
|
|
72
|
+
|
|
73
|
+
if self.session is None:
|
|
74
|
+
raise ConnectionException("Client session is not initialized")
|
|
75
|
+
|
|
76
|
+
try:
|
|
77
|
+
self.ws = await self.session.ws_connect(
|
|
78
|
+
ws_url,
|
|
79
|
+
headers=headers,
|
|
80
|
+
timeout=float(self.config.timeout),
|
|
81
|
+
)
|
|
82
|
+
await self.ws.send_json(
|
|
83
|
+
{
|
|
84
|
+
"type": "Speak",
|
|
85
|
+
"text": text,
|
|
86
|
+
}
|
|
87
|
+
)
|
|
88
|
+
await self.ws.send_json({"type": "Flush"})
|
|
89
|
+
async for msg in self.ws:
|
|
90
|
+
if msg.type == WSMsgType.BINARY:
|
|
91
|
+
# Binary data is the raw audio
|
|
92
|
+
yield self.engine_bytes_to_rasa_audio_bytes(msg.data)
|
|
93
|
+
elif msg.type == WSMsgType.TEXT:
|
|
94
|
+
# Handle control messages if needed
|
|
95
|
+
data = orjson.loads(msg.data)
|
|
96
|
+
if data.get("type") == "Close":
|
|
97
|
+
break
|
|
98
|
+
elif data.get("type") == "Flushed":
|
|
99
|
+
break # End of stream
|
|
100
|
+
elif msg.type == WSMsgType.CLOSED:
|
|
101
|
+
break
|
|
102
|
+
elif msg.type == WSMsgType.ERROR:
|
|
103
|
+
structlogger.error(
|
|
104
|
+
"deepgram.synthesize.ws.error", error=str(msg.data)
|
|
105
|
+
)
|
|
106
|
+
raise TTSError(f"WebSocket error: {msg.data}")
|
|
107
|
+
|
|
108
|
+
# Send a close message
|
|
109
|
+
if self.ws and not self.ws.closed:
|
|
110
|
+
await self.ws.send_json({"type": "Close"})
|
|
111
|
+
|
|
112
|
+
except ClientConnectorError as e:
|
|
113
|
+
structlogger.error("deepgram.synthesize.ws.connection_error", error=str(e))
|
|
114
|
+
raise TTSError(f"Failed to connect to Deepgram TTS service: {e}")
|
|
115
|
+
except TimeoutError as e:
|
|
116
|
+
structlogger.error("deepgram.synthesize.ws.timeout", error=str(e))
|
|
117
|
+
raise TTSError(f"Connection to Deepgram TTS service timed out: {e}")
|
|
118
|
+
except Exception as e:
|
|
119
|
+
structlogger.error("deepgram.synthesize.ws.error", error=str(e))
|
|
120
|
+
raise TTSError(f"Error during TTS synthesis: {e}")
|
|
121
|
+
finally:
|
|
122
|
+
# Ensure connection is closed
|
|
123
|
+
await self.close_connection()
|
|
124
|
+
|
|
125
|
+
def engine_bytes_to_rasa_audio_bytes(self, chunk: bytes) -> RasaAudioBytes:
|
|
126
|
+
"""Convert the generated tts audio bytes into rasa audio bytes."""
|
|
127
|
+
# WebSocket returns raw audio bytes directly
|
|
128
|
+
return RasaAudioBytes(chunk)
|
|
129
|
+
|
|
130
|
+
@staticmethod
|
|
131
|
+
def get_default_config() -> DeepgramTTSConfig:
|
|
132
|
+
return DeepgramTTSConfig(
|
|
133
|
+
model_id="aura-2-andromeda-en",
|
|
134
|
+
endpoint="wss://api.deepgram.com/v1/speak",
|
|
135
|
+
timeout=30,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
@classmethod
|
|
139
|
+
def from_config_dict(cls, config: Dict) -> "DeepgramTTS":
|
|
140
|
+
return cls(DeepgramTTSConfig.from_dict(config))
|
|
@@ -14,6 +14,9 @@ from sanic import ( # type: ignore[attr-defined]
|
|
|
14
14
|
response,
|
|
15
15
|
)
|
|
16
16
|
|
|
17
|
+
# Import twilio at module level to raise error if not installed
|
|
18
|
+
from twilio.twiml.voice_response import VoiceResponse
|
|
19
|
+
|
|
17
20
|
from rasa.core.channels import UserMessage
|
|
18
21
|
from rasa.core.channels.channel import (
|
|
19
22
|
create_auth_requested_response_provider,
|
|
@@ -145,7 +148,8 @@ class TwilioMediaStreamsInputChannel(VoiceInputChannel):
|
|
|
145
148
|
"""Get the sender ID for the channel.
|
|
146
149
|
|
|
147
150
|
Twilio Media Streams uses the Stream ID as Sender ID because
|
|
148
|
-
it is required in OutputChannel.send_text_message to send messages.
|
|
151
|
+
it is required in OutputChannel.send_text_message to send messages.
|
|
152
|
+
"""
|
|
149
153
|
return call_parameters.stream_id # type: ignore[return-value]
|
|
150
154
|
|
|
151
155
|
def channel_bytes_to_rasa_audio_bytes(self, input_bytes: bytes) -> RasaAudioBytes:
|
|
@@ -38,6 +38,7 @@ from rasa.core.channels.voice_stream.call_state import (
|
|
|
38
38
|
)
|
|
39
39
|
from rasa.core.channels.voice_stream.tts.azure import AzureTTS
|
|
40
40
|
from rasa.core.channels.voice_stream.tts.cartesia import CartesiaTTS
|
|
41
|
+
from rasa.core.channels.voice_stream.tts.deepgram import DeepgramTTS
|
|
41
42
|
from rasa.core.channels.voice_stream.tts.tts_cache import TTSCache
|
|
42
43
|
from rasa.core.channels.voice_stream.tts.tts_engine import TTSEngine, TTSError
|
|
43
44
|
from rasa.core.channels.voice_stream.util import (
|
|
@@ -133,6 +134,8 @@ def tts_engine_from_config(tts_config: Dict) -> TTSEngine:
|
|
|
133
134
|
return AzureTTS.from_config_dict(tts_config)
|
|
134
135
|
elif name.lower() == "cartesia":
|
|
135
136
|
return CartesiaTTS.from_config_dict(tts_config)
|
|
137
|
+
elif name.lower() == "deepgram":
|
|
138
|
+
return DeepgramTTS.from_config_dict(tts_config)
|
|
136
139
|
else:
|
|
137
140
|
mark_as_beta_feature("Custom TTS Engine")
|
|
138
141
|
try:
|
|
File without changes
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import dataclasses
|
|
4
|
+
from pathlib import Path
|
|
4
5
|
from typing import Any, Dict, List, Optional, Union
|
|
5
6
|
|
|
6
|
-
from
|
|
7
|
+
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
|
8
|
+
|
|
9
|
+
from rasa.core.constants import MCP_SERVERS_KEY
|
|
7
10
|
from rasa.shared.core.constants import (
|
|
8
11
|
GLOBAL_SILENCE_TIMEOUT_DEFAULT_VALUE,
|
|
9
12
|
GLOBAL_SILENCE_TIMEOUT_KEY,
|
|
@@ -57,14 +60,46 @@ class InteractionHandlingConfig:
|
|
|
57
60
|
)
|
|
58
61
|
|
|
59
62
|
|
|
63
|
+
class MCPServerConfig(BaseModel):
|
|
64
|
+
model_config = ConfigDict(extra="allow")
|
|
65
|
+
name: str = Field(..., description="The name of the MCP server.")
|
|
66
|
+
url: str = Field(..., description="The URL of the MCP server.")
|
|
67
|
+
type: str = Field(..., description="The type of the MCP server.")
|
|
68
|
+
additional_params: Optional[Dict[str, Any]] = Field(
|
|
69
|
+
default_factory=dict, description="Additional parameters for the MCP server."
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
@model_validator(mode="after")
|
|
73
|
+
def validate_type(self) -> MCPServerConfig:
|
|
74
|
+
# validate that type is "http"
|
|
75
|
+
if self.type not in ["http", "https"]:
|
|
76
|
+
raise ValueError(f"Invalid MCP server type: {self.type}")
|
|
77
|
+
# validate that name and url are not empty
|
|
78
|
+
if not self.name or not self.url:
|
|
79
|
+
raise ValueError("Name and URL cannot be empty")
|
|
80
|
+
return self
|
|
81
|
+
|
|
82
|
+
@model_validator(mode="before")
|
|
83
|
+
def collect_additional_params(cls, values: Dict[str, Any]) -> Dict[str, Any]:
|
|
84
|
+
base_fields = {"name", "url", "type"}
|
|
85
|
+
extras = {k: v for k, v in values.items() if k not in base_fields}
|
|
86
|
+
if extras:
|
|
87
|
+
values["additional_params"] = extras
|
|
88
|
+
# remove them from top level so Pydantic doesn’t complain
|
|
89
|
+
for k in extras:
|
|
90
|
+
values.pop(k)
|
|
91
|
+
return values
|
|
92
|
+
|
|
93
|
+
|
|
60
94
|
class AvailableEndpoints:
|
|
61
95
|
"""Collection of configured endpoints."""
|
|
62
96
|
|
|
63
97
|
_instance = None
|
|
64
98
|
|
|
65
99
|
@classmethod
|
|
66
|
-
def read_endpoints(cls, endpoint_file:
|
|
100
|
+
def read_endpoints(cls, endpoint_file: Path) -> AvailableEndpoints:
|
|
67
101
|
"""Read the different endpoints from a yaml file."""
|
|
102
|
+
|
|
68
103
|
nlg = read_endpoint_config(endpoint_file, endpoint_type="nlg")
|
|
69
104
|
nlu = read_endpoint_config(endpoint_file, endpoint_type="nlu")
|
|
70
105
|
action = read_endpoint_config(endpoint_file, endpoint_type="action_endpoint")
|
|
@@ -75,6 +110,14 @@ class AvailableEndpoints:
|
|
|
75
110
|
lock_store = read_endpoint_config(endpoint_file, endpoint_type="lock_store")
|
|
76
111
|
event_broker = read_endpoint_config(endpoint_file, endpoint_type="event_broker")
|
|
77
112
|
vector_store = read_endpoint_config(endpoint_file, endpoint_type="vector_store")
|
|
113
|
+
raw_mcp_servers = read_property_config_from_endpoints_file(
|
|
114
|
+
endpoint_file, property_name=MCP_SERVERS_KEY
|
|
115
|
+
)
|
|
116
|
+
mcp_servers = (
|
|
117
|
+
[MCPServerConfig(**server) for server in raw_mcp_servers]
|
|
118
|
+
if raw_mcp_servers
|
|
119
|
+
else None
|
|
120
|
+
)
|
|
78
121
|
model_groups = read_property_config_from_endpoints_file(
|
|
79
122
|
endpoint_file, property_name="model_groups"
|
|
80
123
|
)
|
|
@@ -89,6 +132,7 @@ class AvailableEndpoints:
|
|
|
89
132
|
)
|
|
90
133
|
|
|
91
134
|
return cls(
|
|
135
|
+
endpoint_file,
|
|
92
136
|
nlg,
|
|
93
137
|
nlu,
|
|
94
138
|
action,
|
|
@@ -97,6 +141,7 @@ class AvailableEndpoints:
|
|
|
97
141
|
lock_store,
|
|
98
142
|
event_broker,
|
|
99
143
|
vector_store,
|
|
144
|
+
mcp_servers,
|
|
100
145
|
model_groups,
|
|
101
146
|
privacy,
|
|
102
147
|
interaction_handling,
|
|
@@ -104,6 +149,7 @@ class AvailableEndpoints:
|
|
|
104
149
|
|
|
105
150
|
def __init__(
|
|
106
151
|
self,
|
|
152
|
+
config_file_path: Optional[Path] = None,
|
|
107
153
|
nlg: Optional[EndpointConfig] = None,
|
|
108
154
|
nlu: Optional[EndpointConfig] = None,
|
|
109
155
|
action: Optional[EndpointConfig] = None,
|
|
@@ -112,6 +158,7 @@ class AvailableEndpoints:
|
|
|
112
158
|
lock_store: Optional[EndpointConfig] = None,
|
|
113
159
|
event_broker: Optional[EndpointConfig] = None,
|
|
114
160
|
vector_store: Optional[EndpointConfig] = None,
|
|
161
|
+
mcp_servers: Optional[List[MCPServerConfig]] = None,
|
|
115
162
|
model_groups: Optional[List[Dict[str, Any]]] = None,
|
|
116
163
|
privacy: Optional[Dict[str, Any]] = None,
|
|
117
164
|
interaction_handling: InteractionHandlingConfig = InteractionHandlingConfig(
|
|
@@ -119,6 +166,7 @@ class AvailableEndpoints:
|
|
|
119
166
|
),
|
|
120
167
|
) -> None:
|
|
121
168
|
"""Create an `AvailableEndpoints` object."""
|
|
169
|
+
self.config_file_path = config_file_path
|
|
122
170
|
self.model = model
|
|
123
171
|
self.action = action
|
|
124
172
|
self.nlu = nlu
|
|
@@ -127,20 +175,7 @@ class AvailableEndpoints:
|
|
|
127
175
|
self.lock_store = lock_store
|
|
128
176
|
self.event_broker = event_broker
|
|
129
177
|
self.vector_store = vector_store
|
|
178
|
+
self.mcp_servers = mcp_servers
|
|
130
179
|
self.model_groups = model_groups
|
|
131
180
|
self.privacy = privacy
|
|
132
181
|
self.interaction_handling = interaction_handling
|
|
133
|
-
|
|
134
|
-
@classmethod
|
|
135
|
-
def get_instance(
|
|
136
|
-
cls, endpoint_file: Optional[str] = DEFAULT_ENDPOINTS_PATH
|
|
137
|
-
) -> AvailableEndpoints:
|
|
138
|
-
"""Get the singleton instance of AvailableEndpoints."""
|
|
139
|
-
# Ensure that the instance is initialized only once.
|
|
140
|
-
if cls._instance is None:
|
|
141
|
-
cls._instance = cls.read_endpoints(endpoint_file)
|
|
142
|
-
return cls._instance
|
|
143
|
-
|
|
144
|
-
@classmethod
|
|
145
|
-
def reset_instance(cls) -> None:
|
|
146
|
-
cls._instance = None
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import dataclasses
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Literal, Optional
|
|
6
|
+
|
|
7
|
+
import structlog
|
|
8
|
+
|
|
9
|
+
from rasa.cli.validation.config_path_validation import get_validated_path
|
|
10
|
+
from rasa.core.config.available_endpoints import AvailableEndpoints
|
|
11
|
+
from rasa.core.config.credentials import CredentialsConfig
|
|
12
|
+
from rasa.core.config.message_procesing_config import MessageProcessingConfig
|
|
13
|
+
from rasa.shared.constants import (
|
|
14
|
+
DEFAULT_CONFIG_PATH,
|
|
15
|
+
DEFAULT_CREDENTIALS_PATH,
|
|
16
|
+
DEFAULT_ENDPOINTS_PATH,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
logger = structlog.get_logger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclasses.dataclass
|
|
23
|
+
class ValidatedConfigPaths:
|
|
24
|
+
credentials_path: Optional[Path] = None
|
|
25
|
+
endpoints_path: Optional[Path] = None
|
|
26
|
+
message_processing_config_path: Optional[Path] = None
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclasses.dataclass
|
|
30
|
+
class ConfigPath:
|
|
31
|
+
target: Optional[Path] = None
|
|
32
|
+
type: Literal["credentials", "endpoints", "config"] = "endpoints"
|
|
33
|
+
alternative_targets: list[Path] = dataclasses.field(default_factory=list)
|
|
34
|
+
default_path: str = ""
|
|
35
|
+
|
|
36
|
+
@staticmethod
|
|
37
|
+
def _validate(
|
|
38
|
+
config_path: ConfigPath, can_be_missing: bool = True
|
|
39
|
+
) -> Optional[Path]:
|
|
40
|
+
validated_path = get_validated_path(
|
|
41
|
+
config_path.target,
|
|
42
|
+
config_path.type,
|
|
43
|
+
config_path.alternative_targets,
|
|
44
|
+
can_be_missing,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
if validated_path is None and can_be_missing:
|
|
48
|
+
logger.debug(
|
|
49
|
+
f"No {config_path.type} configuration file found. "
|
|
50
|
+
f"Proceeding without {config_path.type} configuration."
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
if isinstance(validated_path, str):
|
|
54
|
+
validated_path = Path(validated_path)
|
|
55
|
+
|
|
56
|
+
return validated_path
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@dataclasses.dataclass
|
|
60
|
+
class CredentialsConfigPath(ConfigPath):
|
|
61
|
+
def __init__(self, target: Optional[Path] = None):
|
|
62
|
+
super().__init__(
|
|
63
|
+
target=target,
|
|
64
|
+
type="credentials",
|
|
65
|
+
alternative_targets=[Path(DEFAULT_CREDENTIALS_PATH)],
|
|
66
|
+
default_path=DEFAULT_CREDENTIALS_PATH,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
@staticmethod
|
|
70
|
+
def validate(
|
|
71
|
+
target_config_path: Optional[Path] = None, can_be_missing: bool = True
|
|
72
|
+
) -> Optional[Path]:
|
|
73
|
+
config_path = CredentialsConfigPath(target=target_config_path)
|
|
74
|
+
return ConfigPath._validate(config_path, can_be_missing)
|
|
75
|
+
|
|
76
|
+
@staticmethod
|
|
77
|
+
def default_file_path() -> Path:
|
|
78
|
+
return Path(DEFAULT_CREDENTIALS_PATH)
|
|
79
|
+
|
|
80
|
+
@staticmethod
|
|
81
|
+
def at_path(
|
|
82
|
+
root_path: Path,
|
|
83
|
+
) -> Path:
|
|
84
|
+
return root_path / CredentialsConfigPath.default_file_path()
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@dataclasses.dataclass
|
|
88
|
+
class EndpointsConfigPath(ConfigPath):
|
|
89
|
+
def __init__(self, target: Optional[Path] = None):
|
|
90
|
+
super().__init__(
|
|
91
|
+
target=target,
|
|
92
|
+
type="endpoints",
|
|
93
|
+
alternative_targets=[Path(DEFAULT_ENDPOINTS_PATH)],
|
|
94
|
+
default_path=DEFAULT_ENDPOINTS_PATH,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
@staticmethod
|
|
98
|
+
def validate(
|
|
99
|
+
target_config_path: Optional[Path] = None, can_be_missing: bool = True
|
|
100
|
+
) -> Optional[Path]:
|
|
101
|
+
config_path = EndpointsConfigPath(target=target_config_path)
|
|
102
|
+
return ConfigPath._validate(config_path, can_be_missing)
|
|
103
|
+
|
|
104
|
+
@staticmethod
|
|
105
|
+
def default_file_path() -> Path:
|
|
106
|
+
return Path(DEFAULT_ENDPOINTS_PATH)
|
|
107
|
+
|
|
108
|
+
@staticmethod
|
|
109
|
+
def at_path(
|
|
110
|
+
root_path: Path,
|
|
111
|
+
) -> Path:
|
|
112
|
+
return root_path / EndpointsConfigPath.default_file_path()
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@dataclasses.dataclass
|
|
116
|
+
class MessageProcessingConfigPath(ConfigPath):
|
|
117
|
+
def __init__(self, target: Optional[Path] = None):
|
|
118
|
+
super().__init__(
|
|
119
|
+
target=target,
|
|
120
|
+
type="config",
|
|
121
|
+
alternative_targets=[Path(DEFAULT_CONFIG_PATH)],
|
|
122
|
+
default_path=DEFAULT_CONFIG_PATH,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
@staticmethod
|
|
126
|
+
def validate(
|
|
127
|
+
target_config_path: Optional[Path] = None, can_be_missing: bool = True
|
|
128
|
+
) -> Optional[Path]:
|
|
129
|
+
config_path = MessageProcessingConfigPath(target=target_config_path)
|
|
130
|
+
return ConfigPath._validate(config_path, can_be_missing)
|
|
131
|
+
|
|
132
|
+
@staticmethod
|
|
133
|
+
def default_file_path() -> Path:
|
|
134
|
+
return Path(DEFAULT_CONFIG_PATH)
|
|
135
|
+
|
|
136
|
+
@staticmethod
|
|
137
|
+
def at_path(
|
|
138
|
+
root_path: Path,
|
|
139
|
+
) -> Path:
|
|
140
|
+
return root_path / MessageProcessingConfigPath.default_file_path()
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class Configuration:
|
|
144
|
+
_instance: Optional[Configuration] = None
|
|
145
|
+
|
|
146
|
+
def __init__(
|
|
147
|
+
self,
|
|
148
|
+
endpoints: AvailableEndpoints,
|
|
149
|
+
credentials: Optional[CredentialsConfig] = None,
|
|
150
|
+
message_processing_config: Optional[MessageProcessingConfig] = None,
|
|
151
|
+
):
|
|
152
|
+
self.credentials = credentials
|
|
153
|
+
self.endpoints = endpoints
|
|
154
|
+
self.message_processing_config = message_processing_config
|
|
155
|
+
|
|
156
|
+
@classmethod
|
|
157
|
+
def initialise_empty(cls) -> Configuration:
|
|
158
|
+
if cls._instance is None:
|
|
159
|
+
cls._instance = Configuration(
|
|
160
|
+
credentials=None,
|
|
161
|
+
endpoints=AvailableEndpoints(),
|
|
162
|
+
message_processing_config=None,
|
|
163
|
+
)
|
|
164
|
+
return cls._instance
|
|
165
|
+
|
|
166
|
+
@classmethod
|
|
167
|
+
def initialise_endpoints(cls, endpoints_path: Path) -> Configuration:
|
|
168
|
+
logger.debug(
|
|
169
|
+
"configuration.initialise_endpoints.start", endpoints_path=endpoints_path
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
validated_endpoints_path = EndpointsConfigPath.validate(endpoints_path)
|
|
173
|
+
endpoints = (
|
|
174
|
+
AvailableEndpoints.read_endpoints(validated_endpoints_path)
|
|
175
|
+
if validated_endpoints_path
|
|
176
|
+
else AvailableEndpoints()
|
|
177
|
+
)
|
|
178
|
+
if cls._instance is None:
|
|
179
|
+
cls._instance = Configuration(
|
|
180
|
+
endpoints=endpoints, credentials=None, message_processing_config=None
|
|
181
|
+
)
|
|
182
|
+
else:
|
|
183
|
+
cls._instance.endpoints = endpoints
|
|
184
|
+
return cls._instance
|
|
185
|
+
|
|
186
|
+
@classmethod
|
|
187
|
+
def initialise_empty_endpoints(cls) -> Configuration:
|
|
188
|
+
logger.debug("configuration.initialise_empty_endpoints.start")
|
|
189
|
+
if cls._instance is None:
|
|
190
|
+
cls._instance = Configuration(
|
|
191
|
+
endpoints=AvailableEndpoints(),
|
|
192
|
+
credentials=None,
|
|
193
|
+
message_processing_config=None,
|
|
194
|
+
)
|
|
195
|
+
else:
|
|
196
|
+
cls._instance.endpoints = AvailableEndpoints()
|
|
197
|
+
return cls._instance
|
|
198
|
+
|
|
199
|
+
@classmethod
|
|
200
|
+
def initialise_message_processing(
|
|
201
|
+
cls, message_processing_config_path: Path
|
|
202
|
+
) -> Configuration:
|
|
203
|
+
logger.debug(
|
|
204
|
+
"configuration.initialise_message_processing.start",
|
|
205
|
+
message_processing_config_path=message_processing_config_path,
|
|
206
|
+
)
|
|
207
|
+
validated_message_processing_path = MessageProcessingConfigPath.validate(
|
|
208
|
+
message_processing_config_path
|
|
209
|
+
)
|
|
210
|
+
message_processing_config = (
|
|
211
|
+
MessageProcessingConfig.load_from_file(validated_message_processing_path)
|
|
212
|
+
if validated_message_processing_path
|
|
213
|
+
else None
|
|
214
|
+
)
|
|
215
|
+
if cls._instance is None:
|
|
216
|
+
cls._instance = Configuration(
|
|
217
|
+
message_processing_config=message_processing_config,
|
|
218
|
+
credentials=None,
|
|
219
|
+
endpoints=AvailableEndpoints(),
|
|
220
|
+
)
|
|
221
|
+
else:
|
|
222
|
+
cls._instance.message_processing_config = message_processing_config
|
|
223
|
+
return cls._instance
|
|
224
|
+
|
|
225
|
+
@classmethod
|
|
226
|
+
def initialise_credentials(cls, credentials_path: Path) -> Configuration:
|
|
227
|
+
logger.debug(
|
|
228
|
+
"configuration.initialise_credentials.start",
|
|
229
|
+
credentials_path=credentials_path,
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
validated_credentials_path = CredentialsConfigPath.validate(credentials_path)
|
|
233
|
+
|
|
234
|
+
credentials = (
|
|
235
|
+
CredentialsConfig.load_from_file(validated_credentials_path)
|
|
236
|
+
if validated_credentials_path
|
|
237
|
+
else None
|
|
238
|
+
)
|
|
239
|
+
if cls._instance is None:
|
|
240
|
+
cls._instance = Configuration(
|
|
241
|
+
credentials=credentials,
|
|
242
|
+
endpoints=AvailableEndpoints(),
|
|
243
|
+
message_processing_config=None,
|
|
244
|
+
)
|
|
245
|
+
else:
|
|
246
|
+
cls._instance.credentials = credentials
|
|
247
|
+
return cls._instance
|
|
248
|
+
|
|
249
|
+
@classmethod
|
|
250
|
+
def get_instance(cls) -> Configuration:
|
|
251
|
+
if cls._instance is None:
|
|
252
|
+
raise Exception(
|
|
253
|
+
"Configuration not initialized. "
|
|
254
|
+
"Call appropriate 'initialise' methods to "
|
|
255
|
+
"load the config when Rasa Pro starts: "
|
|
256
|
+
"initialise_endpoints(), "
|
|
257
|
+
"initialise_credentials(), "
|
|
258
|
+
"initialise_message_processing()"
|
|
259
|
+
)
|
|
260
|
+
return cls._instance
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Any, Dict
|
|
5
|
+
|
|
6
|
+
from rasa.shared.utils.yaml import read_config_file
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class CredentialsConfig:
|
|
10
|
+
def __init__(
|
|
11
|
+
self, channels: Dict[str, Dict[str, Any]], config_file_path: Path
|
|
12
|
+
) -> None:
|
|
13
|
+
self.channels = channels
|
|
14
|
+
self.config_file_path = config_file_path
|
|
15
|
+
|
|
16
|
+
@classmethod
|
|
17
|
+
def load_from_file(cls, file_path: Path) -> CredentialsConfig:
|
|
18
|
+
credentials_dict = read_config_file(file_path)
|
|
19
|
+
return cls(credentials_dict, file_path)
|