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.

Files changed (290) hide show
  1. rasa/__main__.py +15 -3
  2. rasa/agents/__init__.py +0 -0
  3. rasa/agents/agent_factory.py +122 -0
  4. rasa/agents/agent_manager.py +211 -0
  5. rasa/agents/constants.py +43 -0
  6. rasa/agents/core/__init__.py +0 -0
  7. rasa/agents/core/agent_protocol.py +107 -0
  8. rasa/agents/core/types.py +81 -0
  9. rasa/agents/exceptions.py +38 -0
  10. rasa/agents/protocol/__init__.py +5 -0
  11. rasa/agents/protocol/a2a/__init__.py +0 -0
  12. rasa/agents/protocol/a2a/a2a_agent.py +879 -0
  13. rasa/agents/protocol/mcp/__init__.py +0 -0
  14. rasa/agents/protocol/mcp/mcp_base_agent.py +726 -0
  15. rasa/agents/protocol/mcp/mcp_open_agent.py +327 -0
  16. rasa/agents/protocol/mcp/mcp_task_agent.py +522 -0
  17. rasa/agents/schemas/__init__.py +13 -0
  18. rasa/agents/schemas/agent_input.py +38 -0
  19. rasa/agents/schemas/agent_output.py +26 -0
  20. rasa/agents/schemas/agent_tool_result.py +65 -0
  21. rasa/agents/schemas/agent_tool_schema.py +186 -0
  22. rasa/agents/templates/__init__.py +0 -0
  23. rasa/agents/templates/mcp_open_agent_prompt_template.jinja2 +20 -0
  24. rasa/agents/templates/mcp_task_agent_prompt_template.jinja2 +22 -0
  25. rasa/agents/utils.py +206 -0
  26. rasa/agents/validation.py +485 -0
  27. rasa/api.py +24 -9
  28. rasa/builder/config.py +6 -2
  29. rasa/builder/guardrails/{lakera.py → clients.py} +55 -5
  30. rasa/builder/guardrails/constants.py +3 -0
  31. rasa/builder/guardrails/models.py +45 -10
  32. rasa/builder/guardrails/policy_checker.py +324 -0
  33. rasa/builder/guardrails/utils.py +42 -276
  34. rasa/builder/llm_service.py +32 -5
  35. rasa/builder/models.py +1 -0
  36. rasa/builder/project_generator.py +6 -1
  37. rasa/builder/service.py +16 -13
  38. rasa/builder/training_service.py +18 -24
  39. rasa/builder/validation_service.py +1 -1
  40. rasa/cli/arguments/default_arguments.py +12 -0
  41. rasa/cli/arguments/run.py +2 -0
  42. rasa/cli/arguments/train.py +2 -0
  43. rasa/cli/data.py +10 -8
  44. rasa/cli/dialogue_understanding_test.py +10 -7
  45. rasa/cli/e2e_test.py +9 -6
  46. rasa/cli/evaluate.py +4 -2
  47. rasa/cli/export.py +5 -2
  48. rasa/cli/inspect.py +8 -4
  49. rasa/cli/interactive.py +5 -4
  50. rasa/cli/llm_fine_tuning.py +11 -6
  51. rasa/cli/project_templates/tutorial/credentials.yml +10 -0
  52. rasa/cli/run.py +12 -10
  53. rasa/cli/scaffold.py +4 -4
  54. rasa/cli/shell.py +9 -5
  55. rasa/cli/studio/studio.py +1 -1
  56. rasa/cli/test.py +34 -14
  57. rasa/cli/train.py +41 -28
  58. rasa/cli/utils.py +1 -393
  59. rasa/cli/validation/__init__.py +0 -0
  60. rasa/cli/validation/bot_config.py +223 -0
  61. rasa/cli/validation/config_path_validation.py +257 -0
  62. rasa/cli/x.py +8 -4
  63. rasa/constants.py +7 -1
  64. rasa/core/actions/action.py +51 -10
  65. rasa/core/actions/grpc_custom_action_executor.py +1 -1
  66. rasa/core/agent.py +19 -2
  67. rasa/core/available_agents.py +229 -0
  68. rasa/core/channels/__init__.py +82 -35
  69. rasa/core/channels/development_inspector.py +3 -3
  70. rasa/core/channels/inspector/README.md +25 -13
  71. rasa/core/channels/inspector/dist/assets/{arc-35222594.js → arc-6177260a.js} +1 -1
  72. rasa/core/channels/inspector/dist/assets/{blockDiagram-38ab4fdb-a0efbfd3.js → blockDiagram-38ab4fdb-b054f038.js} +1 -1
  73. rasa/core/channels/inspector/dist/assets/{c4Diagram-3d4e48cf-0584c0f2.js → c4Diagram-3d4e48cf-f25427d5.js} +1 -1
  74. rasa/core/channels/inspector/dist/assets/channel-bf9cbb34.js +1 -0
  75. rasa/core/channels/inspector/dist/assets/{classDiagram-70f12bd4-39f40dbe.js → classDiagram-70f12bd4-c7a2af53.js} +1 -1
  76. rasa/core/channels/inspector/dist/assets/{classDiagram-v2-f2320105-1ad755f3.js → classDiagram-v2-f2320105-58db65c0.js} +1 -1
  77. rasa/core/channels/inspector/dist/assets/clone-8f9083bb.js +1 -0
  78. rasa/core/channels/inspector/dist/assets/{createText-2e5e7dd3-b0f4f0fe.js → createText-2e5e7dd3-088372e2.js} +1 -1
  79. rasa/core/channels/inspector/dist/assets/{edges-e0da2a9e-9039bff9.js → edges-e0da2a9e-58676240.js} +1 -1
  80. rasa/core/channels/inspector/dist/assets/{erDiagram-9861fffd-65c9b127.js → erDiagram-9861fffd-0c14d7c6.js} +1 -1
  81. rasa/core/channels/inspector/dist/assets/{flowDb-956e92f1-4f08b38e.js → flowDb-956e92f1-ea63f85c.js} +1 -1
  82. rasa/core/channels/inspector/dist/assets/{flowDiagram-66a62f08-e95c362a.js → flowDiagram-66a62f08-a2af48cd.js} +1 -1
  83. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-9ecd5b59.js +1 -0
  84. rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-4a651766-703c3015.js → flowchart-elk-definition-4a651766-6937abe7.js} +1 -1
  85. rasa/core/channels/inspector/dist/assets/{ganttDiagram-c361ad54-699328ea.js → ganttDiagram-c361ad54-7473f357.js} +1 -1
  86. rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-72cf32ee-04cf4b05.js → gitGraphDiagram-72cf32ee-d0c9405e.js} +1 -1
  87. rasa/core/channels/inspector/dist/assets/{graph-ee94449e.js → graph-0a6f8466.js} +1 -1
  88. rasa/core/channels/inspector/dist/assets/{index-3862675e-940162b4.js → index-3862675e-7610671a.js} +1 -1
  89. rasa/core/channels/inspector/dist/assets/index-74e01d94.js +1354 -0
  90. rasa/core/channels/inspector/dist/assets/{infoDiagram-f8f76790-c79c2866.js → infoDiagram-f8f76790-be397dc7.js} +1 -1
  91. rasa/core/channels/inspector/dist/assets/{journeyDiagram-49397b02-84489d30.js → journeyDiagram-49397b02-4cefbf62.js} +1 -1
  92. rasa/core/channels/inspector/dist/assets/{layout-a9aa9858.js → layout-e7fbc2bf.js} +1 -1
  93. rasa/core/channels/inspector/dist/assets/{line-eb73cf26.js → line-a8aa457c.js} +1 -1
  94. rasa/core/channels/inspector/dist/assets/{linear-b3399f9a.js → linear-3351e0d2.js} +1 -1
  95. rasa/core/channels/inspector/dist/assets/{mindmap-definition-fc14e90a-b095bf1a.js → mindmap-definition-fc14e90a-b8cbf605.js} +1 -1
  96. rasa/core/channels/inspector/dist/assets/{pieDiagram-8a3498a8-07644b66.js → pieDiagram-8a3498a8-f327f774.js} +1 -1
  97. rasa/core/channels/inspector/dist/assets/{quadrantDiagram-120e2f19-573a3f9c.js → quadrantDiagram-120e2f19-2854c591.js} +1 -1
  98. rasa/core/channels/inspector/dist/assets/{requirementDiagram-deff3bca-d457e1e1.js → requirementDiagram-deff3bca-964985d5.js} +1 -1
  99. rasa/core/channels/inspector/dist/assets/{sankeyDiagram-04a897e0-9d26e1a2.js → sankeyDiagram-04a897e0-edeb4f33.js} +1 -1
  100. rasa/core/channels/inspector/dist/assets/{sequenceDiagram-704730f1-3a9cde10.js → sequenceDiagram-704730f1-fcf70125.js} +1 -1
  101. rasa/core/channels/inspector/dist/assets/{stateDiagram-587899a1-4f3e8cec.js → stateDiagram-587899a1-0e770395.js} +1 -1
  102. rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-d93cdb3a-e617e5bf.js → stateDiagram-v2-d93cdb3a-af8dcd22.js} +1 -1
  103. rasa/core/channels/inspector/dist/assets/{styles-6aaf32cf-eab30d2f.js → styles-6aaf32cf-36a9e70d.js} +1 -1
  104. rasa/core/channels/inspector/dist/assets/{styles-9a916d00-09994be2.js → styles-9a916d00-884a8b5b.js} +1 -1
  105. rasa/core/channels/inspector/dist/assets/{styles-c10674c1-b7110364.js → styles-c10674c1-dc097813.js} +1 -1
  106. rasa/core/channels/inspector/dist/assets/{svgDrawCommon-08f97a94-3ebc92ad.js → svgDrawCommon-08f97a94-5a2c7eed.js} +1 -1
  107. rasa/core/channels/inspector/dist/assets/{timeline-definition-85554ec2-7d13d2f2.js → timeline-definition-85554ec2-e89c4f6e.js} +1 -1
  108. rasa/core/channels/inspector/dist/assets/{xychartDiagram-e933f94c-488385e1.js → xychartDiagram-e933f94c-afb6fe56.js} +1 -1
  109. rasa/core/channels/inspector/dist/index.html +1 -1
  110. rasa/core/channels/inspector/package.json +18 -18
  111. rasa/core/channels/inspector/src/App.tsx +29 -4
  112. rasa/core/channels/inspector/src/components/DialogueAgentStack.tsx +108 -0
  113. rasa/core/channels/inspector/src/components/{DialogueStack.tsx → DialogueHistoryStack.tsx} +4 -2
  114. rasa/core/channels/inspector/src/helpers/audio/audiostream.ts +7 -4
  115. rasa/core/channels/inspector/src/helpers/formatters.test.ts +4 -0
  116. rasa/core/channels/inspector/src/helpers/formatters.ts +24 -3
  117. rasa/core/channels/inspector/src/helpers/utils.test.ts +127 -0
  118. rasa/core/channels/inspector/src/helpers/utils.ts +66 -1
  119. rasa/core/channels/inspector/src/theme/base/styles.ts +19 -1
  120. rasa/core/channels/inspector/src/types.ts +21 -0
  121. rasa/core/channels/inspector/yarn.lock +336 -189
  122. rasa/core/channels/studio_chat.py +6 -6
  123. rasa/core/channels/telegram.py +4 -9
  124. rasa/core/channels/voice_stream/genesys.py +1 -1
  125. rasa/core/channels/voice_stream/tts/deepgram.py +140 -0
  126. rasa/core/channels/voice_stream/twilio_media_streams.py +5 -1
  127. rasa/core/channels/voice_stream/voice_channel.py +3 -0
  128. rasa/core/config/__init__.py +0 -0
  129. rasa/core/{available_endpoints.py → config/available_endpoints.py} +51 -16
  130. rasa/core/config/configuration.py +260 -0
  131. rasa/core/config/credentials.py +19 -0
  132. rasa/core/config/message_procesing_config.py +34 -0
  133. rasa/core/constants.py +4 -0
  134. rasa/core/policies/enterprise_search_policy.py +5 -3
  135. rasa/core/policies/flow_policy.py +4 -4
  136. rasa/core/policies/flows/agent_executor.py +632 -0
  137. rasa/core/policies/flows/flow_executor.py +136 -75
  138. rasa/core/policies/flows/mcp_tool_executor.py +298 -0
  139. rasa/core/policies/intentless_policy.py +1 -1
  140. rasa/core/policies/ted_policy.py +20 -12
  141. rasa/core/policies/unexpected_intent_policy.py +6 -0
  142. rasa/core/processor.py +68 -44
  143. rasa/core/run.py +37 -8
  144. rasa/core/test.py +4 -0
  145. rasa/core/tracker_stores/tracker_store.py +3 -7
  146. rasa/core/train.py +1 -1
  147. rasa/core/training/interactive.py +20 -18
  148. rasa/core/training/story_conflict.py +5 -5
  149. rasa/core/utils.py +22 -23
  150. rasa/dialogue_understanding/commands/__init__.py +8 -0
  151. rasa/dialogue_understanding/commands/cancel_flow_command.py +19 -5
  152. rasa/dialogue_understanding/commands/chit_chat_answer_command.py +21 -2
  153. rasa/dialogue_understanding/commands/clarify_command.py +20 -2
  154. rasa/dialogue_understanding/commands/continue_agent_command.py +91 -0
  155. rasa/dialogue_understanding/commands/knowledge_answer_command.py +21 -2
  156. rasa/dialogue_understanding/commands/restart_agent_command.py +162 -0
  157. rasa/dialogue_understanding/commands/start_flow_command.py +68 -7
  158. rasa/dialogue_understanding/commands/utils.py +124 -2
  159. rasa/dialogue_understanding/generator/command_parser.py +4 -0
  160. rasa/dialogue_understanding/generator/llm_based_command_generator.py +50 -12
  161. rasa/dialogue_understanding/generator/llm_command_generator.py +1 -1
  162. rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +1 -1
  163. rasa/dialogue_understanding/generator/prompt_templates/agent_command_prompt_v2_claude_3_5_sonnet_20240620_template.jinja2 +66 -0
  164. rasa/dialogue_understanding/generator/prompt_templates/agent_command_prompt_v2_gpt_4o_2024_11_20_template.jinja2 +66 -0
  165. rasa/dialogue_understanding/generator/prompt_templates/agent_command_prompt_v3_claude_3_5_sonnet_20240620_template.jinja2 +89 -0
  166. rasa/dialogue_understanding/generator/prompt_templates/agent_command_prompt_v3_gpt_4o_2024_11_20_template.jinja2 +88 -0
  167. rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +42 -7
  168. rasa/dialogue_understanding/generator/single_step/search_ready_llm_command_generator.py +40 -3
  169. rasa/dialogue_understanding/generator/single_step/single_step_based_llm_command_generator.py +20 -3
  170. rasa/dialogue_understanding/patterns/cancel.py +27 -6
  171. rasa/dialogue_understanding/patterns/clarify.py +3 -14
  172. rasa/dialogue_understanding/patterns/continue_interrupted.py +239 -6
  173. rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +46 -8
  174. rasa/dialogue_understanding/processor/command_processor.py +136 -15
  175. rasa/dialogue_understanding/stack/dialogue_stack.py +98 -2
  176. rasa/dialogue_understanding/stack/frames/flow_stack_frame.py +57 -0
  177. rasa/dialogue_understanding/stack/utils.py +57 -3
  178. rasa/dialogue_understanding/utils.py +24 -4
  179. rasa/dialogue_understanding_test/du_test_runner.py +8 -3
  180. rasa/e2e_test/e2e_test_runner.py +13 -3
  181. rasa/engine/caching.py +2 -2
  182. rasa/engine/constants.py +1 -1
  183. rasa/engine/recipes/default_components.py +138 -49
  184. rasa/engine/recipes/default_recipe.py +108 -11
  185. rasa/engine/runner/dask.py +8 -5
  186. rasa/engine/validation.py +19 -6
  187. rasa/graph_components/validators/default_recipe_validator.py +86 -28
  188. rasa/hooks.py +5 -5
  189. rasa/llm_fine_tuning/utils.py +2 -2
  190. rasa/model_training.py +60 -47
  191. rasa/nlu/classifiers/diet_classifier.py +198 -98
  192. rasa/nlu/classifiers/logistic_regression_classifier.py +1 -4
  193. rasa/nlu/classifiers/mitie_intent_classifier.py +3 -0
  194. rasa/nlu/classifiers/sklearn_intent_classifier.py +1 -3
  195. rasa/nlu/extractors/crf_entity_extractor.py +9 -10
  196. rasa/nlu/extractors/mitie_entity_extractor.py +3 -0
  197. rasa/nlu/extractors/spacy_entity_extractor.py +3 -0
  198. rasa/nlu/featurizers/dense_featurizer/convert_featurizer.py +4 -0
  199. rasa/nlu/featurizers/dense_featurizer/lm_featurizer.py +5 -0
  200. rasa/nlu/featurizers/dense_featurizer/mitie_featurizer.py +2 -0
  201. rasa/nlu/featurizers/dense_featurizer/spacy_featurizer.py +3 -0
  202. rasa/nlu/featurizers/sparse_featurizer/count_vectors_featurizer.py +4 -2
  203. rasa/nlu/featurizers/sparse_featurizer/lexical_syntactic_featurizer.py +4 -0
  204. rasa/nlu/selectors/response_selector.py +10 -2
  205. rasa/nlu/tokenizers/jieba_tokenizer.py +3 -4
  206. rasa/nlu/tokenizers/mitie_tokenizer.py +3 -2
  207. rasa/nlu/tokenizers/spacy_tokenizer.py +3 -2
  208. rasa/nlu/utils/mitie_utils.py +3 -0
  209. rasa/nlu/utils/spacy_utils.py +3 -2
  210. rasa/plugin.py +8 -8
  211. rasa/privacy/privacy_manager.py +12 -3
  212. rasa/server.py +15 -3
  213. rasa/shared/agents/__init__.py +0 -0
  214. rasa/shared/agents/auth/__init__.py +0 -0
  215. rasa/shared/agents/auth/agent_auth_factory.py +105 -0
  216. rasa/shared/agents/auth/agent_auth_manager.py +92 -0
  217. rasa/shared/agents/auth/auth_strategy/__init__.py +19 -0
  218. rasa/shared/agents/auth/auth_strategy/agent_auth_strategy.py +52 -0
  219. rasa/shared/agents/auth/auth_strategy/api_key_auth_strategy.py +42 -0
  220. rasa/shared/agents/auth/auth_strategy/bearer_token_auth_strategy.py +28 -0
  221. rasa/shared/agents/auth/auth_strategy/oauth2_auth_strategy.py +167 -0
  222. rasa/shared/agents/auth/constants.py +12 -0
  223. rasa/shared/agents/auth/types.py +12 -0
  224. rasa/shared/agents/utils.py +35 -0
  225. rasa/shared/constants.py +8 -0
  226. rasa/shared/core/constants.py +16 -1
  227. rasa/shared/core/domain.py +0 -7
  228. rasa/shared/core/events.py +327 -0
  229. rasa/shared/core/flows/constants.py +5 -0
  230. rasa/shared/core/flows/flows_list.py +21 -5
  231. rasa/shared/core/flows/flows_yaml_schema.json +119 -184
  232. rasa/shared/core/flows/steps/call.py +49 -5
  233. rasa/shared/core/flows/steps/collect.py +98 -13
  234. rasa/shared/core/flows/validation.py +372 -8
  235. rasa/shared/core/flows/yaml_flows_io.py +3 -2
  236. rasa/shared/core/slots.py +2 -2
  237. rasa/shared/core/trackers.py +5 -2
  238. rasa/shared/exceptions.py +16 -0
  239. rasa/shared/importers/rasa.py +1 -1
  240. rasa/shared/importers/utils.py +9 -3
  241. rasa/shared/providers/llm/_base_litellm_client.py +41 -9
  242. rasa/shared/providers/llm/litellm_router_llm_client.py +8 -4
  243. rasa/shared/providers/llm/llm_client.py +7 -3
  244. rasa/shared/providers/llm/llm_response.py +66 -0
  245. rasa/shared/providers/llm/self_hosted_llm_client.py +8 -4
  246. rasa/shared/utils/common.py +24 -0
  247. rasa/shared/utils/health_check/health_check.py +7 -3
  248. rasa/shared/utils/llm.py +39 -16
  249. rasa/shared/utils/mcp/__init__.py +0 -0
  250. rasa/shared/utils/mcp/server_connection.py +247 -0
  251. rasa/shared/utils/mcp/utils.py +20 -0
  252. rasa/shared/utils/schemas/events.py +42 -0
  253. rasa/shared/utils/yaml.py +3 -1
  254. rasa/studio/pull/pull.py +3 -2
  255. rasa/studio/train.py +8 -7
  256. rasa/studio/upload.py +3 -6
  257. rasa/telemetry.py +69 -5
  258. rasa/tracing/config.py +45 -12
  259. rasa/tracing/constants.py +14 -0
  260. rasa/tracing/instrumentation/attribute_extractors.py +142 -9
  261. rasa/tracing/instrumentation/instrumentation.py +626 -21
  262. rasa/tracing/instrumentation/intentless_policy_instrumentation.py +4 -4
  263. rasa/tracing/instrumentation/metrics.py +32 -0
  264. rasa/tracing/metric_instrument_provider.py +68 -0
  265. rasa/utils/common.py +92 -1
  266. rasa/utils/endpoints.py +11 -2
  267. rasa/utils/log_utils.py +96 -5
  268. rasa/utils/ml_utils.py +1 -1
  269. rasa/utils/tensorflow/__init__.py +7 -0
  270. rasa/utils/tensorflow/callback.py +136 -101
  271. rasa/utils/tensorflow/crf.py +1 -1
  272. rasa/utils/tensorflow/data_generator.py +21 -8
  273. rasa/utils/tensorflow/layers.py +21 -11
  274. rasa/utils/tensorflow/metrics.py +7 -3
  275. rasa/utils/tensorflow/models.py +56 -8
  276. rasa/utils/tensorflow/rasa_layers.py +8 -6
  277. rasa/utils/tensorflow/transformer.py +2 -3
  278. rasa/utils/train_utils.py +54 -24
  279. rasa/validator.py +5 -5
  280. rasa/version.py +1 -1
  281. {rasa_pro-3.14.0.dev20250922.dist-info → rasa_pro-3.14.0rc1.dist-info}/METADATA +46 -41
  282. {rasa_pro-3.14.0.dev20250922.dist-info → rasa_pro-3.14.0rc1.dist-info}/RECORD +285 -226
  283. rasa/builder/scrape_rasa_docs.py +0 -97
  284. rasa/core/channels/inspector/dist/assets/channel-8e08bed9.js +0 -1
  285. rasa/core/channels/inspector/dist/assets/clone-78c82dea.js +0 -1
  286. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-2b08f601.js +0 -1
  287. rasa/core/channels/inspector/dist/assets/index-c941dcb3.js +0 -1336
  288. {rasa_pro-3.14.0.dev20250922.dist-info → rasa_pro-3.14.0rc1.dist-info}/NOTICE +0 -0
  289. {rasa_pro-3.14.0.dev20250922.dist-info → rasa_pro-3.14.0rc1.dist-info}/WHEEL +0 -0
  290. {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 # type: ignore[misc]
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 # type: ignore[misc]
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 # type: ignore[misc]
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 # type: ignore[misc]
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 # type: ignore[assignment]
551
+ marker_data["latency"] = latency_data
552
552
 
553
553
  return json.dumps(marker_data), message_id
554
554
 
@@ -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
- try:
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 rasa.shared.constants import DEFAULT_ENDPOINTS_PATH
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: str) -> AvailableEndpoints:
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)