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
@@ -0,0 +1,522 @@
1
+ import importlib
2
+ import json
3
+ import re
4
+ from typing import Any, Dict, List, Optional, Tuple
5
+
6
+ import structlog
7
+ from jinja2 import Template
8
+
9
+ from rasa.agents.constants import (
10
+ KEY_CONTENT,
11
+ KEY_ROLE,
12
+ KEY_TOOL_CALL_ID,
13
+ )
14
+ from rasa.agents.core.types import AgentStatus, ProtocolType
15
+ from rasa.agents.protocol.mcp.mcp_base_agent import MCPBaseAgent
16
+ from rasa.agents.schemas import (
17
+ AgentInput,
18
+ AgentOutput,
19
+ AgentToolResult,
20
+ AgentToolSchema,
21
+ )
22
+ from rasa.agents.schemas.agent_input import AgentInputSlot
23
+ from rasa.core.available_agents import AgentMCPServerConfig, ProtocolConfig
24
+ from rasa.shared.agents.utils import make_agent_identifier
25
+ from rasa.shared.constants import (
26
+ ROLE_TOOL,
27
+ )
28
+ from rasa.shared.core.events import SlotSet
29
+ from rasa.shared.exceptions import (
30
+ LLMToolResponseDecodeError,
31
+ ProviderClientAPIException,
32
+ )
33
+ from rasa.shared.providers.llm.llm_response import LLMResponse
34
+ from rasa.utils.pypred import Predicate
35
+
36
+ DEFAULT_TASK_AGENT_PROMPT_TEMPLATE = importlib.resources.read_text(
37
+ "rasa.agents.templates", "mcp_task_agent_prompt_template.jinja2"
38
+ )
39
+
40
+ structlogger = structlog.get_logger()
41
+
42
+
43
+ class MCPTaskAgent(MCPBaseAgent):
44
+ """MCPTaskAgent client implementation"""
45
+
46
+ def __init__(
47
+ self,
48
+ name: str,
49
+ description: str,
50
+ protocol_type: ProtocolConfig,
51
+ server_configs: List[AgentMCPServerConfig],
52
+ llm_config: Optional[Dict[str, Any]] = None,
53
+ prompt_template: Optional[str] = None,
54
+ timeout: Optional[int] = None,
55
+ max_retries: Optional[int] = None,
56
+ ):
57
+ super().__init__(
58
+ name,
59
+ description,
60
+ protocol_type,
61
+ server_configs,
62
+ llm_config,
63
+ prompt_template,
64
+ timeout,
65
+ max_retries,
66
+ )
67
+
68
+ @property
69
+ def protocol_type(self) -> ProtocolType:
70
+ return ProtocolType.MCP_TASK
71
+
72
+ @staticmethod
73
+ def get_default_prompt_template() -> str:
74
+ return DEFAULT_TASK_AGENT_PROMPT_TEMPLATE
75
+
76
+ @classmethod
77
+ def get_agent_specific_built_in_tools(
78
+ cls, agent_input: AgentInput
79
+ ) -> List[AgentToolSchema]:
80
+ """Get agentic specific built-in tools."""
81
+ slot_names = cls._get_slot_names_from_exit_conditions(agent_input)
82
+ slot_definitions = [
83
+ slot for slot in agent_input.slots if slot.name in slot_names
84
+ ]
85
+
86
+ return [
87
+ AgentToolSchema.from_litellm_json_format(
88
+ cls.get_slot_specific_set_slot_tool(slot)
89
+ )
90
+ for slot in slot_definitions
91
+ ]
92
+
93
+ @classmethod
94
+ def _get_slot_names_from_exit_conditions(cls, agent_input: AgentInput) -> List[str]:
95
+ """Extract valid slot names from exit conditions."""
96
+ exit_conditions = agent_input.metadata.get("exit_if", [])
97
+
98
+ # Find all unique names matching "slots.<name>"
99
+ extracted_slot_names = {
100
+ name
101
+ for condition in exit_conditions
102
+ for name in re.findall(r"\bslots\.(\w+)", condition)
103
+ }
104
+
105
+ slot_names = [slot.name for slot in agent_input.slots]
106
+
107
+ # Keep only slots that actually exist in agent_input.slots
108
+ valid_slot_names = [
109
+ slot_name for slot_name in extracted_slot_names if slot_name in slot_names
110
+ ]
111
+
112
+ return valid_slot_names
113
+
114
+ @classmethod
115
+ def get_slot_specific_set_slot_tool(cls, slot: AgentInputSlot) -> Dict[str, Any]:
116
+ """Get the set slot tool."""
117
+ tool_description = f"Set the slot '{slot.name}' to a specific value. "
118
+ tool_description += f"The slot type is {slot.type}."
119
+ if slot.type == "categorical":
120
+ tool_description += f" The allowed values are: {slot.allowed_values}."
121
+
122
+ return {
123
+ "type": "function",
124
+ "function": {
125
+ "name": f"set_slot_{slot.name}",
126
+ "description": tool_description,
127
+ "parameters": {
128
+ "type": "object",
129
+ "properties": {
130
+ "slot_value": {
131
+ "type": "string",
132
+ "description": "The value to assign to the slot.",
133
+ },
134
+ },
135
+ "required": ["slot_value"],
136
+ "additionalProperties": False,
137
+ },
138
+ "strict": True,
139
+ },
140
+ }
141
+
142
+ @classmethod
143
+ def _is_exit_conditions_met(
144
+ cls, agent_input: AgentInput, slots: Dict[str, Any]
145
+ ) -> Tuple[bool, Optional[str]]:
146
+ """Check if the exit conditions are met.
147
+
148
+ Args:
149
+ agent_input: The agent input.
150
+ slots: The slots to check the exit conditions against.
151
+
152
+ Returns:
153
+ A tuple containing a boolean indicating if the exit conditions are met
154
+ and a string indicating if an internal error occurred.
155
+ """
156
+ if not slots:
157
+ return False, None
158
+
159
+ exit_conditions = agent_input.metadata.get("exit_if", [])
160
+ current_context = {"slots": slots}
161
+
162
+ internal_error = None
163
+ all_conditions_met = True
164
+
165
+ for condition in exit_conditions:
166
+ try:
167
+ rendered_template = Template(condition).render(current_context)
168
+ predicate = Predicate(rendered_template)
169
+ condition_result = predicate.evaluate(current_context)
170
+
171
+ # All conditions must be met (AND logic)
172
+ if not condition_result:
173
+ all_conditions_met = False
174
+ break
175
+
176
+ except (TypeError, Exception) as e:
177
+ structlogger.error(
178
+ "mcp_task_agent.is_exit_conditions_met.predicate.error",
179
+ predicate=condition,
180
+ error=str(e),
181
+ )
182
+ all_conditions_met = False
183
+ internal_error = str(e)
184
+ break
185
+
186
+ if internal_error:
187
+ structlogger.debug(
188
+ "mcp_task_agent.is_exit_conditions_met.result",
189
+ event_info="Failed to evaluate exit conditions - error occurred",
190
+ exit_conditions=exit_conditions,
191
+ error=internal_error,
192
+ )
193
+ else:
194
+ structlogger.debug(
195
+ "mcp_task_agent.is_exit_conditions_met.result",
196
+ event_info=f"Exit conditions met: {all_conditions_met}",
197
+ evaulation_result=all_conditions_met,
198
+ exit_conditions=exit_conditions,
199
+ )
200
+
201
+ return all_conditions_met, internal_error
202
+
203
+ def _get_slot_name_from_tool_name(self, tool_name: str) -> Optional[str]:
204
+ """Get the slot name from the tool name."""
205
+ match = re.match(r"^set_slot_(\w+)$", tool_name)
206
+ if match:
207
+ return match.group(1)
208
+ return None
209
+
210
+ def _run_set_slot_tool(
211
+ self, slot_name: str, arguments: Dict[str, Any]
212
+ ) -> Dict[str, Any]:
213
+ """Run the set slot tool."""
214
+ slot_value = arguments.get("slot_value")
215
+
216
+ # Handle type conversion for common cases
217
+ if isinstance(slot_value, str):
218
+ # Convert common boolean strings to actual booleans
219
+ if slot_value.lower() == "true":
220
+ slot_value = True
221
+ elif slot_value.lower() == "false":
222
+ slot_value = False
223
+
224
+ return {slot_name: slot_value}
225
+
226
+ def _generate_agent_task_completed_output(
227
+ self,
228
+ agent_input: AgentInput,
229
+ slots: Dict[str, Any],
230
+ tool_results: Dict[str, AgentToolResult],
231
+ ) -> AgentOutput:
232
+ """Generate an agent task completed output."""
233
+ _slot_names_to_be_filled = self._get_slot_names_from_exit_conditions(
234
+ agent_input
235
+ )
236
+ return AgentOutput(
237
+ id=agent_input.id,
238
+ status=AgentStatus.COMPLETED,
239
+ events=[
240
+ SlotSet(slot_name, slot_value)
241
+ for slot_name, slot_value in slots.items()
242
+ if slot_name in _slot_names_to_be_filled
243
+ ],
244
+ structured_results=self._get_structured_results_for_agent_output(
245
+ agent_input, tool_results
246
+ ),
247
+ )
248
+
249
+ def render_prompt_template(self, context: AgentInput) -> str:
250
+ """Render the prompt template with the provided inputs."""
251
+ slot_names = self._get_slot_names_from_exit_conditions(context)
252
+ current_date, current_time, current_day = self._get_current_date_time_day()
253
+
254
+ return Template(self.prompt_template).render(
255
+ **context.model_dump(exclude={"id", "timestamp", "events"}),
256
+ description=self._description,
257
+ slot_names=slot_names,
258
+ current_date=current_date,
259
+ current_time=current_time,
260
+ current_day=current_day,
261
+ )
262
+
263
+ async def send_message(self, agent_input: AgentInput) -> AgentOutput:
264
+ """Send a message to the LLM and return the response."""
265
+ messages = self.build_messages_for_llm_request(agent_input)
266
+ tool_results: Dict[str, AgentToolResult] = {}
267
+
268
+ _slot_values = {slot.name: slot.value for slot in agent_input.slots}
269
+ _available_tools = self.get_available_tools(agent_input)
270
+ _available_tools_names = [tool.name for tool in _available_tools]
271
+
272
+ # Convert available tools to OpenAI JSON format
273
+ tools_in_openai_format = [
274
+ tool.to_litellm_json_format() for tool in _available_tools
275
+ ]
276
+
277
+ for iteration in range(self.MAX_ITERATIONS):
278
+ try:
279
+ structlogger.debug(
280
+ "mcp_task_agent.send_message.iteration",
281
+ event_info=(
282
+ f"Starting iteration {iteration + 1} for agent {self._name}"
283
+ ),
284
+ agent_id=str(make_agent_identifier(self._name, self.protocol_type)),
285
+ highlight=True,
286
+ )
287
+ # Make the LLM call using the llm_client
288
+ structlogger.debug(
289
+ "mcp_task_agent.send_message.sending_message_to_llm",
290
+ messages=messages,
291
+ json_formatting=["messages"],
292
+ event_info=f"Sending message to LLM (iteration {iteration + 1})",
293
+ agent_name=self._name,
294
+ agent_id=str(make_agent_identifier(self._name, self.protocol_type)),
295
+ )
296
+ llm_response = LLMResponse.ensure_llm_response(
297
+ await self.llm_client.acompletion(
298
+ messages, tools=tools_in_openai_format
299
+ )
300
+ )
301
+
302
+ # If no response from LLM, return an error output.
303
+ if llm_response is None or not (
304
+ llm_response.choices or llm_response.tool_calls
305
+ ):
306
+ event_info = "No response from LLM."
307
+ structlogger.warning(
308
+ "mcp_task_agent.send_message.no_llm_response",
309
+ event_info=event_info,
310
+ agent_name=self._name,
311
+ agent_id=str(
312
+ make_agent_identifier(self._name, self.protocol_type)
313
+ ),
314
+ )
315
+ return AgentOutput(
316
+ id=agent_input.id,
317
+ status=AgentStatus.RECOVERABLE_ERROR,
318
+ error_message=event_info,
319
+ structured_results=(
320
+ self._get_structured_results_for_agent_output(
321
+ agent_input, tool_results
322
+ )
323
+ ),
324
+ )
325
+
326
+ # If no tool calls, return the response directly with input required.
327
+ if not llm_response.tool_calls and len(llm_response.choices) == 1:
328
+ return AgentOutput(
329
+ id=agent_input.id,
330
+ status=AgentStatus.INPUT_REQUIRED,
331
+ response_message=llm_response.choices[0],
332
+ structured_results=(
333
+ self._get_structured_results_for_agent_output(
334
+ agent_input, tool_results
335
+ )
336
+ ),
337
+ )
338
+
339
+ # If there are tool calls, process them.
340
+ if llm_response.tool_calls:
341
+ # Add the assistant message with tool calls to the messages.
342
+ messages.append(
343
+ self._get_assistant_message_with_tool_calls(llm_response)
344
+ )
345
+ for tool_call in llm_response.tool_calls:
346
+ structlogger.debug(
347
+ "mcp_task_agent.send_message.tool_call",
348
+ event_info=f"Processing tool call {tool_call.tool_name}",
349
+ tool_name=tool_call.tool_name,
350
+ tool_args=json.dumps(tool_call.tool_args),
351
+ json_formatting=["tool_args"],
352
+ agent_name=self._name,
353
+ agent_id=str(
354
+ make_agent_identifier(self._name, self.protocol_type)
355
+ ),
356
+ )
357
+
358
+ # If the tool is not available, return a fatal error output.
359
+ if tool_call.tool_name not in _available_tools_names:
360
+ event_info = f"Tool {tool_call.tool_name} is not available."
361
+ structlogger.error(
362
+ "mcp_task_agent.send_message.tool_not_available",
363
+ tool_name=tool_call.tool_name,
364
+ event_info=event_info,
365
+ user_message=agent_input.user_message,
366
+ )
367
+ return AgentOutput(
368
+ id=agent_input.id,
369
+ status=AgentStatus.FATAL_ERROR,
370
+ error_message=event_info,
371
+ )
372
+
373
+ if slot_name := self._get_slot_name_from_tool_name(
374
+ tool_call.tool_name
375
+ ):
376
+ if (
377
+ slot_name in _slot_values
378
+ and "slot_value" in tool_call.tool_args
379
+ ):
380
+ _slot_values.update(
381
+ self._run_set_slot_tool(
382
+ slot_name, tool_call.tool_args
383
+ )
384
+ )
385
+
386
+ # Add the tool call message to the messages for
387
+ # slot-setting tools
388
+ messages.append(
389
+ {
390
+ KEY_ROLE: ROLE_TOOL,
391
+ KEY_TOOL_CALL_ID: tool_call.id,
392
+ KEY_CONTENT: f"Slot {slot_name} set to "
393
+ f"{tool_call.tool_args.get('slot_value')}",
394
+ }
395
+ )
396
+ else:
397
+ return AgentOutput(
398
+ id=agent_input.id,
399
+ status=AgentStatus.FATAL_ERROR,
400
+ error_message=(
401
+ f"The slot `{slot_name}` that the tool "
402
+ f"`{tool_call.tool_name}` is trying to set "
403
+ f"is not found in agent input."
404
+ ),
405
+ )
406
+ else:
407
+ # Execute the tool call.
408
+ tool_output = await self._execute_tool_call(
409
+ tool_call.tool_name,
410
+ tool_call.tool_args,
411
+ )
412
+
413
+ structlogger.debug(
414
+ "mcp_task_agent.send_message.tool_output",
415
+ event_info=(
416
+ f"Tool output for tool call {tool_call.tool_name}"
417
+ ),
418
+ tool_output=tool_output.model_dump(),
419
+ json_formatting=["tool_output"],
420
+ tool_name=tool_call.tool_name,
421
+ agent_name=self._name,
422
+ agent_id=str(
423
+ make_agent_identifier(
424
+ self._name, self.protocol_type
425
+ )
426
+ ),
427
+ )
428
+
429
+ # If the tool call failed, generate an agent error output.
430
+ if tool_output.is_error or tool_output.result is None:
431
+ return self._generate_agent_error_output(
432
+ tool_output, agent_input, tool_call
433
+ )
434
+
435
+ # Store the tool output in the tool_results.
436
+ tool_results[tool_call.id] = tool_output
437
+
438
+ # Add the tool call message to the messages.
439
+ messages.append(
440
+ {
441
+ KEY_ROLE: ROLE_TOOL,
442
+ KEY_TOOL_CALL_ID: tool_call.id,
443
+ KEY_CONTENT: tool_output.result,
444
+ }
445
+ )
446
+
447
+ exit_met, internal_error = self._is_exit_conditions_met(
448
+ agent_input, _slot_values
449
+ )
450
+
451
+ # Agent signals task completion if exit conditions are met.
452
+ if exit_met:
453
+ return self._generate_agent_task_completed_output(
454
+ agent_input, _slot_values, tool_results
455
+ )
456
+
457
+ # If an internal error occurred while checking the exit
458
+ # conditions, return a fatal error output.
459
+ if internal_error:
460
+ return AgentOutput(
461
+ id=agent_input.id,
462
+ status=AgentStatus.FATAL_ERROR,
463
+ response_message=(
464
+ "An internal error occurred while checking the "
465
+ "exit conditions."
466
+ ),
467
+ structured_results=self._get_structured_results_for_agent_output(
468
+ agent_input, tool_results
469
+ ),
470
+ error_message=internal_error,
471
+ )
472
+
473
+ except Exception as e:
474
+ if isinstance(e, ProviderClientAPIException) and isinstance(
475
+ e.original_exception, LLMToolResponseDecodeError
476
+ ):
477
+ structlogger.debug(
478
+ "mcp_task_agent.send_message.malformed_tool_response_error",
479
+ event_info=(
480
+ "Malformed tool response received from LLM "
481
+ "(JSON decode error). Retrying the LLM call."
482
+ ),
483
+ user_message=agent_input.user_message,
484
+ agent_name=self._name,
485
+ agent_id=str(
486
+ make_agent_identifier(self._name, self.protocol_type)
487
+ ),
488
+ original_exception=str(e.original_exception),
489
+ )
490
+ # Continue to make another LLM call by breaking out of the current
491
+ # iteration and letting the loop continue with a fresh LLM request
492
+ messages.append(
493
+ self._get_system_message_for_malformed_tool_response()
494
+ )
495
+ continue
496
+ structlogger.error(
497
+ "mcp_task_agent.send_message.error_in_agent_loop",
498
+ event_info=f"Failed to send message: {e}",
499
+ user_message=agent_input.user_message,
500
+ agent_name=self._name,
501
+ agent_id=str(make_agent_identifier(self._name, self.protocol_type)),
502
+ )
503
+ return AgentOutput(
504
+ id=agent_input.id,
505
+ status=AgentStatus.FATAL_ERROR,
506
+ response_message=f"I encountered an error: {e!s}",
507
+ structured_results=self._get_structured_results_for_agent_output(
508
+ agent_input, tool_results
509
+ ),
510
+ error_message=str(e),
511
+ )
512
+ return AgentOutput(
513
+ id=agent_input.id,
514
+ status=AgentStatus.COMPLETED,
515
+ response_message=(
516
+ "I've completed my research but couldn't provide a final answer within"
517
+ "the allowed steps."
518
+ ),
519
+ structured_results=self._get_structured_results_for_agent_output(
520
+ agent_input, tool_results
521
+ ),
522
+ )
@@ -0,0 +1,13 @@
1
+ from rasa.agents.schemas.agent_input import AgentInput, AgentInputSlot
2
+ from rasa.agents.schemas.agent_output import AgentOutput
3
+ from rasa.agents.schemas.agent_tool_result import AgentToolResult
4
+ from rasa.agents.schemas.agent_tool_schema import AgentToolSchema, CustomToolSchema
5
+
6
+ __all__ = [
7
+ "AgentInput",
8
+ "AgentInputSlot",
9
+ "AgentOutput",
10
+ "AgentToolSchema",
11
+ "AgentToolResult",
12
+ "CustomToolSchema",
13
+ ]
@@ -0,0 +1,38 @@
1
+ from typing import Any, Dict, List, Optional
2
+
3
+ from pydantic import BaseModel
4
+
5
+ from rasa.shared.core.events import Event
6
+
7
+
8
+ class AgentInputSlot(BaseModel):
9
+ """A class that represents the schema of the input slot to the agent."""
10
+
11
+ name: str
12
+ value: Any
13
+ type: str
14
+ allowed_values: Optional[List[Any]] = None
15
+
16
+
17
+ class AgentInput(BaseModel):
18
+ """A class that represents the schema of the input to the agent."""
19
+
20
+ id: str
21
+ user_message: str
22
+ slots: List[AgentInputSlot]
23
+ conversation_history: str
24
+ events: List[Event]
25
+ metadata: Dict[str, Any]
26
+ timestamp: Optional[str] = None
27
+
28
+ class Config:
29
+ """Skip validation for Event class as pydantic does not know how to
30
+ serialize or handle instances of the class.
31
+ """
32
+
33
+ arbitrary_types_allowed = True
34
+
35
+ @property
36
+ def slot_names(self) -> List[str]:
37
+ """Get the names of the slots in the input."""
38
+ return [slot.name for slot in self.slots]
@@ -0,0 +1,26 @@
1
+ from typing import Any, Dict, List, Optional
2
+
3
+ from pydantic import BaseModel
4
+
5
+ from rasa.agents.core.types import AgentStatus
6
+ from rasa.shared.core.events import SlotSet
7
+
8
+
9
+ class AgentOutput(BaseModel):
10
+ """A class that represents the schema of the output from the agent."""
11
+
12
+ id: str
13
+ status: AgentStatus
14
+ response_message: Optional[str] = None
15
+ events: Optional[List[SlotSet]] = None
16
+ structured_results: Optional[List[List[Dict[str, Any]]]] = None
17
+ metadata: Optional[Dict[str, Any]] = None
18
+ timestamp: Optional[str] = None
19
+ error_message: Optional[str] = None
20
+
21
+ class Config:
22
+ """Skip validation for SlotSet class as pydantic does not know how to
23
+ serialize or handle instances of the class.
24
+ """
25
+
26
+ arbitrary_types_allowed = True
@@ -0,0 +1,65 @@
1
+ from typing import Optional
2
+
3
+ from mcp.types import CallToolResult
4
+ from pydantic import BaseModel
5
+
6
+
7
+ class AgentToolResult(BaseModel):
8
+ tool_name: str
9
+ result: Optional[str] = None
10
+ is_error: bool = False
11
+ error_message: Optional[str] = None
12
+
13
+ def __str__(self) -> str:
14
+ """Provide a clean string representation for logging."""
15
+ if self.is_error:
16
+ return (
17
+ f"AgentToolResult(tool_name='{self.tool_name}', "
18
+ f"error='{self.error_message}')"
19
+ )
20
+ return f"AgentToolResult(tool_name='{self.tool_name}', result={self.result})"
21
+
22
+ def __repr__(self) -> str:
23
+ """Provide a detailed representation for debugging."""
24
+ return self.__str__()
25
+
26
+ @classmethod
27
+ def from_mcp_tool_result(
28
+ cls, tool_name: str, tool_result: CallToolResult
29
+ ) -> "AgentToolResult":
30
+ if tool_result.isError:
31
+ return cls(
32
+ tool_name=tool_name,
33
+ result=None,
34
+ is_error=tool_result.isError,
35
+ error_message=str(tool_result.content),
36
+ )
37
+
38
+ # try to use structured content if available
39
+ if tool_result.structuredContent:
40
+ if "result" in tool_result.structuredContent:
41
+ return cls(
42
+ tool_name=tool_name,
43
+ result=str(tool_result.structuredContent["result"]),
44
+ is_error=tool_result.isError,
45
+ )
46
+ return cls(
47
+ tool_name=tool_name,
48
+ result=str(tool_result.structuredContent),
49
+ is_error=tool_result.isError,
50
+ )
51
+ # fallback to content if structured content is not available
52
+ elif tool_result.content:
53
+ return cls(
54
+ tool_name=tool_name,
55
+ result=str([content.model_dump() for content in tool_result.content]),
56
+ is_error=tool_result.isError,
57
+ )
58
+ # if no content is available, return None
59
+ else:
60
+ return cls(
61
+ tool_name=tool_name,
62
+ result=None,
63
+ is_error=tool_result.isError,
64
+ error_message="No content returned from tool `{tool_name}`",
65
+ )