rasa-pro 3.14.0.dev6__py3-none-any.whl → 3.14.0.dev8__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 (491) hide show
  1. rasa/agents/agent_manager.py +1 -1
  2. rasa/agents/constants.py +2 -2
  3. rasa/agents/exceptions.py +31 -1
  4. rasa/agents/protocol/a2a/a2a_agent.py +385 -227
  5. rasa/agents/protocol/mcp/mcp_base_agent.py +37 -19
  6. rasa/agents/protocol/mcp/mcp_open_agent.py +31 -8
  7. rasa/agents/protocol/mcp/mcp_task_agent.py +33 -10
  8. rasa/agents/schemas/agent_output.py +1 -1
  9. rasa/agents/utils.py +95 -1
  10. rasa/agents/validation.py +484 -0
  11. rasa/api.py +9 -6
  12. rasa/builder/README.md +120 -0
  13. rasa/builder/__init__.py +0 -0
  14. rasa/builder/auth.py +176 -0
  15. rasa/builder/config.py +92 -0
  16. rasa/builder/copilot/__init__.py +0 -0
  17. rasa/builder/copilot/constants.py +31 -0
  18. rasa/builder/copilot/copilot.py +450 -0
  19. rasa/builder/copilot/copilot_response_handler.py +522 -0
  20. rasa/builder/copilot/copilot_templated_message_provider.py +58 -0
  21. rasa/builder/copilot/exceptions.py +32 -0
  22. rasa/builder/copilot/models.py +500 -0
  23. rasa/builder/copilot/prompts/__init__.py +0 -0
  24. rasa/builder/copilot/prompts/copilot_system_prompt.jinja2 +766 -0
  25. rasa/builder/copilot/prompts/latest_user_message_context_prompt.jinja2 +61 -0
  26. rasa/builder/copilot/signing.py +305 -0
  27. rasa/builder/copilot/telemetry.py +210 -0
  28. rasa/builder/copilot/templated_messages/__init__.py +0 -0
  29. rasa/builder/copilot/templated_messages/copilot_internal_messages_templates.yml +16 -0
  30. rasa/builder/copilot/templated_messages/copilot_templated_responses.yml +38 -0
  31. rasa/builder/document_retrieval/__init__.py +0 -0
  32. rasa/builder/document_retrieval/constants.py +15 -0
  33. rasa/builder/document_retrieval/inkeep-rag-response-schema.json +64 -0
  34. rasa/builder/document_retrieval/inkeep_document_retrieval.py +238 -0
  35. rasa/builder/document_retrieval/models.py +62 -0
  36. rasa/builder/download.py +140 -0
  37. rasa/builder/exceptions.py +91 -0
  38. rasa/builder/guardrails/__init__.py +1 -0
  39. rasa/builder/guardrails/constants.py +9 -0
  40. rasa/builder/guardrails/exceptions.py +4 -0
  41. rasa/builder/guardrails/lakera.py +206 -0
  42. rasa/builder/guardrails/models.py +231 -0
  43. rasa/builder/guardrails/store.py +238 -0
  44. rasa/builder/guardrails/utils.py +328 -0
  45. rasa/builder/job_manager.py +87 -0
  46. rasa/builder/jobs.py +282 -0
  47. rasa/builder/llm_service.py +246 -0
  48. rasa/builder/logging_utils.py +265 -0
  49. rasa/builder/main.py +243 -0
  50. rasa/builder/models.py +216 -0
  51. rasa/builder/project_generator.py +458 -0
  52. rasa/builder/project_info.py +72 -0
  53. rasa/builder/scrape_rasa_docs.py +97 -0
  54. rasa/builder/service.py +1345 -0
  55. rasa/builder/shared/tracker_context.py +212 -0
  56. rasa/builder/skill_to_bot_prompt.jinja2 +164 -0
  57. rasa/builder/template_cache.py +244 -0
  58. rasa/builder/training_service.py +194 -0
  59. rasa/builder/validation_service.py +97 -0
  60. rasa/cli/arguments/train.py +2 -0
  61. rasa/cli/interactive.py +2 -0
  62. rasa/cli/project_templates/basic/README.md +23 -0
  63. rasa/cli/project_templates/basic/actions/__init__ +0 -0
  64. rasa/cli/project_templates/basic/actions/action_human_handoff.py +40 -0
  65. rasa/cli/project_templates/basic/actions/actions.md +10 -0
  66. rasa/cli/project_templates/basic/config.yml +29 -0
  67. rasa/cli/project_templates/basic/credentials.yml +33 -0
  68. rasa/cli/project_templates/basic/data/data.md +9 -0
  69. rasa/cli/project_templates/basic/data/general/feedback.yml +21 -0
  70. rasa/cli/project_templates/basic/data/general/goodbye.yml +6 -0
  71. rasa/cli/project_templates/basic/data/general/hello.yml +6 -0
  72. rasa/cli/project_templates/basic/data/general/help.yml +6 -0
  73. rasa/cli/project_templates/basic/data/general/human_handoff.yml +16 -0
  74. rasa/cli/project_templates/basic/data/general/show_faqs.yml +6 -0
  75. rasa/cli/project_templates/basic/data/system/patterns/pattern_cannot_handle.yml +7 -0
  76. rasa/cli/project_templates/basic/data/system/patterns/pattern_completed.yml +7 -0
  77. rasa/cli/project_templates/basic/data/system/patterns/pattern_correction.yml +7 -0
  78. rasa/cli/project_templates/basic/data/system/patterns/pattern_search.yml +8 -0
  79. rasa/cli/project_templates/basic/data/system/patterns/pattern_session_start.yml +8 -0
  80. rasa/cli/project_templates/basic/docs/docs.md +5 -0
  81. rasa/cli/project_templates/basic/docs/template.txt +28 -0
  82. rasa/cli/project_templates/basic/domain/domain.md +8 -0
  83. rasa/cli/project_templates/basic/domain/general/feedback.yml +25 -0
  84. rasa/cli/project_templates/basic/domain/general/goodbye.yml +9 -0
  85. rasa/cli/project_templates/basic/domain/general/hello.yml +7 -0
  86. rasa/cli/project_templates/basic/domain/general/help.yml +21 -0
  87. rasa/cli/project_templates/basic/domain/general/human_handoff.yml +32 -0
  88. rasa/cli/project_templates/basic/domain/general/show_faqs.yml +14 -0
  89. rasa/cli/project_templates/basic/domain/system/patterns/pattern_cannot_handle.yml +5 -0
  90. rasa/cli/project_templates/basic/domain/system/patterns/pattern_session_start.yml +19 -0
  91. rasa/cli/project_templates/basic/endpoints.yml +67 -0
  92. rasa/cli/project_templates/basic/prompts/rephraser_demo_personality_prompt.jinja2 +38 -0
  93. rasa/cli/project_templates/default/config.yml +4 -0
  94. rasa/cli/project_templates/default/endpoints.yml +4 -0
  95. rasa/cli/project_templates/finance/README.md +25 -0
  96. rasa/cli/project_templates/finance/actions/__init__.py +46 -0
  97. rasa/cli/project_templates/finance/actions/accounts/__init__.py +0 -0
  98. rasa/cli/project_templates/finance/actions/accounts/action_ask_account.py +47 -0
  99. rasa/cli/project_templates/finance/actions/accounts/action_check_balance.py +40 -0
  100. rasa/cli/project_templates/finance/actions/action_session_start.py +74 -0
  101. rasa/cli/project_templates/finance/actions/actions.md +15 -0
  102. rasa/cli/project_templates/finance/actions/cards/__init__.py +0 -0
  103. rasa/cli/project_templates/finance/actions/cards/action_ask_card.py +48 -0
  104. rasa/cli/project_templates/finance/actions/cards/action_check_card_existence.py +36 -0
  105. rasa/cli/project_templates/finance/actions/cards/action_update_card_status.py +54 -0
  106. rasa/cli/project_templates/finance/actions/database.py +277 -0
  107. rasa/cli/project_templates/finance/actions/transfers/__init__.py +0 -0
  108. rasa/cli/project_templates/finance/actions/transfers/action_add_payee.py +52 -0
  109. rasa/cli/project_templates/finance/actions/transfers/action_ask_account_from.py +51 -0
  110. rasa/cli/project_templates/finance/actions/transfers/action_check_payee_existence.py +40 -0
  111. rasa/cli/project_templates/finance/actions/transfers/action_check_sufficient_funds.py +40 -0
  112. rasa/cli/project_templates/finance/actions/transfers/action_list_payees.py +46 -0
  113. rasa/cli/project_templates/finance/actions/transfers/action_process_immediate_payment.py +18 -0
  114. rasa/cli/project_templates/finance/actions/transfers/action_remove_payee.py +49 -0
  115. rasa/cli/project_templates/finance/actions/transfers/action_schedule_payment.py +19 -0
  116. rasa/cli/project_templates/finance/actions/transfers/action_validate_payment_date.py +36 -0
  117. rasa/cli/project_templates/finance/config.yml +23 -0
  118. rasa/cli/project_templates/finance/credentials.yml +32 -0
  119. rasa/cli/project_templates/finance/csvs/accounts.csv +8 -0
  120. rasa/cli/project_templates/finance/csvs/advisors.csv +7 -0
  121. rasa/cli/project_templates/finance/csvs/appointments.csv +211 -0
  122. rasa/cli/project_templates/finance/csvs/branches.csv +10 -0
  123. rasa/cli/project_templates/finance/csvs/cards.csv +11 -0
  124. rasa/cli/project_templates/finance/csvs/payees.csv +11 -0
  125. rasa/cli/project_templates/finance/csvs/transactions.csv +71 -0
  126. rasa/cli/project_templates/finance/csvs/users.csv +4 -0
  127. rasa/cli/project_templates/finance/data/accounts/check_balance.yml +10 -0
  128. rasa/cli/project_templates/finance/data/cards/block_card.yml +66 -0
  129. rasa/cli/project_templates/finance/data/cards/select_card.yml +12 -0
  130. rasa/cli/project_templates/finance/data/data.md +11 -0
  131. rasa/cli/project_templates/finance/data/general/bot_identity.yml +6 -0
  132. rasa/cli/project_templates/finance/data/general/feedback.yml +20 -0
  133. rasa/cli/project_templates/finance/data/general/goodbye.yml +6 -0
  134. rasa/cli/project_templates/finance/data/general/hello.yml +7 -0
  135. rasa/cli/project_templates/finance/data/general/help.yml +9 -0
  136. rasa/cli/project_templates/finance/data/general/human_handoff.yml +16 -0
  137. rasa/cli/project_templates/finance/data/general/welcome.yml +9 -0
  138. rasa/cli/project_templates/finance/data/system/patterns/pattern_chitchat.yml +5 -0
  139. rasa/cli/project_templates/finance/data/system/patterns/pattern_completed.yml +7 -0
  140. rasa/cli/project_templates/finance/data/system/patterns/pattern_correction.yml +7 -0
  141. rasa/cli/project_templates/finance/data/system/patterns/pattern_search.yml +8 -0
  142. rasa/cli/project_templates/finance/data/system/patterns/pattern_session_start.yml +8 -0
  143. rasa/cli/project_templates/finance/data/system/source/accounts.json +51 -0
  144. rasa/cli/project_templates/finance/data/system/source/advisors.json +44 -0
  145. rasa/cli/project_templates/finance/data/system/source/appointments.json +1474 -0
  146. rasa/cli/project_templates/finance/data/system/source/branches.json +47 -0
  147. rasa/cli/project_templates/finance/data/system/source/cards.json +72 -0
  148. rasa/cli/project_templates/finance/data/system/source/payees.json +74 -0
  149. rasa/cli/project_templates/finance/data/system/source/transactions.json +492 -0
  150. rasa/cli/project_templates/finance/data/system/source/users.json +29 -0
  151. rasa/cli/project_templates/finance/data/transfers/add_payee.yml +29 -0
  152. rasa/cli/project_templates/finance/data/transfers/list_payees.yml +5 -0
  153. rasa/cli/project_templates/finance/data/transfers/remove_payee.yml +21 -0
  154. rasa/cli/project_templates/finance/data/transfers/transfer_money.yml +67 -0
  155. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/block_card/consequences_of_blocking_card.txt +8 -0
  156. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/block_card/reasons_to_block_card.txt +8 -0
  157. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/block_card/recovering_from_card_fraud.txt +8 -0
  158. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/block_card/tips_for_card_security.txt +8 -0
  159. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/block_card/what_to_do_if_card_is_lost.txt +8 -0
  160. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/check_balance/account_balance_security.txt +7 -0
  161. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/check_balance/common_balance_inquiries.txt +8 -0
  162. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/check_balance/methods_to_check_balance.txt +8 -0
  163. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/check_balance/understanding_balance_updates.txt +8 -0
  164. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/check_balance/what_to_do_if_balance_is_incorrect.txt +8 -0
  165. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/manage_payees/benefits_of_authorised_payees.txt +8 -0
  166. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/manage_payees/common_issues_with_payees.txt +8 -0
  167. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/manage_payees/general_payee_information.txt +8 -0
  168. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/manage_payees/payee_management_tips.txt +8 -0
  169. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/manage_payees/understanding_payee_types.txt +8 -0
  170. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/transfer_money/common_transfer_errors.txt +8 -0
  171. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/transfer_money/fees_for_transfers.txt +8 -0
  172. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/transfer_money/general_transfer_information.txt +8 -0
  173. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/transfer_money/security_tips_for_transfers.txt +8 -0
  174. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/transfer_money/transfer_processing_times.txt +8 -0
  175. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part1.txt +50 -0
  176. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part10.txt +50 -0
  177. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part11.txt +48 -0
  178. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part12.txt +50 -0
  179. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part13.txt +50 -0
  180. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part14.txt +47 -0
  181. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part15.txt +50 -0
  182. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part16.txt +50 -0
  183. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part17.txt +47 -0
  184. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part18.txt +50 -0
  185. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part19.txt +50 -0
  186. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part2.txt +50 -0
  187. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part20.txt +47 -0
  188. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part21.txt +50 -0
  189. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part22.txt +50 -0
  190. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part23.txt +47 -0
  191. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part24.txt +50 -0
  192. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part25.txt +50 -0
  193. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part26.txt +47 -0
  194. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part27.txt +50 -0
  195. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part28.txt +50 -0
  196. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part29.txt +47 -0
  197. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part3.txt +47 -0
  198. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part30.txt +50 -0
  199. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part31.txt +50 -0
  200. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part32.txt +47 -0
  201. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part33.txt +50 -0
  202. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part34.txt +50 -0
  203. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part35.txt +47 -0
  204. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part36.txt +50 -0
  205. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part37.txt +50 -0
  206. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part38.txt +47 -0
  207. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part39.txt +50 -0
  208. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part4.txt +50 -0
  209. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part40.txt +50 -0
  210. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part41.txt +47 -0
  211. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part42.txt +50 -0
  212. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part43.txt +50 -0
  213. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part44.txt +47 -0
  214. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part45.txt +50 -0
  215. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part46.txt +50 -0
  216. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part47.txt +47 -0
  217. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part48.txt +50 -0
  218. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part49.txt +50 -0
  219. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part5.txt +50 -0
  220. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part50.txt +47 -0
  221. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part51.txt +50 -0
  222. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part52.txt +50 -0
  223. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part53.txt +47 -0
  224. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part54.txt +50 -0
  225. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part55.txt +50 -0
  226. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part56.txt +47 -0
  227. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part57.txt +50 -0
  228. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part58.txt +50 -0
  229. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part59.txt +47 -0
  230. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part6.txt +47 -0
  231. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part60.txt +50 -0
  232. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part61.txt +50 -0
  233. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part7.txt +50 -0
  234. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part8.txt +50 -0
  235. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part9.txt +47 -0
  236. rasa/cli/project_templates/finance/domain/accounts/check_balance.yml +11 -0
  237. rasa/cli/project_templates/finance/domain/cards/block_card.yml +101 -0
  238. rasa/cli/project_templates/finance/domain/cards/select_card.yml +12 -0
  239. rasa/cli/project_templates/finance/domain/domain.md +10 -0
  240. rasa/cli/project_templates/finance/domain/general/agent_details.yml +12 -0
  241. rasa/cli/project_templates/finance/domain/general/bot_identity.yml +5 -0
  242. rasa/cli/project_templates/finance/domain/general/cannot_handle.yml +5 -0
  243. rasa/cli/project_templates/finance/domain/general/defaults.yml +24 -0
  244. rasa/cli/project_templates/finance/domain/general/feedback.yml +28 -0
  245. rasa/cli/project_templates/finance/domain/general/goodbye.yml +7 -0
  246. rasa/cli/project_templates/finance/domain/general/help.yml +5 -0
  247. rasa/cli/project_templates/finance/domain/general/human_handoff.yml +30 -0
  248. rasa/cli/project_templates/finance/domain/general/utils.yml +13 -0
  249. rasa/cli/project_templates/finance/domain/general/welcome.yml +8 -0
  250. rasa/cli/project_templates/finance/domain/transfers/add_payee.yml +47 -0
  251. rasa/cli/project_templates/finance/domain/transfers/list_payees.yml +4 -0
  252. rasa/cli/project_templates/finance/domain/transfers/remove_payee.yml +16 -0
  253. rasa/cli/project_templates/finance/domain/transfers/transfer_money.yml +79 -0
  254. rasa/cli/project_templates/finance/endpoints.yml +66 -0
  255. rasa/cli/project_templates/finance/prompts/rephraser_demo_personality_prompt.jinja2 +19 -0
  256. rasa/cli/project_templates/telco/README.md +25 -0
  257. rasa/cli/project_templates/telco/actions/__init__.py +0 -0
  258. rasa/cli/project_templates/telco/actions/actions.md +12 -0
  259. rasa/cli/project_templates/telco/actions/billing/__init__.py +0 -0
  260. rasa/cli/project_templates/telco/actions/billing/actions_billing.py +204 -0
  261. rasa/cli/project_templates/telco/actions/general/__init__.py +0 -0
  262. rasa/cli/project_templates/telco/actions/general/action_human_handoff.py +49 -0
  263. rasa/cli/project_templates/telco/actions/network/__init__.py +0 -0
  264. rasa/cli/project_templates/telco/actions/network/actions_get_data_from_db.py +48 -0
  265. rasa/cli/project_templates/telco/actions/network/actions_run_diagnostics.py +28 -0
  266. rasa/cli/project_templates/telco/actions/network/actions_session_start.py +18 -0
  267. rasa/cli/project_templates/telco/config.yml +29 -0
  268. rasa/cli/project_templates/telco/credentials.yml +33 -0
  269. rasa/cli/project_templates/telco/csvs/billing.csv +19 -0
  270. rasa/cli/project_templates/telco/csvs/customers.csv +5 -0
  271. rasa/cli/project_templates/telco/data/billing/flow_understand_bill.yml +45 -0
  272. rasa/cli/project_templates/telco/data/data.md +11 -0
  273. rasa/cli/project_templates/telco/data/general/bot_challenge.yml +6 -0
  274. rasa/cli/project_templates/telco/data/general/feedback.yml +20 -0
  275. rasa/cli/project_templates/telco/data/general/goodbye.yml +6 -0
  276. rasa/cli/project_templates/telco/data/general/hello.yml +6 -0
  277. rasa/cli/project_templates/telco/data/general/human_handoff.yml +16 -0
  278. rasa/cli/project_templates/telco/data/general/patterns.yml +30 -0
  279. rasa/cli/project_templates/telco/data/network/flow_reboot_router.yml +8 -0
  280. rasa/cli/project_templates/telco/data/network/flow_reset_router.yml +7 -0
  281. rasa/cli/project_templates/telco/data/network/flow_solve_internet_issue.yml +73 -0
  282. rasa/cli/project_templates/telco/docs/docs.md +5 -0
  283. rasa/cli/project_templates/telco/docs/network/reset_vs_rboot_router.txt +1 -0
  284. rasa/cli/project_templates/telco/docs/network/restart_router.txt +6 -0
  285. rasa/cli/project_templates/telco/docs/network/run_speed_test.txt +6 -0
  286. rasa/cli/project_templates/telco/domain/billing/understand_bill.yml +102 -0
  287. rasa/cli/project_templates/telco/domain/domain.md +14 -0
  288. rasa/cli/project_templates/telco/domain/general/bot_challenge.yml +4 -0
  289. rasa/cli/project_templates/telco/domain/general/feedback.yml +25 -0
  290. rasa/cli/project_templates/telco/domain/general/goodbye.yml +7 -0
  291. rasa/cli/project_templates/telco/domain/general/hello.yml +5 -0
  292. rasa/cli/project_templates/telco/domain/general/human_handoff.yml +26 -0
  293. rasa/cli/project_templates/telco/domain/general/patterns.yml +33 -0
  294. rasa/cli/project_templates/telco/domain/network/reboot_router.yml +21 -0
  295. rasa/cli/project_templates/telco/domain/network/reset_router.yml +12 -0
  296. rasa/cli/project_templates/telco/domain/network/run_speed_test.yml +25 -0
  297. rasa/cli/project_templates/telco/domain/network/solve_internet_issue.yml +75 -0
  298. rasa/cli/project_templates/telco/domain/shared.yml +129 -0
  299. rasa/cli/project_templates/telco/endpoints.yml +67 -0
  300. rasa/cli/project_templates/telco/prompts/rephraser_demo_personality_prompt.jinja2 +40 -0
  301. rasa/cli/project_templates/telco/tests/e2e_test_cases/billing/understand_bill.yml +67 -0
  302. rasa/cli/project_templates/telco/tests/e2e_test_cases/general/bot_challenge.yml +8 -0
  303. rasa/cli/project_templates/telco/tests/e2e_test_cases/general/feedback.yml +46 -0
  304. rasa/cli/project_templates/telco/tests/e2e_test_cases/general/goodbye.yml +9 -0
  305. rasa/cli/project_templates/telco/tests/e2e_test_cases/general/hello.yml +8 -0
  306. rasa/cli/project_templates/telco/tests/e2e_test_cases/general/human_handoff.yml +35 -0
  307. rasa/cli/project_templates/telco/tests/e2e_test_cases/general/patterns.yml +23 -0
  308. rasa/cli/project_templates/telco/tests/e2e_test_cases/network/solve_internet_issue.yml +57 -0
  309. rasa/cli/project_templates/tutorial/config.yml +2 -1
  310. rasa/cli/scaffold.py +46 -2
  311. rasa/cli/train.py +2 -0
  312. rasa/cli/utils.py +85 -1
  313. rasa/core/actions/action.py +2 -7
  314. rasa/core/available_agents.py +49 -26
  315. rasa/core/available_endpoints.py +17 -2
  316. rasa/core/channels/channel.py +4 -3
  317. rasa/core/channels/constants.py +3 -0
  318. rasa/core/channels/development_inspector.py +2 -22
  319. rasa/core/channels/inspector/README.md +26 -14
  320. rasa/core/channels/inspector/dist/assets/{arc-63212852.js → arc-edef10dd.js} +1 -1
  321. rasa/core/channels/inspector/dist/assets/{blockDiagram-38ab4fdb-eecf6b13.js → blockDiagram-38ab4fdb-49f6762b.js} +1 -1
  322. rasa/core/channels/inspector/dist/assets/{c4Diagram-3d4e48cf-8f798a9a.js → c4Diagram-3d4e48cf-313c08e6.js} +1 -1
  323. rasa/core/channels/inspector/dist/assets/channel-63aa27d1.js +1 -0
  324. rasa/core/channels/inspector/dist/assets/{classDiagram-70f12bd4-df71a04c.js → classDiagram-70f12bd4-35e41ce9.js} +1 -1
  325. rasa/core/channels/inspector/dist/assets/{classDiagram-v2-f2320105-9b275968.js → classDiagram-v2-f2320105-f346068d.js} +1 -1
  326. rasa/core/channels/inspector/dist/assets/clone-5566bae8.js +1 -0
  327. rasa/core/channels/inspector/dist/assets/{createText-2e5e7dd3-1c669cad.js → createText-2e5e7dd3-7a44bce8.js} +1 -1
  328. rasa/core/channels/inspector/dist/assets/{edges-e0da2a9e-b1553799.js → edges-e0da2a9e-d7cf78c7.js} +1 -1
  329. rasa/core/channels/inspector/dist/assets/{erDiagram-9861fffd-112388d6.js → erDiagram-9861fffd-9813e81c.js} +1 -1
  330. rasa/core/channels/inspector/dist/assets/{flowDb-956e92f1-fdebec47.js → flowDb-956e92f1-d8ba0870.js} +1 -1
  331. rasa/core/channels/inspector/dist/assets/{flowDiagram-66a62f08-6280ede1.js → flowDiagram-66a62f08-51f0db4d.js} +1 -1
  332. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-32936074.js +1 -0
  333. rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-4a651766-e1dc03e5.js → flowchart-elk-definition-4a651766-ff9ea384.js} +1 -1
  334. rasa/core/channels/inspector/dist/assets/{ganttDiagram-c361ad54-83f68c51.js → ganttDiagram-c361ad54-a8e13b6b.js} +1 -1
  335. rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-72cf32ee-22f8666f.js → gitGraphDiagram-72cf32ee-3b171c6d.js} +1 -1
  336. rasa/core/channels/inspector/dist/assets/{graph-ca9e6217.js → graph-790ef78b.js} +1 -1
  337. rasa/core/channels/inspector/dist/assets/{index-3862675e-c5ceb692.js → index-3862675e-ecdce073.js} +1 -1
  338. rasa/core/channels/inspector/dist/assets/index-d705da80.js +1352 -0
  339. rasa/core/channels/inspector/dist/assets/{infoDiagram-f8f76790-faa9999b.js → infoDiagram-f8f76790-f5a422fe.js} +1 -1
  340. rasa/core/channels/inspector/dist/assets/{journeyDiagram-49397b02-c4dda8d9.js → journeyDiagram-49397b02-3185b7ac.js} +1 -1
  341. rasa/core/channels/inspector/dist/assets/{layout-d4307784.js → layout-837fd3aa.js} +1 -1
  342. rasa/core/channels/inspector/dist/assets/{line-0567aaa7.js → line-7e05afcb.js} +1 -1
  343. rasa/core/channels/inspector/dist/assets/{linear-c11b95cf.js → linear-162eb295.js} +1 -1
  344. rasa/core/channels/inspector/dist/assets/{mindmap-definition-fc14e90a-0c7d3ca9.js → mindmap-definition-fc14e90a-f4978aee.js} +1 -1
  345. rasa/core/channels/inspector/dist/assets/{pieDiagram-8a3498a8-34b433fa.js → pieDiagram-8a3498a8-b25d0a52.js} +1 -1
  346. rasa/core/channels/inspector/dist/assets/{quadrantDiagram-120e2f19-4cab816e.js → quadrantDiagram-120e2f19-63db1afa.js} +1 -1
  347. rasa/core/channels/inspector/dist/assets/{requirementDiagram-deff3bca-8c22fa9e.js → requirementDiagram-deff3bca-1b486cc9.js} +1 -1
  348. rasa/core/channels/inspector/dist/assets/{sankeyDiagram-04a897e0-70ce9e8e.js → sankeyDiagram-04a897e0-7e795291.js} +1 -1
  349. rasa/core/channels/inspector/dist/assets/{sequenceDiagram-704730f1-fbcd7fc9.js → sequenceDiagram-704730f1-b8aba159.js} +1 -1
  350. rasa/core/channels/inspector/dist/assets/{stateDiagram-587899a1-45f05ea6.js → stateDiagram-587899a1-41529fd5.js} +1 -1
  351. rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-d93cdb3a-beab1ea6.js → stateDiagram-v2-d93cdb3a-b241043c.js} +1 -1
  352. rasa/core/channels/inspector/dist/assets/{styles-6aaf32cf-2f29dbd5.js → styles-6aaf32cf-b5b53234.js} +1 -1
  353. rasa/core/channels/inspector/dist/assets/{styles-9a916d00-951eac83.js → styles-9a916d00-13d138e5.js} +1 -1
  354. rasa/core/channels/inspector/dist/assets/{styles-c10674c1-897fbfdd.js → styles-c10674c1-94cbde3f.js} +1 -1
  355. rasa/core/channels/inspector/dist/assets/{svgDrawCommon-08f97a94-d667fac1.js → svgDrawCommon-08f97a94-453ae764.js} +1 -1
  356. rasa/core/channels/inspector/dist/assets/{timeline-definition-85554ec2-e3205144.js → timeline-definition-85554ec2-8dcb88a4.js} +1 -1
  357. rasa/core/channels/inspector/dist/assets/{xychartDiagram-e933f94c-4abeb0e2.js → xychartDiagram-e933f94c-376af5f0.js} +1 -1
  358. rasa/core/channels/inspector/dist/index.html +2 -2
  359. rasa/core/channels/inspector/index.html +1 -1
  360. rasa/core/channels/inspector/src/App.tsx +42 -44
  361. rasa/core/channels/inspector/src/components/Chat.tsx +2 -3
  362. rasa/core/channels/inspector/src/components/DialogueAgentStack.tsx +108 -0
  363. rasa/core/channels/inspector/src/components/{DialogueStack.tsx → DialogueHistoryStack.tsx} +8 -8
  364. rasa/core/channels/inspector/src/components/DialogueInformation.tsx +20 -3
  365. rasa/core/channels/inspector/src/components/LatencyDisplay.tsx +63 -35
  366. rasa/core/channels/inspector/src/helpers/audio/audiostream.ts +14 -0
  367. rasa/core/channels/inspector/src/helpers/formatters.test.ts +4 -0
  368. rasa/core/channels/inspector/src/helpers/utils.test.ts +127 -0
  369. rasa/core/channels/inspector/src/helpers/utils.ts +66 -1
  370. rasa/core/channels/inspector/src/types.ts +49 -7
  371. rasa/core/channels/socketio.py +212 -51
  372. rasa/core/channels/studio_chat.py +59 -57
  373. rasa/core/channels/voice_stream/asr/asr_event.py +1 -1
  374. rasa/core/channels/voice_stream/asr/azure.py +6 -3
  375. rasa/core/channels/voice_stream/asr/deepgram.py +1 -1
  376. rasa/core/channels/voice_stream/audiocodes.py +3 -0
  377. rasa/core/channels/voice_stream/browser_audio.py +53 -3
  378. rasa/core/channels/voice_stream/genesys.py +2 -1
  379. rasa/core/channels/voice_stream/jambonz.py +9 -1
  380. rasa/core/channels/voice_stream/twilio_media_streams.py +16 -0
  381. rasa/core/channels/voice_stream/voice_channel.py +66 -3
  382. rasa/core/constants.py +6 -0
  383. rasa/core/iam_credentials_providers/__init__.py +0 -0
  384. rasa/core/iam_credentials_providers/aws_iam_credentials_providers.py +66 -0
  385. rasa/core/iam_credentials_providers/credentials_provider_protocol.py +89 -0
  386. rasa/core/policies/enterprise_search_policy.py +4 -7
  387. rasa/core/policies/flows/flow_executor.py +66 -36
  388. rasa/core/policies/ted_policy.py +7 -5
  389. rasa/core/processor.py +32 -0
  390. rasa/core/redis_connection_factory.py +411 -0
  391. rasa/core/run.py +13 -3
  392. rasa/core/tracker_stores/redis_tracker_store.py +32 -14
  393. rasa/core/tracker_stores/sql_tracker_store.py +57 -1
  394. rasa/dialogue_understanding/commands/cancel_flow_command.py +2 -81
  395. rasa/dialogue_understanding/commands/start_flow_command.py +18 -113
  396. rasa/dialogue_understanding/commands/utils.py +118 -0
  397. rasa/dialogue_understanding/generator/flow_retrieval.py +10 -9
  398. rasa/dialogue_understanding/generator/prompt_templates/agent_command_prompt_v2_claude_3_5_sonnet_20240620_template.jinja2 +10 -5
  399. rasa/dialogue_understanding/generator/prompt_templates/agent_command_prompt_v2_gpt_4o_2024_11_20_template.jinja2 +10 -5
  400. rasa/dialogue_understanding/generator/prompt_templates/agent_command_prompt_v3_claude_3_5_sonnet_20240620_template.jinja2 +20 -12
  401. rasa/dialogue_understanding/generator/prompt_templates/agent_command_prompt_v3_gpt_4o_2024_11_20_template.jinja2 +19 -12
  402. rasa/dialogue_understanding/generator/single_step/single_step_based_llm_command_generator.py +6 -35
  403. rasa/dialogue_understanding/patterns/cancel.py +27 -6
  404. rasa/dialogue_understanding/patterns/clarify.py +3 -14
  405. rasa/dialogue_understanding/patterns/continue_interrupted.py +185 -114
  406. rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +18 -24
  407. rasa/dialogue_understanding/processor/command_processor.py +35 -0
  408. rasa/dialogue_understanding/stack/utils.py +43 -4
  409. rasa/dialogue_understanding/utils.py +24 -4
  410. rasa/engine/graph.py +5 -1
  411. rasa/engine/recipes/default_components.py +78 -10
  412. rasa/engine/recipes/default_recipe.py +41 -1
  413. rasa/engine/storage/local_model_storage.py +83 -3
  414. rasa/graph_components/validators/default_recipe_validator.py +153 -135
  415. rasa/model_manager/model_api.py +4 -5
  416. rasa/model_manager/runner_service.py +1 -1
  417. rasa/model_manager/socket_bridge.py +20 -15
  418. rasa/model_manager/trainer_service.py +12 -9
  419. rasa/model_manager/utils.py +1 -29
  420. rasa/model_manager/warm_rasa_process.py +1 -1
  421. rasa/model_training.py +22 -6
  422. rasa/nlu/classifiers/diet_classifier.py +22 -6
  423. rasa/nlu/classifiers/logistic_regression_classifier.py +18 -0
  424. rasa/nlu/extractors/extractor.py +1 -2
  425. rasa/shared/agents/auth/__init__.py +0 -0
  426. rasa/shared/agents/auth/agent_auth_factory.py +74 -0
  427. rasa/shared/agents/auth/agent_auth_manager.py +86 -0
  428. rasa/shared/agents/auth/auth_strategy/__init__.py +19 -0
  429. rasa/shared/agents/auth/auth_strategy/agent_auth_strategy.py +52 -0
  430. rasa/shared/agents/auth/auth_strategy/api_key_auth_strategy.py +42 -0
  431. rasa/shared/agents/auth/auth_strategy/bearer_token_auth_strategy.py +28 -0
  432. rasa/shared/agents/auth/auth_strategy/oauth2_auth_strategy.py +159 -0
  433. rasa/shared/agents/auth/constants.py +11 -0
  434. rasa/shared/agents/auth/types.py +11 -0
  435. rasa/shared/constants.py +3 -0
  436. rasa/shared/core/constants.py +6 -6
  437. rasa/shared/core/domain.py +58 -11
  438. rasa/shared/core/events.py +2 -0
  439. rasa/shared/core/flows/constants.py +5 -0
  440. rasa/shared/core/flows/flow_step.py +7 -1
  441. rasa/shared/core/flows/flows_list.py +6 -0
  442. rasa/shared/core/flows/steps/call.py +15 -12
  443. rasa/shared/core/flows/validation.py +238 -44
  444. rasa/shared/core/flows/yaml_flows_io.py +15 -6
  445. rasa/shared/core/slots.py +4 -0
  446. rasa/shared/exceptions.py +12 -0
  447. rasa/shared/importers/importer.py +6 -0
  448. rasa/shared/importers/utils.py +77 -1
  449. rasa/shared/nlu/training_data/schemas/responses.yml +3 -0
  450. rasa/shared/providers/_utils.py +60 -44
  451. rasa/shared/providers/embedding/default_litellm_embedding_client.py +2 -0
  452. rasa/shared/providers/llm/_base_litellm_client.py +2 -2
  453. rasa/shared/providers/llm/default_litellm_llm_client.py +2 -0
  454. rasa/shared/providers/llm/llm_response.py +4 -4
  455. rasa/shared/utils/common.py +24 -0
  456. rasa/shared/utils/health_check/health_check.py +7 -3
  457. rasa/shared/utils/llm.py +2 -1
  458. rasa/shared/utils/mcp/server_connection.py +108 -27
  459. rasa/shared/utils/mcp/utils.py +20 -0
  460. rasa/studio/upload.py +16 -47
  461. rasa/telemetry.py +97 -23
  462. rasa/tracing/config.py +38 -12
  463. rasa/tracing/instrumentation/attribute_extractors.py +5 -1
  464. rasa/tracing/instrumentation/instrumentation.py +85 -8
  465. rasa/utils/common.py +1 -1
  466. rasa/utils/io.py +27 -9
  467. rasa/utils/json_utils.py +6 -1
  468. rasa/utils/log_utils.py +5 -1
  469. rasa/utils/openapi.py +144 -0
  470. rasa/utils/tensorflow/__init__.py +29 -0
  471. rasa/utils/tensorflow/callback.py +1 -1
  472. rasa/utils/tensorflow/crf.py +1 -1
  473. rasa/utils/tensorflow/data_generator.py +21 -8
  474. rasa/utils/tensorflow/layers.py +11 -4
  475. rasa/utils/tensorflow/metrics.py +7 -3
  476. rasa/utils/tensorflow/models.py +41 -6
  477. rasa/utils/tensorflow/rasa_layers.py +6 -4
  478. rasa/utils/tensorflow/transformer.py +2 -3
  479. rasa/utils/train_utils.py +68 -38
  480. rasa/validator.py +18 -16
  481. rasa/version.py +1 -1
  482. rasa_pro-3.14.0.dev8.dist-info/METADATA +199 -0
  483. {rasa_pro-3.14.0.dev6.dist-info → rasa_pro-3.14.0.dev8.dist-info}/RECORD +486 -173
  484. rasa/core/channels/inspector/dist/assets/channel-0cd70adf.js +0 -1
  485. rasa/core/channels/inspector/dist/assets/clone-a0f9c4ed.js +0 -1
  486. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-de9cc4aa.js +0 -1
  487. rasa/core/channels/inspector/dist/assets/index-3e293924.js +0 -1353
  488. rasa_pro-3.14.0.dev6.dist-info/METADATA +0 -190
  489. {rasa_pro-3.14.0.dev6.dist-info → rasa_pro-3.14.0.dev8.dist-info}/NOTICE +0 -0
  490. {rasa_pro-3.14.0.dev6.dist-info → rasa_pro-3.14.0.dev8.dist-info}/WHEEL +0 -0
  491. {rasa_pro-3.14.0.dev6.dist-info → rasa_pro-3.14.0.dev8.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,1345 @@
1
+ # mypy: disable-error-code=misc
2
+ import asyncio
3
+ import sys
4
+ import time
5
+ from http import HTTPStatus
6
+ from typing import Any, Optional
7
+
8
+ import structlog
9
+ from sanic import Blueprint, HTTPResponse, response
10
+ from sanic.request import Request
11
+ from sanic_openapi import openapi
12
+
13
+ import rasa
14
+ from rasa.builder.auth import HEADER_USER_ID, is_auth_required_now, protected
15
+ from rasa.builder.config import (
16
+ COPILOT_ASSISTANT_TRACKER_MAX_TURNS,
17
+ COPILOT_HANDLER_ROLLING_BUFFER_SIZE,
18
+ GUARDRAILS_ENABLE_BLOCKING,
19
+ HELLO_RASA_PROJECT_ID,
20
+ )
21
+ from rasa.builder.copilot.constants import ROLE_USER, SIGNATURE_VERSION_V1
22
+ from rasa.builder.copilot.copilot_response_handler import CopilotResponseHandler
23
+ from rasa.builder.copilot.exceptions import (
24
+ CopilotStreamError,
25
+ InvalidCopilotChatHistorySignature,
26
+ MissingCopilotChatHistorySignature,
27
+ )
28
+ from rasa.builder.copilot.models import (
29
+ CopilotContext,
30
+ CopilotRequest,
31
+ GeneratedContent,
32
+ ReferenceEntry,
33
+ ReferenceSection,
34
+ ResponseCategory,
35
+ ResponseCompleteness,
36
+ )
37
+ from rasa.builder.copilot.signing import (
38
+ create_signature_envelope_for_handler,
39
+ create_signature_envelope_for_text,
40
+ verify_signature,
41
+ )
42
+ from rasa.builder.copilot.telemetry import CopilotTelemetry
43
+ from rasa.builder.download import create_bot_project_archive
44
+ from rasa.builder.guardrails.constants import (
45
+ BLOCK_SCOPE_PROJECT,
46
+ BLOCK_SCOPE_USER,
47
+ BlockScope,
48
+ )
49
+ from rasa.builder.guardrails.store import guardrails_store
50
+ from rasa.builder.guardrails.utils import (
51
+ check_assistant_chat_for_policy_violations,
52
+ check_copilot_chat_for_policy_violations,
53
+ )
54
+ from rasa.builder.job_manager import job_manager
55
+ from rasa.builder.jobs import (
56
+ run_prompt_to_bot_job,
57
+ run_replace_all_files_job,
58
+ run_template_to_bot_job,
59
+ )
60
+ from rasa.builder.llm_service import llm_service
61
+ from rasa.builder.logging_utils import (
62
+ capture_exception_with_context,
63
+ get_recent_logs,
64
+ )
65
+ from rasa.builder.models import (
66
+ AgentStatus,
67
+ ApiErrorResponse,
68
+ AssistantInfo,
69
+ BotData,
70
+ JobCreateResponse,
71
+ JobStatus,
72
+ JobStatusEvent,
73
+ PromptRequest,
74
+ ServerSentEvent,
75
+ TemplateRequest,
76
+ )
77
+ from rasa.builder.project_generator import ProjectGenerator
78
+ from rasa.builder.shared.tracker_context import TrackerContext
79
+ from rasa.core.agent import Agent
80
+ from rasa.core.channels.studio_chat import StudioChatInput
81
+ from rasa.core.exceptions import AgentNotReady
82
+ from rasa.shared.core.flows.flows_list import FlowsList
83
+ from rasa.shared.core.flows.yaml_flows_io import get_flows_as_json
84
+ from rasa.shared.core.trackers import DialogueStateTracker
85
+ from rasa.shared.importers.utils import DOMAIN_KEYS
86
+ from rasa.utils.json_utils import extract_values
87
+ from rasa.utils.openapi import model_to_schema
88
+
89
+ structlogger = structlog.get_logger()
90
+
91
+ # Create the blueprint
92
+ bp = Blueprint("bot_builder", url_prefix="/api")
93
+
94
+
95
+ def setup_project_generator(project_folder: str) -> ProjectGenerator:
96
+ """Initialize and return a ProjectGenerator instance."""
97
+ # Ensure the project folder is in sys.path
98
+ if project_folder not in sys.path:
99
+ sys.path.insert(0, project_folder)
100
+
101
+ structlogger.info(
102
+ "bot_builder_service.service_initialized", project_folder=project_folder
103
+ )
104
+
105
+ return ProjectGenerator(project_folder)
106
+
107
+
108
+ def get_project_generator(request: Request) -> ProjectGenerator:
109
+ """Get the project generator from app context."""
110
+ return request.app.ctx.project_generator
111
+
112
+
113
+ def get_input_channel(request: Request) -> StudioChatInput:
114
+ """Get the input channel from app context."""
115
+ return request.app.ctx.input_channel
116
+
117
+
118
+ async def extract_bot_data_from_agent(agent: Agent) -> BotData:
119
+ """Extract BotData from an Agent.
120
+
121
+ Args:
122
+ agent: The agent to extract data from
123
+
124
+ Returns:
125
+ BotData containing flows, domain, config, endpoints, and nlu data
126
+ """
127
+ domain = agent.domain.as_dict() if agent.domain else {}
128
+ flows = (
129
+ await agent.processor.get_flows()
130
+ if agent.processor
131
+ else FlowsList(underlying_flows=[])
132
+ )
133
+ return BotData(
134
+ flows=get_flows_as_json(flows),
135
+ domain=extract_values(domain, DOMAIN_KEYS),
136
+ )
137
+
138
+
139
+ async def get_agent_status(request: Request) -> AgentStatus:
140
+ """Get the status of the agent."""
141
+ if request.app.ctx.agent is None:
142
+ return AgentStatus.not_loaded
143
+ agent: Agent = request.app.ctx.agent
144
+ if agent.is_ready():
145
+ return AgentStatus.ready
146
+ return AgentStatus.not_ready
147
+
148
+
149
+ # Health check endpoint
150
+ @bp.route("/", methods=["GET"])
151
+ @openapi.summary("Health check endpoint")
152
+ @openapi.description(
153
+ "Returns the health status of the Bot Builder service including version "
154
+ "information and authentication requirements"
155
+ )
156
+ @openapi.tag("health")
157
+ @openapi.response(
158
+ 200,
159
+ {
160
+ "application/json": {
161
+ "status": str,
162
+ "service": str,
163
+ "rasa_version": str,
164
+ "auth_required": bool,
165
+ "agent_status": str,
166
+ }
167
+ },
168
+ )
169
+ @openapi.response(
170
+ 500,
171
+ {"application/json": model_to_schema(ApiErrorResponse)},
172
+ description="Internal server error",
173
+ )
174
+ async def health(request: Request) -> HTTPResponse:
175
+ """Health check endpoint."""
176
+ project_generator = get_project_generator(request)
177
+ return response.json(
178
+ {
179
+ "status": "ok",
180
+ "service": "bot-builder",
181
+ "rasa_version": rasa.__version__,
182
+ "agent_status": await get_agent_status(request),
183
+ "auth_required": is_auth_required_now(
184
+ project_info=project_generator.project_info
185
+ ),
186
+ }
187
+ )
188
+
189
+
190
+ @bp.route("/job-events/<job_id>", methods=["GET"])
191
+ @openapi.summary("Stream job progress events")
192
+ @openapi.description(
193
+ "Stream server-sent events (SSE) tracking real-time job progress.\n\n"
194
+ "**Connect with:** `Accept: text/event-stream`.\n\n"
195
+ "**SSE Event Example:**\n"
196
+ "```text\n"
197
+ "event: received\n"
198
+ 'data: {"status": "received"}\n'
199
+ "```\n\n"
200
+ )
201
+ @openapi.tag("job-events")
202
+ @openapi.parameter(
203
+ "job_id", str, location="path", description="The id of the job to stream events for"
204
+ )
205
+ @openapi.response(
206
+ 200,
207
+ {"text/event-stream": str},
208
+ description="Server-sent events stream. See documentation for event types.",
209
+ example=(
210
+ "event: received\n"
211
+ 'data: {"status": "received"}\n'
212
+ "\n"
213
+ "event: generating\n"
214
+ 'data: {"status": "generating"}\n'
215
+ ),
216
+ )
217
+ @openapi.response(
218
+ 404,
219
+ {"application/json": model_to_schema(ApiErrorResponse)},
220
+ description="Unknown job_id: No such job exists.",
221
+ )
222
+ @openapi.response(
223
+ 500,
224
+ {"application/json": model_to_schema(ApiErrorResponse)},
225
+ description="Internal server error",
226
+ )
227
+ @openapi.parameter(
228
+ HEADER_USER_ID,
229
+ description=(
230
+ "Optional user id to associate requests (e.g., for telemetry/guardrails)."
231
+ ),
232
+ _in="header",
233
+ required=False,
234
+ schema=str,
235
+ )
236
+ async def job_events(request: Request, job_id: str) -> HTTPResponse:
237
+ try:
238
+ job = job_manager.get_job(job_id)
239
+ if job is None:
240
+ return response.json(
241
+ ApiErrorResponse(
242
+ error="Job not found", details={"job_id": job_id}
243
+ ).model_dump(),
244
+ status=404,
245
+ )
246
+
247
+ stream = await request.respond(content_type="text/event-stream")
248
+
249
+ try:
250
+ async for evt in job.event_stream():
251
+ await stream.send(evt.format())
252
+ except Exception as exc:
253
+ # Handle exceptions within the SSE stream context
254
+ capture_exception_with_context(
255
+ exc,
256
+ "bot_builder_service.job_events.streaming_error",
257
+ extra={"job_id": job_id},
258
+ tags={"endpoint": "/api/job-events/<job_id>"},
259
+ )
260
+ # Send error event in SSE format instead of JSON response
261
+ error_event = JobStatusEvent.from_status(
262
+ status=JobStatus.error.value,
263
+ message=f"Failed to stream job events: {exc}",
264
+ ).format()
265
+ await stream.send(error_event)
266
+ finally:
267
+ await stream.eof()
268
+
269
+ return stream
270
+ except Exception as exc:
271
+ # This exception handler only applies before stream.respond() is called
272
+ capture_exception_with_context(
273
+ exc,
274
+ "bot_builder_service.job_events.unexpected_error",
275
+ extra={"job_id": job_id},
276
+ tags={"endpoint": "/api/job-events/<job_id>"},
277
+ )
278
+ return response.json(
279
+ ApiErrorResponse(
280
+ error="Failed to stream job events", details={"error": str(exc)}
281
+ ).model_dump(),
282
+ status=HTTPStatus.INTERNAL_SERVER_ERROR,
283
+ )
284
+
285
+
286
+ @bp.route("/prompt-to-bot", methods=["POST"])
287
+ @openapi.summary("Generate bot from natural language prompt")
288
+ @openapi.description(
289
+ "Creates a complete conversational AI bot from a natural language prompt. "
290
+ "Returns immediately with a job ID. Connect to `/job-events/<job_id>` to "
291
+ "receive server-sent events (SSE) for real-time progress tracking "
292
+ "throughout the bot creation process.\n\n"
293
+ "**SSE Event Flow** (via `/job-events/<job_id>`):\n"
294
+ "1. `received` - Request received by server\n"
295
+ "2. `generating` - Generating bot project files\n"
296
+ "3. `generation_success` - Bot generation completed successfully\n"
297
+ "4. `training` - Training the bot model\n"
298
+ "5. `train_success` - Model training completed\n"
299
+ "6. `done` - Bot creation completed\n\n"
300
+ "**Error Events:**\n"
301
+ "- `generation_error` - Failed to generate bot from prompt\n"
302
+ "- `train_error` - Bot generated but training failed\n"
303
+ "- `validation_error` - Generated bot configuration is invalid\n"
304
+ "- `error` - Unexpected error occurred\n\n"
305
+ "**Usage:**\n"
306
+ "1. Send POST request with Content-Type: application/json\n"
307
+ "2. The response will be a JSON object `{job_id: ...}`\n"
308
+ "3. Connect to `/job-events/<job_id>` for a server-sent event stream of progress."
309
+ )
310
+ @openapi.tag("bot-generation")
311
+ @openapi.body(
312
+ {"application/json": model_to_schema(PromptRequest)},
313
+ description="Prompt request with natural language description.",
314
+ required=True,
315
+ example={
316
+ "prompt": (
317
+ "Create a customer support bot that can help users with order inquiries, "
318
+ "product questions, and returns processing. The bot should be friendly "
319
+ "and able to escalate to human agents when needed."
320
+ )
321
+ },
322
+ )
323
+ @openapi.response(
324
+ 200,
325
+ {"application/json": model_to_schema(JobCreateResponse)},
326
+ description="Job created. Poll or subscribe to /job-events/<job_id> for progress.",
327
+ )
328
+ @openapi.response(
329
+ 400,
330
+ {"application/json": model_to_schema(ApiErrorResponse)},
331
+ description="Validation error in request payload",
332
+ )
333
+ @openapi.response(
334
+ 500,
335
+ {"application/json": model_to_schema(ApiErrorResponse)},
336
+ description="Internal server error",
337
+ )
338
+ @openapi.parameter(
339
+ HEADER_USER_ID,
340
+ description=(
341
+ "Optional user id to associate requests (e.g., for telemetry/guardrails)."
342
+ ),
343
+ _in="header",
344
+ required=False,
345
+ schema=str,
346
+ )
347
+ async def handle_prompt_to_bot(request: Request) -> HTTPResponse:
348
+ """Handle prompt-to-bot generation requests."""
349
+ try:
350
+ payload = PromptRequest(**request.json)
351
+ except Exception as exc:
352
+ return response.json(
353
+ ApiErrorResponse(
354
+ error="Invalid request", details={"error": str(exc)}
355
+ ).model_dump(),
356
+ status=400,
357
+ )
358
+
359
+ try:
360
+ # Allocate job and schedule background task
361
+ job = job_manager.create_job()
362
+ request.app.add_task(run_prompt_to_bot_job(request.app, job, payload.prompt))
363
+ return response.json(JobCreateResponse(job_id=job.id).model_dump(), status=200)
364
+ except Exception as exc:
365
+ capture_exception_with_context(
366
+ exc,
367
+ "bot_builder_service.prompt_to_bot.unexpected_error",
368
+ tags={"endpoint": "/api/prompt-to-bot"},
369
+ )
370
+ return response.json(
371
+ ApiErrorResponse(
372
+ error="Failed to create prompt-to-bot job",
373
+ details={"error": str(exc)},
374
+ ).model_dump(),
375
+ status=HTTPStatus.INTERNAL_SERVER_ERROR,
376
+ )
377
+
378
+
379
+ @bp.route("/template-to-bot", methods=["POST"])
380
+ @openapi.summary("Generate bot from predefined template")
381
+ @openapi.description(
382
+ "Creates a complete conversational AI bot from a predefined template. "
383
+ "Returns immediately with a job ID. Connect to `/job-events/<job_id>` to "
384
+ "receive server-sent events (SSE) for real-time progress tracking "
385
+ "throughout the bot creation process.\n\n"
386
+ "**SSE Event Flow** (via `/job-events/<job_id>`):\n"
387
+ "1. `received` - Request received by server\n"
388
+ "2. `generating` - Initializing bot from template\n"
389
+ "3. `generation_success` - Template initialization completed successfully\n"
390
+ "4. `training` - Training the bot model\n"
391
+ "5. `train_success` - Model training completed\n"
392
+ "6. `done` - Bot creation completed\n\n"
393
+ "**Error Events:**\n"
394
+ "- `generation_error` - Failed to initialize bot from template\n"
395
+ "- `train_error` - Template loaded but training failed\n"
396
+ "- `validation_error` - Template configuration is invalid\n"
397
+ "- `error` - Unexpected error occurred\n\n"
398
+ "**Usage:**\n"
399
+ "1. Send POST request with Content-Type: application/json\n"
400
+ "2. The response will be a JSON object `{job_id: ...}`\n"
401
+ "3. Connect to `/job-events/<job_id>` for a server-sent event stream of progress."
402
+ )
403
+ @openapi.tag("bot-generation")
404
+ @openapi.body(
405
+ {"application/json": model_to_schema(TemplateRequest)},
406
+ description="Template request with template name.",
407
+ required=True,
408
+ example={"template_name": "telco"},
409
+ )
410
+ @openapi.response(
411
+ 200,
412
+ {"application/json": model_to_schema(JobCreateResponse)},
413
+ description="Job created. Poll or subscribe to /job-events/<job_id> for progress.",
414
+ )
415
+ @openapi.response(
416
+ 400,
417
+ {"application/json": model_to_schema(ApiErrorResponse)},
418
+ description="Validation error in request payload or invalid template name",
419
+ )
420
+ @openapi.response(
421
+ 500,
422
+ {"application/json": model_to_schema(ApiErrorResponse)},
423
+ description="Internal server error",
424
+ )
425
+ @openapi.parameter(
426
+ HEADER_USER_ID,
427
+ description=(
428
+ "Optional user id to associate requests (e.g., for telemetry/guardrails)."
429
+ ),
430
+ _in="header",
431
+ required=False,
432
+ schema=str,
433
+ )
434
+ async def handle_template_to_bot(request: Request) -> HTTPResponse:
435
+ """Create a new template-to-bot job and return job_id immediately."""
436
+ try:
437
+ template_data = TemplateRequest(**request.json)
438
+ except Exception as exc:
439
+ return response.json(
440
+ ApiErrorResponse(
441
+ error="Invalid request", details={"error": str(exc)}
442
+ ).model_dump(),
443
+ status=400,
444
+ )
445
+
446
+ try:
447
+ # allocate job and schedule background task
448
+ job = job_manager.create_job()
449
+ request.app.add_task(
450
+ run_template_to_bot_job(request.app, job, template_data.template_name)
451
+ )
452
+ return response.json(JobCreateResponse(job_id=job.id).model_dump(), status=200)
453
+ except Exception as exc:
454
+ capture_exception_with_context(
455
+ exc,
456
+ "bot_builder_service.template_to_bot.unexpected_error",
457
+ tags={"endpoint": "/api/template-to-bot"},
458
+ )
459
+ return response.json(
460
+ ApiErrorResponse(
461
+ error="Failed to create template-to-bot job",
462
+ details={"error": str(exc)},
463
+ ).model_dump(),
464
+ status=HTTPStatus.INTERNAL_SERVER_ERROR,
465
+ )
466
+
467
+
468
+ @bp.route("/files", methods=["GET"])
469
+ @openapi.summary("Get bot files")
470
+ @openapi.description(
471
+ "Retrieves the current bot configuration files including domain.yml, "
472
+ "config.yml, flows.yml, NLU data, and other project files as a "
473
+ "dictionary mapping file names to their string contents"
474
+ )
475
+ @openapi.tag("bot-files")
476
+ @openapi.response(
477
+ 200,
478
+ {"application/json": {str: Optional[str]}},
479
+ description="Bot files retrieved successfully",
480
+ )
481
+ @openapi.response(
482
+ 500,
483
+ {"application/json": model_to_schema(ApiErrorResponse)},
484
+ description="Internal server error",
485
+ )
486
+ @openapi.parameter(
487
+ HEADER_USER_ID,
488
+ description=(
489
+ "Optional user id to associate requests (e.g., for telemetry/guardrails)."
490
+ ),
491
+ _in="header",
492
+ required=False,
493
+ schema=str,
494
+ )
495
+ async def get_bot_files(request: Request) -> HTTPResponse:
496
+ """Get current bot files."""
497
+ try:
498
+ project_generator = get_project_generator(request)
499
+ bot_files = project_generator.get_bot_files()
500
+ return response.json(bot_files)
501
+ except Exception as exc:
502
+ capture_exception_with_context(
503
+ exc,
504
+ "bot_builder_service.get_bot_files.unexpected_error",
505
+ tags={"endpoint": "/api/files", "method": "GET"},
506
+ )
507
+ return response.json(
508
+ ApiErrorResponse(
509
+ error="Failed to retrieve bot files", details={"error": str(exc)}
510
+ ).model_dump(),
511
+ status=HTTPStatus.INTERNAL_SERVER_ERROR,
512
+ )
513
+
514
+
515
+ @bp.route("/files", methods=["POST"])
516
+ @openapi.summary("Replace all bot files")
517
+ @openapi.description(
518
+ "Replaces all bot configuration files with the provided files, deletes any "
519
+ "files not included in the request (excluding .rasa/ and models/ directories), "
520
+ "and retrains the model. Returns immediately with a job ID. Connect to "
521
+ "`/job-events/<job_id>` for real-time SSE progress tracking."
522
+ "\n\n"
523
+ "**File Management:**\n"
524
+ "- All files in the request are written to the project folder\n"
525
+ "- Files not included in the request are deleted from the project\n"
526
+ "- Files/folders starting with `.rasa/` or `models/` are excluded from deletion\n\n"
527
+ "**SSE Event Flow:** (available via /job-events/<job_id>)\n"
528
+ "1. `received` - Request received by server\n"
529
+ "2. `validating` - Validating bot configuration files\n"
530
+ "3. `validation_success` - File validation completed successfully\n"
531
+ "4. `training` - Training the bot model with updated files\n"
532
+ "5. `train_success` - Model training completed\n"
533
+ "6. `done` - Bot files replacement completed\n\n"
534
+ "**Error Events (can occur at any time):**\n"
535
+ "- `validation_error` - Bot configuration files are invalid\n"
536
+ "- `train_error` - Files updated but training failed\n"
537
+ "- `error` - Unexpected error occurred\n\n"
538
+ "**Usage:**\n"
539
+ "1. Send POST request with Content-Type: application/json\n"
540
+ "2. The response will be a JSON object `{job_id: ...}`\n"
541
+ "3. Connect to `/job-events/<job_id>` for a server-sent event stream of progress."
542
+ )
543
+ @openapi.tag("bot-files")
544
+ @openapi.body(
545
+ {"application/json": {str: Optional[str]}},
546
+ description=(
547
+ "A dictionary mapping file names to their complete content. "
548
+ "All files in the project will be replaced with these files. "
549
+ "Files not included in the request will be deleted from the project "
550
+ "(except for .rasa/ and models/ directories). "
551
+ "The file name should be the relative path from the project root."
552
+ ),
553
+ required=True,
554
+ example={
555
+ "domain.yml": (
556
+ "version: '3.1'\n"
557
+ "intents:\n - greet\n - goodbye\n"
558
+ "responses:\n utter_greet:\n - text: 'Hello!'\n"
559
+ " utter_goodbye:\n - text: 'Goodbye!'"
560
+ ),
561
+ "config.yml": (
562
+ "version: '3.1'\n"
563
+ "pipeline:\n - name: WhitespaceTokenizer\n"
564
+ " - name: RegexFeaturizer\n - name: LexicalSyntacticFeaturizer\n"
565
+ " - name: CountVectorsFeaturizer\n"
566
+ "policies:\n - name: MemoizationPolicy\n - name: RulePolicy"
567
+ ),
568
+ "data/nlu.yml": (
569
+ "version: '3.1'\n"
570
+ "nlu:\n- intent: greet\n examples: |\n - hello\n - hi"
571
+ ),
572
+ },
573
+ )
574
+ @openapi.response(
575
+ 200,
576
+ {"application/json": model_to_schema(JobCreateResponse)},
577
+ description=(
578
+ "Job created. Poll or subscribe to /job-events/<job_id> "
579
+ "for progress and SSE updates on file replacement and training."
580
+ ),
581
+ )
582
+ @openapi.response(
583
+ 400,
584
+ {"application/json": model_to_schema(ApiErrorResponse)},
585
+ description="Validation error in bot files",
586
+ )
587
+ @openapi.response(
588
+ 500,
589
+ {"application/json": model_to_schema(ApiErrorResponse)},
590
+ description="Internal server error",
591
+ )
592
+ @openapi.response(
593
+ 401,
594
+ {"application/json": model_to_schema(ApiErrorResponse)},
595
+ description=(
596
+ "Authentication failed - Authorization header missing or invalid. "
597
+ "Auth may be conditionally required depending on server "
598
+ "configuration."
599
+ ),
600
+ )
601
+ @openapi.parameter(
602
+ "Authorization",
603
+ description=(
604
+ "Bearer token for authentication. Required after auth start window "
605
+ "or if configured."
606
+ ),
607
+ _in="header",
608
+ required=False,
609
+ schema=str,
610
+ )
611
+ @openapi.parameter(
612
+ HEADER_USER_ID,
613
+ description=(
614
+ "Optional user id to associate requests (e.g., for telemetry/guardrails)."
615
+ ),
616
+ _in="header",
617
+ required=False,
618
+ schema=str,
619
+ )
620
+ @protected()
621
+ async def replace_all_bot_files(request: Request) -> HTTPResponse:
622
+ """Replace all bot files with server-sent events for progress tracking."""
623
+ try:
624
+ bot_files = request.json
625
+ except Exception as exc:
626
+ return response.json(
627
+ ApiErrorResponse(
628
+ error="Invalid request", details={"error": str(exc)}
629
+ ).model_dump(),
630
+ status=400,
631
+ )
632
+
633
+ try:
634
+ job = job_manager.create_job()
635
+ request.app.add_task(run_replace_all_files_job(request.app, job, bot_files))
636
+ return response.json(JobCreateResponse(job_id=job.id).model_dump(), status=200)
637
+ except Exception as exc:
638
+ capture_exception_with_context(
639
+ exc,
640
+ "bot_builder_service.replace_all_bot_files.unexpected_error",
641
+ tags={"endpoint": "/api/files", "method": "POST"},
642
+ )
643
+ return response.json(
644
+ ApiErrorResponse(
645
+ error="Failed to replace bot files", details={"error": str(exc)}
646
+ ).model_dump(),
647
+ status=HTTPStatus.INTERNAL_SERVER_ERROR,
648
+ )
649
+
650
+
651
+ @bp.route("/data", methods=["GET"])
652
+ @openapi.summary("Get bot data")
653
+ @openapi.description(
654
+ "Retrieves the current bot data in CALM import format with flows, domain, "
655
+ "config, endpoints, and NLU data"
656
+ )
657
+ @openapi.tag("bot-info")
658
+ @openapi.response(
659
+ 200,
660
+ {"application/json": model_to_schema(BotData)},
661
+ description="Bot data retrieved successfully",
662
+ )
663
+ @openapi.response(
664
+ 409,
665
+ {"application/json": model_to_schema(ApiErrorResponse)},
666
+ description="Agent not ready",
667
+ )
668
+ @openapi.response(
669
+ 500,
670
+ {"application/json": model_to_schema(ApiErrorResponse)},
671
+ description="Internal server error",
672
+ )
673
+ @openapi.parameter(
674
+ HEADER_USER_ID,
675
+ description=(
676
+ "Optional user id to associate requests (e.g., for telemetry/guardrails)."
677
+ ),
678
+ _in="header",
679
+ required=False,
680
+ schema=str,
681
+ )
682
+ async def get_bot_data(request: Request) -> HTTPResponse:
683
+ """Get current bot data in CALM import format."""
684
+ try:
685
+ agent: Optional[Agent] = request.app.ctx.agent
686
+ if not agent:
687
+ raise AgentNotReady(
688
+ "Can't retrieve the data without an agent being loaded."
689
+ )
690
+
691
+ bot_data = await extract_bot_data_from_agent(agent)
692
+
693
+ return response.json(bot_data.model_dump())
694
+ except AgentNotReady as e:
695
+ return response.json(
696
+ ApiErrorResponse(
697
+ error="Agent not ready",
698
+ details={"error": str(e)},
699
+ ).model_dump(),
700
+ status=HTTPStatus.CONFLICT,
701
+ )
702
+ except Exception as exc:
703
+ capture_exception_with_context(
704
+ exc,
705
+ "bot_builder_service.get_bot_data.unexpected_error",
706
+ tags={"endpoint": "/api/data"},
707
+ )
708
+ return response.json(
709
+ ApiErrorResponse(
710
+ error="Failed to retrieve bot data",
711
+ details={"error": str(exc)},
712
+ ).model_dump(),
713
+ status=HTTPStatus.INTERNAL_SERVER_ERROR,
714
+ )
715
+
716
+
717
+ @bp.route("/assistant", methods=["GET"])
718
+ @openapi.summary("Get assistant info")
719
+ @openapi.description(
720
+ "Returns basic information about the loaded assistant, including the assistant id "
721
+ "as configured in the model's metadata (from config.yml)."
722
+ )
723
+ @openapi.tag("bot-info")
724
+ @openapi.response(
725
+ 200,
726
+ {"application/json": model_to_schema(AssistantInfo)},
727
+ description="Assistant info retrieved successfully",
728
+ )
729
+ @openapi.response(
730
+ 409,
731
+ {"application/json": model_to_schema(ApiErrorResponse)},
732
+ description="Agent not ready",
733
+ )
734
+ @openapi.response(
735
+ 500,
736
+ {"application/json": model_to_schema(ApiErrorResponse)},
737
+ description="Internal server error",
738
+ )
739
+ @openapi.parameter(
740
+ HEADER_USER_ID,
741
+ description=(
742
+ "Optional user id to associate requests (e.g., for telemetry/guardrails)."
743
+ ),
744
+ _in="header",
745
+ required=False,
746
+ schema=str,
747
+ )
748
+ async def get_bot_info(request: Request) -> HTTPResponse:
749
+ """Return assistant info including assistant id from model metadata."""
750
+ try:
751
+ agent: Optional[Agent] = request.app.ctx.agent
752
+ if not agent:
753
+ raise AgentNotReady("Can't retrieve bot info without a loaded agent.")
754
+
755
+ assistant_id: Optional[str] = (
756
+ agent.processor.model_metadata.assistant_id
757
+ if agent.processor
758
+ and agent.processor.model_metadata
759
+ and hasattr(agent.processor.model_metadata, "assistant_id")
760
+ else None
761
+ )
762
+
763
+ return response.json(AssistantInfo(assistant_id=assistant_id).model_dump())
764
+ except AgentNotReady as e:
765
+ return response.json(
766
+ ApiErrorResponse(
767
+ error="Agent not ready",
768
+ details={"error": str(e)},
769
+ ).model_dump(),
770
+ status=HTTPStatus.CONFLICT,
771
+ )
772
+ except Exception as exc:
773
+ capture_exception_with_context(
774
+ exc,
775
+ "bot_builder_service.get_bot_info.unexpected_error",
776
+ tags={"endpoint": "/api/assistant"},
777
+ )
778
+ return response.json(
779
+ ApiErrorResponse(
780
+ error="Failed to retrieve bot info",
781
+ details={"error": str(exc)},
782
+ ).model_dump(),
783
+ status=HTTPStatus.INTERNAL_SERVER_ERROR,
784
+ )
785
+
786
+
787
+ @bp.route("/download", methods=["GET"])
788
+ @openapi.summary("Download bot project as tar.gz")
789
+ @openapi.description(
790
+ "Downloads the current bot project files as a compressed tar.gz archive. "
791
+ "Includes all configuration files and a .env file with RASA_PRO_LICENSE. "
792
+ "Requires valid JWT token in Authorization header."
793
+ )
794
+ @openapi.tag("bot-files")
795
+ @openapi.parameter(
796
+ "Authorization",
797
+ description=("Bearer token for authentication. Always required for this endpoint."),
798
+ _in="header",
799
+ required=True,
800
+ schema=str,
801
+ )
802
+ @openapi.parameter(
803
+ HEADER_USER_ID,
804
+ description=(
805
+ "Optional user id to associate requests (e.g., for telemetry/guardrails)."
806
+ ),
807
+ _in="header",
808
+ required=False,
809
+ schema=str,
810
+ )
811
+ @openapi.parameter(
812
+ "project_name",
813
+ description="Name of the project for the archive filename and pyproject.toml",
814
+ _in="query",
815
+ required=False,
816
+ schema=str,
817
+ )
818
+ @openapi.response(
819
+ 200,
820
+ {"application/gzip": bytes},
821
+ description="Bot project downloaded successfully as tar.gz",
822
+ )
823
+ @openapi.response(
824
+ 401,
825
+ {"application/json": model_to_schema(ApiErrorResponse)},
826
+ description=(
827
+ "Authentication failed - Authorization header missing or invalid. "
828
+ "Authentication is always required for this endpoint."
829
+ ),
830
+ )
831
+ @openapi.response(
832
+ 500,
833
+ {"application/json": model_to_schema(ApiErrorResponse)},
834
+ description="Internal server error",
835
+ )
836
+ @protected(always_required=True)
837
+ async def download_bot_project(request: Request) -> HTTPResponse:
838
+ """Download bot project as tar.gz archive."""
839
+ try:
840
+ # Token verification is enforced by the
841
+ # protected(always_required=True) decorator.
842
+
843
+ # Get bot files
844
+ project_generator = get_project_generator(request)
845
+ bot_files = project_generator.get_bot_files()
846
+
847
+ # Get project name from query parameters, default to "bot-project"
848
+ project_name = request.args.get("project_name", "bot-project")
849
+
850
+ # Create tar.gz archive
851
+ tar_data = create_bot_project_archive(bot_files, project_name)
852
+
853
+ structlogger.info(
854
+ "bot_builder_service.download_bot_project.success",
855
+ user_sub=(getattr(request.ctx, "auth_payload", None) or {}).get("sub"),
856
+ files_count=len(bot_files),
857
+ archive_size=len(tar_data),
858
+ payload=getattr(request.ctx, "auth_payload", None),
859
+ project_name=project_name,
860
+ )
861
+
862
+ return response.raw(
863
+ tar_data,
864
+ content_type="application/gzip",
865
+ headers={
866
+ "Content-Disposition": f"attachment; filename={project_name}.tar.gz"
867
+ },
868
+ )
869
+
870
+ except Exception as exc:
871
+ capture_exception_with_context(
872
+ exc,
873
+ "bot_builder_service.download_bot_project.unexpected_error",
874
+ tags={"endpoint": "/api/download"},
875
+ )
876
+ return response.json(
877
+ ApiErrorResponse(
878
+ error="Failed to create bot project archive",
879
+ details={"error": str(exc)},
880
+ ).model_dump(),
881
+ status=500,
882
+ )
883
+
884
+
885
+ @bp.route("/copilot", methods=["POST"])
886
+ @openapi.summary("AI copilot for bot building")
887
+ @openapi.description(
888
+ "Provides LLM-powered copilot assistance with streaming markdown responses. "
889
+ "Returns server-sent events (SSE) for real-time streaming of copilot responses.\n\n"
890
+ "The event's `event` field is the type of event. For this endpoint it will always "
891
+ "be: `copilot_response`.\n\n"
892
+ )
893
+ @openapi.tag("copilot")
894
+ @openapi.body(
895
+ {"application/json": model_to_schema(CopilotRequest)},
896
+ description=(
897
+ "Copilot request containing: "
898
+ "1. conversation history between user and copilot, "
899
+ "2. session ID for tracking conversation context with the bot being built."
900
+ ),
901
+ required=True,
902
+ )
903
+ @openapi.response(
904
+ 200,
905
+ {"text/event-stream": model_to_schema(ServerSentEvent)},
906
+ description=(
907
+ "Server-sent events stream with copilot responses and references. "
908
+ "The event's `event` field is the type "
909
+ "of event. For this endpoint it will always be: `copilot_response`. "
910
+ "The event's data field is a JSON dump. The following describes the event data "
911
+ "field:\n"
912
+ "- `response_category` can be one of the following:\n"
913
+ " - `copilot` - Stream token generated by the copilot.\n"
914
+ " - `out_of_scope_detection` - Response coming from the the out of scope detection.\n" # noqa: E501
915
+ " - `roleplay_detection` - Response coming from the the roleplay detection.\n"
916
+ " - `reference` - Reference section.\n"
917
+ " - `training_error_log_analysis` - Used to flag the responses that are training error log analysis.\n" # noqa: E501
918
+ " - `e2e_testing_error_log_analysis` - Used to flag the responses that are e2e testing error log analysis.\n" # noqa: E501
919
+ " - `guardrail_policy_violation` - Used to flag the responses that are flagged as a violation of the guardrail policy.\n\n" # noqa: E501
920
+ "- `completeness`: Whether this is a streaming token or complete response.\n\n"
921
+ "- `content`: The actual response content (for streaming tokens).\n\n"
922
+ "- `references`: (Only for `reference` response category) List of reference entries. Each reference entry is a dictionary with the following keys:\n\n" # noqa: E501
923
+ " - `index`: Reference index as an integer number.\n"
924
+ " - `title`: Reference title as a string.\n"
925
+ " - `url`: Reference URL as a string.\n\n"
926
+ ),
927
+ examples={
928
+ "Streaming token from copilot": {
929
+ "summary": "Streaming token from copilot",
930
+ "value": GeneratedContent(
931
+ content="<token generated by the copilot>",
932
+ response_category=ResponseCategory.COPILOT,
933
+ response_completeness=ResponseCompleteness.TOKEN,
934
+ )
935
+ .to_sse_event()
936
+ .model_dump(),
937
+ },
938
+ "Complete response for the following response categories: out_of_scope_detection / roleplay_detection / training_error_log_analysis / e2e_testing_error_log_analysis / guardrail_policy_violation": { # noqa: E501
939
+ "summary": "Complete response from copilot",
940
+ "value": GeneratedContent(
941
+ content="<response from the out of scope detection>",
942
+ response_category=ResponseCategory.OUT_OF_SCOPE_DETECTION,
943
+ response_completeness=ResponseCompleteness.COMPLETE,
944
+ )
945
+ .to_sse_event()
946
+ .model_dump(),
947
+ },
948
+ "Response with the references": {
949
+ "summary": "Reference section with all references",
950
+ "value": ReferenceSection(
951
+ references=[
952
+ ReferenceEntry(
953
+ index=1,
954
+ title="Title of the reference",
955
+ url="https://rasa.com/docs/...",
956
+ ),
957
+ ReferenceEntry(
958
+ index=2,
959
+ title="Title of another reference",
960
+ url="https://rasa.com/docs/...",
961
+ ),
962
+ ],
963
+ response_category=ResponseCategory.REFERENCE,
964
+ response_completeness=ResponseCompleteness.COMPLETE,
965
+ )
966
+ .to_sse_event()
967
+ .model_dump(),
968
+ },
969
+ },
970
+ )
971
+ @openapi.response(
972
+ 400,
973
+ {"application/json": model_to_schema(ApiErrorResponse)},
974
+ description="Validation error in request",
975
+ )
976
+ @openapi.response(
977
+ 502,
978
+ {"application/json": model_to_schema(ApiErrorResponse)},
979
+ description="LLM generation failed.",
980
+ )
981
+ @openapi.response(
982
+ 500,
983
+ {"application/json": model_to_schema(ApiErrorResponse)},
984
+ description="Internal server error.",
985
+ )
986
+ @openapi.response(
987
+ 401,
988
+ {"application/json": model_to_schema(ApiErrorResponse)},
989
+ description=(
990
+ "Authentication failed - Authorization header missing or invalid. "
991
+ "Auth may be conditionally required depending on server "
992
+ "configuration."
993
+ ),
994
+ )
995
+ @openapi.parameter(
996
+ "Authorization",
997
+ description=(
998
+ "Bearer token for authentication. Required after auth start window "
999
+ "or if configured."
1000
+ ),
1001
+ _in="header",
1002
+ required=False,
1003
+ schema=str,
1004
+ )
1005
+ @openapi.parameter(
1006
+ HEADER_USER_ID,
1007
+ description=("Required user id used for telemetry and guardrails (X-User-Id)."),
1008
+ _in="header",
1009
+ required=True,
1010
+ schema=str,
1011
+ )
1012
+ @protected()
1013
+ async def copilot(request: Request) -> None:
1014
+ """Handle copilot requests with streaming markdown responses."""
1015
+ sse = await request.respond(content_type="text/event-stream")
1016
+ project_generator = get_project_generator(request)
1017
+
1018
+ try:
1019
+ # 1. Validate and unpack input
1020
+ req = CopilotRequest(**request.json)
1021
+
1022
+ # Require user identifier via header and fail fast if missing
1023
+ user_id = (request.headers.get(HEADER_USER_ID) or "").strip()
1024
+ if not user_id:
1025
+ structlogger.error(
1026
+ "bot_builder_service.copilot.missing_or_empty_user_id_header",
1027
+ event_info=f"Missing or empty required header: {HEADER_USER_ID}",
1028
+ )
1029
+ await sse.send(
1030
+ ServerSentEvent(
1031
+ event="error",
1032
+ data={
1033
+ "error": f"Missing or empty required header: {HEADER_USER_ID}"
1034
+ },
1035
+ ).format()
1036
+ )
1037
+ return
1038
+
1039
+ telemetry = CopilotTelemetry(project_id=HELLO_RASA_PROJECT_ID, user_id=user_id)
1040
+ structlogger.debug("builder.copilot.telemetry.request.init")
1041
+
1042
+ if req.last_message and req.last_message.role == ROLE_USER:
1043
+ structlogger.debug("builder.copilot.telemetry.request.user_turn")
1044
+ # Offload telemetry logging to a background task
1045
+ request.app.add_task(
1046
+ asyncio.to_thread(
1047
+ telemetry.log_user_turn, req.last_message.get_text_content()
1048
+ )
1049
+ )
1050
+
1051
+ # 2. Check if we need to block the request due to too many guardrails violations
1052
+ if (scope := await _get_copilot_block_scope(user_id)) is not None:
1053
+ message = CopilotResponseHandler.respond_to_guardrail_blocked(scope)
1054
+ await sse.send(message.to_sse_event().format())
1055
+ return
1056
+
1057
+ # 3 Verify the request signature
1058
+ try:
1059
+ await verify_signature(req)
1060
+ except InvalidCopilotChatHistorySignature:
1061
+ version = getattr(req, "signature_version", None) or SIGNATURE_VERSION_V1
1062
+ await sse.send(
1063
+ ServerSentEvent(
1064
+ event="copilot_response",
1065
+ data={"error": "invalid_history_signature", "version": version},
1066
+ ).format()
1067
+ )
1068
+ return
1069
+ except MissingCopilotChatHistorySignature:
1070
+ version = getattr(req, "signature_version", None) or SIGNATURE_VERSION_V1
1071
+ await sse.send(
1072
+ ServerSentEvent(
1073
+ event="copilot_response",
1074
+ data={"error": "missing_history_signature", "version": version},
1075
+ ).format()
1076
+ )
1077
+ return
1078
+
1079
+ # 4. Get the necessary context for the copilot
1080
+ tracker = await current_tracker_from_input_channel(request.app, req.session_id)
1081
+ tracker_context = TrackerContext.from_tracker(
1082
+ tracker, max_turns=COPILOT_ASSISTANT_TRACKER_MAX_TURNS
1083
+ )
1084
+ if tracker_context is not None:
1085
+ tracker_context = await check_assistant_chat_for_policy_violations(
1086
+ tracker_context=tracker_context,
1087
+ hello_rasa_user_id=user_id,
1088
+ hello_rasa_project_id=HELLO_RASA_PROJECT_ID,
1089
+ )
1090
+
1091
+ # Copilot doesn't need to know about the docs and any file that is not a core
1092
+ # assistant file
1093
+ relevant_assistant_files = project_generator.get_bot_files(
1094
+ exclude_docs_directory=True,
1095
+ allowed_file_extensions=["yaml", "yml", "py", "jinja", "jinja2"],
1096
+ )
1097
+ context = CopilotContext(
1098
+ tracker_context=tracker_context,
1099
+ assistant_logs=get_recent_logs(),
1100
+ assistant_files=relevant_assistant_files,
1101
+ copilot_chat_history=req.copilot_chat_history,
1102
+ )
1103
+
1104
+ # 5. Run guardrail policy checks. If any policy violations are detected,
1105
+ # send a response and end the stream.
1106
+ guardrail_response: Optional[
1107
+ GeneratedContent
1108
+ ] = await check_copilot_chat_for_policy_violations(
1109
+ context=context,
1110
+ hello_rasa_user_id=user_id,
1111
+ hello_rasa_project_id=HELLO_RASA_PROJECT_ID,
1112
+ )
1113
+ if guardrail_response is not None:
1114
+ blocked_or_violation_message = (
1115
+ await _handle_guardrail_violation_and_maybe_block(
1116
+ sse=sse,
1117
+ user_id=user_id,
1118
+ violation_response=guardrail_response,
1119
+ )
1120
+ )
1121
+
1122
+ # Send signature for the guardrail response
1123
+ if envelope := await create_signature_envelope_for_text(
1124
+ req=req,
1125
+ text=blocked_or_violation_message.content,
1126
+ category=blocked_or_violation_message.response_category,
1127
+ ):
1128
+ await sse.send(envelope.format())
1129
+
1130
+ return
1131
+
1132
+ # 6. Get the original response stream from copilot and handle it with the
1133
+ # copilot response handler
1134
+ start_timestamp = time.perf_counter()
1135
+ copilot_client = llm_service.instantiate_copilot()
1136
+ (original_stream, generation_context) = await copilot_client.generate_response(
1137
+ context
1138
+ )
1139
+
1140
+ copilot_response_handler = llm_service.instantiate_handler(
1141
+ COPILOT_HANDLER_ROLLING_BUFFER_SIZE
1142
+ )
1143
+ intercepted_stream = copilot_response_handler.handle_response(original_stream)
1144
+
1145
+ # 7. Stream the intercepted response
1146
+ async for token in intercepted_stream:
1147
+ await sse.send(token.to_sse_event().format())
1148
+
1149
+ # 8. Offload telemetry logging to a background task
1150
+ request.app.add_task(
1151
+ asyncio.to_thread(
1152
+ telemetry.log_copilot_from_handler,
1153
+ handler=copilot_response_handler,
1154
+ used_documents=generation_context.relevant_documents,
1155
+ latency_ms=int((time.perf_counter() - start_timestamp) * 1000),
1156
+ system_message=generation_context.system_message,
1157
+ chat_history=generation_context.chat_history,
1158
+ **copilot_client.usage_statistics.model_dump(),
1159
+ )
1160
+ )
1161
+
1162
+ # 9. Once the stream is over, extract and send references
1163
+ # if any documents were used
1164
+ if generation_context.relevant_documents:
1165
+ reference_section = copilot_response_handler.extract_references(
1166
+ generation_context.relevant_documents
1167
+ )
1168
+ await sse.send(reference_section.to_sse_event().format())
1169
+
1170
+ # 10. Sign the next history
1171
+ if envelope := await create_signature_envelope_for_handler(
1172
+ req, copilot_response_handler
1173
+ ):
1174
+ await sse.send(envelope.format())
1175
+
1176
+ except CopilotStreamError as e:
1177
+ capture_exception_with_context(
1178
+ e,
1179
+ "bot_builder_service.copilot.generation_error",
1180
+ extra={"session_id": req.session_id},
1181
+ tags={"endpoint": "/api/copilot"},
1182
+ )
1183
+ await sse.send(
1184
+ ServerSentEvent(
1185
+ event="error",
1186
+ data={"error": str(e)},
1187
+ ).format()
1188
+ )
1189
+
1190
+ except Exception as exc:
1191
+ capture_exception_with_context(
1192
+ exc,
1193
+ "bot_builder_service.copilot.unexpected_error",
1194
+ extra={"session_id": req.session_id if "req" in locals() else None},
1195
+ tags={"endpoint": "/api/copilot"},
1196
+ )
1197
+ await sse.send(
1198
+ ServerSentEvent(
1199
+ event="error",
1200
+ data={"error": str(exc)},
1201
+ ).format()
1202
+ )
1203
+
1204
+ finally:
1205
+ await sse.eof()
1206
+
1207
+
1208
+ @bp.route("/copilot/internal_message_templates/<template_name>", methods=["GET"])
1209
+ @openapi.summary("Get templated response for copilot internal message")
1210
+ @openapi.description(
1211
+ "Returns the templated response text for a given template name from the "
1212
+ "copilot internal message formatter. This endpoint provides access to the "
1213
+ "predefined templates used for formatting internal system messages."
1214
+ )
1215
+ @openapi.tag("copilot")
1216
+ @openapi.parameter(
1217
+ "template_name",
1218
+ str,
1219
+ location="path",
1220
+ description=(
1221
+ "The template name to get the template for."
1222
+ "(e.g., 'training_error_log_analysis', 'e2e_testing_error_log_analysis')",
1223
+ ),
1224
+ )
1225
+ @openapi.response(
1226
+ 200,
1227
+ {"application/json": {"template": str, "template_name": str}},
1228
+ description="Successfully retrieved the template for the given template name",
1229
+ example={
1230
+ "template": "The assistant training failed. Your task is ...",
1231
+ "template_name": "training_error_log_analysis",
1232
+ },
1233
+ )
1234
+ @openapi.response(
1235
+ 404,
1236
+ {"application/json": model_to_schema(ApiErrorResponse)},
1237
+ description="Template not found for the given template name",
1238
+ )
1239
+ @openapi.parameter(
1240
+ HEADER_USER_ID,
1241
+ description=(
1242
+ "Optional user id to associate requests (e.g., for telemetry/guardrails)."
1243
+ ),
1244
+ _in="header",
1245
+ required=False,
1246
+ schema=str,
1247
+ )
1248
+ async def get_copilot_internal_message_template(
1249
+ request: Request, template_name: str
1250
+ ) -> HTTPResponse:
1251
+ """Get templated response for copilot internal message formatter."""
1252
+ try:
1253
+ # Try to get the template for the given template name
1254
+ template = llm_service.copilot_internal_message_templates.get(template_name)
1255
+ structlogger.info(
1256
+ "bot_builder_service.get_copilot_internal_message_template.template_found",
1257
+ template_name=template_name,
1258
+ template=template,
1259
+ )
1260
+
1261
+ if template is None:
1262
+ structlogger.warning(
1263
+ "bot_builder_service.get_copilot_internal_message_template.not_found",
1264
+ template_name=template_name,
1265
+ )
1266
+ return response.json(
1267
+ ApiErrorResponse(
1268
+ error="Template not found", details={"template_name": template_name}
1269
+ ).model_dump(),
1270
+ status=404,
1271
+ )
1272
+
1273
+ return response.json({"template": template, "template_name": template_name})
1274
+
1275
+ except Exception as e:
1276
+ structlogger.error(
1277
+ "bot_builder_service.get_copilot_internal_message_template.error",
1278
+ error=str(e),
1279
+ template_name=template_name,
1280
+ )
1281
+ return response.json(
1282
+ ApiErrorResponse(
1283
+ error="Internal server error", details={"template_name": template_name}
1284
+ ).model_dump(),
1285
+ status=500,
1286
+ )
1287
+
1288
+
1289
+ async def current_tracker_from_input_channel(
1290
+ app: Any, session_id: str
1291
+ ) -> Optional[DialogueStateTracker]:
1292
+ """Generate chat bot context from current conversation."""
1293
+ if app.ctx.agent and session_id:
1294
+ return await app.ctx.agent.tracker_store.retrieve(session_id)
1295
+ else:
1296
+ return None
1297
+
1298
+
1299
+ async def _get_copilot_block_scope(user_id: str) -> Optional[BlockScope]:
1300
+ """Return the guardrail block scope for Copilot if blocked.
1301
+
1302
+ Args:
1303
+ user_id: User identifier.
1304
+
1305
+ Returns:
1306
+ 'user' or 'project' when blocked, otherwise None.
1307
+ """
1308
+ if not GUARDRAILS_ENABLE_BLOCKING:
1309
+ return None
1310
+
1311
+ return await guardrails_store.check_block_scope(user_id)
1312
+
1313
+
1314
+ async def _handle_guardrail_violation_and_maybe_block(
1315
+ sse: Any,
1316
+ violation_response: GeneratedContent,
1317
+ user_id: str,
1318
+ ) -> GeneratedContent:
1319
+ """Record a violation, apply block if threshold crossed, and respond.
1320
+
1321
+ Args:
1322
+ sse: Active SSE stream.
1323
+ violation_response: The default violation warning response.
1324
+ user_id: User identifier.
1325
+
1326
+ Returns:
1327
+ The GeneratedContent message that was sent to the client.
1328
+ """
1329
+ if not GUARDRAILS_ENABLE_BLOCKING:
1330
+ await sse.send(violation_response.to_sse_event().format())
1331
+ return violation_response
1332
+
1333
+ result = await guardrails_store.record_violation(user_id)
1334
+
1335
+ if result.user_blocked_now:
1336
+ message = CopilotResponseHandler.respond_to_guardrail_blocked(BLOCK_SCOPE_USER)
1337
+ elif result.project_blocked_now:
1338
+ message = CopilotResponseHandler.respond_to_guardrail_blocked(
1339
+ BLOCK_SCOPE_PROJECT
1340
+ )
1341
+ else:
1342
+ message = violation_response
1343
+
1344
+ await sse.send(message.to_sse_event().format())
1345
+ return message