rasa-pro 3.14.0.dev20250922__py3-none-any.whl → 3.14.0rc2__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 (304) 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/copilot/constants.py +4 -1
  30. rasa/builder/copilot/copilot.py +155 -79
  31. rasa/builder/copilot/models.py +304 -108
  32. rasa/builder/copilot/prompts/copilot_training_error_handler_prompt.jinja2 +53 -0
  33. rasa/builder/guardrails/{lakera.py → clients.py} +55 -5
  34. rasa/builder/guardrails/constants.py +3 -0
  35. rasa/builder/guardrails/models.py +45 -10
  36. rasa/builder/guardrails/policy_checker.py +324 -0
  37. rasa/builder/guardrails/utils.py +42 -276
  38. rasa/builder/jobs.py +182 -12
  39. rasa/builder/llm_service.py +32 -5
  40. rasa/builder/models.py +13 -3
  41. rasa/builder/project_generator.py +6 -1
  42. rasa/builder/service.py +31 -15
  43. rasa/builder/training_service.py +18 -24
  44. rasa/builder/validation_service.py +1 -1
  45. rasa/cli/arguments/default_arguments.py +12 -0
  46. rasa/cli/arguments/run.py +2 -0
  47. rasa/cli/arguments/train.py +2 -0
  48. rasa/cli/data.py +10 -8
  49. rasa/cli/dialogue_understanding_test.py +10 -7
  50. rasa/cli/e2e_test.py +9 -6
  51. rasa/cli/evaluate.py +4 -2
  52. rasa/cli/export.py +5 -2
  53. rasa/cli/inspect.py +8 -4
  54. rasa/cli/interactive.py +5 -4
  55. rasa/cli/llm_fine_tuning.py +11 -6
  56. rasa/cli/project_templates/finance/domain/general/help.yml +0 -0
  57. rasa/cli/project_templates/tutorial/credentials.yml +10 -0
  58. rasa/cli/run.py +12 -10
  59. rasa/cli/scaffold.py +4 -4
  60. rasa/cli/shell.py +9 -5
  61. rasa/cli/studio/studio.py +1 -1
  62. rasa/cli/test.py +34 -14
  63. rasa/cli/train.py +41 -28
  64. rasa/cli/utils.py +1 -393
  65. rasa/cli/validation/__init__.py +0 -0
  66. rasa/cli/validation/bot_config.py +223 -0
  67. rasa/cli/validation/config_path_validation.py +257 -0
  68. rasa/cli/x.py +8 -4
  69. rasa/constants.py +7 -1
  70. rasa/core/actions/action.py +51 -10
  71. rasa/core/actions/grpc_custom_action_executor.py +1 -1
  72. rasa/core/agent.py +19 -2
  73. rasa/core/available_agents.py +229 -0
  74. rasa/core/brokers/kafka.py +5 -1
  75. rasa/core/channels/__init__.py +82 -35
  76. rasa/core/channels/development_inspector.py +3 -3
  77. rasa/core/channels/inspector/README.md +25 -13
  78. rasa/core/channels/inspector/dist/assets/{arc-35222594.js → arc-6177260a.js} +1 -1
  79. rasa/core/channels/inspector/dist/assets/{blockDiagram-38ab4fdb-a0efbfd3.js → blockDiagram-38ab4fdb-b054f038.js} +1 -1
  80. rasa/core/channels/inspector/dist/assets/{c4Diagram-3d4e48cf-0584c0f2.js → c4Diagram-3d4e48cf-f25427d5.js} +1 -1
  81. rasa/core/channels/inspector/dist/assets/channel-bf9cbb34.js +1 -0
  82. rasa/core/channels/inspector/dist/assets/{classDiagram-70f12bd4-39f40dbe.js → classDiagram-70f12bd4-c7a2af53.js} +1 -1
  83. rasa/core/channels/inspector/dist/assets/{classDiagram-v2-f2320105-1ad755f3.js → classDiagram-v2-f2320105-58db65c0.js} +1 -1
  84. rasa/core/channels/inspector/dist/assets/clone-8f9083bb.js +1 -0
  85. rasa/core/channels/inspector/dist/assets/{createText-2e5e7dd3-b0f4f0fe.js → createText-2e5e7dd3-088372e2.js} +1 -1
  86. rasa/core/channels/inspector/dist/assets/{edges-e0da2a9e-9039bff9.js → edges-e0da2a9e-58676240.js} +1 -1
  87. rasa/core/channels/inspector/dist/assets/{erDiagram-9861fffd-65c9b127.js → erDiagram-9861fffd-0c14d7c6.js} +1 -1
  88. rasa/core/channels/inspector/dist/assets/{flowDb-956e92f1-4f08b38e.js → flowDb-956e92f1-ea63f85c.js} +1 -1
  89. rasa/core/channels/inspector/dist/assets/{flowDiagram-66a62f08-e95c362a.js → flowDiagram-66a62f08-a2af48cd.js} +1 -1
  90. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-9ecd5b59.js +1 -0
  91. rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-4a651766-703c3015.js → flowchart-elk-definition-4a651766-6937abe7.js} +1 -1
  92. rasa/core/channels/inspector/dist/assets/{ganttDiagram-c361ad54-699328ea.js → ganttDiagram-c361ad54-7473f357.js} +1 -1
  93. rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-72cf32ee-04cf4b05.js → gitGraphDiagram-72cf32ee-d0c9405e.js} +1 -1
  94. rasa/core/channels/inspector/dist/assets/{graph-ee94449e.js → graph-0a6f8466.js} +1 -1
  95. rasa/core/channels/inspector/dist/assets/{index-3862675e-940162b4.js → index-3862675e-7610671a.js} +1 -1
  96. rasa/core/channels/inspector/dist/assets/index-74e01d94.js +1354 -0
  97. rasa/core/channels/inspector/dist/assets/{infoDiagram-f8f76790-c79c2866.js → infoDiagram-f8f76790-be397dc7.js} +1 -1
  98. rasa/core/channels/inspector/dist/assets/{journeyDiagram-49397b02-84489d30.js → journeyDiagram-49397b02-4cefbf62.js} +1 -1
  99. rasa/core/channels/inspector/dist/assets/{layout-a9aa9858.js → layout-e7fbc2bf.js} +1 -1
  100. rasa/core/channels/inspector/dist/assets/{line-eb73cf26.js → line-a8aa457c.js} +1 -1
  101. rasa/core/channels/inspector/dist/assets/{linear-b3399f9a.js → linear-3351e0d2.js} +1 -1
  102. rasa/core/channels/inspector/dist/assets/{mindmap-definition-fc14e90a-b095bf1a.js → mindmap-definition-fc14e90a-b8cbf605.js} +1 -1
  103. rasa/core/channels/inspector/dist/assets/{pieDiagram-8a3498a8-07644b66.js → pieDiagram-8a3498a8-f327f774.js} +1 -1
  104. rasa/core/channels/inspector/dist/assets/{quadrantDiagram-120e2f19-573a3f9c.js → quadrantDiagram-120e2f19-2854c591.js} +1 -1
  105. rasa/core/channels/inspector/dist/assets/{requirementDiagram-deff3bca-d457e1e1.js → requirementDiagram-deff3bca-964985d5.js} +1 -1
  106. rasa/core/channels/inspector/dist/assets/{sankeyDiagram-04a897e0-9d26e1a2.js → sankeyDiagram-04a897e0-edeb4f33.js} +1 -1
  107. rasa/core/channels/inspector/dist/assets/{sequenceDiagram-704730f1-3a9cde10.js → sequenceDiagram-704730f1-fcf70125.js} +1 -1
  108. rasa/core/channels/inspector/dist/assets/{stateDiagram-587899a1-4f3e8cec.js → stateDiagram-587899a1-0e770395.js} +1 -1
  109. rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-d93cdb3a-e617e5bf.js → stateDiagram-v2-d93cdb3a-af8dcd22.js} +1 -1
  110. rasa/core/channels/inspector/dist/assets/{styles-6aaf32cf-eab30d2f.js → styles-6aaf32cf-36a9e70d.js} +1 -1
  111. rasa/core/channels/inspector/dist/assets/{styles-9a916d00-09994be2.js → styles-9a916d00-884a8b5b.js} +1 -1
  112. rasa/core/channels/inspector/dist/assets/{styles-c10674c1-b7110364.js → styles-c10674c1-dc097813.js} +1 -1
  113. rasa/core/channels/inspector/dist/assets/{svgDrawCommon-08f97a94-3ebc92ad.js → svgDrawCommon-08f97a94-5a2c7eed.js} +1 -1
  114. rasa/core/channels/inspector/dist/assets/{timeline-definition-85554ec2-7d13d2f2.js → timeline-definition-85554ec2-e89c4f6e.js} +1 -1
  115. rasa/core/channels/inspector/dist/assets/{xychartDiagram-e933f94c-488385e1.js → xychartDiagram-e933f94c-afb6fe56.js} +1 -1
  116. rasa/core/channels/inspector/dist/index.html +1 -1
  117. rasa/core/channels/inspector/package.json +18 -18
  118. rasa/core/channels/inspector/src/App.tsx +29 -4
  119. rasa/core/channels/inspector/src/components/DialogueAgentStack.tsx +108 -0
  120. rasa/core/channels/inspector/src/components/{DialogueStack.tsx → DialogueHistoryStack.tsx} +4 -2
  121. rasa/core/channels/inspector/src/helpers/audio/audiostream.ts +7 -4
  122. rasa/core/channels/inspector/src/helpers/formatters.test.ts +4 -0
  123. rasa/core/channels/inspector/src/helpers/formatters.ts +24 -3
  124. rasa/core/channels/inspector/src/helpers/utils.test.ts +127 -0
  125. rasa/core/channels/inspector/src/helpers/utils.ts +66 -1
  126. rasa/core/channels/inspector/src/theme/base/styles.ts +19 -1
  127. rasa/core/channels/inspector/src/types.ts +21 -0
  128. rasa/core/channels/inspector/yarn.lock +336 -189
  129. rasa/core/channels/studio_chat.py +6 -6
  130. rasa/core/channels/telegram.py +4 -9
  131. rasa/core/channels/voice_stream/genesys.py +1 -1
  132. rasa/core/channels/voice_stream/tts/deepgram.py +140 -0
  133. rasa/core/channels/voice_stream/twilio_media_streams.py +5 -1
  134. rasa/core/channels/voice_stream/voice_channel.py +3 -0
  135. rasa/core/concurrent_lock_store.py +38 -21
  136. rasa/core/config/__init__.py +0 -0
  137. rasa/core/{available_endpoints.py → config/available_endpoints.py} +51 -16
  138. rasa/core/config/configuration.py +260 -0
  139. rasa/core/config/credentials.py +19 -0
  140. rasa/core/config/message_procesing_config.py +34 -0
  141. rasa/core/constants.py +10 -0
  142. rasa/core/iam_credentials_providers/aws_iam_credentials_providers.py +69 -4
  143. rasa/core/iam_credentials_providers/credentials_provider_protocol.py +2 -1
  144. rasa/core/lock_store.py +4 -0
  145. rasa/core/policies/enterprise_search_policy.py +5 -3
  146. rasa/core/policies/flow_policy.py +4 -4
  147. rasa/core/policies/flows/agent_executor.py +632 -0
  148. rasa/core/policies/flows/flow_executor.py +136 -75
  149. rasa/core/policies/flows/mcp_tool_executor.py +298 -0
  150. rasa/core/policies/intentless_policy.py +1 -1
  151. rasa/core/policies/ted_policy.py +20 -12
  152. rasa/core/policies/unexpected_intent_policy.py +6 -0
  153. rasa/core/processor.py +68 -44
  154. rasa/core/redis_connection_factory.py +7 -2
  155. rasa/core/run.py +37 -8
  156. rasa/core/test.py +4 -0
  157. rasa/core/tracker_stores/redis_tracker_store.py +4 -0
  158. rasa/core/tracker_stores/sql_tracker_store.py +3 -1
  159. rasa/core/tracker_stores/tracker_store.py +3 -7
  160. rasa/core/train.py +1 -1
  161. rasa/core/training/interactive.py +20 -18
  162. rasa/core/training/story_conflict.py +5 -5
  163. rasa/core/utils.py +22 -23
  164. rasa/dialogue_understanding/commands/__init__.py +8 -0
  165. rasa/dialogue_understanding/commands/cancel_flow_command.py +19 -5
  166. rasa/dialogue_understanding/commands/chit_chat_answer_command.py +21 -2
  167. rasa/dialogue_understanding/commands/clarify_command.py +20 -2
  168. rasa/dialogue_understanding/commands/continue_agent_command.py +91 -0
  169. rasa/dialogue_understanding/commands/knowledge_answer_command.py +21 -2
  170. rasa/dialogue_understanding/commands/restart_agent_command.py +162 -0
  171. rasa/dialogue_understanding/commands/start_flow_command.py +68 -7
  172. rasa/dialogue_understanding/commands/utils.py +124 -2
  173. rasa/dialogue_understanding/generator/command_parser.py +4 -0
  174. rasa/dialogue_understanding/generator/llm_based_command_generator.py +50 -12
  175. rasa/dialogue_understanding/generator/llm_command_generator.py +1 -1
  176. rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +1 -1
  177. rasa/dialogue_understanding/generator/prompt_templates/agent_command_prompt_v2_claude_3_5_sonnet_20240620_template.jinja2 +66 -0
  178. rasa/dialogue_understanding/generator/prompt_templates/agent_command_prompt_v2_gpt_4o_2024_11_20_template.jinja2 +66 -0
  179. rasa/dialogue_understanding/generator/prompt_templates/agent_command_prompt_v3_claude_3_5_sonnet_20240620_template.jinja2 +89 -0
  180. rasa/dialogue_understanding/generator/prompt_templates/agent_command_prompt_v3_gpt_4o_2024_11_20_template.jinja2 +88 -0
  181. rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +42 -7
  182. rasa/dialogue_understanding/generator/single_step/search_ready_llm_command_generator.py +40 -3
  183. rasa/dialogue_understanding/generator/single_step/single_step_based_llm_command_generator.py +20 -3
  184. rasa/dialogue_understanding/patterns/cancel.py +27 -6
  185. rasa/dialogue_understanding/patterns/clarify.py +3 -14
  186. rasa/dialogue_understanding/patterns/continue_interrupted.py +239 -6
  187. rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +46 -8
  188. rasa/dialogue_understanding/processor/command_processor.py +136 -15
  189. rasa/dialogue_understanding/stack/dialogue_stack.py +98 -2
  190. rasa/dialogue_understanding/stack/frames/flow_stack_frame.py +57 -0
  191. rasa/dialogue_understanding/stack/utils.py +57 -3
  192. rasa/dialogue_understanding/utils.py +24 -4
  193. rasa/dialogue_understanding_test/du_test_runner.py +8 -3
  194. rasa/e2e_test/e2e_test_runner.py +13 -3
  195. rasa/engine/caching.py +2 -2
  196. rasa/engine/constants.py +1 -1
  197. rasa/engine/recipes/default_components.py +138 -49
  198. rasa/engine/recipes/default_recipe.py +108 -11
  199. rasa/engine/runner/dask.py +8 -5
  200. rasa/engine/validation.py +19 -6
  201. rasa/graph_components/validators/default_recipe_validator.py +86 -28
  202. rasa/hooks.py +5 -5
  203. rasa/llm_fine_tuning/utils.py +2 -2
  204. rasa/model_training.py +60 -47
  205. rasa/nlu/classifiers/diet_classifier.py +198 -98
  206. rasa/nlu/classifiers/logistic_regression_classifier.py +1 -4
  207. rasa/nlu/classifiers/mitie_intent_classifier.py +3 -0
  208. rasa/nlu/classifiers/sklearn_intent_classifier.py +1 -3
  209. rasa/nlu/extractors/crf_entity_extractor.py +9 -10
  210. rasa/nlu/extractors/mitie_entity_extractor.py +3 -0
  211. rasa/nlu/extractors/spacy_entity_extractor.py +3 -0
  212. rasa/nlu/featurizers/dense_featurizer/convert_featurizer.py +4 -0
  213. rasa/nlu/featurizers/dense_featurizer/lm_featurizer.py +5 -0
  214. rasa/nlu/featurizers/dense_featurizer/mitie_featurizer.py +2 -0
  215. rasa/nlu/featurizers/dense_featurizer/spacy_featurizer.py +3 -0
  216. rasa/nlu/featurizers/sparse_featurizer/count_vectors_featurizer.py +4 -2
  217. rasa/nlu/featurizers/sparse_featurizer/lexical_syntactic_featurizer.py +4 -0
  218. rasa/nlu/selectors/response_selector.py +10 -2
  219. rasa/nlu/tokenizers/jieba_tokenizer.py +3 -4
  220. rasa/nlu/tokenizers/mitie_tokenizer.py +3 -2
  221. rasa/nlu/tokenizers/spacy_tokenizer.py +3 -2
  222. rasa/nlu/utils/mitie_utils.py +3 -0
  223. rasa/nlu/utils/spacy_utils.py +3 -2
  224. rasa/plugin.py +8 -8
  225. rasa/privacy/privacy_manager.py +12 -3
  226. rasa/server.py +15 -3
  227. rasa/shared/agents/__init__.py +0 -0
  228. rasa/shared/agents/auth/__init__.py +0 -0
  229. rasa/shared/agents/auth/agent_auth_factory.py +105 -0
  230. rasa/shared/agents/auth/agent_auth_manager.py +92 -0
  231. rasa/shared/agents/auth/auth_strategy/__init__.py +19 -0
  232. rasa/shared/agents/auth/auth_strategy/agent_auth_strategy.py +52 -0
  233. rasa/shared/agents/auth/auth_strategy/api_key_auth_strategy.py +42 -0
  234. rasa/shared/agents/auth/auth_strategy/bearer_token_auth_strategy.py +28 -0
  235. rasa/shared/agents/auth/auth_strategy/oauth2_auth_strategy.py +167 -0
  236. rasa/shared/agents/auth/constants.py +12 -0
  237. rasa/shared/agents/auth/types.py +12 -0
  238. rasa/shared/agents/utils.py +35 -0
  239. rasa/shared/constants.py +8 -0
  240. rasa/shared/core/constants.py +16 -1
  241. rasa/shared/core/domain.py +0 -7
  242. rasa/shared/core/events.py +327 -0
  243. rasa/shared/core/flows/constants.py +5 -0
  244. rasa/shared/core/flows/flows_list.py +21 -5
  245. rasa/shared/core/flows/flows_yaml_schema.json +119 -184
  246. rasa/shared/core/flows/steps/call.py +49 -5
  247. rasa/shared/core/flows/steps/collect.py +98 -13
  248. rasa/shared/core/flows/validation.py +372 -8
  249. rasa/shared/core/flows/yaml_flows_io.py +3 -2
  250. rasa/shared/core/slots.py +2 -2
  251. rasa/shared/core/trackers.py +5 -2
  252. rasa/shared/exceptions.py +16 -0
  253. rasa/shared/importers/rasa.py +1 -1
  254. rasa/shared/importers/utils.py +9 -3
  255. rasa/shared/providers/llm/_base_litellm_client.py +41 -9
  256. rasa/shared/providers/llm/litellm_router_llm_client.py +8 -4
  257. rasa/shared/providers/llm/llm_client.py +7 -3
  258. rasa/shared/providers/llm/llm_response.py +66 -0
  259. rasa/shared/providers/llm/self_hosted_llm_client.py +8 -4
  260. rasa/shared/utils/common.py +24 -0
  261. rasa/shared/utils/health_check/health_check.py +7 -3
  262. rasa/shared/utils/llm.py +39 -16
  263. rasa/shared/utils/mcp/__init__.py +0 -0
  264. rasa/shared/utils/mcp/server_connection.py +247 -0
  265. rasa/shared/utils/mcp/utils.py +20 -0
  266. rasa/shared/utils/schemas/events.py +42 -0
  267. rasa/shared/utils/yaml.py +3 -1
  268. rasa/studio/pull/pull.py +3 -2
  269. rasa/studio/train.py +8 -7
  270. rasa/studio/upload.py +3 -6
  271. rasa/telemetry.py +69 -5
  272. rasa/tracing/config.py +45 -12
  273. rasa/tracing/constants.py +14 -0
  274. rasa/tracing/instrumentation/attribute_extractors.py +142 -9
  275. rasa/tracing/instrumentation/instrumentation.py +626 -21
  276. rasa/tracing/instrumentation/intentless_policy_instrumentation.py +4 -4
  277. rasa/tracing/instrumentation/metrics.py +32 -0
  278. rasa/tracing/metric_instrument_provider.py +68 -0
  279. rasa/utils/common.py +92 -1
  280. rasa/utils/endpoints.py +11 -2
  281. rasa/utils/log_utils.py +96 -5
  282. rasa/utils/ml_utils.py +1 -1
  283. rasa/utils/tensorflow/__init__.py +7 -0
  284. rasa/utils/tensorflow/callback.py +136 -101
  285. rasa/utils/tensorflow/crf.py +1 -1
  286. rasa/utils/tensorflow/data_generator.py +21 -8
  287. rasa/utils/tensorflow/layers.py +21 -11
  288. rasa/utils/tensorflow/metrics.py +7 -3
  289. rasa/utils/tensorflow/models.py +56 -8
  290. rasa/utils/tensorflow/rasa_layers.py +8 -6
  291. rasa/utils/tensorflow/transformer.py +2 -3
  292. rasa/utils/train_utils.py +54 -24
  293. rasa/validator.py +5 -5
  294. rasa/version.py +1 -1
  295. {rasa_pro-3.14.0.dev20250922.dist-info → rasa_pro-3.14.0rc2.dist-info}/METADATA +47 -41
  296. {rasa_pro-3.14.0.dev20250922.dist-info → rasa_pro-3.14.0rc2.dist-info}/RECORD +299 -238
  297. rasa/builder/scrape_rasa_docs.py +0 -97
  298. rasa/core/channels/inspector/dist/assets/channel-8e08bed9.js +0 -1
  299. rasa/core/channels/inspector/dist/assets/clone-78c82dea.js +0 -1
  300. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-2b08f601.js +0 -1
  301. rasa/core/channels/inspector/dist/assets/index-c941dcb3.js +0 -1336
  302. {rasa_pro-3.14.0.dev20250922.dist-info → rasa_pro-3.14.0rc2.dist-info}/NOTICE +0 -0
  303. {rasa_pro-3.14.0.dev20250922.dist-info → rasa_pro-3.14.0rc2.dist-info}/WHEEL +0 -0
  304. {rasa_pro-3.14.0.dev20250922.dist-info → rasa_pro-3.14.0rc2.dist-info}/entry_points.txt +0 -0
