rasa-pro 3.14.0.dev3__py3-none-any.whl → 3.14.0.dev5__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 (437) hide show
  1. rasa/api.py +0 -5
  2. rasa/builder/README.md +120 -0
  3. rasa/builder/auth.py +176 -0
  4. rasa/builder/config.py +115 -0
  5. rasa/builder/copilot/constants.py +25 -0
  6. rasa/builder/copilot/copilot.py +372 -0
  7. rasa/builder/copilot/copilot_response_handler.py +487 -0
  8. rasa/builder/copilot/copilot_templated_message_provider.py +58 -0
  9. rasa/builder/copilot/exceptions.py +20 -0
  10. rasa/builder/copilot/models.py +431 -0
  11. rasa/builder/copilot/prompts/copilot_system_prompt.jinja2 +726 -0
  12. rasa/builder/copilot/telemetry.py +195 -0
  13. rasa/builder/copilot/templated_messages/copilot_internal_messages_templates.yml +16 -0
  14. rasa/builder/copilot/templated_messages/copilot_templated_responses.yml +26 -0
  15. rasa/builder/document_retrieval/constants.py +15 -0
  16. rasa/builder/document_retrieval/inkeep-rag-response-schema.json +64 -0
  17. rasa/builder/document_retrieval/inkeep_document_retrieval.py +238 -0
  18. rasa/builder/document_retrieval/models.py +62 -0
  19. rasa/builder/download.py +140 -0
  20. rasa/builder/exceptions.py +55 -0
  21. rasa/builder/guardrails/__init__.py +1 -0
  22. rasa/builder/guardrails/constants.py +3 -0
  23. rasa/builder/guardrails/exceptions.py +4 -0
  24. rasa/builder/guardrails/lakera.py +206 -0
  25. rasa/builder/guardrails/models.py +199 -0
  26. rasa/builder/guardrails/utils.py +305 -0
  27. rasa/builder/job_manager.py +87 -0
  28. rasa/builder/jobs.py +234 -0
  29. rasa/builder/llm_service.py +246 -0
  30. rasa/builder/logging_utils.py +209 -0
  31. rasa/builder/main.py +174 -0
  32. rasa/builder/models.py +197 -0
  33. rasa/builder/project_generator.py +450 -0
  34. rasa/builder/project_info.py +72 -0
  35. rasa/builder/scrape_rasa_docs.py +97 -0
  36. rasa/builder/service.py +1142 -0
  37. rasa/builder/shared/tracker_context.py +212 -0
  38. rasa/builder/skill_to_bot_prompt.jinja2 +164 -0
  39. rasa/builder/training_service.py +132 -0
  40. rasa/builder/validation_service.py +93 -0
  41. rasa/cli/arguments/default_arguments.py +0 -12
  42. rasa/cli/arguments/run.py +0 -2
  43. rasa/cli/dialogue_understanding_test.py +0 -4
  44. rasa/cli/e2e_test.py +0 -4
  45. rasa/cli/inspect.py +0 -3
  46. rasa/cli/llm_fine_tuning.py +0 -5
  47. rasa/cli/project_templates/basic/actions/action_api.py +15 -0
  48. rasa/cli/project_templates/basic/actions/action_human_handoff.py +44 -0
  49. rasa/cli/project_templates/basic/config.yml +23 -0
  50. rasa/cli/project_templates/basic/credentials.yml +34 -0
  51. rasa/cli/project_templates/basic/data/general/feedback.yml +20 -0
  52. rasa/cli/project_templates/basic/data/general/goodbye.yml +6 -0
  53. rasa/cli/project_templates/basic/data/general/hello.yml +7 -0
  54. rasa/cli/project_templates/basic/data/general/help.yml +6 -0
  55. rasa/cli/project_templates/basic/data/general/human_handoff.yml +16 -0
  56. rasa/cli/project_templates/basic/data/general/welcome.yml +9 -0
  57. rasa/cli/project_templates/basic/data/system/pattern_completed.yml +7 -0
  58. rasa/cli/project_templates/basic/data/system/pattern_correction.yml +7 -0
  59. rasa/cli/project_templates/basic/data/system/pattern_search.yml +8 -0
  60. rasa/cli/project_templates/basic/data/system/pattern_session_start.yml +8 -0
  61. rasa/cli/project_templates/basic/docs/rasa_assistant_qa.txt +65 -0
  62. rasa/cli/project_templates/basic/docs/template.txt +7 -0
  63. rasa/cli/project_templates/basic/domain/general/assistant_details.yml +12 -0
  64. rasa/cli/project_templates/basic/domain/general/bot_identity.yml +5 -0
  65. rasa/cli/project_templates/basic/domain/general/cannot_handle.yml +5 -0
  66. rasa/cli/project_templates/basic/domain/general/feedback.yml +28 -0
  67. rasa/cli/project_templates/basic/domain/general/goodbye.yml +7 -0
  68. rasa/cli/project_templates/basic/domain/general/help.yml +5 -0
  69. rasa/cli/project_templates/basic/domain/general/human_handoff_domain.yml +35 -0
  70. rasa/cli/project_templates/basic/domain/general/utils.yml +13 -0
  71. rasa/cli/project_templates/basic/domain/general/welcome.yml +7 -0
  72. rasa/cli/project_templates/basic/endpoints.yml +73 -0
  73. rasa/cli/project_templates/basic/prompts/rephraser_demo_personality_prompt.jinja2 +19 -0
  74. rasa/cli/project_templates/finance/actions/__init__.py +46 -0
  75. rasa/cli/project_templates/finance/actions/accounts/action_ask_account.py +47 -0
  76. rasa/cli/project_templates/finance/actions/accounts/action_check_balance.py +40 -0
  77. rasa/cli/project_templates/finance/actions/action_session_start.py +74 -0
  78. rasa/cli/project_templates/finance/actions/cards/action_ask_card.py +48 -0
  79. rasa/cli/project_templates/finance/actions/cards/action_check_card_existence.py +36 -0
  80. rasa/cli/project_templates/finance/actions/cards/action_update_card_status.py +54 -0
  81. rasa/cli/project_templates/finance/actions/database.py +277 -0
  82. rasa/cli/project_templates/finance/actions/transfers/__init__.py +0 -0
  83. rasa/cli/project_templates/finance/actions/transfers/action_add_payee.py +52 -0
  84. rasa/cli/project_templates/finance/actions/transfers/action_ask_account_from.py +51 -0
  85. rasa/cli/project_templates/finance/actions/transfers/action_check_payee_existence.py +40 -0
  86. rasa/cli/project_templates/finance/actions/transfers/action_check_sufficient_funds.py +40 -0
  87. rasa/cli/project_templates/finance/actions/transfers/action_list_payees.py +46 -0
  88. rasa/cli/project_templates/finance/actions/transfers/action_process_immediate_payment.py +18 -0
  89. rasa/cli/project_templates/finance/actions/transfers/action_remove_payee.py +49 -0
  90. rasa/cli/project_templates/finance/actions/transfers/action_schedule_payment.py +19 -0
  91. rasa/cli/project_templates/finance/actions/transfers/action_validate_payment_date.py +36 -0
  92. rasa/cli/project_templates/finance/config.yml +21 -0
  93. rasa/cli/project_templates/finance/credentials.yml +32 -0
  94. rasa/cli/project_templates/finance/csvs/accounts.csv +8 -0
  95. rasa/cli/project_templates/finance/csvs/advisors.csv +7 -0
  96. rasa/cli/project_templates/finance/csvs/appointments.csv +211 -0
  97. rasa/cli/project_templates/finance/csvs/branches.csv +10 -0
  98. rasa/cli/project_templates/finance/csvs/cards.csv +11 -0
  99. rasa/cli/project_templates/finance/csvs/payees.csv +11 -0
  100. rasa/cli/project_templates/finance/csvs/transactions.csv +71 -0
  101. rasa/cli/project_templates/finance/csvs/users.csv +4 -0
  102. rasa/cli/project_templates/finance/data/accounts/check_balance.yml +10 -0
  103. rasa/cli/project_templates/finance/data/cards/block_card.yml +66 -0
  104. rasa/cli/project_templates/finance/data/cards/select_card.yml +12 -0
  105. rasa/cli/project_templates/finance/data/general/bot_identity.yml +6 -0
  106. rasa/cli/project_templates/finance/data/general/feedback.yml +20 -0
  107. rasa/cli/project_templates/finance/data/general/goodbye.yml +6 -0
  108. rasa/cli/project_templates/finance/data/general/hello.yml +7 -0
  109. rasa/cli/project_templates/finance/data/general/help.yml +9 -0
  110. rasa/cli/project_templates/finance/data/general/human_handoff.yml +16 -0
  111. rasa/cli/project_templates/finance/data/general/welcome.yml +9 -0
  112. rasa/cli/project_templates/finance/data/system/patterns/pattern_chitchat.yml +5 -0
  113. rasa/cli/project_templates/finance/data/system/patterns/pattern_completed.yml +7 -0
  114. rasa/cli/project_templates/finance/data/system/patterns/pattern_correction.yml +7 -0
  115. rasa/cli/project_templates/finance/data/system/patterns/pattern_search.yml +8 -0
  116. rasa/cli/project_templates/finance/data/system/patterns/pattern_session_start.yml +8 -0
  117. rasa/cli/project_templates/finance/data/system/source/accounts.json +51 -0
  118. rasa/cli/project_templates/finance/data/system/source/advisors.json +44 -0
  119. rasa/cli/project_templates/finance/data/system/source/appointments.json +1474 -0
  120. rasa/cli/project_templates/finance/data/system/source/branches.json +47 -0
  121. rasa/cli/project_templates/finance/data/system/source/cards.json +72 -0
  122. rasa/cli/project_templates/finance/data/system/source/payees.json +74 -0
  123. rasa/cli/project_templates/finance/data/system/source/transactions.json +492 -0
  124. rasa/cli/project_templates/finance/data/system/source/users.json +29 -0
  125. rasa/cli/project_templates/finance/data/transfers/add_payee.yml +29 -0
  126. rasa/cli/project_templates/finance/data/transfers/list_payees.yml +5 -0
  127. rasa/cli/project_templates/finance/data/transfers/remove_payee.yml +21 -0
  128. rasa/cli/project_templates/finance/data/transfers/transfer_money.yml +67 -0
  129. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/block_card/consequences_of_blocking_card.txt +8 -0
  130. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/block_card/reasons_to_block_card.txt +8 -0
  131. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/block_card/recovering_from_card_fraud.txt +8 -0
  132. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/block_card/tips_for_card_security.txt +8 -0
  133. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/block_card/what_to_do_if_card_is_lost.txt +8 -0
  134. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/check_balance/account_balance_security.txt +7 -0
  135. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/check_balance/common_balance_inquiries.txt +8 -0
  136. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/check_balance/methods_to_check_balance.txt +8 -0
  137. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/check_balance/understanding_balance_updates.txt +8 -0
  138. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/check_balance/what_to_do_if_balance_is_incorrect.txt +8 -0
  139. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/manage_payees/benefits_of_authorised_payees.txt +8 -0
  140. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/manage_payees/common_issues_with_payees.txt +8 -0
  141. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/manage_payees/general_payee_information.txt +8 -0
  142. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/manage_payees/payee_management_tips.txt +8 -0
  143. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/manage_payees/understanding_payee_types.txt +8 -0
  144. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/transfer_money/common_transfer_errors.txt +8 -0
  145. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/transfer_money/fees_for_transfers.txt +8 -0
  146. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/transfer_money/general_transfer_information.txt +8 -0
  147. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/transfer_money/security_tips_for_transfers.txt +8 -0
  148. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/transfer_money/transfer_processing_times.txt +8 -0
  149. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part1.txt +50 -0
  150. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part10.txt +50 -0
  151. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part11.txt +48 -0
  152. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part12.txt +50 -0
  153. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part13.txt +50 -0
  154. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part14.txt +47 -0
  155. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part15.txt +50 -0
  156. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part16.txt +50 -0
  157. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part17.txt +47 -0
  158. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part18.txt +50 -0
  159. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part19.txt +50 -0
  160. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part2.txt +50 -0
  161. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part20.txt +47 -0
  162. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part21.txt +50 -0
  163. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part22.txt +50 -0
  164. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part23.txt +47 -0
  165. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part24.txt +50 -0
  166. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part25.txt +50 -0
  167. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part26.txt +47 -0
  168. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part27.txt +50 -0
  169. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part28.txt +50 -0
  170. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part29.txt +47 -0
  171. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part3.txt +47 -0
  172. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part30.txt +50 -0
  173. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part31.txt +50 -0
  174. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part32.txt +47 -0
  175. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part33.txt +50 -0
  176. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part34.txt +50 -0
  177. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part35.txt +47 -0
  178. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part36.txt +50 -0
  179. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part37.txt +50 -0
  180. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part38.txt +47 -0
  181. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part39.txt +50 -0
  182. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part4.txt +50 -0
  183. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part40.txt +50 -0
  184. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part41.txt +47 -0
  185. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part42.txt +50 -0
  186. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part43.txt +50 -0
  187. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part44.txt +47 -0
  188. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part45.txt +50 -0
  189. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part46.txt +50 -0
  190. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part47.txt +47 -0
  191. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part48.txt +50 -0
  192. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part49.txt +50 -0
  193. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part5.txt +50 -0
  194. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part50.txt +47 -0
  195. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part51.txt +50 -0
  196. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part52.txt +50 -0
  197. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part53.txt +47 -0
  198. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part54.txt +50 -0
  199. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part55.txt +50 -0
  200. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part56.txt +47 -0
  201. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part57.txt +50 -0
  202. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part58.txt +50 -0
  203. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part59.txt +47 -0
  204. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part6.txt +47 -0
  205. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part60.txt +50 -0
  206. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part61.txt +50 -0
  207. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part7.txt +50 -0
  208. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part8.txt +50 -0
  209. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part9.txt +47 -0
  210. rasa/cli/project_templates/finance/domain/accounts/check_balance.yml +11 -0
  211. rasa/cli/project_templates/finance/domain/cards/block_card.yml +101 -0
  212. rasa/cli/project_templates/finance/domain/cards/select_card.yml +12 -0
  213. rasa/cli/project_templates/finance/domain/general/assistant_details.yml +12 -0
  214. rasa/cli/project_templates/finance/domain/general/bot_identity.yml +5 -0
  215. rasa/cli/project_templates/finance/domain/general/cannot_handle.yml +5 -0
  216. rasa/cli/project_templates/finance/domain/general/defaults.yml +24 -0
  217. rasa/cli/project_templates/finance/domain/general/feedback.yml +28 -0
  218. rasa/cli/project_templates/finance/domain/general/goodbye.yml +7 -0
  219. rasa/cli/project_templates/finance/domain/general/help.yml +5 -0
  220. rasa/cli/project_templates/finance/domain/general/human_handoff.yml +30 -0
  221. rasa/cli/project_templates/finance/domain/general/utils.yml +13 -0
  222. rasa/cli/project_templates/finance/domain/general/welcome.yml +8 -0
  223. rasa/cli/project_templates/finance/domain/transfers/add_payee.yml +47 -0
  224. rasa/cli/project_templates/finance/domain/transfers/list_payees.yml +4 -0
  225. rasa/cli/project_templates/finance/domain/transfers/remove_payee.yml +16 -0
  226. rasa/cli/project_templates/finance/domain/transfers/transfer_money.yml +79 -0
  227. rasa/cli/project_templates/finance/endpoints.yml +63 -0
  228. rasa/cli/project_templates/finance/prompts/rephraser_demo_personality_prompt.jinja2 +19 -0
  229. rasa/cli/project_templates/telco/actions/__init__.py +0 -0
  230. rasa/cli/project_templates/telco/actions/actions_billing.py +204 -0
  231. rasa/cli/project_templates/telco/actions/actions_get_data_from_db.py +48 -0
  232. rasa/cli/project_templates/telco/actions/actions_run_diagnostics.py +28 -0
  233. rasa/cli/project_templates/telco/actions/actions_session_start.py +18 -0
  234. rasa/cli/project_templates/telco/config.yml +25 -0
  235. rasa/cli/project_templates/telco/credentials.yml +33 -0
  236. rasa/cli/project_templates/telco/csvs/billing.csv +10 -0
  237. rasa/cli/project_templates/telco/csvs/customers.csv +5 -0
  238. rasa/cli/project_templates/telco/data/flows/flow_global.yml +5 -0
  239. rasa/cli/project_templates/telco/data/flows/flow_reboot_router.yml +8 -0
  240. rasa/cli/project_templates/telco/data/flows/flow_reset_router.yml +7 -0
  241. rasa/cli/project_templates/telco/data/flows/flow_solve_internet_issue.yml +73 -0
  242. rasa/cli/project_templates/telco/data/flows/flow_undertand_bill.yml +45 -0
  243. rasa/cli/project_templates/telco/data/patterns/pattern_completed.yml +7 -0
  244. rasa/cli/project_templates/telco/data/patterns/pattern_human_handoff.yml +6 -0
  245. rasa/cli/project_templates/telco/data/patterns/pattern_search.yml +7 -0
  246. rasa/cli/project_templates/telco/data/patterns/pattern_session_start.yml +9 -0
  247. rasa/cli/project_templates/telco/docs/reset_vs_rboot_router.txt +1 -0
  248. rasa/cli/project_templates/telco/docs/restart_router.txt +6 -0
  249. rasa/cli/project_templates/telco/docs/run_speed_test.txt +6 -0
  250. rasa/cli/project_templates/telco/domain/domain_global.yml +29 -0
  251. rasa/cli/project_templates/telco/domain/domain_patterns.yml +17 -0
  252. rasa/cli/project_templates/telco/domain/domain_reboot_router.yml +20 -0
  253. rasa/cli/project_templates/telco/domain/domain_reset_router.yml +11 -0
  254. rasa/cli/project_templates/telco/domain/domain_run_speed_test.yml +24 -0
  255. rasa/cli/project_templates/telco/domain/domain_solve_internet_issue.yml +74 -0
  256. rasa/cli/project_templates/telco/domain/domain_undertand_bill.yml +102 -0
  257. rasa/cli/project_templates/telco/endpoints.yml +60 -0
  258. rasa/{dialogue_understanding/generator/prompt_templates/agent_command_prompt_v2_gpt_4o_2024_11_20_template.jinja2 → cli/project_templates/telco/prompts/command-generator.jinja2} +3 -7
  259. rasa/cli/project_templates/telco/tests/e2e_results_failed.yml +62 -0
  260. rasa/cli/project_templates/telco/tests/e2e_results_passed.yml +130 -0
  261. rasa/cli/project_templates/telco/tests/e2e_test_cases/billing_test_cases.yml +68 -0
  262. rasa/cli/project_templates/telco/tests/e2e_test_cases/global_test_cases.yml +13 -0
  263. rasa/cli/project_templates/telco/tests/e2e_test_cases/internet_slow_test_case.yml +47 -0
  264. rasa/cli/project_templates/telco/tests/e2e_test_cases/out_of_scope_test_case.yml +21 -0
  265. rasa/cli/project_templates/telco/tests/e2e_test_cases/patterns_test_cases.yml +15 -0
  266. rasa/cli/project_templates/tutorial/config.yml +2 -1
  267. rasa/cli/run.py +0 -4
  268. rasa/cli/scaffold.py +46 -2
  269. rasa/cli/shell.py +0 -3
  270. rasa/constants.py +0 -6
  271. rasa/core/actions/action.py +2 -43
  272. rasa/core/agent.py +0 -16
  273. rasa/core/available_endpoints.py +0 -30
  274. rasa/core/channels/channel.py +4 -3
  275. rasa/core/channels/constants.py +3 -0
  276. rasa/core/channels/development_inspector.py +1 -1
  277. rasa/core/channels/inspector/dist/assets/{arc-2e78c586.js → arc-18042c22.js} +1 -1
  278. rasa/core/channels/inspector/dist/assets/{blockDiagram-38ab4fdb-806b712e.js → blockDiagram-38ab4fdb-fdd6bcfa.js} +1 -1
  279. rasa/core/channels/inspector/dist/assets/{c4Diagram-3d4e48cf-0745efa9.js → c4Diagram-3d4e48cf-f5ae6786.js} +1 -1
  280. rasa/core/channels/inspector/dist/assets/channel-b9b536fc.js +1 -0
  281. rasa/core/channels/inspector/dist/assets/{classDiagram-70f12bd4-7bd1082b.js → classDiagram-70f12bd4-81efba3e.js} +1 -1
  282. rasa/core/channels/inspector/dist/assets/{classDiagram-v2-f2320105-d937ba49.js → classDiagram-v2-f2320105-3b6b6a92.js} +1 -1
  283. rasa/core/channels/inspector/dist/assets/clone-78d2ddcf.js +1 -0
  284. rasa/core/channels/inspector/dist/assets/{createText-2e5e7dd3-a2a564ca.js → createText-2e5e7dd3-31422447.js} +1 -1
  285. rasa/core/channels/inspector/dist/assets/{edges-e0da2a9e-b5256940.js → edges-e0da2a9e-518a90db.js} +1 -1
  286. rasa/core/channels/inspector/dist/assets/{erDiagram-9861fffd-e6883ad2.js → erDiagram-9861fffd-a6d3c25a.js} +1 -1
  287. rasa/core/channels/inspector/dist/assets/{flowDb-956e92f1-e576fc02.js → flowDb-956e92f1-e048c2be.js} +1 -1
  288. rasa/core/channels/inspector/dist/assets/{flowDiagram-66a62f08-2e298d01.js → flowDiagram-66a62f08-c7474c91.js} +1 -1
  289. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-8b09c060.js +1 -0
  290. rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-4a651766-dd7b150a.js → flowchart-elk-definition-4a651766-cb4d8723.js} +1 -1
  291. rasa/core/channels/inspector/dist/assets/{ganttDiagram-c361ad54-5b79575c.js → ganttDiagram-c361ad54-346636a2.js} +1 -1
  292. rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-72cf32ee-3016f40a.js → gitGraphDiagram-72cf32ee-7c508874.js} +1 -1
  293. rasa/core/channels/inspector/dist/assets/{graph-3e19170f.js → graph-14702d8a.js} +1 -1
  294. rasa/core/channels/inspector/dist/assets/{index-3862675e-eb9c86de.js → index-3862675e-f18b534b.js} +1 -1
  295. rasa/core/channels/inspector/dist/assets/index-4d4bdf3a.js +1335 -0
  296. rasa/core/channels/inspector/dist/assets/{infoDiagram-f8f76790-b4280e4d.js → infoDiagram-f8f76790-64154b83.js} +1 -1
  297. rasa/core/channels/inspector/dist/assets/{journeyDiagram-49397b02-556091f8.js → journeyDiagram-49397b02-833a5f95.js} +1 -1
  298. rasa/core/channels/inspector/dist/assets/{layout-08436411.js → layout-5a3b2123.js} +1 -1
  299. rasa/core/channels/inspector/dist/assets/{line-683c4f3b.js → line-2272a8c7.js} +1 -1
  300. rasa/core/channels/inspector/dist/assets/{linear-cee6d791.js → linear-35bcf273.js} +1 -1
  301. rasa/core/channels/inspector/dist/assets/{mindmap-definition-fc14e90a-a0bf0b1a.js → mindmap-definition-fc14e90a-92dcb0e9.js} +1 -1
  302. rasa/core/channels/inspector/dist/assets/{pieDiagram-8a3498a8-3730d5c4.js → pieDiagram-8a3498a8-94dbc900.js} +1 -1
  303. rasa/core/channels/inspector/dist/assets/{quadrantDiagram-120e2f19-12a20fed.js → quadrantDiagram-120e2f19-8b7a9c33.js} +1 -1
  304. rasa/core/channels/inspector/dist/assets/{requirementDiagram-deff3bca-b9732102.js → requirementDiagram-deff3bca-6f7eab81.js} +1 -1
  305. rasa/core/channels/inspector/dist/assets/{sankeyDiagram-04a897e0-a2e72776.js → sankeyDiagram-04a897e0-f43e581d.js} +1 -1
  306. rasa/core/channels/inspector/dist/assets/{sequenceDiagram-704730f1-8b7a76bb.js → sequenceDiagram-704730f1-0bcbefc3.js} +1 -1
  307. rasa/core/channels/inspector/dist/assets/{stateDiagram-587899a1-e65853ac.js → stateDiagram-587899a1-b8a74083.js} +1 -1
  308. rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-d93cdb3a-6f58a44b.js → stateDiagram-v2-d93cdb3a-2070218f.js} +1 -1
  309. rasa/core/channels/inspector/dist/assets/{styles-6aaf32cf-df25b934.js → styles-6aaf32cf-f1d54e34.js} +1 -1
  310. rasa/core/channels/inspector/dist/assets/{styles-9a916d00-88357141.js → styles-9a916d00-980de489.js} +1 -1
  311. rasa/core/channels/inspector/dist/assets/{styles-c10674c1-d600174d.js → styles-c10674c1-3c03abde.js} +1 -1
  312. rasa/core/channels/inspector/dist/assets/{svgDrawCommon-08f97a94-4adc3e0b.js → svgDrawCommon-08f97a94-46ba068f.js} +1 -1
  313. rasa/core/channels/inspector/dist/assets/{timeline-definition-85554ec2-42816fa1.js → timeline-definition-85554ec2-901f5e3d.js} +1 -1
  314. rasa/core/channels/inspector/dist/assets/{xychartDiagram-e933f94c-621eb66a.js → xychartDiagram-e933f94c-acbc628a.js} +1 -1
  315. rasa/core/channels/inspector/dist/index.html +2 -2
  316. rasa/core/channels/inspector/index.html +1 -1
  317. rasa/core/channels/inspector/src/App.tsx +10 -11
  318. rasa/core/channels/inspector/src/components/DialogueInformation.tsx +12 -3
  319. rasa/core/channels/inspector/src/components/DialogueStack.tsx +5 -7
  320. rasa/core/channels/inspector/src/helpers/formatters.ts +3 -24
  321. rasa/core/channels/inspector/src/theme/base/styles.ts +1 -19
  322. rasa/core/channels/inspector/src/types.ts +0 -4
  323. rasa/core/channels/socketio.py +212 -51
  324. rasa/core/channels/studio_chat.py +43 -23
  325. rasa/core/channels/voice_stream/voice_channel.py +5 -3
  326. rasa/core/constants.py +0 -4
  327. rasa/core/policies/enterprise_search_policy.py +6 -11
  328. rasa/core/policies/flow_policy.py +4 -4
  329. rasa/core/policies/flows/flow_executor.py +45 -415
  330. rasa/core/processor.py +1 -6
  331. rasa/core/run.py +14 -11
  332. rasa/core/utils.py +1 -21
  333. rasa/dialogue_understanding/commands/__init__.py +0 -8
  334. rasa/dialogue_understanding/commands/cancel_flow_command.py +4 -97
  335. rasa/dialogue_understanding/commands/chit_chat_answer_command.py +0 -11
  336. rasa/dialogue_understanding/commands/clarify_command.py +0 -10
  337. rasa/dialogue_understanding/commands/knowledge_answer_command.py +0 -11
  338. rasa/dialogue_understanding/commands/start_flow_command.py +8 -164
  339. rasa/dialogue_understanding/commands/utils.py +2 -6
  340. rasa/dialogue_understanding/generator/command_parser.py +0 -4
  341. rasa/dialogue_understanding/generator/flow_retrieval.py +10 -9
  342. rasa/dialogue_understanding/generator/llm_based_command_generator.py +12 -50
  343. rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +6 -7
  344. rasa/dialogue_understanding/generator/single_step/search_ready_llm_command_generator.py +6 -7
  345. rasa/dialogue_understanding/generator/single_step/single_step_based_llm_command_generator.py +2 -41
  346. rasa/dialogue_understanding/patterns/continue_interrupted.py +1 -163
  347. rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +8 -52
  348. rasa/dialogue_understanding/processor/command_processor.py +15 -31
  349. rasa/dialogue_understanding/stack/dialogue_stack.py +2 -123
  350. rasa/dialogue_understanding/stack/frames/flow_stack_frame.py +0 -57
  351. rasa/dialogue_understanding/stack/utils.py +2 -17
  352. rasa/dialogue_understanding_test/du_test_runner.py +2 -7
  353. rasa/e2e_test/e2e_test_runner.py +0 -5
  354. rasa/engine/storage/local_model_storage.py +45 -2
  355. rasa/model_manager/model_api.py +4 -5
  356. rasa/model_manager/runner_service.py +1 -1
  357. rasa/model_manager/socket_bridge.py +20 -14
  358. rasa/model_manager/trainer_service.py +12 -9
  359. rasa/model_manager/utils.py +1 -29
  360. rasa/server.py +0 -10
  361. rasa/shared/constants.py +0 -5
  362. rasa/shared/core/constants.py +1 -12
  363. rasa/shared/core/domain.py +58 -11
  364. rasa/shared/core/events.py +0 -319
  365. rasa/shared/core/flows/flow_step.py +7 -1
  366. rasa/shared/core/flows/flows_list.py +2 -2
  367. rasa/shared/core/flows/flows_yaml_schema.json +186 -112
  368. rasa/shared/core/flows/steps/call.py +5 -53
  369. rasa/shared/core/flows/validation.py +7 -46
  370. rasa/shared/core/flows/yaml_flows_io.py +16 -8
  371. rasa/shared/core/slots.py +4 -0
  372. rasa/shared/importers/importer.py +6 -0
  373. rasa/shared/importers/utils.py +77 -1
  374. rasa/shared/providers/_utils.py +60 -44
  375. rasa/shared/providers/embedding/default_litellm_embedding_client.py +2 -0
  376. rasa/shared/providers/llm/_base_litellm_client.py +7 -39
  377. rasa/shared/providers/llm/default_litellm_llm_client.py +2 -0
  378. rasa/shared/providers/llm/litellm_router_llm_client.py +4 -8
  379. rasa/shared/providers/llm/llm_client.py +3 -7
  380. rasa/shared/providers/llm/llm_response.py +0 -49
  381. rasa/shared/providers/llm/self_hosted_llm_client.py +4 -8
  382. rasa/shared/utils/llm.py +5 -28
  383. rasa/shared/utils/schemas/events.py +0 -42
  384. rasa/studio/upload.py +16 -47
  385. rasa/telemetry.py +97 -23
  386. rasa/tracing/instrumentation/instrumentation.py +2 -4
  387. rasa/utils/common.py +0 -79
  388. rasa/utils/io.py +27 -9
  389. rasa/utils/json_utils.py +6 -1
  390. rasa/utils/log_utils.py +5 -1
  391. rasa/utils/openapi.py +144 -0
  392. rasa/validator.py +7 -3
  393. rasa/version.py +1 -1
  394. {rasa_pro-3.14.0.dev3.dist-info → rasa_pro-3.14.0.dev5.dist-info}/METADATA +3 -3
  395. {rasa_pro-3.14.0.dev3.dist-info → rasa_pro-3.14.0.dev5.dist-info}/RECORD +405 -174
  396. rasa/agents/agent_factory.py +0 -122
  397. rasa/agents/agent_manager.py +0 -162
  398. rasa/agents/constants.py +0 -32
  399. rasa/agents/core/agent_protocol.py +0 -108
  400. rasa/agents/core/types.py +0 -70
  401. rasa/agents/exceptions.py +0 -8
  402. rasa/agents/protocol/__init__.py +0 -5
  403. rasa/agents/protocol/a2a/a2a_agent.py +0 -51
  404. rasa/agents/protocol/mcp/mcp_base_agent.py +0 -674
  405. rasa/agents/protocol/mcp/mcp_open_agent.py +0 -276
  406. rasa/agents/protocol/mcp/mcp_task_agent.py +0 -469
  407. rasa/agents/schemas/__init__.py +0 -12
  408. rasa/agents/schemas/agent_input.py +0 -38
  409. rasa/agents/schemas/agent_output.py +0 -26
  410. rasa/agents/schemas/agent_tool_result.py +0 -51
  411. rasa/agents/schemas/agent_tool_schema.py +0 -134
  412. rasa/agents/templates/mcp_open_agent_prompt_template.jinja2 +0 -15
  413. rasa/agents/templates/mcp_task_agent_prompt_template.jinja2 +0 -17
  414. rasa/agents/utils.py +0 -72
  415. rasa/core/available_agents.py +0 -196
  416. rasa/core/channels/inspector/dist/assets/channel-c436ca7c.js +0 -1
  417. rasa/core/channels/inspector/dist/assets/clone-50dd656b.js +0 -1
  418. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-2b2aeaf8.js +0 -1
  419. rasa/core/channels/inspector/dist/assets/index-1bd9135e.js +0 -1353
  420. rasa/core/policies/flows/mcp_tool_executor.py +0 -277
  421. rasa/dialogue_understanding/commands/continue_agent_command.py +0 -91
  422. rasa/dialogue_understanding/commands/restart_agent_command.py +0 -162
  423. rasa/dialogue_understanding/generator/prompt_templates/agent_command_prompt_v2_claude_3_5_sonnet_20240620_template.jinja2 +0 -61
  424. rasa/dialogue_understanding/generator/prompt_templates/agent_command_prompt_v3_claude_3_5_sonnet_20240620_template.jinja2 +0 -81
  425. rasa/dialogue_understanding/generator/prompt_templates/agent_command_prompt_v3_gpt_4o_2024_11_20_template.jinja2 +0 -81
  426. rasa/shared/agents/utils.py +0 -35
  427. rasa/shared/utils/mcp/server_connection.py +0 -157
  428. /rasa/{agents → builder}/__init__.py +0 -0
  429. /rasa/{agents/core → builder/copilot}/__init__.py +0 -0
  430. /rasa/{agents/protocol/a2a → builder/copilot/prompts}/__init__.py +0 -0
  431. /rasa/{agents/protocol/mcp → builder/copilot/templated_messages}/__init__.py +0 -0
  432. /rasa/{agents/templates → builder/document_retrieval}/__init__.py +0 -0
  433. /rasa/{shared/agents → cli/project_templates/finance/actions/accounts}/__init__.py +0 -0
  434. /rasa/{shared/utils/mcp → cli/project_templates/finance/actions/cards}/__init__.py +0 -0
  435. {rasa_pro-3.14.0.dev3.dist-info → rasa_pro-3.14.0.dev5.dist-info}/NOTICE +0 -0
  436. {rasa_pro-3.14.0.dev3.dist-info → rasa_pro-3.14.0.dev5.dist-info}/WHEEL +0 -0
  437. {rasa_pro-3.14.0.dev3.dist-info → rasa_pro-3.14.0.dev5.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,1142 @@
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
+ from rasa.builder.auth import HEADER_USER_ID, is_auth_required_now, protected
14
+ from rasa.builder.config import (
15
+ COPILOT_ASSISTANT_TRACKER_MAX_TURNS,
16
+ COPILOT_HANDLER_ROLLING_BUFFER_SIZE,
17
+ HELLO_RASA_PROJECT_ID,
18
+ )
19
+ from rasa.builder.copilot.constants import ROLE_USER
20
+ from rasa.builder.copilot.exceptions import CopilotStreamError
21
+ from rasa.builder.copilot.models import (
22
+ CopilotContext,
23
+ CopilotRequest,
24
+ GeneratedContent,
25
+ ReferenceEntry,
26
+ ReferenceSection,
27
+ ResponseCategory,
28
+ ResponseCompleteness,
29
+ )
30
+ from rasa.builder.copilot.telemetry import CopilotTelemetry
31
+ from rasa.builder.download import create_bot_project_archive
32
+ from rasa.builder.guardrails.utils import (
33
+ check_assistant_chat_for_policy_violations,
34
+ check_copilot_chat_for_policy_violations,
35
+ )
36
+ from rasa.builder.job_manager import job_manager
37
+ from rasa.builder.jobs import (
38
+ run_prompt_to_bot_job,
39
+ run_template_to_bot_job,
40
+ run_update_files_job,
41
+ )
42
+ from rasa.builder.llm_service import llm_service
43
+ from rasa.builder.logging_utils import (
44
+ capture_exception_with_context,
45
+ get_recent_logs,
46
+ )
47
+ from rasa.builder.models import (
48
+ ApiErrorResponse,
49
+ AssistantInfo,
50
+ BotData,
51
+ JobCreateResponse,
52
+ JobStatus,
53
+ JobStatusEvent,
54
+ PromptRequest,
55
+ ServerSentEvent,
56
+ TemplateRequest,
57
+ )
58
+ from rasa.builder.project_generator import ProjectGenerator
59
+ from rasa.builder.shared.tracker_context import TrackerContext
60
+ from rasa.core.agent import Agent
61
+ from rasa.core.channels.studio_chat import StudioChatInput
62
+ from rasa.core.exceptions import AgentNotReady
63
+ from rasa.shared.core.flows.flows_list import FlowsList
64
+ from rasa.shared.core.flows.yaml_flows_io import get_flows_as_json
65
+ from rasa.shared.core.trackers import DialogueStateTracker
66
+ from rasa.shared.importers.utils import DOMAIN_KEYS
67
+ from rasa.utils.json_utils import extract_values
68
+ from rasa.utils.openapi import model_to_schema
69
+
70
+ structlogger = structlog.get_logger()
71
+
72
+ # Create the blueprint
73
+ bp = Blueprint("bot_builder", url_prefix="/api")
74
+
75
+
76
+ def setup_project_generator(project_folder: Optional[str] = None) -> ProjectGenerator:
77
+ """Initialize and return a ProjectGenerator instance."""
78
+ if project_folder is None:
79
+ import tempfile
80
+
81
+ project_folder = tempfile.mkdtemp(prefix="rasa_builder_")
82
+
83
+ # Ensure the project folder is in sys.path
84
+ if project_folder not in sys.path:
85
+ sys.path.insert(0, project_folder)
86
+
87
+ structlogger.info(
88
+ "bot_builder_service.service_initialized", project_folder=project_folder
89
+ )
90
+
91
+ return ProjectGenerator(project_folder)
92
+
93
+
94
+ def get_project_generator(request: Request) -> ProjectGenerator:
95
+ """Get the project generator from app context."""
96
+ return request.app.ctx.project_generator
97
+
98
+
99
+ def get_input_channel(request: Request) -> StudioChatInput:
100
+ """Get the input channel from app context."""
101
+ return request.app.ctx.input_channel
102
+
103
+
104
+ async def extract_bot_data_from_agent(agent: Agent) -> BotData:
105
+ """Extract BotData from an Agent.
106
+
107
+ Args:
108
+ agent: The agent to extract data from
109
+
110
+ Returns:
111
+ BotData containing flows, domain, config, endpoints, and nlu data
112
+ """
113
+ domain = agent.domain.as_dict() if agent.domain else {}
114
+ flows = (
115
+ await agent.processor.get_flows()
116
+ if agent.processor
117
+ else FlowsList(underlying_flows=[])
118
+ )
119
+ return BotData(
120
+ flows=get_flows_as_json(flows),
121
+ domain=extract_values(domain, DOMAIN_KEYS),
122
+ )
123
+
124
+
125
+ # Health check endpoint
126
+ @bp.route("/", methods=["GET"])
127
+ @openapi.summary("Health check endpoint")
128
+ @openapi.description("Returns the health status of the Bot Builder service")
129
+ @openapi.tag("health")
130
+ @openapi.response(200, {"application/json": {"status": str, "service": str}})
131
+ async def health(request: Request) -> HTTPResponse:
132
+ """Health check endpoint."""
133
+ project_generator = get_project_generator(request)
134
+ return response.json(
135
+ {
136
+ "status": "ok",
137
+ "service": "bot-builder",
138
+ "auth_required": is_auth_required_now(
139
+ project_info=project_generator.project_info
140
+ ),
141
+ }
142
+ )
143
+
144
+
145
+ @bp.route("/job-events/<job_id>", methods=["GET"])
146
+ @openapi.summary("Stream job progress events")
147
+ @openapi.description(
148
+ "Stream server-sent events (SSE) tracking real-time job progress.\n\n"
149
+ "**Connect with:** `Accept: text/event-stream`.\n\n"
150
+ "**SSE Event Example:**\n"
151
+ "```text\n"
152
+ "event: received\n"
153
+ 'data: {"status": "received"}\n'
154
+ "```\n\n"
155
+ )
156
+ @openapi.tag("job-events")
157
+ @openapi.parameter(
158
+ "job_id", str, location="path", description="The id of the job to stream events for"
159
+ )
160
+ @openapi.response(
161
+ 200,
162
+ {"text/event-stream": str},
163
+ description="Server-sent events stream. See documentation for event types.",
164
+ example=(
165
+ "event: received\n"
166
+ 'data: {"status": "received"}\n'
167
+ "\n"
168
+ "event: generating\n"
169
+ 'data: {"status": "generating"}\n'
170
+ ),
171
+ )
172
+ @openapi.response(
173
+ 404,
174
+ {"application/json": model_to_schema(ApiErrorResponse)},
175
+ description="Unknown job_id: No such job exists.",
176
+ )
177
+ @openapi.parameter(
178
+ HEADER_USER_ID,
179
+ description=(
180
+ "Optional user id to associate requests (e.g., for telemetry/guardrails)."
181
+ ),
182
+ _in="header",
183
+ required=False,
184
+ schema=str,
185
+ )
186
+ async def job_events(request: Request, job_id: str) -> HTTPResponse:
187
+ try:
188
+ job = job_manager.get_job(job_id)
189
+ if job is None:
190
+ return response.json(
191
+ ApiErrorResponse(
192
+ error="Job not found", details={"job_id": job_id}
193
+ ).model_dump(),
194
+ status=404,
195
+ )
196
+
197
+ stream = await request.respond(content_type="text/event-stream")
198
+
199
+ try:
200
+ async for evt in job.event_stream():
201
+ await stream.send(evt.format())
202
+ except Exception as exc:
203
+ # Handle exceptions within the SSE stream context
204
+ capture_exception_with_context(
205
+ exc,
206
+ "bot_builder_service.job_events.streaming_error",
207
+ extra={"job_id": job_id},
208
+ tags={"endpoint": "/api/job-events/<job_id>"},
209
+ )
210
+ # Send error event in SSE format instead of JSON response
211
+ error_event = JobStatusEvent.from_status(
212
+ status=JobStatus.error.value,
213
+ message=f"Failed to stream job events: {exc}",
214
+ ).format()
215
+ await stream.send(error_event)
216
+ finally:
217
+ await stream.eof()
218
+
219
+ return stream
220
+ except Exception as exc:
221
+ # This exception handler only applies before stream.respond() is called
222
+ capture_exception_with_context(
223
+ exc,
224
+ "bot_builder_service.job_events.unexpected_error",
225
+ extra={"job_id": job_id},
226
+ tags={"endpoint": "/api/job-events/<job_id>"},
227
+ )
228
+ return response.json(
229
+ ApiErrorResponse(
230
+ error="Failed to stream job events", details={"error": str(exc)}
231
+ ).model_dump(),
232
+ status=HTTPStatus.INTERNAL_SERVER_ERROR,
233
+ )
234
+
235
+
236
+ @bp.route("/prompt-to-bot", methods=["POST"])
237
+ @openapi.summary("Generate bot from natural language prompt")
238
+ @openapi.description(
239
+ "Creates a complete conversational AI bot from a natural language prompt. "
240
+ "Returns immediately with a job ID. Connect to `/job-events/<job_id>` to "
241
+ "receive server-sent events (SSE) for real-time progress tracking "
242
+ "throughout the bot creation process.\n\n"
243
+ "**SSE Event Flow** (via `/job-events/<job_id>`):\n"
244
+ "1. `received` - Request received by server\n"
245
+ "2. `generating` - Generating bot project files\n"
246
+ "3. `generation_success` - Bot generation completed successfully\n"
247
+ "4. `training` - Training the bot model\n"
248
+ "5. `train_success` - Model training completed\n"
249
+ "6. `done` - Bot creation completed\n\n"
250
+ "**Error Events:**\n"
251
+ "- `generation_error` - Failed to generate bot from prompt\n"
252
+ "- `train_error` - Bot generated but training failed\n"
253
+ "- `validation_error` - Generated bot configuration is invalid\n"
254
+ "- `error` - Unexpected error occurred\n\n"
255
+ "**Usage:**\n"
256
+ "1. Send POST request with Content-Type: application/json\n"
257
+ "2. The response will be a JSON object `{job_id: ...}`\n"
258
+ "3. Connect to `/job-events/<job_id>` for a server-sent event stream of progress."
259
+ )
260
+ @openapi.tag("bot-generation")
261
+ @openapi.body(
262
+ {"application/json": model_to_schema(PromptRequest)},
263
+ description="Prompt request with natural language description.",
264
+ required=True,
265
+ )
266
+ @openapi.response(
267
+ 200,
268
+ {"application/json": model_to_schema(JobCreateResponse)},
269
+ description="Job created. Poll or subscribe to /job-events/<job_id> for progress.",
270
+ )
271
+ @openapi.response(
272
+ 400,
273
+ {"application/json": model_to_schema(ApiErrorResponse)},
274
+ description="Validation error in request payload",
275
+ )
276
+ @openapi.response(
277
+ 500,
278
+ {"application/json": model_to_schema(ApiErrorResponse)},
279
+ description="Internal server error",
280
+ )
281
+ @openapi.parameter(
282
+ HEADER_USER_ID,
283
+ description=(
284
+ "Optional user id to associate requests (e.g., for telemetry/guardrails)."
285
+ ),
286
+ _in="header",
287
+ required=False,
288
+ schema=str,
289
+ )
290
+ async def handle_prompt_to_bot(request: Request) -> HTTPResponse:
291
+ """Handle prompt-to-bot generation requests."""
292
+ try:
293
+ payload = PromptRequest(**request.json)
294
+ except Exception as exc:
295
+ return response.json(
296
+ ApiErrorResponse(
297
+ error="Invalid request", details={"error": str(exc)}
298
+ ).model_dump(),
299
+ status=400,
300
+ )
301
+
302
+ try:
303
+ # Allocate job and schedule background task
304
+ job = job_manager.create_job()
305
+ request.app.add_task(run_prompt_to_bot_job(request.app, job, payload.prompt))
306
+ return response.json(JobCreateResponse(job_id=job.id).model_dump(), status=200)
307
+ except Exception as exc:
308
+ capture_exception_with_context(
309
+ exc,
310
+ "bot_builder_service.prompt_to_bot.unexpected_error",
311
+ tags={"endpoint": "/api/prompt-to-bot"},
312
+ )
313
+ return response.json(
314
+ ApiErrorResponse(
315
+ error="Failed to create prompt-to-bot job",
316
+ details={"error": str(exc)},
317
+ ).model_dump(),
318
+ status=HTTPStatus.INTERNAL_SERVER_ERROR,
319
+ )
320
+
321
+
322
+ @bp.route("/template-to-bot", methods=["POST"])
323
+ @openapi.summary("Generate bot from predefined template")
324
+ @openapi.description(
325
+ "Creates a complete conversational AI bot from a predefined template. "
326
+ "Returns immediately with a job ID. Connect to `/job-events/<job_id>` to "
327
+ "receive server-sent events (SSE) for real-time progress tracking "
328
+ "throughout the bot creation process.\n\n"
329
+ "**SSE Event Flow** (via `/job-events/<job_id>`):\n"
330
+ "1. `received` - Request received by server\n"
331
+ "2. `generating` - Initializing bot from template\n"
332
+ "3. `generation_success` - Template initialization completed successfully\n"
333
+ "4. `training` - Training the bot model\n"
334
+ "5. `train_success` - Model training completed\n"
335
+ "6. `done` - Bot creation completed\n\n"
336
+ "**Error Events:**\n"
337
+ "- `generation_error` - Failed to initialize bot from template\n"
338
+ "- `train_error` - Template loaded but training failed\n"
339
+ "- `validation_error` - Template configuration is invalid\n"
340
+ "- `error` - Unexpected error occurred\n\n"
341
+ "**Usage:**\n"
342
+ "1. Send POST request with Content-Type: application/json\n"
343
+ "2. The response will be a JSON object `{job_id: ...}`\n"
344
+ "3. Connect to `/job-events/<job_id>` for a server-sent event stream of progress."
345
+ )
346
+ @openapi.tag("bot-generation")
347
+ @openapi.body(
348
+ {"application/json": model_to_schema(TemplateRequest)},
349
+ description="Template request with template name.",
350
+ required=True,
351
+ )
352
+ @openapi.response(
353
+ 200,
354
+ {"application/json": model_to_schema(JobCreateResponse)},
355
+ description="Job created. Poll or subscribe to /job-events/<job_id> for progress.",
356
+ )
357
+ @openapi.response(
358
+ 400,
359
+ {"application/json": model_to_schema(ApiErrorResponse)},
360
+ description="Validation error in request payload or invalid template name",
361
+ )
362
+ @openapi.response(
363
+ 500,
364
+ {"application/json": model_to_schema(ApiErrorResponse)},
365
+ description="Internal server error",
366
+ )
367
+ @openapi.parameter(
368
+ HEADER_USER_ID,
369
+ description=(
370
+ "Optional user id to associate requests (e.g., for telemetry/guardrails)."
371
+ ),
372
+ _in="header",
373
+ required=False,
374
+ schema=str,
375
+ )
376
+ async def handle_template_to_bot(request: Request) -> HTTPResponse:
377
+ """Create a new template-to-bot job and return job_id immediately."""
378
+ try:
379
+ template_data = TemplateRequest(**request.json)
380
+ except Exception as exc:
381
+ return response.json(
382
+ ApiErrorResponse(
383
+ error="Invalid request", details={"error": str(exc)}
384
+ ).model_dump(),
385
+ status=400,
386
+ )
387
+
388
+ try:
389
+ # allocate job and schedule background task
390
+ job = job_manager.create_job()
391
+ request.app.add_task(
392
+ run_template_to_bot_job(request.app, job, template_data.template_name)
393
+ )
394
+ return response.json(JobCreateResponse(job_id=job.id).model_dump(), status=200)
395
+ except Exception as exc:
396
+ capture_exception_with_context(
397
+ exc,
398
+ "bot_builder_service.template_to_bot.unexpected_error",
399
+ tags={"endpoint": "/api/template-to-bot"},
400
+ )
401
+ return response.json(
402
+ ApiErrorResponse(
403
+ error="Failed to create template-to-bot job",
404
+ details={"error": str(exc)},
405
+ ).model_dump(),
406
+ status=HTTPStatus.INTERNAL_SERVER_ERROR,
407
+ )
408
+
409
+
410
+ @bp.route("/files", methods=["GET"])
411
+ @openapi.summary("Get bot files")
412
+ @openapi.description("Retrieves the current bot configuration files and data")
413
+ @openapi.tag("bot-files")
414
+ @openapi.response(
415
+ 200,
416
+ {"application/json": {str: Optional[str]}},
417
+ description="Bot files retrieved successfully",
418
+ )
419
+ @openapi.response(
420
+ 500,
421
+ {"application/json": model_to_schema(ApiErrorResponse)},
422
+ description="Internal server error",
423
+ )
424
+ @openapi.parameter(
425
+ HEADER_USER_ID,
426
+ description=(
427
+ "Optional user id to associate requests (e.g., for telemetry/guardrails)."
428
+ ),
429
+ _in="header",
430
+ required=False,
431
+ schema=str,
432
+ )
433
+ async def get_bot_files(request: Request) -> HTTPResponse:
434
+ """Get current bot files."""
435
+ try:
436
+ project_generator = get_project_generator(request)
437
+ bot_files = project_generator.get_bot_files()
438
+ return response.json(bot_files)
439
+ except Exception as exc:
440
+ capture_exception_with_context(
441
+ exc,
442
+ "bot_builder_service.get_bot_files.unexpected_error",
443
+ tags={"endpoint": "/api/files", "method": "GET"},
444
+ )
445
+ return response.json(
446
+ ApiErrorResponse(
447
+ error="Failed to retrieve bot files", details={"error": str(exc)}
448
+ ).model_dump(),
449
+ status=HTTPStatus.INTERNAL_SERVER_ERROR,
450
+ )
451
+
452
+
453
+ @bp.route("/files", methods=["PUT"])
454
+ @openapi.summary("Update bot files")
455
+ @openapi.description(
456
+ "Updates the bot configuration files and retrains the model. "
457
+ "Returns immediately with a job ID. Connect to `/job-events/<job_id>` "
458
+ "for real-time SSE progress tracking."
459
+ "\n\n"
460
+ "**SSE Event Flow:** (available via /job-events/<job_id>)\n"
461
+ "1. `received` - Request received by server\n"
462
+ "2. `validating` - Validating bot configuration files\n"
463
+ "3. `validation_success` - File validation completed successfully\n"
464
+ "4. `training` - Training the bot model with updated files\n"
465
+ "5. `train_success` - Model training completed\n"
466
+ "6. `done` - Bot files update completed\n\n"
467
+ "**Error Events (can occur at any time):**\n"
468
+ "- `validation_error` - Bot configuration files are invalid\n"
469
+ "- `train_error` - Files updated but training failed\n"
470
+ "- `error` - Unexpected error occurred\n\n"
471
+ "**Usage:**\n"
472
+ "1. Send PUT request with Content-Type: application/json\n"
473
+ "2. The response will be a JSON object `{job_id: ...}`\n"
474
+ "3. Connect to `/job-events/<job_id>` for a server-sent event stream of progress."
475
+ )
476
+ @openapi.tag("bot-files")
477
+ @openapi.body(
478
+ {"application/json": {"file_name": str}},
479
+ description="A dictionary mapping file names to their updated content. "
480
+ "The file name should be the name of the file in the project folder. "
481
+ "Files that are not in the request will not be updated.",
482
+ required=True,
483
+ )
484
+ @openapi.response(
485
+ 200,
486
+ {"application/json": model_to_schema(JobCreateResponse)},
487
+ description=(
488
+ "Job created. Poll or subscribe to /job-events/<job_id> "
489
+ "for progress and SSE updates."
490
+ ),
491
+ )
492
+ @openapi.response(
493
+ 400,
494
+ {"application/json": model_to_schema(ApiErrorResponse)},
495
+ description="Validation error in bot files",
496
+ )
497
+ @openapi.response(
498
+ 500,
499
+ {"application/json": model_to_schema(ApiErrorResponse)},
500
+ description="Internal server error",
501
+ )
502
+ @openapi.response(
503
+ 401,
504
+ {"application/json": model_to_schema(ApiErrorResponse)},
505
+ description=(
506
+ "Authentication failed - Authorization header missing or invalid. "
507
+ "Auth may be conditionally required depending on server "
508
+ "configuration."
509
+ ),
510
+ )
511
+ @openapi.parameter(
512
+ "Authorization",
513
+ description=(
514
+ "Bearer token for authentication. Required after auth start window "
515
+ "or if configured."
516
+ ),
517
+ _in="header",
518
+ required=False,
519
+ schema=str,
520
+ )
521
+ @openapi.parameter(
522
+ HEADER_USER_ID,
523
+ description=(
524
+ "Optional user id to associate requests (e.g., for telemetry/guardrails)."
525
+ ),
526
+ _in="header",
527
+ required=False,
528
+ schema=str,
529
+ )
530
+ @protected()
531
+ async def update_bot_files(request: Request) -> HTTPResponse:
532
+ """Update bot files with server-sent events for progress tracking."""
533
+ try:
534
+ bot_files = request.json
535
+ except Exception as exc:
536
+ return response.json(
537
+ ApiErrorResponse(
538
+ error="Invalid request", details={"error": str(exc)}
539
+ ).model_dump(),
540
+ status=400,
541
+ )
542
+
543
+ try:
544
+ job = job_manager.create_job()
545
+ request.app.add_task(run_update_files_job(request.app, job, bot_files))
546
+ return response.json(JobCreateResponse(job_id=job.id).model_dump(), status=200)
547
+ except Exception as exc:
548
+ capture_exception_with_context(
549
+ exc,
550
+ "bot_builder_service.update_bot_files.unexpected_error",
551
+ tags={"endpoint": "/api/files", "method": "PUT"},
552
+ )
553
+ return response.json(
554
+ ApiErrorResponse(
555
+ error="Failed to update bot files", details={"error": str(exc)}
556
+ ).model_dump(),
557
+ status=HTTPStatus.INTERNAL_SERVER_ERROR,
558
+ )
559
+
560
+
561
+ @bp.route("/data", methods=["GET"])
562
+ @openapi.summary("Get bot data")
563
+ @openapi.description(
564
+ "Retrieves the current bot data in CALM import format with flows, domain, "
565
+ "config, endpoints, and NLU data"
566
+ )
567
+ @openapi.tag("bot-data")
568
+ @openapi.response(
569
+ 200,
570
+ {"application/json": model_to_schema(BotData)},
571
+ description="Bot data retrieved successfully",
572
+ )
573
+ @openapi.response(
574
+ 409,
575
+ {"application/json": model_to_schema(ApiErrorResponse)},
576
+ description="Agent not ready",
577
+ )
578
+ @openapi.response(
579
+ 500,
580
+ {"application/json": model_to_schema(ApiErrorResponse)},
581
+ description="Internal server error",
582
+ )
583
+ @openapi.parameter(
584
+ HEADER_USER_ID,
585
+ description=(
586
+ "Optional user id to associate requests (e.g., for telemetry/guardrails)."
587
+ ),
588
+ _in="header",
589
+ required=False,
590
+ schema=str,
591
+ )
592
+ async def get_bot_data(request: Request) -> HTTPResponse:
593
+ """Get current bot data in CALM import format."""
594
+ try:
595
+ agent: Optional[Agent] = request.app.ctx.agent
596
+ if not agent:
597
+ raise AgentNotReady(
598
+ "Can't retrieve the data without an agent being loaded."
599
+ )
600
+
601
+ bot_data = await extract_bot_data_from_agent(agent)
602
+
603
+ return response.json(bot_data.model_dump())
604
+ except AgentNotReady as e:
605
+ return response.json(
606
+ ApiErrorResponse(
607
+ error="Agent not ready",
608
+ details={"error": str(e)},
609
+ ).model_dump(),
610
+ status=HTTPStatus.CONFLICT,
611
+ )
612
+ except Exception as exc:
613
+ capture_exception_with_context(
614
+ exc,
615
+ "bot_builder_service.get_bot_data.unexpected_error",
616
+ tags={"endpoint": "/api/data"},
617
+ )
618
+ return response.json(
619
+ ApiErrorResponse(
620
+ error="Failed to retrieve bot data",
621
+ details={"error": str(exc)},
622
+ ).model_dump(),
623
+ status=HTTPStatus.INTERNAL_SERVER_ERROR,
624
+ )
625
+
626
+
627
+ @bp.route("/assistant", methods=["GET"])
628
+ @openapi.summary("Get assistant info")
629
+ @openapi.description(
630
+ "Returns basic information about the loaded assistant, including the assistant id "
631
+ "as configured in the model's metadata (from config.yml)."
632
+ )
633
+ @openapi.tag("assistant")
634
+ @openapi.response(
635
+ 200,
636
+ {"application/json": model_to_schema(AssistantInfo)},
637
+ description="Assistant info retrieved successfully",
638
+ )
639
+ @openapi.response(
640
+ 409,
641
+ {"application/json": model_to_schema(ApiErrorResponse)},
642
+ description="Agent not ready",
643
+ )
644
+ @openapi.response(
645
+ 500,
646
+ {"application/json": model_to_schema(ApiErrorResponse)},
647
+ description="Internal server error",
648
+ )
649
+ @openapi.parameter(
650
+ HEADER_USER_ID,
651
+ description=(
652
+ "Optional user id to associate requests (e.g., for telemetry/guardrails)."
653
+ ),
654
+ _in="header",
655
+ required=False,
656
+ schema=str,
657
+ )
658
+ async def get_bot_info(request: Request) -> HTTPResponse:
659
+ """Return assistant info including assistant id from model metadata."""
660
+ try:
661
+ agent: Optional[Agent] = request.app.ctx.agent
662
+ if not agent:
663
+ raise AgentNotReady("Can't retrieve bot info without a loaded agent.")
664
+
665
+ assistant_id: Optional[str] = (
666
+ agent.processor.model_metadata.assistant_id
667
+ if agent.processor
668
+ and agent.processor.model_metadata
669
+ and hasattr(agent.processor.model_metadata, "assistant_id")
670
+ else None
671
+ )
672
+
673
+ return response.json(AssistantInfo(assistant_id=assistant_id).model_dump())
674
+ except AgentNotReady as e:
675
+ return response.json(
676
+ ApiErrorResponse(
677
+ error="Agent not ready",
678
+ details={"error": str(e)},
679
+ ).model_dump(),
680
+ status=HTTPStatus.CONFLICT,
681
+ )
682
+ except Exception as exc:
683
+ capture_exception_with_context(
684
+ exc,
685
+ "bot_builder_service.get_bot_info.unexpected_error",
686
+ tags={"endpoint": "/api/assistant"},
687
+ )
688
+ return response.json(
689
+ ApiErrorResponse(
690
+ error="Failed to retrieve bot info",
691
+ details={"error": str(exc)},
692
+ ).model_dump(),
693
+ status=HTTPStatus.INTERNAL_SERVER_ERROR,
694
+ )
695
+
696
+
697
+ @bp.route("/download", methods=["GET"])
698
+ @openapi.summary("Download bot project as tar.gz")
699
+ @openapi.description(
700
+ "Downloads the current bot project files as a compressed tar.gz archive. "
701
+ "Includes all configuration files and a .env file with RASA_PRO_LICENSE. "
702
+ "Requires valid JWT token in Authorization header."
703
+ )
704
+ @openapi.tag("bot-files")
705
+ @openapi.parameter(
706
+ "Authorization",
707
+ description="Bearer token for authentication",
708
+ _in="header",
709
+ required=True,
710
+ schema=str,
711
+ )
712
+ @openapi.parameter(
713
+ HEADER_USER_ID,
714
+ description=(
715
+ "Optional user id to associate requests (e.g., for telemetry/guardrails)."
716
+ ),
717
+ _in="header",
718
+ required=False,
719
+ schema=str,
720
+ )
721
+ @openapi.parameter(
722
+ "project_name",
723
+ description="Name of the project for the archive filename and pyproject.toml",
724
+ _in="query",
725
+ required=False,
726
+ schema=str,
727
+ )
728
+ @openapi.response(
729
+ 200,
730
+ {"application/gzip": bytes},
731
+ description="Bot project downloaded successfully as tar.gz",
732
+ )
733
+ @openapi.response(
734
+ 401,
735
+ {"application/json": model_to_schema(ApiErrorResponse)},
736
+ description="Authentication failed - invalid or missing token",
737
+ )
738
+ @openapi.response(
739
+ 500,
740
+ {"application/json": model_to_schema(ApiErrorResponse)},
741
+ description="Internal server error",
742
+ )
743
+ @protected(always_required=True)
744
+ async def download_bot_project(request: Request) -> HTTPResponse:
745
+ """Download bot project as tar.gz archive."""
746
+ try:
747
+ # Token verification is enforced by the
748
+ # protected(always_required=True) decorator.
749
+
750
+ # Get bot files
751
+ project_generator = get_project_generator(request)
752
+ bot_files = project_generator.get_bot_files()
753
+
754
+ # Get project name from query parameters, default to "bot-project"
755
+ project_name = request.args.get("project_name", "bot-project")
756
+
757
+ # Create tar.gz archive
758
+ tar_data = create_bot_project_archive(bot_files, project_name)
759
+
760
+ structlogger.info(
761
+ "bot_builder_service.download_bot_project.success",
762
+ user_sub=(getattr(request.ctx, "auth_payload", None) or {}).get("sub"),
763
+ files_count=len(bot_files),
764
+ archive_size=len(tar_data),
765
+ payload=getattr(request.ctx, "auth_payload", None),
766
+ project_name=project_name,
767
+ )
768
+
769
+ return response.raw(
770
+ tar_data,
771
+ content_type="application/gzip",
772
+ headers={
773
+ "Content-Disposition": f"attachment; filename={project_name}.tar.gz"
774
+ },
775
+ )
776
+
777
+ except Exception as exc:
778
+ capture_exception_with_context(
779
+ exc,
780
+ "bot_builder_service.download_bot_project.unexpected_error",
781
+ tags={"endpoint": "/api/download"},
782
+ )
783
+ return response.json(
784
+ ApiErrorResponse(
785
+ error="Failed to create bot project archive",
786
+ details={"error": str(exc)},
787
+ ).model_dump(),
788
+ status=500,
789
+ )
790
+
791
+
792
+ @bp.route("/copilot", methods=["POST"])
793
+ @openapi.summary("AI copilot for bot building")
794
+ @openapi.description(
795
+ "Provides LLM-powered copilot assistance with streaming markdown responses. "
796
+ "Returns server-sent events (SSE) for real-time streaming of copilot responses.\n\n"
797
+ "The event's `event` field is the type of event. For this endpoint it will always "
798
+ "be: `copilot_response`.\n\n"
799
+ )
800
+ @openapi.tag("copilot")
801
+ @openapi.body(
802
+ {"application/json": model_to_schema(CopilotRequest)},
803
+ description=(
804
+ "Copilot request containing: "
805
+ "1. conversation history between user and copilot, "
806
+ "2. session ID for tracking conversation context with the bot being built."
807
+ ),
808
+ required=True,
809
+ )
810
+ @openapi.response(
811
+ 200,
812
+ {"text/event-stream": model_to_schema(ServerSentEvent)},
813
+ description=(
814
+ "Server-sent events stream with copilot responses and references. "
815
+ "The event's `event` field is the type "
816
+ "of event. For this endpoint it will always be: `copilot_response`. "
817
+ "The event's data field is a JSON dump. The following describes the event data "
818
+ "field:\n"
819
+ "- `response_category` can be one of the following:\n"
820
+ " - `copilot` - Stream token generated by the copilot.\n"
821
+ " - `out_of_scope_detection` - Response coming from the the out of scope detection.\n" # noqa: E501
822
+ " - `roleplay_detection` - Response coming from the the roleplay detection.\n"
823
+ " - `reference` - Reference section.\n"
824
+ " - `training_error_log_analysis` - Used to flag the responses that are training error log analysis.\n" # noqa: E501
825
+ " - `e2e_testing_error_log_analysis` - Used to flag the responses that are e2e testing error log analysis.\n" # noqa: E501
826
+ " - `guardrail_policy_violation` - Used to flag the responses that are flagged as a violation of the guardrail policy.\n\n" # noqa: E501
827
+ "- `completeness`: Whether this is a streaming token or complete response.\n\n"
828
+ "- `content`: The actual response content (for streaming tokens).\n\n"
829
+ "- `references`: (Only for `reference` response category) List of reference entries. Each reference entry is a dictionary with the following keys:\n\n" # noqa: E501
830
+ " - `index`: Reference index as an integer number.\n"
831
+ " - `title`: Reference title as a string.\n"
832
+ " - `url`: Reference URL as a string.\n\n"
833
+ ),
834
+ examples={
835
+ "Streaming token from copilot": {
836
+ "summary": "Streaming token from copilot",
837
+ "value": GeneratedContent(
838
+ content="<token generated by the copilot>",
839
+ response_category=ResponseCategory.COPILOT,
840
+ response_completeness=ResponseCompleteness.TOKEN,
841
+ )
842
+ .to_sse_event()
843
+ .model_dump(),
844
+ },
845
+ "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
846
+ "summary": "Complete response from copilot",
847
+ "value": GeneratedContent(
848
+ content="<response from the out of scope detection>",
849
+ response_category=ResponseCategory.OUT_OF_SCOPE_DETECTION,
850
+ response_completeness=ResponseCompleteness.COMPLETE,
851
+ )
852
+ .to_sse_event()
853
+ .model_dump(),
854
+ },
855
+ "Response with the references": {
856
+ "summary": "Reference section with all references",
857
+ "value": ReferenceSection(
858
+ references=[
859
+ ReferenceEntry(
860
+ index=1,
861
+ title="Title of the reference",
862
+ url="https://rasa.com/docs/...",
863
+ ),
864
+ ReferenceEntry(
865
+ index=2,
866
+ title="Title of another reference",
867
+ url="https://rasa.com/docs/...",
868
+ ),
869
+ ],
870
+ response_category=ResponseCategory.REFERENCE,
871
+ response_completeness=ResponseCompleteness.COMPLETE,
872
+ )
873
+ .to_sse_event()
874
+ .model_dump(),
875
+ },
876
+ },
877
+ )
878
+ @openapi.response(
879
+ 400,
880
+ {"application/json": model_to_schema(ApiErrorResponse)},
881
+ description="Validation error in request",
882
+ )
883
+ @openapi.response(
884
+ 502,
885
+ {"application/json": model_to_schema(ApiErrorResponse)},
886
+ description="LLM generation failed.",
887
+ )
888
+ @openapi.response(
889
+ 500,
890
+ {"application/json": model_to_schema(ApiErrorResponse)},
891
+ description="Internal server error.",
892
+ )
893
+ @openapi.response(
894
+ 401,
895
+ {"application/json": model_to_schema(ApiErrorResponse)},
896
+ description=(
897
+ "Authentication failed - Authorization header missing or invalid. "
898
+ "Auth may be conditionally required depending on server "
899
+ "configuration."
900
+ ),
901
+ )
902
+ @openapi.parameter(
903
+ "Authorization",
904
+ description=(
905
+ "Bearer token for authentication. Required after auth start window "
906
+ "or if configured."
907
+ ),
908
+ _in="header",
909
+ required=False,
910
+ schema=str,
911
+ )
912
+ @openapi.parameter(
913
+ HEADER_USER_ID,
914
+ description=(
915
+ "Optional user id to associate requests (e.g., for telemetry/guardrails)."
916
+ ),
917
+ _in="header",
918
+ required=False,
919
+ schema=str,
920
+ )
921
+ @protected()
922
+ async def copilot(request: Request) -> None:
923
+ """Handle copilot requests with streaming markdown responses."""
924
+ sse = await request.respond(content_type="text/event-stream")
925
+ project_generator = get_project_generator(request)
926
+
927
+ try:
928
+ # 1. Validate and unpack input
929
+ req = CopilotRequest(**request.json)
930
+
931
+ telemetry = CopilotTelemetry(
932
+ project_id=HELLO_RASA_PROJECT_ID,
933
+ user_id=request.headers.get(HEADER_USER_ID),
934
+ )
935
+ structlogger.debug("builder.copilot.telemetry.request.init")
936
+
937
+ if req.last_message and req.last_message.role == ROLE_USER:
938
+ structlogger.debug("builder.copilot.telemetry.request.user_turn")
939
+ # Offload telemetry logging to a background task
940
+ request.app.add_task(
941
+ asyncio.to_thread(
942
+ telemetry.log_user_turn, req.last_message.get_text_content()
943
+ )
944
+ )
945
+
946
+ # 2. Get the necessary context for the copilot
947
+ tracker = await current_tracker_from_input_channel(request.app, req.session_id)
948
+ tracker_context = TrackerContext.from_tracker(
949
+ tracker, max_turns=COPILOT_ASSISTANT_TRACKER_MAX_TURNS
950
+ )
951
+ if tracker_context is not None:
952
+ tracker_context = await check_assistant_chat_for_policy_violations(
953
+ tracker_context=tracker_context,
954
+ hello_rasa_user_id=request.headers.get(HEADER_USER_ID),
955
+ hello_rasa_project_id=HELLO_RASA_PROJECT_ID,
956
+ )
957
+
958
+ # Copilot doesn't need to know about the docs and any file that is not a core
959
+ # assistant file
960
+ relevant_assistant_files = project_generator.get_bot_files(
961
+ exclude_docs_directory=True,
962
+ allowed_file_extensions=["yaml", "yml", "py", "jinja", "jinja2"],
963
+ )
964
+ context = CopilotContext(
965
+ tracker_context=tracker_context,
966
+ assistant_logs=get_recent_logs(),
967
+ assistant_files=relevant_assistant_files,
968
+ copilot_chat_history=req.copilot_chat_history,
969
+ )
970
+
971
+ # 3. Run guardrail policy checks. If any policy violations are detected,
972
+ # send a response and end the stream.
973
+ guardrail_response: Optional[
974
+ GeneratedContent
975
+ ] = await check_copilot_chat_for_policy_violations(
976
+ context=context,
977
+ hello_rasa_user_id=request.headers.get(HEADER_USER_ID),
978
+ hello_rasa_project_id=HELLO_RASA_PROJECT_ID,
979
+ )
980
+ if guardrail_response is not None:
981
+ await sse.send(guardrail_response.to_sse_event().format())
982
+ await sse.eof()
983
+ return
984
+
985
+ # 4. Get the original response stream from copilot and handle it with the
986
+ # copilot response handler
987
+ start_timestamp = time.perf_counter()
988
+ copilot_client = llm_service.instantiate_copilot()
989
+ (
990
+ original_stream,
991
+ used_documents,
992
+ ) = await copilot_client.generate_response(context)
993
+
994
+ copilot_response_handler = llm_service.instantiate_handler(
995
+ COPILOT_HANDLER_ROLLING_BUFFER_SIZE
996
+ )
997
+ intercepted_stream = copilot_response_handler.handle_response(original_stream)
998
+
999
+ # 5. Stream the intercepted response
1000
+ async for token in intercepted_stream:
1001
+ await sse.send(token.to_sse_event().format())
1002
+
1003
+ # Offload telemetry logging to a background task
1004
+ request.app.add_task(
1005
+ asyncio.to_thread(
1006
+ telemetry.log_copilot_from_handler,
1007
+ handler=copilot_response_handler,
1008
+ used_documents=used_documents,
1009
+ latency_ms=int((time.perf_counter() - start_timestamp) * 1000),
1010
+ **copilot_client.usage_statistics.model_dump(),
1011
+ )
1012
+ )
1013
+
1014
+ # 6. Once the stream is over, extract and send references
1015
+ # if any documents were used
1016
+ if used_documents:
1017
+ reference_section = copilot_response_handler.extract_references(
1018
+ used_documents
1019
+ )
1020
+ await sse.send(reference_section.to_sse_event().format())
1021
+
1022
+ except CopilotStreamError as e:
1023
+ capture_exception_with_context(
1024
+ e,
1025
+ "bot_builder_service.copilot.generation_error",
1026
+ extra={"session_id": req.session_id},
1027
+ tags={"endpoint": "/api/copilot"},
1028
+ )
1029
+ await sse.send(
1030
+ ServerSentEvent(
1031
+ event="error",
1032
+ data={"error": str(e)},
1033
+ ).format()
1034
+ )
1035
+
1036
+ except Exception as exc:
1037
+ capture_exception_with_context(
1038
+ exc,
1039
+ "bot_builder_service.copilot.unexpected_error",
1040
+ extra={"session_id": req.session_id if "req" in locals() else None},
1041
+ tags={"endpoint": "/api/copilot"},
1042
+ )
1043
+ await sse.send(
1044
+ ServerSentEvent(
1045
+ event="error",
1046
+ data={"error": str(exc)},
1047
+ ).format()
1048
+ )
1049
+
1050
+ finally:
1051
+ await sse.eof()
1052
+
1053
+
1054
+ @bp.route("/copilot/internal_message_templates/<template_name>", methods=["GET"])
1055
+ @openapi.summary("Get templated response for copilot internal message")
1056
+ @openapi.description(
1057
+ "Returns the templated response text for a given template name from the "
1058
+ "copilot internal message formatter. This endpoint provides access to the "
1059
+ "predefined templates used for formatting internal system messages."
1060
+ )
1061
+ @openapi.tag("copilot")
1062
+ @openapi.parameter(
1063
+ "template_name",
1064
+ str,
1065
+ location="path",
1066
+ description=(
1067
+ "The template name to get the template for."
1068
+ "(e.g., 'training_error_log_analysis', 'e2e_testing_error_log_analysis')",
1069
+ ),
1070
+ )
1071
+ @openapi.response(
1072
+ 200,
1073
+ {"application/json": {"template": str, "template_name": str}},
1074
+ description="Successfully retrieved the template for the given template name",
1075
+ example={
1076
+ "template": "The assistant training failed. Your task is ...",
1077
+ "template_name": "training_error_log_analysis",
1078
+ },
1079
+ )
1080
+ @openapi.response(
1081
+ 404,
1082
+ {"application/json": model_to_schema(ApiErrorResponse)},
1083
+ description="Template not found for the given template name",
1084
+ )
1085
+ @openapi.parameter(
1086
+ HEADER_USER_ID,
1087
+ description=(
1088
+ "Optional user id to associate requests (e.g., for telemetry/guardrails)."
1089
+ ),
1090
+ _in="header",
1091
+ required=False,
1092
+ schema=str,
1093
+ )
1094
+ async def get_copilot_internal_message_template(
1095
+ request: Request, template_name: str
1096
+ ) -> HTTPResponse:
1097
+ """Get templated response for copilot internal message formatter."""
1098
+ try:
1099
+ # Try to get the template for the given template name
1100
+ template = llm_service.copilot_internal_message_templates.get(template_name)
1101
+ structlogger.info(
1102
+ "bot_builder_service.get_copilot_internal_message_template.template_found",
1103
+ template_name=template_name,
1104
+ template=template,
1105
+ )
1106
+
1107
+ if template is None:
1108
+ structlogger.warning(
1109
+ "bot_builder_service.get_copilot_internal_message_template.not_found",
1110
+ template_name=template_name,
1111
+ )
1112
+ return response.json(
1113
+ ApiErrorResponse(
1114
+ error="Template not found", details={"template_name": template_name}
1115
+ ).model_dump(),
1116
+ status=404,
1117
+ )
1118
+
1119
+ return response.json({"template": template, "template_name": template_name})
1120
+
1121
+ except Exception as e:
1122
+ structlogger.error(
1123
+ "bot_builder_service.get_copilot_internal_message_template.error",
1124
+ error=str(e),
1125
+ template_name=template_name,
1126
+ )
1127
+ return response.json(
1128
+ ApiErrorResponse(
1129
+ error="Internal server error", details={"template_name": template_name}
1130
+ ).model_dump(),
1131
+ status=500,
1132
+ )
1133
+
1134
+
1135
+ async def current_tracker_from_input_channel(
1136
+ app: Any, session_id: str
1137
+ ) -> Optional[DialogueStateTracker]:
1138
+ """Generate chat bot context from current conversation."""
1139
+ if app.ctx.agent and session_id:
1140
+ return await app.ctx.agent.tracker_store.retrieve(session_id)
1141
+ else:
1142
+ return None