rasa-pro 3.13.12__py3-none-any.whl → 3.14.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of rasa-pro might be problematic. Click here for more details.

Files changed (586) 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 +213 -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 +889 -0
  13. rasa/agents/protocol/mcp/__init__.py +0 -0
  14. rasa/agents/protocol/mcp/mcp_base_agent.py +778 -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 +228 -0
  26. rasa/agents/validation.py +538 -0
  27. rasa/api.py +23 -9
  28. rasa/builder/README.md +120 -0
  29. rasa/builder/__init__.py +0 -0
  30. rasa/builder/auth.py +176 -0
  31. rasa/builder/config.py +96 -0
  32. rasa/builder/copilot/__init__.py +0 -0
  33. rasa/builder/copilot/constants.py +38 -0
  34. rasa/builder/copilot/copilot.py +562 -0
  35. rasa/builder/copilot/copilot_response_handler.py +522 -0
  36. rasa/builder/copilot/copilot_templated_message_provider.py +81 -0
  37. rasa/builder/copilot/exceptions.py +32 -0
  38. rasa/builder/copilot/models.py +690 -0
  39. rasa/builder/copilot/prompts/__init__.py +0 -0
  40. rasa/builder/copilot/prompts/copilot_system_prompt.jinja2 +787 -0
  41. rasa/builder/copilot/prompts/copilot_training_error_handler_prompt.jinja2 +53 -0
  42. rasa/builder/copilot/prompts/latest_user_message_context_prompt.jinja2 +91 -0
  43. rasa/builder/copilot/signing.py +305 -0
  44. rasa/builder/copilot/telemetry.py +234 -0
  45. rasa/builder/copilot/templated_messages/__init__.py +0 -0
  46. rasa/builder/copilot/templated_messages/copilot_internal_messages_templates.yml +16 -0
  47. rasa/builder/copilot/templated_messages/copilot_templated_responses.yml +41 -0
  48. rasa/builder/copilot/templated_messages/copilot_welcome_messages.yml +56 -0
  49. rasa/builder/document_retrieval/__init__.py +0 -0
  50. rasa/builder/document_retrieval/constants.py +15 -0
  51. rasa/builder/document_retrieval/inkeep-rag-response-schema.json +64 -0
  52. rasa/builder/document_retrieval/inkeep_document_retrieval.py +238 -0
  53. rasa/builder/document_retrieval/models.py +62 -0
  54. rasa/builder/download.py +140 -0
  55. rasa/builder/exceptions.py +91 -0
  56. rasa/builder/guardrails/__init__.py +1 -0
  57. rasa/builder/guardrails/clients.py +256 -0
  58. rasa/builder/guardrails/constants.py +12 -0
  59. rasa/builder/guardrails/exceptions.py +4 -0
  60. rasa/builder/guardrails/models.py +266 -0
  61. rasa/builder/guardrails/policy_checker.py +324 -0
  62. rasa/builder/guardrails/store.py +238 -0
  63. rasa/builder/guardrails/utils.py +94 -0
  64. rasa/builder/job_manager.py +87 -0
  65. rasa/builder/jobs.py +609 -0
  66. rasa/builder/llm_service.py +273 -0
  67. rasa/builder/logging_utils.py +265 -0
  68. rasa/builder/main.py +234 -0
  69. rasa/builder/models.py +229 -0
  70. rasa/builder/project_generator.py +463 -0
  71. rasa/builder/project_info.py +72 -0
  72. rasa/builder/service.py +1367 -0
  73. rasa/builder/shared/tracker_context.py +212 -0
  74. rasa/builder/skill_to_bot_prompt.jinja2 +164 -0
  75. rasa/builder/template_cache.py +69 -0
  76. rasa/builder/training_service.py +188 -0
  77. rasa/builder/validation_service.py +101 -0
  78. rasa/cli/arguments/data.py +9 -0
  79. rasa/cli/arguments/default_arguments.py +12 -0
  80. rasa/cli/arguments/run.py +2 -0
  81. rasa/cli/arguments/train.py +2 -0
  82. rasa/cli/data.py +78 -10
  83. rasa/cli/dialogue_understanding_test.py +11 -7
  84. rasa/cli/e2e_test.py +10 -6
  85. rasa/cli/evaluate.py +4 -2
  86. rasa/cli/export.py +5 -2
  87. rasa/cli/inspect.py +9 -4
  88. rasa/cli/interactive.py +8 -4
  89. rasa/cli/llm_fine_tuning.py +12 -6
  90. rasa/cli/project_templates/basic/README.md +23 -0
  91. rasa/cli/project_templates/basic/actions/__init__ +0 -0
  92. rasa/cli/project_templates/basic/actions/action_human_handoff.py +40 -0
  93. rasa/cli/project_templates/basic/actions/actions.md +10 -0
  94. rasa/cli/project_templates/basic/config.yml +29 -0
  95. rasa/cli/project_templates/basic/credentials.yml +33 -0
  96. rasa/cli/project_templates/basic/data/data.md +8 -0
  97. rasa/cli/project_templates/basic/data/general/feedback.yml +21 -0
  98. rasa/cli/project_templates/basic/data/general/goodbye.yml +6 -0
  99. rasa/cli/project_templates/basic/data/general/hello.yml +6 -0
  100. rasa/cli/project_templates/basic/data/general/help.yml +6 -0
  101. rasa/cli/project_templates/basic/data/general/human_handoff.yml +16 -0
  102. rasa/cli/project_templates/basic/data/general/show_faqs.yml +6 -0
  103. rasa/cli/project_templates/basic/data/system/patterns/pattern_cannot_handle.yml +7 -0
  104. rasa/cli/project_templates/basic/data/system/patterns/pattern_completed.yml +7 -0
  105. rasa/cli/project_templates/basic/data/system/patterns/pattern_correction.yml +7 -0
  106. rasa/cli/project_templates/basic/data/system/patterns/pattern_search.yml +8 -0
  107. rasa/cli/project_templates/basic/data/system/patterns/pattern_session_start.yml +8 -0
  108. rasa/cli/project_templates/basic/docs/docs.md +5 -0
  109. rasa/cli/project_templates/basic/docs/template.txt +28 -0
  110. rasa/cli/project_templates/basic/domain/domain.md +11 -0
  111. rasa/cli/project_templates/basic/domain/general/feedback.yml +25 -0
  112. rasa/cli/project_templates/basic/domain/general/goodbye.yml +9 -0
  113. rasa/cli/project_templates/basic/domain/general/hello.yml +7 -0
  114. rasa/cli/project_templates/basic/domain/general/help.yml +21 -0
  115. rasa/cli/project_templates/basic/domain/general/human_handoff.yml +32 -0
  116. rasa/cli/project_templates/basic/domain/general/show_faqs.yml +14 -0
  117. rasa/cli/project_templates/basic/domain/system/patterns/pattern_cannot_handle.yml +5 -0
  118. rasa/cli/project_templates/basic/domain/system/patterns/pattern_session_start.yml +19 -0
  119. rasa/cli/project_templates/basic/endpoints.yml +67 -0
  120. rasa/cli/project_templates/basic/prompts/rephraser_demo_personality_prompt.jinja2 +38 -0
  121. rasa/cli/project_templates/basic/tests/e2e_test_cases/without_stub/general/feedback.yml +46 -0
  122. rasa/cli/project_templates/basic/tests/e2e_test_cases/without_stub/general/goodbye.yml +9 -0
  123. rasa/cli/project_templates/basic/tests/e2e_test_cases/without_stub/general/hello.yml +8 -0
  124. rasa/cli/project_templates/basic/tests/e2e_test_cases/without_stub/general/help.yml +8 -0
  125. rasa/cli/project_templates/basic/tests/e2e_test_cases/without_stub/general/human_handoff.yml +41 -0
  126. rasa/cli/project_templates/basic/tests/e2e_test_cases/without_stub/general/patterns.yml +32 -0
  127. rasa/cli/project_templates/basic/tests/e2e_test_cases/without_stub/general/show_faqs.yml +8 -0
  128. rasa/cli/project_templates/default/config.yml +4 -0
  129. rasa/cli/project_templates/default/endpoints.yml +4 -0
  130. rasa/cli/project_templates/defaults.py +1 -0
  131. rasa/cli/project_templates/finance/README.md +26 -0
  132. rasa/cli/project_templates/finance/actions/__init__.py +0 -0
  133. rasa/cli/project_templates/finance/actions/accounts/__init__.py +0 -0
  134. rasa/cli/project_templates/finance/actions/accounts/check_balance.py +18 -0
  135. rasa/cli/project_templates/finance/actions/actions.md +15 -0
  136. rasa/cli/project_templates/finance/actions/cards/__init__.py +0 -0
  137. rasa/cli/project_templates/finance/actions/cards/check_that_card_exists.py +21 -0
  138. rasa/cli/project_templates/finance/actions/cards/list_cards.py +22 -0
  139. rasa/cli/project_templates/finance/actions/contacts/__init__.py +0 -0
  140. rasa/cli/project_templates/finance/actions/contacts/add_contact.py +30 -0
  141. rasa/cli/project_templates/finance/actions/contacts/list_contacts.py +22 -0
  142. rasa/cli/project_templates/finance/actions/contacts/remove_contact.py +35 -0
  143. rasa/cli/project_templates/finance/actions/db.py +117 -0
  144. rasa/cli/project_templates/finance/actions/general/__init__.py +0 -0
  145. rasa/cli/project_templates/finance/actions/general/action_human_handoff.py +49 -0
  146. rasa/cli/project_templates/finance/actions/transfers/__init__.py +0 -0
  147. rasa/cli/project_templates/finance/actions/transfers/check_transfer_funds.py +27 -0
  148. rasa/cli/project_templates/finance/actions/transfers/check_transfer_limit.py +36 -0
  149. rasa/cli/project_templates/finance/actions/transfers/execute_recurrent_payment.py +20 -0
  150. rasa/cli/project_templates/finance/actions/transfers/execute_transfer.py +45 -0
  151. rasa/cli/project_templates/finance/actions/transfers/list_transactions.py +32 -0
  152. rasa/cli/project_templates/finance/config.yml +29 -0
  153. rasa/cli/project_templates/finance/credentials.yml +33 -0
  154. rasa/cli/project_templates/finance/data/accounts/check_balance.yml +9 -0
  155. rasa/cli/project_templates/finance/data/accounts/download_statements.yml +26 -0
  156. rasa/cli/project_templates/finance/data/bills/bill_pay_reminder.yml +25 -0
  157. rasa/cli/project_templates/finance/data/cards/activate_card.yml +35 -0
  158. rasa/cli/project_templates/finance/data/cards/block_card.yml +45 -0
  159. rasa/cli/project_templates/finance/data/cards/list_cards.yml +14 -0
  160. rasa/cli/project_templates/finance/data/cards/replace_card.yml +16 -0
  161. rasa/cli/project_templates/finance/data/cards/replace_eligible_card.yml +29 -0
  162. rasa/cli/project_templates/finance/data/contacts/add_contact.yml +33 -0
  163. rasa/cli/project_templates/finance/data/contacts/list_contacts.yml +14 -0
  164. rasa/cli/project_templates/finance/data/contacts/remove_contact.yml +31 -0
  165. rasa/cli/project_templates/finance/data/data.md +14 -0
  166. rasa/cli/project_templates/finance/data/general/bot_challenge.yml +6 -0
  167. rasa/cli/project_templates/finance/data/general/feedback.yml +20 -0
  168. rasa/cli/project_templates/finance/data/general/goodbye.yml +6 -0
  169. rasa/cli/project_templates/finance/data/general/hello.yml +6 -0
  170. rasa/cli/project_templates/finance/data/general/help.yml +9 -0
  171. rasa/cli/project_templates/finance/data/general/human_handoff.yml +16 -0
  172. rasa/cli/project_templates/finance/data/general/welcome.yml +9 -0
  173. rasa/cli/project_templates/finance/data/system/patterns/pattern_completed.yml +7 -0
  174. rasa/cli/project_templates/finance/data/system/patterns/pattern_correction.yml +7 -0
  175. rasa/cli/project_templates/finance/data/system/patterns/pattern_search.yml +8 -0
  176. rasa/cli/project_templates/finance/data/system/patterns/pattern_session_start.yml +8 -0
  177. rasa/cli/project_templates/finance/data/transfers/check_transfer_limit.yml +18 -0
  178. rasa/cli/project_templates/finance/data/transfers/list_transactions.yml +46 -0
  179. rasa/cli/project_templates/finance/data/transfers/move_money_between_accounts.yml +51 -0
  180. rasa/cli/project_templates/finance/data/transfers/transfer_money.yml +34 -0
  181. rasa/cli/project_templates/finance/data/transfers/transfer_money_to_a_third_party.yml +175 -0
  182. rasa/cli/project_templates/finance/db/cards.json +18 -0
  183. rasa/cli/project_templates/finance/db/contacts.json +10 -0
  184. rasa/cli/project_templates/finance/db/my_account.json +6 -0
  185. rasa/cli/project_templates/finance/db/transactions.json +22 -0
  186. rasa/cli/project_templates/finance/docs/docs.md +8 -0
  187. rasa/cli/project_templates/finance/docs/fenlo_banking_faq/account_features/budgeting_analytics.txt +22 -0
  188. rasa/cli/project_templates/finance/docs/fenlo_banking_faq/account_features/multi_currency_accounts.txt +19 -0
  189. rasa/cli/project_templates/finance/docs/fenlo_banking_faq/account_features/premium_benefits.txt +19 -0
  190. rasa/cli/project_templates/finance/docs/fenlo_banking_faq/card_management/contactless_limits.txt +16 -0
  191. rasa/cli/project_templates/finance/docs/fenlo_banking_faq/card_management/freeze_unfreeze_card.txt +16 -0
  192. rasa/cli/project_templates/finance/docs/fenlo_banking_faq/card_management/lost_stolen_card.txt +19 -0
  193. rasa/cli/project_templates/finance/docs/fenlo_banking_faq/money_transfers/instant_payments.txt +19 -0
  194. rasa/cli/project_templates/finance/docs/fenlo_banking_faq/money_transfers/international_transfers.txt +19 -0
  195. rasa/cli/project_templates/finance/docs/fenlo_banking_faq/security_fraud/fraud_protection.txt +22 -0
  196. rasa/cli/project_templates/finance/docs/fenlo_banking_faq/security_fraud/secure_payments.txt +22 -0
  197. rasa/cli/project_templates/finance/domain/accounts/check_balance.yml +15 -0
  198. rasa/cli/project_templates/finance/domain/accounts/download_statements.yml +40 -0
  199. rasa/cli/project_templates/finance/domain/bills/bill_pay_reminder.yml +49 -0
  200. rasa/cli/project_templates/finance/domain/cards/activate_card.yml +24 -0
  201. rasa/cli/project_templates/finance/domain/cards/block_card.yml +44 -0
  202. rasa/cli/project_templates/finance/domain/cards/list_cards.yml +16 -0
  203. rasa/cli/project_templates/finance/domain/cards/replace_card.yml +43 -0
  204. rasa/cli/project_templates/finance/domain/cards/shared.yml +15 -0
  205. rasa/cli/project_templates/finance/domain/contacts/add_contact.yml +37 -0
  206. rasa/cli/project_templates/finance/domain/contacts/list_contacts.yml +16 -0
  207. rasa/cli/project_templates/finance/domain/contacts/remove_contact.yml +32 -0
  208. rasa/cli/project_templates/finance/domain/domain.md +18 -0
  209. rasa/cli/project_templates/finance/domain/general/_shared.yml +39 -0
  210. rasa/cli/project_templates/finance/domain/general/bot_challenge.yml +4 -0
  211. rasa/cli/project_templates/finance/domain/general/cannot_handle.yml +8 -0
  212. rasa/cli/project_templates/finance/domain/general/feedback.yml +25 -0
  213. rasa/cli/project_templates/finance/domain/general/goodbye.yml +7 -0
  214. rasa/cli/project_templates/finance/domain/general/help.yml +0 -0
  215. rasa/cli/project_templates/finance/domain/general/human_handoff.yml +31 -0
  216. rasa/cli/project_templates/finance/domain/general/welcome.yml +39 -0
  217. rasa/cli/project_templates/finance/domain/transfers/check_transfer_limit.yml +32 -0
  218. rasa/cli/project_templates/finance/domain/transfers/list_transactions.yml +44 -0
  219. rasa/cli/project_templates/finance/domain/transfers/shared.yml +17 -0
  220. rasa/cli/project_templates/finance/domain/transfers/transfer_money.yml +221 -0
  221. rasa/cli/project_templates/finance/endpoints.yml +67 -0
  222. rasa/cli/project_templates/finance/prompts/rephraser_demo_personality_prompt.jinja2 +38 -0
  223. rasa/cli/project_templates/finance/tests/e2e_test_cases/without_stub/accounts/check_balance.yml +9 -0
  224. rasa/cli/project_templates/finance/tests/e2e_test_cases/without_stub/accounts/download_statements.yml +43 -0
  225. rasa/cli/project_templates/finance/tests/e2e_test_cases/without_stub/cards/block_card.yml +55 -0
  226. rasa/cli/project_templates/finance/tests/e2e_test_cases/without_stub/general/bot_challenge.yml +8 -0
  227. rasa/cli/project_templates/finance/tests/e2e_test_cases/without_stub/general/feedback.yml +46 -0
  228. rasa/cli/project_templates/finance/tests/e2e_test_cases/without_stub/general/goodbye.yml +9 -0
  229. rasa/cli/project_templates/finance/tests/e2e_test_cases/without_stub/general/hello.yml +8 -0
  230. rasa/cli/project_templates/finance/tests/e2e_test_cases/without_stub/general/human_handoff.yml +35 -0
  231. rasa/cli/project_templates/finance/tests/e2e_test_cases/without_stub/general/patterns.yml +22 -0
  232. rasa/cli/project_templates/finance/tests/e2e_test_cases/without_stub/transfers/transfer_money.yml +56 -0
  233. rasa/cli/project_templates/telco/README.md +25 -0
  234. rasa/cli/project_templates/telco/actions/__init__.py +0 -0
  235. rasa/cli/project_templates/telco/actions/actions.md +12 -0
  236. rasa/cli/project_templates/telco/actions/billing/__init__.py +0 -0
  237. rasa/cli/project_templates/telco/actions/billing/actions_billing.py +204 -0
  238. rasa/cli/project_templates/telco/actions/general/__init__.py +0 -0
  239. rasa/cli/project_templates/telco/actions/general/action_human_handoff.py +49 -0
  240. rasa/cli/project_templates/telco/actions/network/__init__.py +0 -0
  241. rasa/cli/project_templates/telco/actions/network/actions_get_data_from_db.py +48 -0
  242. rasa/cli/project_templates/telco/actions/network/actions_run_diagnostics.py +28 -0
  243. rasa/cli/project_templates/telco/actions/network/actions_session_start.py +18 -0
  244. rasa/cli/project_templates/telco/config.yml +29 -0
  245. rasa/cli/project_templates/telco/credentials.yml +33 -0
  246. rasa/cli/project_templates/telco/csvs/billing.csv +19 -0
  247. rasa/cli/project_templates/telco/csvs/customers.csv +5 -0
  248. rasa/cli/project_templates/telco/data/billing/flow_understand_bill.yml +45 -0
  249. rasa/cli/project_templates/telco/data/data.md +11 -0
  250. rasa/cli/project_templates/telco/data/general/bot_challenge.yml +6 -0
  251. rasa/cli/project_templates/telco/data/general/feedback.yml +20 -0
  252. rasa/cli/project_templates/telco/data/general/goodbye.yml +6 -0
  253. rasa/cli/project_templates/telco/data/general/hello.yml +6 -0
  254. rasa/cli/project_templates/telco/data/general/human_handoff.yml +16 -0
  255. rasa/cli/project_templates/telco/data/general/patterns.yml +30 -0
  256. rasa/cli/project_templates/telco/data/network/flow_reboot_router.yml +8 -0
  257. rasa/cli/project_templates/telco/data/network/flow_reset_router.yml +7 -0
  258. rasa/cli/project_templates/telco/data/network/flow_solve_internet_issue.yml +73 -0
  259. rasa/cli/project_templates/telco/docs/docs.md +8 -0
  260. rasa/cli/project_templates/telco/docs/network/reset_vs_rboot_router.txt +1 -0
  261. rasa/cli/project_templates/telco/docs/network/restart_router.txt +6 -0
  262. rasa/cli/project_templates/telco/docs/network/run_speed_test.txt +6 -0
  263. rasa/cli/project_templates/telco/domain/billing/understand_bill.yml +102 -0
  264. rasa/cli/project_templates/telco/domain/domain.md +13 -0
  265. rasa/cli/project_templates/telco/domain/general/bot_challenge.yml +4 -0
  266. rasa/cli/project_templates/telco/domain/general/feedback.yml +25 -0
  267. rasa/cli/project_templates/telco/domain/general/goodbye.yml +7 -0
  268. rasa/cli/project_templates/telco/domain/general/hello.yml +5 -0
  269. rasa/cli/project_templates/telco/domain/general/human_handoff.yml +26 -0
  270. rasa/cli/project_templates/telco/domain/general/patterns.yml +33 -0
  271. rasa/cli/project_templates/telco/domain/network/reboot_router.yml +21 -0
  272. rasa/cli/project_templates/telco/domain/network/reset_router.yml +12 -0
  273. rasa/cli/project_templates/telco/domain/network/run_speed_test.yml +25 -0
  274. rasa/cli/project_templates/telco/domain/network/solve_internet_issue.yml +74 -0
  275. rasa/cli/project_templates/telco/domain/shared.yml +129 -0
  276. rasa/cli/project_templates/telco/endpoints.yml +67 -0
  277. rasa/cli/project_templates/telco/prompts/rephraser_demo_personality_prompt.jinja2 +40 -0
  278. rasa/cli/project_templates/telco/tests/e2e_test_cases/with_stub/network/solve_internet_not_slow.yml +33 -0
  279. rasa/cli/project_templates/telco/tests/e2e_test_cases/with_stub/network/solve_internet_slow.yml +47 -0
  280. rasa/cli/project_templates/telco/tests/e2e_test_cases/without_stub/billing/understand_bill.yml +67 -0
  281. rasa/cli/project_templates/telco/tests/e2e_test_cases/without_stub/general/bot_challenge.yml +8 -0
  282. rasa/cli/project_templates/telco/tests/e2e_test_cases/without_stub/general/feedback.yml +46 -0
  283. rasa/cli/project_templates/telco/tests/e2e_test_cases/without_stub/general/goodbye.yml +9 -0
  284. rasa/cli/project_templates/telco/tests/e2e_test_cases/without_stub/general/hello.yml +8 -0
  285. rasa/cli/project_templates/telco/tests/e2e_test_cases/without_stub/general/human_handoff.yml +35 -0
  286. rasa/cli/project_templates/telco/tests/e2e_test_cases/without_stub/general/patterns.yml +23 -0
  287. rasa/cli/project_templates/tutorial/config.yml +2 -1
  288. rasa/cli/project_templates/tutorial/credentials.yml +10 -0
  289. rasa/cli/run.py +8 -10
  290. rasa/cli/scaffold.py +50 -6
  291. rasa/cli/shell.py +10 -5
  292. rasa/cli/studio/studio.py +1 -1
  293. rasa/cli/test.py +34 -14
  294. rasa/cli/train.py +44 -30
  295. rasa/cli/utils.py +1 -393
  296. rasa/cli/validation/__init__.py +0 -0
  297. rasa/cli/validation/bot_config.py +232 -0
  298. rasa/cli/validation/config_path_validation.py +257 -0
  299. rasa/cli/x.py +8 -4
  300. rasa/constants.py +7 -1
  301. rasa/core/actions/action.py +53 -13
  302. rasa/core/actions/action_exceptions.py +1 -1
  303. rasa/core/actions/action_run_slot_rejections.py +1 -1
  304. rasa/core/actions/grpc_custom_action_executor.py +1 -1
  305. rasa/core/agent.py +22 -2
  306. rasa/core/available_agents.py +239 -0
  307. rasa/core/brokers/broker.py +1 -1
  308. rasa/core/brokers/kafka.py +56 -8
  309. rasa/core/channels/__init__.py +82 -35
  310. rasa/core/channels/channel.py +4 -3
  311. rasa/core/channels/constants.py +3 -0
  312. rasa/core/channels/development_inspector.py +29 -16
  313. rasa/core/channels/hangouts.py +2 -2
  314. rasa/core/channels/inspector/README.md +25 -13
  315. rasa/core/channels/inspector/dist/assets/{arc-0b11fe30.js → arc-6177260a.js} +1 -1
  316. rasa/core/channels/inspector/dist/assets/{blockDiagram-38ab4fdb-9eef30a7.js → blockDiagram-38ab4fdb-b054f038.js} +1 -1
  317. rasa/core/channels/inspector/dist/assets/{c4Diagram-3d4e48cf-03e94f28.js → c4Diagram-3d4e48cf-f25427d5.js} +1 -1
  318. rasa/core/channels/inspector/dist/assets/channel-bf9cbb34.js +1 -0
  319. rasa/core/channels/inspector/dist/assets/{classDiagram-70f12bd4-95c09eba.js → classDiagram-70f12bd4-c7a2af53.js} +1 -1
  320. rasa/core/channels/inspector/dist/assets/{classDiagram-v2-f2320105-38e8446c.js → classDiagram-v2-f2320105-58db65c0.js} +1 -1
  321. rasa/core/channels/inspector/dist/assets/clone-8f9083bb.js +1 -0
  322. rasa/core/channels/inspector/dist/assets/{createText-2e5e7dd3-57dc3038.js → createText-2e5e7dd3-088372e2.js} +1 -1
  323. rasa/core/channels/inspector/dist/assets/{edges-e0da2a9e-4bac0545.js → edges-e0da2a9e-58676240.js} +1 -1
  324. rasa/core/channels/inspector/dist/assets/{erDiagram-9861fffd-81795c90.js → erDiagram-9861fffd-0c14d7c6.js} +1 -1
  325. rasa/core/channels/inspector/dist/assets/{flowDb-956e92f1-89489ae6.js → flowDb-956e92f1-ea63f85c.js} +1 -1
  326. rasa/core/channels/inspector/dist/assets/{flowDiagram-66a62f08-cd152627.js → flowDiagram-66a62f08-a2af48cd.js} +1 -1
  327. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-9ecd5b59.js +1 -0
  328. rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-4a651766-3da369bc.js → flowchart-elk-definition-4a651766-6937abe7.js} +1 -1
  329. rasa/core/channels/inspector/dist/assets/{ganttDiagram-c361ad54-85ec16f8.js → ganttDiagram-c361ad54-7473f357.js} +1 -1
  330. rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-72cf32ee-495bc140.js → gitGraphDiagram-72cf32ee-d0c9405e.js} +1 -1
  331. rasa/core/channels/inspector/dist/assets/{graph-1ec4d266.js → graph-0a6f8466.js} +1 -1
  332. rasa/core/channels/inspector/dist/assets/{index-3862675e-0a0e97c9.js → index-3862675e-7610671a.js} +1 -1
  333. rasa/core/channels/inspector/dist/assets/index-74e01d94.js +1354 -0
  334. rasa/core/channels/inspector/dist/assets/{infoDiagram-f8f76790-4d54bcde.js → infoDiagram-f8f76790-be397dc7.js} +1 -1
  335. rasa/core/channels/inspector/dist/assets/{journeyDiagram-49397b02-dc097114.js → journeyDiagram-49397b02-4cefbf62.js} +1 -1
  336. rasa/core/channels/inspector/dist/assets/{layout-1a08981e.js → layout-e7fbc2bf.js} +1 -1
  337. rasa/core/channels/inspector/dist/assets/{line-95f7f1d3.js → line-a8aa457c.js} +1 -1
  338. rasa/core/channels/inspector/dist/assets/{linear-97e69543.js → linear-3351e0d2.js} +1 -1
  339. rasa/core/channels/inspector/dist/assets/{mindmap-definition-fc14e90a-8c71ff03.js → mindmap-definition-fc14e90a-b8cbf605.js} +1 -1
  340. rasa/core/channels/inspector/dist/assets/{pieDiagram-8a3498a8-f14c71c7.js → pieDiagram-8a3498a8-f327f774.js} +1 -1
  341. rasa/core/channels/inspector/dist/assets/{quadrantDiagram-120e2f19-f1d3c9ff.js → quadrantDiagram-120e2f19-2854c591.js} +1 -1
  342. rasa/core/channels/inspector/dist/assets/{requirementDiagram-deff3bca-bfa2412f.js → requirementDiagram-deff3bca-964985d5.js} +1 -1
  343. rasa/core/channels/inspector/dist/assets/{sankeyDiagram-04a897e0-53f2c97b.js → sankeyDiagram-04a897e0-edeb4f33.js} +1 -1
  344. rasa/core/channels/inspector/dist/assets/{sequenceDiagram-704730f1-319d7c0e.js → sequenceDiagram-704730f1-fcf70125.js} +1 -1
  345. rasa/core/channels/inspector/dist/assets/{stateDiagram-587899a1-76a09418.js → stateDiagram-587899a1-0e770395.js} +1 -1
  346. rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-d93cdb3a-a67f15d4.js → stateDiagram-v2-d93cdb3a-af8dcd22.js} +1 -1
  347. rasa/core/channels/inspector/dist/assets/{styles-6aaf32cf-0654e7c3.js → styles-6aaf32cf-36a9e70d.js} +1 -1
  348. rasa/core/channels/inspector/dist/assets/{styles-9a916d00-1394bb9d.js → styles-9a916d00-884a8b5b.js} +1 -1
  349. rasa/core/channels/inspector/dist/assets/{styles-c10674c1-e4c5bdae.js → styles-c10674c1-dc097813.js} +1 -1
  350. rasa/core/channels/inspector/dist/assets/{svgDrawCommon-08f97a94-50957104.js → svgDrawCommon-08f97a94-5a2c7eed.js} +1 -1
  351. rasa/core/channels/inspector/dist/assets/{timeline-definition-85554ec2-b0885a6a.js → timeline-definition-85554ec2-e89c4f6e.js} +1 -1
  352. rasa/core/channels/inspector/dist/assets/{xychartDiagram-e933f94c-79e6541a.js → xychartDiagram-e933f94c-afb6fe56.js} +1 -1
  353. rasa/core/channels/inspector/dist/index.html +1 -1
  354. rasa/core/channels/inspector/package.json +18 -18
  355. rasa/core/channels/inspector/src/App.tsx +56 -12
  356. rasa/core/channels/inspector/src/components/DiagramFlow.tsx +1 -1
  357. rasa/core/channels/inspector/src/components/DialogueAgentStack.tsx +108 -0
  358. rasa/core/channels/inspector/src/components/{DialogueStack.tsx → DialogueHistoryStack.tsx} +4 -2
  359. rasa/core/channels/inspector/src/components/DialogueInformation.tsx +20 -3
  360. rasa/core/channels/inspector/src/components/LatencyDisplay.tsx +296 -0
  361. rasa/core/channels/inspector/src/components/LoadingSpinner.tsx +6 -2
  362. rasa/core/channels/inspector/src/helpers/audio/audiostream.ts +26 -4
  363. rasa/core/channels/inspector/src/helpers/formatters.test.ts +4 -0
  364. rasa/core/channels/inspector/src/helpers/formatters.ts +24 -3
  365. rasa/core/channels/inspector/src/helpers/utils.test.ts +127 -0
  366. rasa/core/channels/inspector/src/helpers/utils.ts +66 -1
  367. rasa/core/channels/inspector/src/theme/base/styles.ts +19 -1
  368. rasa/core/channels/inspector/src/types.ts +55 -1
  369. rasa/core/channels/inspector/yarn.lock +336 -189
  370. rasa/core/channels/socketio.py +212 -51
  371. rasa/core/channels/studio_chat.py +82 -32
  372. rasa/core/channels/telegram.py +4 -9
  373. rasa/core/channels/voice_ready/twilio_voice.py +1 -1
  374. rasa/core/channels/voice_stream/asr/asr_event.py +1 -1
  375. rasa/core/channels/voice_stream/asr/azure.py +6 -3
  376. rasa/core/channels/voice_stream/asr/deepgram.py +1 -1
  377. rasa/core/channels/voice_stream/audiocodes.py +11 -6
  378. rasa/core/channels/voice_stream/browser_audio.py +91 -4
  379. rasa/core/channels/voice_stream/call_state.py +13 -2
  380. rasa/core/channels/voice_stream/genesys.py +19 -15
  381. rasa/core/channels/voice_stream/jambonz.py +22 -12
  382. rasa/core/channels/voice_stream/tts/deepgram.py +140 -0
  383. rasa/core/channels/voice_stream/twilio_media_streams.py +35 -14
  384. rasa/core/channels/voice_stream/util.py +11 -1
  385. rasa/core/channels/voice_stream/voice_channel.py +170 -32
  386. rasa/core/concurrent_lock_store.py +83 -16
  387. rasa/core/config/__init__.py +0 -0
  388. rasa/core/{available_endpoints.py → config/available_endpoints.py} +56 -18
  389. rasa/core/config/configuration.py +295 -0
  390. rasa/core/config/credentials.py +19 -0
  391. rasa/core/config/message_procesing_config.py +34 -0
  392. rasa/core/constants.py +17 -0
  393. rasa/core/exceptions.py +1 -1
  394. rasa/core/featurizers/tracker_featurizers.py +3 -2
  395. rasa/core/iam_credentials_providers/__init__.py +0 -0
  396. rasa/core/iam_credentials_providers/aws_iam_credentials_providers.py +291 -0
  397. rasa/core/iam_credentials_providers/credentials_provider_protocol.py +91 -0
  398. rasa/core/lock_store.py +50 -10
  399. rasa/core/nlg/contextual_response_rephraser.py +5 -0
  400. rasa/core/nlg/generator.py +1 -1
  401. rasa/core/persistor.py +7 -7
  402. rasa/core/policies/enterprise_search_policy.py +9 -10
  403. rasa/core/policies/flow_policy.py +4 -4
  404. rasa/core/policies/flows/agent_executor.py +720 -0
  405. rasa/core/policies/flows/flow_exceptions.py +5 -2
  406. rasa/core/policies/flows/flow_executor.py +146 -77
  407. rasa/core/policies/flows/mcp_tool_executor.py +304 -0
  408. rasa/core/policies/intentless_policy.py +1 -1
  409. rasa/core/policies/rule_policy.py +1 -1
  410. rasa/core/policies/ted_policy.py +20 -12
  411. rasa/core/policies/unexpected_intent_policy.py +6 -0
  412. rasa/core/processor.py +100 -44
  413. rasa/core/redis_connection_factory.py +474 -0
  414. rasa/core/run.py +49 -10
  415. rasa/core/test.py +4 -0
  416. rasa/core/tracker_stores/redis_tracker_store.py +36 -14
  417. rasa/core/tracker_stores/sql_tracker_store.py +59 -1
  418. rasa/core/tracker_stores/tracker_store.py +3 -7
  419. rasa/core/train.py +1 -1
  420. rasa/core/training/interactive.py +20 -18
  421. rasa/core/training/story_conflict.py +5 -5
  422. rasa/core/utils.py +22 -23
  423. rasa/dialogue_understanding/commands/__init__.py +8 -0
  424. rasa/dialogue_understanding/commands/cancel_flow_command.py +20 -6
  425. rasa/dialogue_understanding/commands/chit_chat_answer_command.py +21 -2
  426. rasa/dialogue_understanding/commands/clarify_command.py +20 -2
  427. rasa/dialogue_understanding/commands/continue_agent_command.py +91 -0
  428. rasa/dialogue_understanding/commands/knowledge_answer_command.py +21 -2
  429. rasa/dialogue_understanding/commands/restart_agent_command.py +162 -0
  430. rasa/dialogue_understanding/commands/start_flow_command.py +75 -7
  431. rasa/dialogue_understanding/commands/utils.py +135 -2
  432. rasa/dialogue_understanding/generator/command_parser.py +4 -0
  433. rasa/dialogue_understanding/generator/flow_retrieval.py +0 -9
  434. rasa/dialogue_understanding/generator/llm_based_command_generator.py +52 -12
  435. rasa/dialogue_understanding/generator/llm_command_generator.py +1 -1
  436. rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +1 -1
  437. rasa/dialogue_understanding/generator/prompt_templates/agent_command_prompt_v2_claude_3_5_sonnet_20240620_template.jinja2 +66 -0
  438. rasa/dialogue_understanding/generator/prompt_templates/agent_command_prompt_v2_gpt_4o_2024_11_20_template.jinja2 +66 -0
  439. rasa/dialogue_understanding/generator/prompt_templates/agent_command_prompt_v3_claude_3_5_sonnet_20240620_template.jinja2 +89 -0
  440. rasa/dialogue_understanding/generator/prompt_templates/agent_command_prompt_v3_gpt_4o_2024_11_20_template.jinja2 +88 -0
  441. rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +42 -7
  442. rasa/dialogue_understanding/generator/single_step/search_ready_llm_command_generator.py +40 -3
  443. rasa/dialogue_understanding/generator/single_step/single_step_based_llm_command_generator.py +20 -3
  444. rasa/dialogue_understanding/patterns/cancel.py +27 -6
  445. rasa/dialogue_understanding/patterns/clarify.py +3 -14
  446. rasa/dialogue_understanding/patterns/continue_interrupted.py +239 -6
  447. rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +49 -9
  448. rasa/dialogue_understanding/processor/command_processor.py +136 -15
  449. rasa/dialogue_understanding/stack/dialogue_stack.py +98 -2
  450. rasa/dialogue_understanding/stack/frames/flow_stack_frame.py +57 -0
  451. rasa/dialogue_understanding/stack/utils.py +57 -3
  452. rasa/dialogue_understanding/utils.py +24 -4
  453. rasa/dialogue_understanding_test/du_test_runner.py +8 -3
  454. rasa/e2e_test/e2e_test_runner.py +13 -3
  455. rasa/engine/caching.py +2 -2
  456. rasa/engine/constants.py +1 -1
  457. rasa/engine/graph.py +5 -1
  458. rasa/engine/loader.py +12 -0
  459. rasa/engine/recipes/default_components.py +138 -49
  460. rasa/engine/recipes/default_recipe.py +108 -11
  461. rasa/engine/runner/dask.py +8 -5
  462. rasa/engine/validation.py +25 -8
  463. rasa/graph_components/validators/default_recipe_validator.py +86 -28
  464. rasa/hooks.py +5 -5
  465. rasa/llm_fine_tuning/utils.py +2 -2
  466. rasa/model_manager/model_api.py +4 -5
  467. rasa/model_manager/runner_service.py +2 -2
  468. rasa/model_manager/socket_bridge.py +21 -17
  469. rasa/model_manager/trainer_service.py +12 -9
  470. rasa/model_manager/utils.py +1 -29
  471. rasa/model_manager/warm_rasa_process.py +13 -3
  472. rasa/model_training.py +60 -47
  473. rasa/nlu/classifiers/diet_classifier.py +198 -98
  474. rasa/nlu/classifiers/logistic_regression_classifier.py +1 -4
  475. rasa/nlu/classifiers/mitie_intent_classifier.py +3 -0
  476. rasa/nlu/classifiers/sklearn_intent_classifier.py +1 -3
  477. rasa/nlu/extractors/crf_entity_extractor.py +9 -10
  478. rasa/nlu/extractors/mitie_entity_extractor.py +3 -0
  479. rasa/nlu/extractors/spacy_entity_extractor.py +3 -0
  480. rasa/nlu/featurizers/dense_featurizer/convert_featurizer.py +4 -0
  481. rasa/nlu/featurizers/dense_featurizer/lm_featurizer.py +5 -0
  482. rasa/nlu/featurizers/dense_featurizer/mitie_featurizer.py +2 -0
  483. rasa/nlu/featurizers/dense_featurizer/spacy_featurizer.py +3 -0
  484. rasa/nlu/featurizers/sparse_featurizer/count_vectors_featurizer.py +4 -2
  485. rasa/nlu/featurizers/sparse_featurizer/lexical_syntactic_featurizer.py +4 -0
  486. rasa/nlu/selectors/response_selector.py +10 -2
  487. rasa/nlu/tokenizers/jieba_tokenizer.py +3 -4
  488. rasa/nlu/tokenizers/mitie_tokenizer.py +3 -2
  489. rasa/nlu/tokenizers/spacy_tokenizer.py +3 -2
  490. rasa/nlu/utils/mitie_utils.py +3 -0
  491. rasa/nlu/utils/spacy_utils.py +3 -2
  492. rasa/plugin.py +8 -8
  493. rasa/privacy/privacy_config.py +1 -1
  494. rasa/privacy/privacy_manager.py +12 -3
  495. rasa/server.py +15 -3
  496. rasa/shared/agents/__init__.py +0 -0
  497. rasa/shared/agents/auth/__init__.py +0 -0
  498. rasa/shared/agents/auth/agent_auth_factory.py +105 -0
  499. rasa/shared/agents/auth/agent_auth_manager.py +92 -0
  500. rasa/shared/agents/auth/auth_strategy/__init__.py +19 -0
  501. rasa/shared/agents/auth/auth_strategy/agent_auth_strategy.py +52 -0
  502. rasa/shared/agents/auth/auth_strategy/api_key_auth_strategy.py +42 -0
  503. rasa/shared/agents/auth/auth_strategy/bearer_token_auth_strategy.py +28 -0
  504. rasa/shared/agents/auth/auth_strategy/oauth2_auth_strategy.py +170 -0
  505. rasa/shared/agents/auth/constants.py +13 -0
  506. rasa/shared/agents/auth/types.py +12 -0
  507. rasa/shared/agents/auth/utils.py +85 -0
  508. rasa/shared/agents/utils.py +35 -0
  509. rasa/shared/constants.py +11 -0
  510. rasa/shared/core/constants.py +17 -1
  511. rasa/shared/core/domain.py +62 -22
  512. rasa/shared/core/events.py +329 -0
  513. rasa/shared/core/flows/constants.py +5 -0
  514. rasa/shared/core/flows/flow.py +1 -1
  515. rasa/shared/core/flows/flow_step.py +7 -1
  516. rasa/shared/core/flows/flows_list.py +21 -5
  517. rasa/shared/core/flows/flows_yaml_schema.json +119 -184
  518. rasa/shared/core/flows/steps/call.py +57 -6
  519. rasa/shared/core/flows/steps/collect.py +98 -13
  520. rasa/shared/core/flows/validation.py +372 -8
  521. rasa/shared/core/flows/yaml_flows_io.py +19 -10
  522. rasa/shared/core/slots.py +6 -2
  523. rasa/shared/core/trackers.py +5 -2
  524. rasa/shared/core/training_data/story_reader/story_reader.py +1 -1
  525. rasa/shared/exceptions.py +39 -2
  526. rasa/shared/importers/importer.py +6 -0
  527. rasa/shared/importers/rasa.py +1 -1
  528. rasa/shared/importers/utils.py +86 -4
  529. rasa/shared/nlu/training_data/schemas/responses.yml +3 -0
  530. rasa/shared/providers/llm/_base_litellm_client.py +41 -9
  531. rasa/shared/providers/llm/litellm_router_llm_client.py +10 -6
  532. rasa/shared/providers/llm/llm_client.py +7 -3
  533. rasa/shared/providers/llm/llm_response.py +66 -0
  534. rasa/shared/providers/llm/self_hosted_llm_client.py +8 -4
  535. rasa/shared/utils/common.py +26 -1
  536. rasa/shared/utils/health_check/health_check.py +7 -3
  537. rasa/shared/utils/llm.py +92 -19
  538. rasa/shared/utils/mcp/__init__.py +0 -0
  539. rasa/shared/utils/mcp/server_connection.py +250 -0
  540. rasa/shared/utils/mcp/utils.py +20 -0
  541. rasa/shared/utils/schemas/events.py +42 -0
  542. rasa/shared/utils/yaml.py +3 -1
  543. rasa/studio/download.py +3 -0
  544. rasa/studio/prompts.py +1 -0
  545. rasa/studio/pull/pull.py +3 -2
  546. rasa/studio/train.py +8 -7
  547. rasa/studio/upload.py +19 -52
  548. rasa/telemetry.py +166 -28
  549. rasa/tracing/config.py +45 -12
  550. rasa/tracing/constants.py +14 -0
  551. rasa/tracing/instrumentation/attribute_extractors.py +142 -9
  552. rasa/tracing/instrumentation/instrumentation.py +626 -21
  553. rasa/tracing/instrumentation/intentless_policy_instrumentation.py +4 -4
  554. rasa/tracing/instrumentation/metrics.py +32 -0
  555. rasa/tracing/metric_instrument_provider.py +68 -0
  556. rasa/utils/common.py +92 -1
  557. rasa/utils/endpoints.py +11 -2
  558. rasa/utils/io.py +27 -9
  559. rasa/utils/json_utils.py +6 -1
  560. rasa/utils/log_utils.py +121 -7
  561. rasa/utils/ml_utils.py +1 -1
  562. rasa/utils/openapi.py +144 -0
  563. rasa/utils/plotting.py +1 -1
  564. rasa/utils/pypred.py +45 -0
  565. rasa/utils/tensorflow/__init__.py +7 -0
  566. rasa/utils/tensorflow/callback.py +136 -101
  567. rasa/utils/tensorflow/crf.py +1 -1
  568. rasa/utils/tensorflow/data_generator.py +21 -8
  569. rasa/utils/tensorflow/layers.py +21 -11
  570. rasa/utils/tensorflow/metrics.py +7 -3
  571. rasa/utils/tensorflow/models.py +56 -8
  572. rasa/utils/tensorflow/rasa_layers.py +8 -6
  573. rasa/utils/tensorflow/transformer.py +2 -3
  574. rasa/utils/train_utils.py +54 -24
  575. rasa/validator.py +149 -16
  576. rasa/version.py +1 -1
  577. rasa_pro-3.14.0.dist-info/METADATA +212 -0
  578. {rasa_pro-3.13.12.dist-info → rasa_pro-3.14.0.dist-info}/RECORD +581 -269
  579. rasa/core/channels/inspector/dist/assets/channel-51d02e9e.js +0 -1
  580. rasa/core/channels/inspector/dist/assets/clone-cc738fa6.js +0 -1
  581. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-0c716443.js +0 -1
  582. rasa/core/channels/inspector/dist/assets/index-c804b295.js +0 -1335
  583. rasa_pro-3.13.12.dist-info/METADATA +0 -192
  584. {rasa_pro-3.13.12.dist-info → rasa_pro-3.14.0.dist-info}/NOTICE +0 -0
  585. {rasa_pro-3.13.12.dist-info → rasa_pro-3.14.0.dist-info}/WHEEL +0 -0
  586. {rasa_pro-3.13.12.dist-info → rasa_pro-3.14.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,889 @@
1
+ import asyncio
2
+ import json
3
+ import os
4
+ import time
5
+ import uuid
6
+ from contextlib import aclosing
7
+ from typing import Any, ClassVar, Dict, List, Optional
8
+ from urllib.parse import urlparse
9
+
10
+ import httpx
11
+ import structlog
12
+ from a2a.client import (
13
+ A2ACardResolver,
14
+ A2AClientError,
15
+ A2AClientHTTPError,
16
+ A2AClientJSONError,
17
+ Client,
18
+ ClientConfig,
19
+ ClientEvent,
20
+ ClientFactory,
21
+ )
22
+ from a2a.client.errors import A2AClientJSONRPCError
23
+ from a2a.types import (
24
+ AgentCard,
25
+ Artifact,
26
+ DataPart,
27
+ FilePart,
28
+ FileWithUri,
29
+ InternalError,
30
+ InvalidAgentResponseError,
31
+ Message,
32
+ Part,
33
+ Role,
34
+ Task,
35
+ TaskQueryParams,
36
+ TaskState,
37
+ TextPart,
38
+ TransportProtocol,
39
+ )
40
+ from pydantic import ValidationError
41
+
42
+ from rasa.agents.constants import (
43
+ A2A_AGENT_CONTEXT_ID_KEY,
44
+ A2A_AGENT_TASK_ID_KEY,
45
+ AGENT_DEFAULT_MAX_RETRIES,
46
+ AGENT_DEFAULT_TIMEOUT_SECONDS,
47
+ AGENT_METADATA_STRUCTURED_RESULTS_KEY,
48
+ MAX_AGENT_RETRY_DELAY_SECONDS,
49
+ )
50
+ from rasa.agents.core.agent_protocol import AgentProtocol
51
+ from rasa.agents.core.types import AgentStatus, ProtocolType
52
+ from rasa.agents.schemas import AgentInput, AgentOutput
53
+ from rasa.core.available_agents import AgentConfig
54
+ from rasa.shared.agents.auth.agent_auth_manager import AgentAuthManager
55
+ from rasa.shared.exceptions import (
56
+ AgentInitializationException,
57
+ InvalidParameterException,
58
+ RasaException,
59
+ )
60
+
61
+ A2A_TASK_POOLING_INITIAL_DELAY = 0.5
62
+ A2A_TASK_POOLING_MAX_WAIT = 60
63
+
64
+ structlogger = structlog.get_logger()
65
+
66
+
67
+ class A2AAgent(AgentProtocol):
68
+ """A2A client implementation."""
69
+
70
+ __SUPPORTED_OUTPUT_MODES: ClassVar[list[str]] = [
71
+ "text",
72
+ "text/plain",
73
+ "application/json",
74
+ ]
75
+
76
+ # ============================================================================
77
+ # Initialization & Setup
78
+ # ============================================================================
79
+
80
+ def __init__(
81
+ self,
82
+ name: str,
83
+ description: str,
84
+ agent_card_path: str,
85
+ timeout: int,
86
+ max_retries: int,
87
+ auth_config: Optional[Dict[str, Any]] = None,
88
+ ) -> None:
89
+ self._name = name
90
+ self._description = description
91
+ self._agent_card_path = agent_card_path
92
+ self._timeout = timeout
93
+ self._max_retries = max_retries
94
+ self._auth_config = auth_config
95
+
96
+ self.agent_card: Optional[AgentCard] = None
97
+ self._client: Optional[Client] = None
98
+
99
+ @classmethod
100
+ def from_config(cls, config: AgentConfig) -> AgentProtocol:
101
+ """Initialize the A2A Agent with the given configuration."""
102
+ agent_card_path = (
103
+ config.configuration.agent_card if config.configuration else None
104
+ )
105
+ if not agent_card_path:
106
+ raise InvalidParameterException(
107
+ "Agent card path or URL must be provided in the configuration "
108
+ "for A2A agents."
109
+ )
110
+
111
+ timeout = (
112
+ config.configuration.timeout
113
+ if config.configuration and config.configuration.timeout
114
+ else AGENT_DEFAULT_TIMEOUT_SECONDS
115
+ )
116
+ max_retries = (
117
+ config.configuration.max_retries
118
+ if config.configuration and config.configuration.max_retries
119
+ else AGENT_DEFAULT_MAX_RETRIES
120
+ )
121
+
122
+ _auth_config = config.configuration.auth if config.configuration else None
123
+ return cls(
124
+ name=config.agent.name,
125
+ description=config.agent.description,
126
+ agent_card_path=agent_card_path,
127
+ timeout=timeout,
128
+ max_retries=max_retries,
129
+ auth_config=_auth_config,
130
+ )
131
+
132
+ @property
133
+ def protocol_type(self) -> ProtocolType:
134
+ return ProtocolType.A2A
135
+
136
+ # ============================================================================
137
+ # Connection Management
138
+ # ============================================================================
139
+
140
+ async def connect(self) -> None:
141
+ """Fetch the AgentCard and initialize the A2A client."""
142
+ from rasa.nlu.utils import is_url
143
+
144
+ if is_url(self._agent_card_path):
145
+ self.agent_card = await A2AAgent._resolve_agent_card_with_retry(
146
+ self._agent_card_path, self._timeout, self._max_retries
147
+ )
148
+ else:
149
+ self.agent_card = A2AAgent._load_agent_card_from_file(self._agent_card_path)
150
+ structlogger.debug(
151
+ "a2a_agent.from_config",
152
+ event_info=f"Loaded agent card from {self._agent_card_path}",
153
+ agent_card=self.agent_card.model_dump(),
154
+ json_formatting=["agent_card"],
155
+ )
156
+
157
+ try:
158
+ self._client = self._init_client()
159
+ structlogger.debug(
160
+ "a2a_agent.connect.agent_client_initialized",
161
+ event_info=f"Initialized A2A client for agent '{self._name}'. "
162
+ f"Performing health check using the URL {self.agent_card.url}",
163
+ )
164
+ except Exception as exception:
165
+ structlogger.error(
166
+ "a2a_agent.connect.error",
167
+ event_info="Failed to initialize A2A client",
168
+ agent_name=self._name,
169
+ error=str(exception),
170
+ )
171
+ raise AgentInitializationException(
172
+ f"Failed to initialize A2A client "
173
+ f"for agent '{self._name}': {exception}",
174
+ ) from exception
175
+
176
+ await self._perform_health_check()
177
+ structlogger.debug(
178
+ "a2a_agent.connect.success",
179
+ event_info=f"Connected to A2A server '{self._name}' "
180
+ f"at {self.agent_card.url}",
181
+ )
182
+
183
+ async def disconnect(self) -> None:
184
+ """We don't need to explicitly disconnect the A2A client."""
185
+ return
186
+
187
+ # ============================================================================
188
+ # Core Protocol Methods
189
+ # ============================================================================
190
+
191
+ async def process_input(self, agent_input: AgentInput) -> AgentInput:
192
+ """Pre-process the input before sending it to the agent."""
193
+ # A2A-specific input processing logic
194
+ return agent_input
195
+
196
+ async def run(self, agent_input: AgentInput) -> AgentOutput:
197
+ """Send a message to Agent/server and return response."""
198
+ if not self._client or not self.agent_card:
199
+ structlogger.error(
200
+ "a2a_agent.run.error",
201
+ event_info="A2A client is not initialized. Call connect() first.",
202
+ )
203
+ return AgentOutput(
204
+ id=agent_input.id,
205
+ status=AgentStatus.FATAL_ERROR,
206
+ error_message="Client not initialized",
207
+ )
208
+
209
+ structlogger.info(
210
+ "a2a_agent.run.start",
211
+ event_info="Running A2A agent",
212
+ agent_name=self._name,
213
+ )
214
+ message = self._prepare_message(agent_input)
215
+
216
+ task_id: Optional[str] = None
217
+ events_received = 0
218
+ try:
219
+ # Use aclosing to ensure proper cleanup of the async generator
220
+ stream = self._client.send_message(message)
221
+ async with aclosing(stream) as stream: # type: ignore[type-var]
222
+ async for event in stream:
223
+ events_received += 1
224
+ agent_output = self._handle_send_message_response(
225
+ agent_input, event
226
+ )
227
+ if agent_output is not None:
228
+ return agent_output
229
+ else:
230
+ # Not a terminal response, save taskID (in case that's the only
231
+ # event, and we need to pool) and continue waiting for events
232
+ if (
233
+ isinstance(event, tuple)
234
+ and len(event) == 2
235
+ and isinstance(event[0], Task)
236
+ ):
237
+ task_id = event[0].id
238
+ continue
239
+ except A2AClientJSONRPCError as e:
240
+ return self._handle_json_rpc_error_response(agent_input, e.error)
241
+ except A2AClientError as exception:
242
+ structlogger.error(
243
+ "a2a_agent.run.send_message.error",
244
+ event_info="Error during sending message to A2A agent",
245
+ agent_name=self._name,
246
+ error=str(exception),
247
+ )
248
+ return AgentOutput(
249
+ id=agent_input.id,
250
+ status=AgentStatus.FATAL_ERROR,
251
+ error_message=f"Send message error: {exception!s}",
252
+ )
253
+
254
+ # The stream has ended, but we didn't get a terminal response.
255
+ # Check if we received any events at all.
256
+ if events_received == 0:
257
+ structlogger.error(
258
+ "a2a_agent.run.no_events_received",
259
+ event_info="No events received from A2A agent after sending message",
260
+ agent_name=self._name,
261
+ )
262
+ return AgentOutput(
263
+ id=agent_input.id,
264
+ status=AgentStatus.RECOVERABLE_ERROR,
265
+ error_message="No events received from A2A agent",
266
+ )
267
+
268
+ # Now we need to poll the task until it reaches a terminal state.
269
+ if not task_id:
270
+ structlogger.error(
271
+ "a2a_agent.run.pooling.missing_id",
272
+ event_info="Missing task_id for polling",
273
+ agent_name=self._name,
274
+ task_id=task_id,
275
+ )
276
+ return AgentOutput(
277
+ id=agent_input.id,
278
+ status=AgentStatus.FATAL_ERROR,
279
+ error_message="Missing task_id for polling",
280
+ )
281
+ return await self._pool_task_until_terminal(
282
+ agent_input=agent_input,
283
+ task_id=task_id,
284
+ max_wait=A2A_TASK_POOLING_MAX_WAIT,
285
+ initial_delay=A2A_TASK_POOLING_INITIAL_DELAY,
286
+ max_delay=MAX_AGENT_RETRY_DELAY_SECONDS,
287
+ )
288
+
289
+ async def process_output(self, output: AgentOutput) -> AgentOutput:
290
+ """Post-process the output before returning it to Rasa."""
291
+ # A2A-specific output processing logic
292
+ return output
293
+
294
+ # ============================================================================
295
+ # Message Processing & Response Handling
296
+ # ============================================================================
297
+
298
+ def _handle_send_message_response(
299
+ self, agent_input: AgentInput, response: ClientEvent | Message
300
+ ) -> Optional[AgentOutput]:
301
+ """Handle possible response types from the A2A client.
302
+
303
+ In case of streaming, the response can be either exactly *one* Message,
304
+ or a *series* of tuples of (Task, Optional[TaskUpdateEvent]).
305
+
306
+ In case of pooling, the response can be either exactly *one* Message,
307
+ or exactly *one* tuple of (Task, None).
308
+
309
+ If the agent response is terminal (i.e., completed, failed, etc.),
310
+ this method will return an AgentOutput.
311
+ Otherwise, the task is still in progress (i.e., submitted, working), so this
312
+ method will return None, so that the streaming or pooling agent can continue
313
+ to wait for updates.
314
+ """
315
+ if isinstance(response, Message):
316
+ return self._handle_message_response(agent_input, response)
317
+ elif (
318
+ isinstance(response, tuple)
319
+ and len(response) == 2
320
+ and isinstance(response[0], Task)
321
+ ):
322
+ return self._handle_client_event(agent_input, response)
323
+ else:
324
+ # Currently, no other response types exist, so this branch is
325
+ # unreachable. It is kept as a safeguard against future changes
326
+ # to the A2A protocol: if new response types are introduced,
327
+ # the agent will log an error instead of crashing.
328
+ return self._handle_unexpected_response_type(agent_input, response)
329
+
330
+ def _handle_json_rpc_error_response(
331
+ self, agent_input: AgentInput, error: Any
332
+ ) -> AgentOutput:
333
+ structlogger.error(
334
+ "a2a_agent.run.error",
335
+ event_info="Received JSON-RPC error response from A2A agent",
336
+ agent_name=self._name,
337
+ error=str(error),
338
+ )
339
+ if isinstance(
340
+ error,
341
+ (
342
+ InternalError,
343
+ InvalidAgentResponseError,
344
+ ),
345
+ ):
346
+ return AgentOutput(
347
+ id=agent_input.id,
348
+ status=AgentStatus.RECOVERABLE_ERROR,
349
+ error_message=str(error),
350
+ )
351
+ else:
352
+ return AgentOutput(
353
+ id=agent_input.id,
354
+ status=AgentStatus.FATAL_ERROR,
355
+ error_message=str(error),
356
+ )
357
+
358
+ def _handle_client_event(
359
+ self, agent_input: AgentInput, client_event: ClientEvent
360
+ ) -> Optional[AgentOutput]:
361
+ task = client_event[0]
362
+ update_event = client_event[1]
363
+ structlogger.debug(
364
+ "a2a_agent.run.client_event_received",
365
+ event_info="Received client event from A2A",
366
+ task=task.model_dump() if task else None,
367
+ update_event=update_event.model_dump() if update_event else None,
368
+ json_formatting=["task", "update_event"],
369
+ )
370
+
371
+ return self._handle_task(agent_input=agent_input, task=task)
372
+
373
+ def _handle_message_response(
374
+ self, agent_input: AgentInput, message: Message
375
+ ) -> Optional[AgentOutput]:
376
+ structlogger.debug(
377
+ "a2a_agent.run.message_received",
378
+ event_info="Received message from A2A",
379
+ agent_name=self._name,
380
+ message=message.model_dump(),
381
+ json_formatting=["message"],
382
+ )
383
+ metadata = agent_input.metadata or {}
384
+ metadata[A2A_AGENT_CONTEXT_ID_KEY] = message.context_id
385
+
386
+ return AgentOutput(
387
+ id=agent_input.id,
388
+ status=AgentStatus.INPUT_REQUIRED,
389
+ response_message=self._generate_response_message_from_parts(message.parts),
390
+ metadata=metadata,
391
+ )
392
+
393
+ def _handle_unexpected_response_type(
394
+ self, agent_input: AgentInput, response_result: Any
395
+ ) -> AgentOutput:
396
+ structlogger.error(
397
+ "a2a_agent.run.unexpected_response_type",
398
+ event_info="Received unexpected response type from A2A server "
399
+ "during streaming",
400
+ agent_name=self._name,
401
+ response_type=type(response_result),
402
+ )
403
+ return AgentOutput(
404
+ id=agent_input.id,
405
+ status=AgentStatus.FATAL_ERROR,
406
+ error_message=f"Unexpected response type: {type(response_result)}",
407
+ )
408
+
409
+ def _handle_task(
410
+ self,
411
+ agent_input: AgentInput,
412
+ task: Task,
413
+ ) -> Optional[AgentOutput]:
414
+ """If task status is terminal (e.g. completed, failed) return AgentOutput.
415
+
416
+ If the task is still in progress (i.e., submitted, working), return None,
417
+ so that the streaming or pooling agent can continue to wait for updates.
418
+ """
419
+ state = task.status.state
420
+
421
+ metadata = agent_input.metadata or {}
422
+ metadata[A2A_AGENT_CONTEXT_ID_KEY] = task.context_id
423
+ metadata[A2A_AGENT_TASK_ID_KEY] = task.id
424
+
425
+ if state == TaskState.input_required:
426
+ response_message = (
427
+ self._generate_response_message_from_parts(task.status.message.parts)
428
+ if task.status.message
429
+ else ""
430
+ ) # This should not happen, but as type of message property
431
+ # is optional, so we need to handle it
432
+ return AgentOutput(
433
+ id=agent_input.id,
434
+ status=AgentStatus.INPUT_REQUIRED,
435
+ response_message=response_message,
436
+ metadata=metadata,
437
+ )
438
+ elif state == TaskState.completed:
439
+ response_message = self._generate_completed_response_message(task)
440
+ structured_results = (
441
+ self._generate_structured_results_from_artifacts(
442
+ agent_input, task.artifacts
443
+ )
444
+ if task.artifacts
445
+ else None
446
+ )
447
+ return AgentOutput(
448
+ id=agent_input.id,
449
+ status=AgentStatus.COMPLETED,
450
+ response_message=response_message,
451
+ structured_results=structured_results,
452
+ metadata=metadata,
453
+ )
454
+ elif (
455
+ state == TaskState.failed
456
+ or state == TaskState.canceled
457
+ or state == TaskState.rejected
458
+ or state == TaskState.auth_required
459
+ ):
460
+ structlogger.error(
461
+ "a2a_agent.run_streaming_agent.unsuccessful_task_state",
462
+ event_info="Task execution finished with an unsuccessful state",
463
+ agent_name=self._name,
464
+ state=state,
465
+ )
466
+ return AgentOutput(
467
+ id=agent_input.id,
468
+ status=AgentStatus.RECOVERABLE_ERROR,
469
+ error_message=f"Task state: {state}",
470
+ metadata=metadata,
471
+ )
472
+ elif state == TaskState.submitted or state == TaskState.working:
473
+ # The task is still in progress, return None to continue waiting for updates
474
+ return None
475
+ elif state == TaskState.unknown:
476
+ # The task has an unknown state. Perhaps this is a transient condition.
477
+ # Return None to continue waiting for updates
478
+ structlogger.warning(
479
+ "a2a_agent.run_streaming_agent.unknown_task_state",
480
+ event_info="Task is in unknown state, continuing to wait for updates",
481
+ agent_name=self._name,
482
+ state=state,
483
+ )
484
+ return None
485
+ else:
486
+ structlogger.error(
487
+ "a2a_agent.run_streaming_agent.unexpected_task_state",
488
+ event_info="Unexpected task state received from A2A",
489
+ agent_name=self._name,
490
+ state=state,
491
+ )
492
+ return AgentOutput(
493
+ id=agent_input.id,
494
+ status=AgentStatus.FATAL_ERROR,
495
+ error_message=f"Unexpected task state: {state}",
496
+ metadata=metadata,
497
+ )
498
+
499
+ # ============================================================================
500
+ # Message Preparation & Formatting
501
+ # ============================================================================
502
+
503
+ @staticmethod
504
+ def _prepare_message(agent_input: AgentInput) -> Message:
505
+ parts: List[Part] = []
506
+ if agent_input.metadata and A2A_AGENT_CONTEXT_ID_KEY in agent_input.metadata:
507
+ # Agent knows the conversation history already, send the last
508
+ # user message only
509
+ parts.append(Part(root=TextPart(text=agent_input.user_message)))
510
+ else:
511
+ # Send the full conversation history
512
+ parts.append(Part(root=TextPart(text=agent_input.conversation_history)))
513
+
514
+ if len(agent_input.slots) > 0:
515
+ slots_dict: Dict[str, Any] = {
516
+ "slots": [
517
+ slot.model_dump(exclude={"type", "allowed_values"})
518
+ for slot in agent_input.slots
519
+ if slot.value is not None
520
+ ]
521
+ }
522
+ parts.append(Part(root=DataPart(data=slots_dict)))
523
+
524
+ agent_message = Message(
525
+ role=Role.user,
526
+ parts=parts,
527
+ message_id=str(uuid.uuid4()),
528
+ context_id=agent_input.metadata.get(A2A_AGENT_CONTEXT_ID_KEY, None),
529
+ task_id=agent_input.metadata.get(A2A_AGENT_TASK_ID_KEY, None),
530
+ )
531
+ structlogger.debug(
532
+ "a2a_agent.prepare_message",
533
+ event_info="Prepared message to send to A2A server",
534
+ agent_name=agent_input.id,
535
+ message=agent_message.model_dump(),
536
+ json_formatting=["message"],
537
+ )
538
+ return agent_message
539
+
540
+ # ============================================================================
541
+ # Task Management & Polling
542
+ # ============================================================================
543
+
544
+ async def _pool_task_until_terminal(
545
+ self,
546
+ agent_input: AgentInput,
547
+ task_id: str,
548
+ max_wait: int,
549
+ initial_delay: float,
550
+ max_delay: int,
551
+ ) -> AgentOutput:
552
+ """Poll the task status until it reaches a terminal state or times out."""
553
+ if not self._client:
554
+ structlogger.error(
555
+ "a2a_agent.pool_task_until_terminal.error",
556
+ event_info="A2A client is not initialized. Call connect() first.",
557
+ )
558
+ return AgentOutput(
559
+ id=agent_input.id,
560
+ status=AgentStatus.FATAL_ERROR,
561
+ error_message="Client not initialized",
562
+ )
563
+
564
+ structlogger.debug(
565
+ "a2a_agent.pool_task_until_terminal.start",
566
+ event_info="Start polling task from A2A server",
567
+ agent_name=self._name,
568
+ task_id=task_id,
569
+ max_wait=max_wait,
570
+ initial_delay=initial_delay,
571
+ max_delay=max_delay,
572
+ )
573
+ start_time = time.monotonic()
574
+ delay = initial_delay
575
+
576
+ while True:
577
+ try:
578
+ task = await self._client.get_task(TaskQueryParams(id=task_id))
579
+ agent_output = self._handle_task(agent_input=agent_input, task=task)
580
+ if agent_output is not None:
581
+ # Reached a terminal state, return the output
582
+ return agent_output
583
+
584
+ elapsed = time.monotonic() - start_time
585
+ if elapsed >= max_wait:
586
+ structlogger.debug(
587
+ "a2a_agent.pool_task_until_terminal.timeout",
588
+ event_info="Polling task from A2A server timed out",
589
+ agent_name=self._name,
590
+ task_id=task_id,
591
+ elapsed=elapsed,
592
+ max_wait=max_wait,
593
+ )
594
+ return AgentOutput(
595
+ id=agent_input.id,
596
+ status=AgentStatus.FATAL_ERROR,
597
+ error_message="Polling timed out",
598
+ )
599
+
600
+ structlogger.error(
601
+ "a2a_agent.pool_task_until_terminal.waiting",
602
+ event_info="Task not in terminal state yet, waiting to poll again",
603
+ delay=delay,
604
+ agent_name=self._name,
605
+ task_id=task_id,
606
+ elapsed=elapsed,
607
+ max_wait=max_wait,
608
+ )
609
+ await asyncio.sleep(delay)
610
+ # Exponential backoff with cap
611
+ delay = min(delay * 2, max_delay)
612
+
613
+ except A2AClientError as exception:
614
+ structlogger.error(
615
+ "a2a_agent.pool_task_until_terminal.error",
616
+ event_info="Error during polling task from A2A server",
617
+ agent_name=self._name,
618
+ error=str(exception),
619
+ )
620
+ return AgentOutput(
621
+ id=agent_input.id,
622
+ status=AgentStatus.FATAL_ERROR,
623
+ error_message=f"Polling error: {exception!s}",
624
+ )
625
+
626
+ # ============================================================================
627
+ # Response Generation & Formatting
628
+ # ============================================================================
629
+
630
+ @staticmethod
631
+ def _generate_response_message_from_parts(parts: Optional[List[Part]]) -> str:
632
+ """Convert a list of Part objects to a single string message."""
633
+ result = ""
634
+ if not parts:
635
+ return result
636
+ for part in parts:
637
+ if isinstance(part.root, TextPart):
638
+ result += part.root.text + "\n"
639
+ elif isinstance(part.root, DataPart):
640
+ # DataPart results will be returned as a part of the structured results,
641
+ # we don't need to include it in the response message
642
+ continue
643
+ elif isinstance(part.root, FilePart) and isinstance(
644
+ part.root.file, FileWithUri
645
+ ):
646
+ # If the file is a FileWithUri, we can include the URI
647
+ result += f"File: {part.root.file.uri}\n"
648
+ else:
649
+ structlogger.warning(
650
+ "a2a_agent._parts_to_single_message.warning",
651
+ event_info="Unsupported part type encountered",
652
+ part_type=type(part.root),
653
+ )
654
+ return result.strip()
655
+
656
+ @staticmethod
657
+ def _generate_completed_response_message(task: Task) -> str:
658
+ """Generate a response message for a completed task.
659
+
660
+ In case of completed tasks, the final message might be in
661
+ the task status message or in the artifacts (or both).
662
+ """
663
+ # We need to preserve the order of the message,
664
+ # but also make sure to remove any duplicates.
665
+ result: List[str] = []
666
+ if task.status.message:
667
+ message = A2AAgent._generate_response_message_from_parts(
668
+ task.status.message.parts
669
+ )
670
+ if message and message not in result:
671
+ result.append(message)
672
+ if task.artifacts:
673
+ for artifact in task.artifacts:
674
+ message = A2AAgent._generate_response_message_from_parts(artifact.parts)
675
+ if message and message not in result:
676
+ result.append(message)
677
+ return "\n".join(result)
678
+
679
+ @staticmethod
680
+ def _generate_structured_results_from_artifacts(
681
+ agent_input: AgentInput, artifacts: List[Artifact]
682
+ ) -> Optional[List[List[Dict[str, Any]]]]:
683
+ structured_results_of_current_iteration: List[Dict[str, Any]] = []
684
+ # There might be multiple artifacts in the response, each of them might
685
+ # contain multiple parts. We will treat each DataPart in each artifact
686
+ # as a separate tool result. The tool name will be the agent ID + index
687
+ # of the artifact + index of the part.
688
+ # E.g., foo_0_1, foo_0_2, foo_1_0, etc.
689
+ for artifact_index, artifact in enumerate(artifacts):
690
+ for part_index, part in enumerate(artifact.parts):
691
+ if isinstance(part.root, DataPart) and len(part.root.data) > 0:
692
+ structured_result = {
693
+ "name": f"{agent_input.id}_{artifact_index}_{part_index}",
694
+ "type": "data",
695
+ "result": part.root.data,
696
+ }
697
+ structured_results_of_current_iteration.append(structured_result)
698
+ elif isinstance(part.root, FilePart) and isinstance(
699
+ part.root.file, FileWithUri
700
+ ):
701
+ structured_result = {
702
+ "name": f"{agent_input.id}_{artifact_index}_{part_index}",
703
+ "type": "file",
704
+ "result ": {
705
+ "uri": part.root.file.uri,
706
+ "name": part.root.file.name,
707
+ "mime_type": part.root.file.mime_type,
708
+ },
709
+ }
710
+ structured_results_of_current_iteration.append(structured_result)
711
+
712
+ previous_structured_results: List[List[Dict[str, Any]]] = (
713
+ agent_input.metadata.get(AGENT_METADATA_STRUCTURED_RESULTS_KEY, []) or []
714
+ )
715
+ previous_structured_results.append(structured_results_of_current_iteration)
716
+
717
+ return previous_structured_results
718
+
719
+ # ============================================================================
720
+ # Agent Card Management
721
+ # ============================================================================
722
+
723
+ @staticmethod
724
+ def _load_agent_card_from_file(agent_card_path: str) -> AgentCard:
725
+ """Load agent card from JSON file."""
726
+ try:
727
+ with open(os.path.abspath(agent_card_path), "r") as f:
728
+ card_data = json.load(f)
729
+ return AgentCard.model_validate(card_data)
730
+
731
+ except FileNotFoundError as e:
732
+ raise AgentInitializationException(
733
+ f"Agent card file not found: {agent_card_path}",
734
+ ) from e
735
+ except (IOError, PermissionError) as e:
736
+ raise AgentInitializationException(
737
+ f"Error reading agent card file {agent_card_path}: {e}",
738
+ ) from e
739
+ except json.JSONDecodeError as e:
740
+ raise AgentInitializationException(
741
+ f"Invalid JSON in agent card file {agent_card_path}: {e}",
742
+ ) from e
743
+ except ValidationError as e:
744
+ raise AgentInitializationException(
745
+ f"Failed to load agent card from {agent_card_path}: {e}",
746
+ ) from e
747
+
748
+ @staticmethod
749
+ async def _resolve_agent_card_with_retry(
750
+ agent_card_path: str, timeout: int, max_retries: int
751
+ ) -> AgentCard:
752
+ """Resolve the agent card from a given path or URL."""
753
+ # split agent_card_path into base URL and path
754
+ try:
755
+ url_parts = urlparse(agent_card_path)
756
+ base_url = f"{url_parts.scheme}://{url_parts.netloc}"
757
+ path = url_parts.path
758
+ except ValueError:
759
+ raise RasaException(f"Could not parse the URL: '{agent_card_path}'.")
760
+ structlogger.debug(
761
+ "a2a_agent.resolve_agent_card",
762
+ event_info="Resolving agent card from remote URL",
763
+ agent_card_path=agent_card_path,
764
+ base_url=base_url,
765
+ path=path,
766
+ timeout=timeout,
767
+ )
768
+
769
+ for attempt in range(max_retries):
770
+ if attempt > 0:
771
+ structlogger.debug(
772
+ "a2a_agent.resolve_agent_card.retrying",
773
+ agent_card_path=f"{base_url}/{path}",
774
+ attempt=attempt + 1,
775
+ num_retries=max_retries,
776
+ )
777
+
778
+ try:
779
+ agent_card = await A2ACardResolver(
780
+ httpx.AsyncClient(timeout=timeout),
781
+ base_url=base_url,
782
+ agent_card_path=path,
783
+ ).get_agent_card()
784
+
785
+ if agent_card:
786
+ return agent_card
787
+ except (A2AClientHTTPError, A2AClientJSONError) as exception:
788
+ structlogger.warning(
789
+ "a2a_agent.resolve_agent_card.error",
790
+ event_info="Error while resolving agent card",
791
+ agent_card_path=agent_card_path,
792
+ attempt=attempt + 1,
793
+ num_retries=max_retries,
794
+ error=str(exception),
795
+ )
796
+ if attempt < max_retries - 1:
797
+ # exponential backoff - wait longer with each retry
798
+ # 1 second, 2 seconds, 4 seconds, etc.
799
+ await asyncio.sleep(min(2**attempt, MAX_AGENT_RETRY_DELAY_SECONDS))
800
+
801
+ raise AgentInitializationException(
802
+ f"Failed to resolve agent card from {agent_card_path} after "
803
+ f"{max_retries} attempts.",
804
+ )
805
+
806
+ # ============================================================================
807
+ # Client Initialization & Health Checks
808
+ # ============================================================================
809
+
810
+ def _init_client(self) -> Client:
811
+ _agent_manager = AgentAuthManager.load_auth(self._auth_config)
812
+ auth_strategy = _agent_manager.get_auth() if _agent_manager else None
813
+ factory = ClientFactory(
814
+ config=ClientConfig(
815
+ httpx_client=httpx.AsyncClient(
816
+ timeout=self._timeout, auth=auth_strategy
817
+ ),
818
+ streaming=True,
819
+ supported_transports=[
820
+ TransportProtocol.jsonrpc,
821
+ TransportProtocol.http_json,
822
+ TransportProtocol.grpc,
823
+ ],
824
+ accepted_output_modes=self.__SUPPORTED_OUTPUT_MODES,
825
+ )
826
+ )
827
+ return factory.create(self.agent_card)
828
+
829
+ async def _perform_health_check(self) -> None:
830
+ if not self._client or not self.agent_card:
831
+ structlogger.error(
832
+ "a2a_agent.health_check.error",
833
+ event_info="A2A client is not initialized. Call connect() first.",
834
+ )
835
+ raise AgentInitializationException("Client not initialized")
836
+
837
+ try:
838
+ test_message = Message(
839
+ role=Role.user,
840
+ parts=[Part(root=TextPart(text="hello"))],
841
+ message_id=str(uuid.uuid4()),
842
+ )
843
+ # Use aclosing to ensure proper cleanup of the async generator
844
+ stream = self._client.send_message(test_message)
845
+ async with aclosing(stream) as stream: # type: ignore[type-var]
846
+ async for event in stream:
847
+ if (
848
+ isinstance(event, Message)
849
+ or isinstance(event, tuple)
850
+ and len(event) == 2
851
+ and isinstance(event[0], Task)
852
+ ):
853
+ # We got a valid response, health check succeeded
854
+ return
855
+
856
+ event_info = "Unexpected response type during health check"
857
+ structlogger.error(
858
+ "a2a_agent.health_check.unexpected_response",
859
+ event_info=event_info,
860
+ agent_name=self._name,
861
+ response=event,
862
+ url=str(self.agent_card.url),
863
+ )
864
+ raise AgentInitializationException(f"{event_info}: {event}")
865
+ # If the loop completes with no return, no events were received
866
+ event_info = (
867
+ f"Health check failed for A2A agent '{self._name}' "
868
+ f"at {self.agent_card.url}: no events received"
869
+ )
870
+ structlogger.error(
871
+ "a2a_agent.health_check.no_events",
872
+ event_info=event_info,
873
+ agent_name=self._name,
874
+ url=str(self.agent_card.url),
875
+ )
876
+ raise AgentInitializationException(event_info)
877
+ except Exception as exception:
878
+ event_info = (
879
+ f"Health check failed for A2A agent '{self._name}' at "
880
+ f"{self.agent_card.url}: {exception!s}"
881
+ )
882
+ structlogger.error(
883
+ "a2a_agent.health_check.failed",
884
+ event_info=event_info,
885
+ agent_name=self._name,
886
+ error=str(exception),
887
+ url=str(self.agent_card.url),
888
+ )
889
+ raise AgentInitializationException(event_info) from exception