@@ -4,12 +4,11 @@ from typing import Any, Dict, List, Optional, Text
4
4
 
5
5
  import structlog
6
6
  from jinja2 import Template
7
- from structlog.contextvars import (
8
- bound_contextvars,
9
- )
7
+ from structlog.contextvars import bound_contextvars
10
8
 
11
- from rasa.core.available_endpoints import AvailableEndpoints
9
+ from rasa.core.config.configuration import Configuration
12
10
  from rasa.core.constants import ACTIVE_FLOW_METADATA_KEY, STEP_ID_METADATA_KEY
11
+ from rasa.core.policies.flows.agent_executor import run_agent
13
12
  from rasa.core.policies.flows.flow_exceptions import (
14
13
  FlowCircuitBreakerTrippedException,
15
14
  FlowException,
@@ -21,7 +20,7 @@ from rasa.core.policies.flows.flow_step_result import (
21
20
  FlowStepResult,
22
21
  PauseFlowReturnPrediction,
23
22
  )
24
- from rasa.dialogue_understanding.commands import CancelFlowCommand
23
+ from rasa.core.policies.flows.mcp_tool_executor import call_mcp_tool
25
24
  from rasa.dialogue_understanding.patterns.cancel import CancelPatternFlowStackFrame
26
25
  from rasa.dialogue_understanding.patterns.collect_information import (
27
26
  FLOW_PATTERN_COLLECT_INFORMATION,
@@ -48,14 +47,18 @@ from rasa.dialogue_understanding.stack.frames import (
48
47
  UserFlowStackFrame,
49
48
  )
50
49
  from rasa.dialogue_understanding.stack.frames.flow_stack_frame import (
50
+ AgentStackFrame,
51
51
  FlowStackFrameType,
52
52
  )
53
53
  from rasa.dialogue_understanding.stack.utils import (
54
- top_user_flow_frame,
54
+ user_frames_on_the_stack,
55
55
  )
56
+ from rasa.dialogue_understanding.utils import assemble_options_string
56
57
  from rasa.shared.constants import RASA_PATTERN_HUMAN_HANDOFF
57
58
  from rasa.shared.core.constants import (
58
59
  ACTION_LISTEN_NAME,
60
+ GLOBAL_SILENCE_TIMEOUT_DEFAULT_VALUE,
61
+ SILENCE_TIMEOUT_CHANNEL_KEY,
59
62
  SILENCE_TIMEOUT_SLOT,
60
63
  )
61
64
  from rasa.shared.core.events import (
@@ -66,11 +69,7 @@ from rasa.shared.core.events import (
66
69
  SlotSet,
67
70
  )
68
71
  from rasa.shared.core.flows import FlowsList
69
- from rasa.shared.core.flows.flow import (
70
- END_STEP,
71
- Flow,
72
- FlowStep,
73
- )
72
+ from rasa.shared.core.flows.flow import END_STEP, Flow, FlowStep
74
73
  from rasa.shared.core.flows.flow_step_links import (
75
74
  ElseFlowStepLink,
76
75
  IfFlowStepLink,
@@ -88,9 +87,7 @@ from rasa.shared.core.flows.steps import (
88
87
  )
89
88
  from rasa.shared.core.flows.steps.constants import START_STEP
90
89
  from rasa.shared.core.slots import Slot, SlotRejection
91
- from rasa.shared.core.trackers import (
92
- DialogueStateTracker,
93
- )
90
+ from rasa.shared.core.trackers import DialogueStateTracker
94
91
  from rasa.utils.pypred import Predicate
95
92
 
96
93
  structlogger = structlog.get_logger()
@@ -148,6 +145,13 @@ def select_next_step_id(
148
145
  tracker: DialogueStateTracker,
149
146
  ) -> Optional[Text]:
150
147
  """Selects the next step id based on the current step."""
148
+ # if the current step is a call step to an agent, and we already have an
149
+ # AgentStackFrame on top of the stack, we need to return the current
150
+ # step id again in order to loop back to the agent.
151
+ top_stack_frame = tracker.stack.top()
152
+ if top_stack_frame and isinstance(top_stack_frame, AgentStackFrame):
153
+ return current.id
154
+
151
155
  next_step = current.next
152
156
  if len(next_step.links) == 1 and isinstance(next_step.links[0], StaticFlowStepLink):
153
157
  return next_step.links[0].target
@@ -234,38 +238,58 @@ def trigger_pattern_continue_interrupted(
234
238
  stack: DialogueStack,
235
239
  flows: FlowsList,
236
240
  tracker: DialogueStateTracker,
237
- ) -> List[Event]:
241
+ ) -> None:
238
242
  """Trigger the pattern to continue an interrupted flow if needed."""
239
- events: List[Event] = []
240
-
241
- # get previously started user flow that will be continued
242
- interrupted_user_flow_frame = top_user_flow_frame(stack)
243
- interrupted_user_flow_step = (
244
- interrupted_user_flow_frame.step(flows) if interrupted_user_flow_frame else None
245
- )
246
- interrupted_user_flow = (
247
- interrupted_user_flow_frame.flow(flows) if interrupted_user_flow_frame else None
248
- )
249
-
243
+ # only trigger the pattern if the current frame is a user flow frame
244
+ # with a frame type of interrupt
250
245
  if (
251
- isinstance(current_frame, UserFlowStackFrame)
252
- and interrupted_user_flow_step is not None
253
- and interrupted_user_flow is not None
254
- and current_frame.frame_type == FlowStackFrameType.INTERRUPT
255
- and not is_step_end_of_flow(interrupted_user_flow_step)
246
+ not isinstance(current_frame, UserFlowStackFrame)
247
+ or current_frame.frame_type != FlowStackFrameType.INTERRUPT
256
248
  ):
257
- stack.push(
258
- ContinueInterruptedPatternFlowStackFrame(
259
- previous_flow_name=interrupted_user_flow.readable_name(
260
- language=tracker.current_language
261
- ),
262
- )
249
+ return None
250
+
251
+ # get all previously interrupted user flows
252
+ interrupted_user_flow_stack_frames = user_frames_on_the_stack(stack)
253
+
254
+ interrupted_user_flows_to_continue: List[UserFlowStackFrame] = []
255
+ # check if interrupted user flows can be continued
256
+ # i.e. the flow is not at the end of the flow
257
+ for frame in interrupted_user_flow_stack_frames:
258
+ interrupted_user_flow_step = frame.step(flows)
259
+ interrupted_user_flow = frame.flow(flows)
260
+ if (
261
+ interrupted_user_flow_step is not None
262
+ and interrupted_user_flow is not None
263
+ and not is_step_end_of_flow(interrupted_user_flow_step)
264
+ ):
265
+ interrupted_user_flows_to_continue.append(frame)
266
+
267
+ # if there are no interrupted user flows to continue,
268
+ # we don't need to trigger the pattern
269
+ if len(interrupted_user_flows_to_continue) == 0:
270
+ return None
271
+
272
+ # get the flow names and ids of the interrupted flows
273
+ # and assemble the options string
274
+ flow_names: List[str] = []
275
+ flow_ids: List[str] = []
276
+ for frame in interrupted_user_flows_to_continue:
277
+ flow_names.append(
278
+ frame.flow(flows).readable_name(language=tracker.current_language)
263
279
  )
264
- events.append(
265
- FlowResumed(interrupted_user_flow.id, interrupted_user_flow_step.id)
280
+ flow_ids.append(frame.flow_id)
281
+ options_string = assemble_options_string(flow_names)
282
+
283
+ # trigger the pattern to continue the interrupted flows
284
+ stack.push(
285
+ ContinueInterruptedPatternFlowStackFrame(
286
+ interrupted_flow_names=flow_names,
287
+ interrupted_flow_ids=flow_ids,
288
+ interrupted_flow_options=options_string,
266
289
  )
290
+ )
267
291
 
268
- return events
292
+ return None
269
293
 
270
294
 
271
295
  def trigger_pattern_completed(
@@ -359,8 +383,11 @@ def reset_scoped_slots(
359
383
  return events
360
384
 
361
385
 
362
- def advance_flows(
363
- tracker: DialogueStateTracker, available_actions: List[str], flows: FlowsList
386
+ async def advance_flows(
387
+ tracker: DialogueStateTracker,
388
+ available_actions: List[str],
389
+ flows: FlowsList,
390
+ slots: List[Slot],
364
391
  ) -> FlowActionPrediction:
365
392
  """Advance the current flows until the next action.
366
393
 
@@ -368,6 +395,7 @@ def advance_flows(
368
395
  tracker: The tracker to get the next action for.
369
396
  available_actions: The actions that are available in the domain.
370
397
  flows: All flows.
398
+ slots: The slots that are available in the domain.
371
399
 
372
400
  Returns:
373
401
  The predicted action and the events to run.
@@ -377,13 +405,16 @@ def advance_flows(
377
405
  # if there are no flows, there is nothing to do
378
406
  return FlowActionPrediction(None, 0.0)
379
407
 
380
- return advance_flows_until_next_action(tracker, available_actions, flows)
408
+ return await advance_flows_until_next_action(
409
+ tracker, available_actions, flows, slots
410
+ )
381
411
 
382
412
 
383
- def advance_flows_until_next_action(
413
+ async def advance_flows_until_next_action(
384
414
  tracker: DialogueStateTracker,
385
415
  available_actions: List[str],
386
416
  flows: FlowsList,
417
+ slots: List[Slot],
387
418
  ) -> FlowActionPrediction:
388
419
  """Advance the flow and select the next action to execute.
389
420
 
@@ -441,7 +472,7 @@ def advance_flows_until_next_action(
441
472
 
442
473
  with bound_contextvars(step_id=next_step.id):
443
474
  step_stack = tracker.stack
444
- step_result = run_step(
475
+ step_result = await run_step(
445
476
  next_step,
446
477
  current_flow,
447
478
  step_stack,
@@ -449,6 +480,7 @@ def advance_flows_until_next_action(
449
480
  available_actions,
450
481
  flows,
451
482
  previous_step_id,
483
+ slots,
452
484
  )
453
485
  new_events = step_result.events
454
486
  if (
@@ -477,10 +509,9 @@ def advance_flows_until_next_action(
477
509
  # make sure we really return all events that got created during the
478
510
  # step execution of all steps (not only the last one)
479
511
  prediction.events = gathered_events
480
- prediction.metadata = {
481
- ACTIVE_FLOW_METADATA_KEY: tracker.active_flow,
482
- STEP_ID_METADATA_KEY: tracker.current_step_id,
483
- }
512
+ prediction.metadata = prediction.metadata or {}
513
+ prediction.metadata[ACTIVE_FLOW_METADATA_KEY] = tracker.active_flow
514
+ prediction.metadata[STEP_ID_METADATA_KEY] = tracker.current_step_id
484
515
  return prediction
485
516
  else:
486
517
  structlogger.warning("flow.step.execution.no_action")
@@ -522,6 +553,8 @@ def validate_collect_step(
522
553
 
523
554
  def cancel_flow_and_push_internal_error(stack: DialogueStack, flow_name: str) -> None:
524
555
  """Cancel the top user flow and push the internal error pattern."""
556
+ from rasa.dialogue_understanding.commands import CancelFlowCommand
557
+
525
558
  top_frame = stack.top()
526
559
 
527
560
  if isinstance(top_frame, BaseFlowStackFrame):
@@ -550,7 +583,7 @@ def attach_stack_metadata_to_events(
550
583
  event.metadata[ACTIVE_FLOW_METADATA_KEY] = flow_id
551
584
 
552
585
 
553
- def run_step(
586
+ async def run_step(
554
587
  step: FlowStep,
555
588
  flow: Flow,
556
589
  stack: DialogueStack,
@@ -558,6 +591,7 @@ def run_step(
558
591
  available_actions: List[str],
559
592
  flows: FlowsList,
560
593
  previous_step_id: str,
594
+ slots: List[Slot],
561
595
  ) -> FlowStepResult:
562
596
  """Run a single step of a flow.
563
597
 
@@ -576,6 +610,7 @@ def run_step(
576
610
  available_actions: The actions that are available in the domain.
577
611
  flows: All flows.
578
612
  previous_step_id: The ID of the previous step.
613
+ slots: The slots that are available in the domain.
579
614
 
580
615
  Returns:
581
616
  A result of running the step describing where to transition to.
@@ -615,7 +650,7 @@ def run_step(
615
650
  return _run_link_step(initial_events, stack, step)
616
651
 
617
652
  elif isinstance(step, CallFlowStep):
618
- return _run_call_step(initial_events, stack, step)
653
+ return await _run_call_step(initial_events, stack, step, tracker, slots)
619
654
 
620
655
  elif isinstance(step, SetSlotsFlowStep):
621
656
  return _run_set_slot_step(initial_events, step)
@@ -667,12 +702,10 @@ def _run_end_step(
667
702
  structlogger.debug("flow.step.run.flow_end")
668
703
  current_frame = stack.pop()
669
704
  trigger_pattern_completed(current_frame, stack, flows)
670
- resumed_events = trigger_pattern_continue_interrupted(
671
- current_frame, stack, flows, tracker
672
- )
705
+ trigger_pattern_continue_interrupted(current_frame, stack, flows, tracker)
673
706
  reset_events: List[Event] = reset_scoped_slots(current_frame, flow, tracker)
674
707
  return ContinueFlowWithNextStep(
675
- events=initial_events + reset_events + resumed_events, has_flow_ended=True
708
+ events=initial_events + reset_events, has_flow_ended=True
676
709
  )
677
710
 
678
711
 
@@ -684,17 +717,26 @@ def _run_set_slot_step(
684
717
  return ContinueFlowWithNextStep(events=initial_events + slot_events)
685
718
 
686
719
 
687
- def _run_call_step(
688
- initial_events: List[Event], stack: DialogueStack, step: CallFlowStep
720
+ async def _run_call_step(
721
+ initial_events: List[Event],
722
+ stack: DialogueStack,
723
+ step: CallFlowStep,
724
+ tracker: DialogueStateTracker,
725
+ slots: List[Slot],
689
726
  ) -> FlowStepResult:
690
727
  structlogger.debug("flow.step.run.call")
691
- stack.push(
692
- UserFlowStackFrame(
693
- flow_id=step.call,
694
- frame_type=FlowStackFrameType.CALL,
695
- ),
696
- )
697
- return ContinueFlowWithNextStep(events=initial_events)
728
+ if step.is_calling_mcp_tool():
729
+ return await call_mcp_tool(initial_events, stack, step, tracker)
730
+ elif step.is_calling_agent():
731
+ return await run_agent(initial_events, stack, step, tracker, slots)
732
+ else:
733
+ stack.push(
734
+ UserFlowStackFrame(
735
+ flow_id=step.call,
736
+ frame_type=FlowStackFrameType.CALL,
737
+ ),
738
+ )
739
+ return ContinueFlowWithNextStep(events=initial_events)
698
740
 
699
741
 
700
742
  def _run_link_step(
@@ -797,21 +839,41 @@ def _silence_timeout_events_for_collect_step(
797
839
  ) -> List[Event]:
798
840
  events: List[Event] = []
799
841
 
800
- silence_timeout = (
801
- AvailableEndpoints.get_instance().interaction_handling.global_silence_timeout
802
- )
803
-
804
842
  if step.silence_timeout:
843
+ input_channel_name = tracker.get_latest_input_channel()
844
+
805
845
  structlogger.debug(
806
- "flow.step.run.adjusting_silence_timeout",
846
+ "flow.step.run.using_step_silence_timeout",
807
847
  duration=step.silence_timeout,
808
848
  collect=step.collect,
809
849
  )
810
850
 
811
- silence_timeout = step.silence_timeout
851
+ silence_timeout = step.silence_timeout.get_silence_for_channel(
852
+ input_channel_name
853
+ )
812
854
  else:
855
+ input_channel_name = tracker.get_latest_input_channel()
856
+ credentials_config = Configuration.get_instance().credentials
857
+
858
+ if credentials_config:
859
+ channel_config = (
860
+ credentials_config.channels.get(input_channel_name)
861
+ if input_channel_name
862
+ else None
863
+ )
864
+
865
+ silence_timeout = (
866
+ channel_config.get(
867
+ SILENCE_TIMEOUT_CHANNEL_KEY, GLOBAL_SILENCE_TIMEOUT_DEFAULT_VALUE
868
+ )
869
+ if channel_config
870
+ else GLOBAL_SILENCE_TIMEOUT_DEFAULT_VALUE
871
+ )
872
+ else:
873
+ silence_timeout = GLOBAL_SILENCE_TIMEOUT_DEFAULT_VALUE
874
+
813
875
  structlogger.debug(
814
- "flow.step.run.reset_silence_timeout_to_global",
876
+ "flow.step.run.use_channel_silence_timeout",
815
877
  duration=silence_timeout,
816
878
  collect=step.collect,
817
879
  )
@@ -828,14 +890,13 @@ def _append_global_silence_timeout_event(
828
890
  events: List[Event], tracker: DialogueStateTracker
829
891
  ) -> None:
830
892
  current_silence_timeout = tracker.get_slot(SILENCE_TIMEOUT_SLOT)
831
- global_silence_timeout = (
832
- AvailableEndpoints.get_instance().interaction_handling.global_silence_timeout
833
- )
893
+ endpoints = Configuration.get_instance().endpoints
894
+ global_silence_timeout = endpoints.interaction_handling.global_silence_timeout
834
895
 
835
896
  if current_silence_timeout != global_silence_timeout:
836
897
  events.append(
837
898
  SlotSet(
838
899
  SILENCE_TIMEOUT_SLOT,
839
- AvailableEndpoints.get_instance().interaction_handling.global_silence_timeout,
900
+ global_silence_timeout,
840
901
  )
841
902
  )
@@ -0,0 +1,298 @@
1
+ import json
2
+ from typing import Any, Dict, List, Optional
3
+
4
+ import structlog
5
+ from jinja2.sandbox import SandboxedEnvironment
6
+ from mcp.types import CallToolResult
7
+
8
+ from rasa.core.config.configuration import Configuration
9
+ from rasa.core.policies.flows.flow_step_result import (
10
+ ContinueFlowWithNextStep,
11
+ FlowStepResult,
12
+ )
13
+ from rasa.dialogue_understanding.patterns.internal_error import (
14
+ InternalErrorPatternFlowStackFrame,
15
+ )
16
+ from rasa.dialogue_understanding.stack.dialogue_stack import DialogueStack
17
+ from rasa.shared.core.events import Event, SlotSet
18
+ from rasa.shared.core.flows.steps import CallFlowStep
19
+ from rasa.shared.core.trackers import DialogueStateTracker
20
+ from rasa.shared.utils.mcp.server_connection import MCPServerConnection
21
+ from rasa.utils.common import ensure_jsonified_iterable
22
+
23
+ structlogger = structlog.get_logger()
24
+
25
+ CONFIG_VALUE = "value"
26
+ CONFIG_SLOT = "slot"
27
+
28
+
29
+ async def call_mcp_tool(
30
+ initial_events: List[Event],
31
+ stack: DialogueStack,
32
+ step: CallFlowStep,
33
+ tracker: DialogueStateTracker,
34
+ ) -> FlowStepResult:
35
+ """Run an MCP tool call step."""
36
+ structlogger.debug(
37
+ "flow.step.call_mcp_tool",
38
+ tool_id=step.call,
39
+ mcp_server=step.mcp_server,
40
+ mapping=step.mapping,
41
+ step_id=step.id,
42
+ flow_id=step.flow_id,
43
+ json_formatting=["mapping"],
44
+ )
45
+
46
+ try:
47
+ return await _execute_mcp_tool_call(initial_events, stack, step, tracker)
48
+ except Exception as e:
49
+ return _handle_mcp_tool_error(
50
+ stack,
51
+ initial_events,
52
+ error_message=f"Failed to execute MCP tool call: {e}.",
53
+ tool_name=step.call,
54
+ mcp_server=step.mcp_server,
55
+ )
56
+
57
+
58
+ async def _execute_mcp_tool_call(
59
+ initial_events: List[Event],
60
+ stack: DialogueStack,
61
+ step: CallFlowStep,
62
+ tracker: DialogueStateTracker,
63
+ ) -> FlowStepResult:
64
+ """Execute the MCP tool call with proper error handling."""
65
+ mcp_server_connection = None
66
+ try:
67
+ # Connect to the MCP server
68
+ mcp_server_connection = await _connect_to_mcp_server(step.mcp_server)
69
+
70
+ if not mcp_server_connection:
71
+ return _handle_mcp_tool_error(
72
+ stack,
73
+ initial_events,
74
+ f"Cannot connect to MCP server '{step.mcp_server}'.",
75
+ tool_name=step.call,
76
+ mcp_server=step.mcp_server,
77
+ )
78
+
79
+ # Validate tool availability
80
+ if not await _is_tool_available(mcp_server_connection, step.call):
81
+ return _handle_mcp_tool_error(
82
+ stack,
83
+ initial_events,
84
+ f"Tool '{step.call}' is not available on MCP server "
85
+ f"'{step.mcp_server}'.",
86
+ tool_name=step.call,
87
+ mcp_server=step.mcp_server,
88
+ )
89
+
90
+ # This should not happen, but we need to check for type checking to pass
91
+ if not step.mapping:
92
+ return _handle_mcp_tool_error(
93
+ stack,
94
+ initial_events,
95
+ f"No mapping found for tool '{step.call}'.",
96
+ tool_name=step.call,
97
+ mcp_server=step.mcp_server,
98
+ )
99
+
100
+ # Prepare arguments for the tool call
101
+ arguments = _prepare_tool_arguments(step.mapping["input"], tracker)
102
+
103
+ # Call the tool with parameters
104
+ mcp_server = await mcp_server_connection.ensure_active_session()
105
+ result = await mcp_server.call_tool(step.call, arguments)
106
+
107
+ # Handle tool execution result
108
+ if result is None or result.isError:
109
+ return _handle_mcp_tool_error(
110
+ stack,
111
+ initial_events,
112
+ f"Tool '{step.call}' execution failed: {result.content}.",
113
+ tool_name=step.call,
114
+ mcp_server=step.mcp_server,
115
+ )
116
+ elif not result.content:
117
+ structlogger.warning(
118
+ "call_mcp_tool.empty_tool_result",
119
+ tool_name=step.call,
120
+ mcp_server=step.mcp_server,
121
+ )
122
+ else:
123
+ structlogger.debug(
124
+ "call_mcp_tool.tool_execution_success",
125
+ tool_name=step.call,
126
+ mcp_server=step.mcp_server,
127
+ result_content=result.content,
128
+ result_structured_content=result.structuredContent,
129
+ json_formatting=["result_content", "result_structured_content"],
130
+ )
131
+
132
+ # Process successful result
133
+ if set_slot_event := _process_tool_result(result, step.mapping["output"]):
134
+ initial_events.extend(set_slot_event)
135
+ else:
136
+ return _handle_mcp_tool_error(
137
+ stack,
138
+ initial_events,
139
+ f"Failed to process tool result for '{step.call}'.",
140
+ tool_name=step.call,
141
+ mcp_server=step.mcp_server,
142
+ )
143
+
144
+ return ContinueFlowWithNextStep(events=initial_events)
145
+
146
+ finally:
147
+ # Always clean up the connection to prevent resource leaks
148
+ if mcp_server_connection:
149
+ try:
150
+ await mcp_server_connection.close()
151
+ except Exception as e:
152
+ structlogger.warning(
153
+ "call_mcp_tool.connection_cleanup_failed",
154
+ tool_name=step.call,
155
+ mcp_server=step.mcp_server,
156
+ error=str(e),
157
+ )
158
+
159
+
160
+ async def _is_tool_available(
161
+ mcp_server_connection: MCPServerConnection, tool_name: str
162
+ ) -> bool:
163
+ """Check if the specified tool is available on the MCP server."""
164
+ try:
165
+ # Get the active session from the connection
166
+ mcp_server = await mcp_server_connection.ensure_active_session()
167
+ available_tools = await mcp_server.list_tools()
168
+ tool_names = [tool.name for tool in available_tools.tools]
169
+ return tool_name in tool_names
170
+ except Exception as e:
171
+ structlogger.warning(
172
+ "call_mcp_tool.tool_availability_check_failed",
173
+ tool_name=tool_name,
174
+ error=str(e),
175
+ )
176
+ return False
177
+
178
+
179
+ async def _connect_to_mcp_server(
180
+ mcp_server_name: Optional[str],
181
+ ) -> Optional[MCPServerConnection]:
182
+ """Connect to the MCP server."""
183
+ if not mcp_server_name:
184
+ return None
185
+
186
+ # get the MCP server config from the available endpoints
187
+ endpoints = Configuration.get_instance().endpoints
188
+ mcp_servers = endpoints.mcp_servers
189
+ if not mcp_servers:
190
+ return None
191
+
192
+ mcp_server_configs = [
193
+ mcp_server for mcp_server in mcp_servers if mcp_server.name == mcp_server_name
194
+ ]
195
+ if not mcp_server_configs or len(mcp_server_configs) != 1:
196
+ return None
197
+
198
+ mcp_server_config = mcp_server_configs[0]
199
+
200
+ mcp_server_connection = MCPServerConnection.from_config(
201
+ mcp_server_config.model_dump()
202
+ )
203
+
204
+ # Ensure the connection is established and return the connection object
205
+ await mcp_server_connection.ensure_active_session()
206
+ return mcp_server_connection
207
+
208
+
209
+ def _prepare_tool_arguments(
210
+ input_mapping: List[Dict[str, str]], tracker: DialogueStateTracker
211
+ ) -> Dict[str, Any]:
212
+ """Prepare arguments for the tool call from slot values."""
213
+ arguments = {}
214
+ for argument in input_mapping:
215
+ slot_value = tracker.get_slot(argument["slot"])
216
+ arguments[argument["param"]] = slot_value
217
+ return arguments
218
+
219
+
220
+ def _jsonify_slot_value(value: Any) -> str | int | float | bool | None:
221
+ """Prepare value for SlotSet: iterables -> JSON string, primitives -> as-is"""
222
+ if isinstance(value, (list, dict)) and len(value):
223
+ return json.dumps(ensure_jsonified_iterable(value))
224
+ return value
225
+
226
+
227
+ def _get_slot_value_from_jinja2_expression(
228
+ result_expression: str,
229
+ result_as_dict: Dict[str, Any],
230
+ ) -> Any:
231
+ """Get the slot value from the Jinja2 expression"""
232
+ # Create a sandboxed environment to evaluate the expression
233
+ _env = SandboxedEnvironment()
234
+
235
+ # Compile the expression
236
+ compiled_expr = _env.compile_expression(result_expression)
237
+
238
+ # Evaluate the expression
239
+ return compiled_expr(result_as_dict)
240
+
241
+
242
+ def _process_tool_result(
243
+ result: CallToolResult,
244
+ output_mapping: List[Dict[str, str]],
245
+ ) -> Optional[List[SlotSet]]:
246
+ """Create a SetSlot event for the tool result using Jinja2 expressions"""
247
+ try:
248
+ _result_as_dict = {"result": result.model_dump()}
249
+ slots = []
250
+ for mapping in output_mapping:
251
+ try:
252
+ result_expression = mapping[CONFIG_VALUE]
253
+
254
+ # Get the slot value from the Jinja2 expression
255
+ slot_value = _get_slot_value_from_jinja2_expression(
256
+ result_expression, _result_as_dict
257
+ )
258
+
259
+ slots.append(
260
+ SlotSet(mapping[CONFIG_SLOT], _jsonify_slot_value(slot_value))
261
+ )
262
+ except Exception as e:
263
+ structlogger.error(
264
+ "call_mcp_tool.value_not_found_in_tool_result",
265
+ slot=mapping[CONFIG_SLOT],
266
+ value=mapping[CONFIG_VALUE],
267
+ result=_result_as_dict,
268
+ error=str(e),
269
+ json_formatting=["result"],
270
+ )
271
+ return None
272
+ return slots
273
+ except Exception as e:
274
+ structlogger.error(
275
+ "call_mcp_tool.result_processing_failed",
276
+ error=str(e),
277
+ result=result,
278
+ json_formatting=["result"],
279
+ )
280
+ return None
281
+
282
+
283
+ def _handle_mcp_tool_error(
284
+ stack: DialogueStack,
285
+ events: List[Event],
286
+ error_message: str,
287
+ tool_name: str,
288
+ mcp_server: Optional[str],
289
+ ) -> FlowStepResult:
290
+ """Handle MCP tool errors consistently."""
291
+ structlogger.error(
292
+ "call_mcp_tool.error",
293
+ error_message=error_message,
294
+ tool_name=tool_name,
295
+ mcp_server=mcp_server,
296
+ )
297
+ stack.push(InternalErrorPatternFlowStackFrame())
298
+ return ContinueFlowWithNextStep(events=events)
@@ -5,7 +5,7 @@ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Text, Tuple
5
5
 
6
6
  import structlog
7
7
  import tiktoken
8
- from deprecated import deprecated # type: ignore[import]
8
+ from deprecated import deprecated # type: ignore[import-untyped]
9
9
  from jinja2 import Template
10
10
  from langchain.docstore.document import Document
11
11
  from langchain.schema.embeddings import Embeddings