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

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

Potentially problematic release.


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

Files changed (586) hide show
  1. rasa/__main__.py +15 -3
  2. rasa/agents/__init__.py +0 -0
  3. rasa/agents/agent_factory.py +122 -0
  4. rasa/agents/agent_manager.py +213 -0
  5. rasa/agents/constants.py +43 -0
  6. rasa/agents/core/__init__.py +0 -0
  7. rasa/agents/core/agent_protocol.py +107 -0
  8. rasa/agents/core/types.py +81 -0
  9. rasa/agents/exceptions.py +38 -0
  10. rasa/agents/protocol/__init__.py +5 -0
  11. rasa/agents/protocol/a2a/__init__.py +0 -0
  12. rasa/agents/protocol/a2a/a2a_agent.py +889 -0
  13. rasa/agents/protocol/mcp/__init__.py +0 -0
  14. rasa/agents/protocol/mcp/mcp_base_agent.py +778 -0
  15. rasa/agents/protocol/mcp/mcp_open_agent.py +327 -0
  16. rasa/agents/protocol/mcp/mcp_task_agent.py +522 -0
  17. rasa/agents/schemas/__init__.py +13 -0
  18. rasa/agents/schemas/agent_input.py +38 -0
  19. rasa/agents/schemas/agent_output.py +26 -0
  20. rasa/agents/schemas/agent_tool_result.py +65 -0
  21. rasa/agents/schemas/agent_tool_schema.py +186 -0
  22. rasa/agents/templates/__init__.py +0 -0
  23. rasa/agents/templates/mcp_open_agent_prompt_template.jinja2 +20 -0
  24. rasa/agents/templates/mcp_task_agent_prompt_template.jinja2 +22 -0
  25. rasa/agents/utils.py +228 -0
  26. rasa/agents/validation.py +538 -0
  27. rasa/api.py +23 -9
  28. rasa/builder/README.md +120 -0
  29. rasa/builder/__init__.py +0 -0
  30. rasa/builder/auth.py +176 -0
  31. rasa/builder/config.py +96 -0
  32. rasa/builder/copilot/__init__.py +0 -0
  33. rasa/builder/copilot/constants.py +38 -0
  34. rasa/builder/copilot/copilot.py +562 -0
  35. rasa/builder/copilot/copilot_response_handler.py +522 -0
  36. rasa/builder/copilot/copilot_templated_message_provider.py +81 -0
  37. rasa/builder/copilot/exceptions.py +32 -0
  38. rasa/builder/copilot/models.py +690 -0
  39. rasa/builder/copilot/prompts/__init__.py +0 -0
  40. rasa/builder/copilot/prompts/copilot_system_prompt.jinja2 +787 -0
  41. rasa/builder/copilot/prompts/copilot_training_error_handler_prompt.jinja2 +53 -0
  42. rasa/builder/copilot/prompts/latest_user_message_context_prompt.jinja2 +91 -0
  43. rasa/builder/copilot/signing.py +305 -0
  44. rasa/builder/copilot/telemetry.py +234 -0
  45. rasa/builder/copilot/templated_messages/__init__.py +0 -0
  46. rasa/builder/copilot/templated_messages/copilot_internal_messages_templates.yml +16 -0
  47. rasa/builder/copilot/templated_messages/copilot_templated_responses.yml +41 -0
  48. rasa/builder/copilot/templated_messages/copilot_welcome_messages.yml +56 -0
  49. rasa/builder/document_retrieval/__init__.py +0 -0
  50. rasa/builder/document_retrieval/constants.py +15 -0
  51. rasa/builder/document_retrieval/inkeep-rag-response-schema.json +64 -0
  52. rasa/builder/document_retrieval/inkeep_document_retrieval.py +238 -0
  53. rasa/builder/document_retrieval/models.py +62 -0
  54. rasa/builder/download.py +140 -0
  55. rasa/builder/exceptions.py +91 -0
  56. rasa/builder/guardrails/__init__.py +1 -0
  57. rasa/builder/guardrails/clients.py +256 -0
  58. rasa/builder/guardrails/constants.py +12 -0
  59. rasa/builder/guardrails/exceptions.py +4 -0
  60. rasa/builder/guardrails/models.py +266 -0
  61. rasa/builder/guardrails/policy_checker.py +324 -0
  62. rasa/builder/guardrails/store.py +238 -0
  63. rasa/builder/guardrails/utils.py +94 -0
  64. rasa/builder/job_manager.py +87 -0
  65. rasa/builder/jobs.py +609 -0
  66. rasa/builder/llm_service.py +273 -0
  67. rasa/builder/logging_utils.py +265 -0
  68. rasa/builder/main.py +234 -0
  69. rasa/builder/models.py +229 -0
  70. rasa/builder/project_generator.py +463 -0
  71. rasa/builder/project_info.py +72 -0
  72. rasa/builder/service.py +1367 -0
  73. rasa/builder/shared/tracker_context.py +212 -0
  74. rasa/builder/skill_to_bot_prompt.jinja2 +164 -0
  75. rasa/builder/template_cache.py +69 -0
  76. rasa/builder/training_service.py +188 -0
  77. rasa/builder/validation_service.py +101 -0
  78. rasa/cli/arguments/data.py +9 -0
  79. rasa/cli/arguments/default_arguments.py +12 -0
  80. rasa/cli/arguments/run.py +2 -0
  81. rasa/cli/arguments/train.py +2 -0
  82. rasa/cli/data.py +78 -10
  83. rasa/cli/dialogue_understanding_test.py +11 -7
  84. rasa/cli/e2e_test.py +10 -6
  85. rasa/cli/evaluate.py +4 -2
  86. rasa/cli/export.py +5 -2
  87. rasa/cli/inspect.py +9 -4
  88. rasa/cli/interactive.py +8 -4
  89. rasa/cli/llm_fine_tuning.py +12 -6
  90. rasa/cli/project_templates/basic/README.md +23 -0
  91. rasa/cli/project_templates/basic/actions/__init__ +0 -0
  92. rasa/cli/project_templates/basic/actions/action_human_handoff.py +40 -0
  93. rasa/cli/project_templates/basic/actions/actions.md +10 -0
  94. rasa/cli/project_templates/basic/config.yml +29 -0
  95. rasa/cli/project_templates/basic/credentials.yml +33 -0
  96. rasa/cli/project_templates/basic/data/data.md +8 -0
  97. rasa/cli/project_templates/basic/data/general/feedback.yml +21 -0
  98. rasa/cli/project_templates/basic/data/general/goodbye.yml +6 -0
  99. rasa/cli/project_templates/basic/data/general/hello.yml +6 -0
  100. rasa/cli/project_templates/basic/data/general/help.yml +6 -0
  101. rasa/cli/project_templates/basic/data/general/human_handoff.yml +16 -0
  102. rasa/cli/project_templates/basic/data/general/show_faqs.yml +6 -0
  103. rasa/cli/project_templates/basic/data/system/patterns/pattern_cannot_handle.yml +7 -0
  104. rasa/cli/project_templates/basic/data/system/patterns/pattern_completed.yml +7 -0
  105. rasa/cli/project_templates/basic/data/system/patterns/pattern_correction.yml +7 -0
  106. rasa/cli/project_templates/basic/data/system/patterns/pattern_search.yml +8 -0
  107. rasa/cli/project_templates/basic/data/system/patterns/pattern_session_start.yml +8 -0
  108. rasa/cli/project_templates/basic/docs/docs.md +5 -0
  109. rasa/cli/project_templates/basic/docs/template.txt +28 -0
  110. rasa/cli/project_templates/basic/domain/domain.md +11 -0
  111. rasa/cli/project_templates/basic/domain/general/feedback.yml +25 -0
  112. rasa/cli/project_templates/basic/domain/general/goodbye.yml +9 -0
  113. rasa/cli/project_templates/basic/domain/general/hello.yml +7 -0
  114. rasa/cli/project_templates/basic/domain/general/help.yml +21 -0
  115. rasa/cli/project_templates/basic/domain/general/human_handoff.yml +32 -0
  116. rasa/cli/project_templates/basic/domain/general/show_faqs.yml +14 -0
  117. rasa/cli/project_templates/basic/domain/system/patterns/pattern_cannot_handle.yml +5 -0
  118. rasa/cli/project_templates/basic/domain/system/patterns/pattern_session_start.yml +19 -0
  119. rasa/cli/project_templates/basic/endpoints.yml +67 -0
  120. rasa/cli/project_templates/basic/prompts/rephraser_demo_personality_prompt.jinja2 +38 -0
  121. rasa/cli/project_templates/basic/tests/e2e_test_cases/without_stub/general/feedback.yml +46 -0
  122. rasa/cli/project_templates/basic/tests/e2e_test_cases/without_stub/general/goodbye.yml +9 -0
  123. rasa/cli/project_templates/basic/tests/e2e_test_cases/without_stub/general/hello.yml +8 -0
  124. rasa/cli/project_templates/basic/tests/e2e_test_cases/without_stub/general/help.yml +8 -0
  125. rasa/cli/project_templates/basic/tests/e2e_test_cases/without_stub/general/human_handoff.yml +41 -0
  126. rasa/cli/project_templates/basic/tests/e2e_test_cases/without_stub/general/patterns.yml +32 -0
  127. rasa/cli/project_templates/basic/tests/e2e_test_cases/without_stub/general/show_faqs.yml +8 -0
  128. rasa/cli/project_templates/default/config.yml +4 -0
  129. rasa/cli/project_templates/default/endpoints.yml +4 -0
  130. rasa/cli/project_templates/defaults.py +1 -0
  131. rasa/cli/project_templates/finance/README.md +26 -0
  132. rasa/cli/project_templates/finance/actions/__init__.py +0 -0
  133. rasa/cli/project_templates/finance/actions/accounts/__init__.py +0 -0
  134. rasa/cli/project_templates/finance/actions/accounts/check_balance.py +18 -0
  135. rasa/cli/project_templates/finance/actions/actions.md +15 -0
  136. rasa/cli/project_templates/finance/actions/cards/__init__.py +0 -0
  137. rasa/cli/project_templates/finance/actions/cards/check_that_card_exists.py +21 -0
  138. rasa/cli/project_templates/finance/actions/cards/list_cards.py +22 -0
  139. rasa/cli/project_templates/finance/actions/contacts/__init__.py +0 -0
  140. rasa/cli/project_templates/finance/actions/contacts/add_contact.py +30 -0
  141. rasa/cli/project_templates/finance/actions/contacts/list_contacts.py +22 -0
  142. rasa/cli/project_templates/finance/actions/contacts/remove_contact.py +35 -0
  143. rasa/cli/project_templates/finance/actions/db.py +117 -0
  144. rasa/cli/project_templates/finance/actions/general/__init__.py +0 -0
  145. rasa/cli/project_templates/finance/actions/general/action_human_handoff.py +49 -0
  146. rasa/cli/project_templates/finance/actions/transfers/__init__.py +0 -0
  147. rasa/cli/project_templates/finance/actions/transfers/check_transfer_funds.py +27 -0
  148. rasa/cli/project_templates/finance/actions/transfers/check_transfer_limit.py +36 -0
  149. rasa/cli/project_templates/finance/actions/transfers/execute_recurrent_payment.py +20 -0
  150. rasa/cli/project_templates/finance/actions/transfers/execute_transfer.py +45 -0
  151. rasa/cli/project_templates/finance/actions/transfers/list_transactions.py +32 -0
  152. rasa/cli/project_templates/finance/config.yml +29 -0
  153. rasa/cli/project_templates/finance/credentials.yml +33 -0
  154. rasa/cli/project_templates/finance/data/accounts/check_balance.yml +9 -0
  155. rasa/cli/project_templates/finance/data/accounts/download_statements.yml +26 -0
  156. rasa/cli/project_templates/finance/data/bills/bill_pay_reminder.yml +25 -0
  157. rasa/cli/project_templates/finance/data/cards/activate_card.yml +35 -0
  158. rasa/cli/project_templates/finance/data/cards/block_card.yml +45 -0
  159. rasa/cli/project_templates/finance/data/cards/list_cards.yml +14 -0
  160. rasa/cli/project_templates/finance/data/cards/replace_card.yml +16 -0
  161. rasa/cli/project_templates/finance/data/cards/replace_eligible_card.yml +29 -0
  162. rasa/cli/project_templates/finance/data/contacts/add_contact.yml +33 -0
  163. rasa/cli/project_templates/finance/data/contacts/list_contacts.yml +14 -0
  164. rasa/cli/project_templates/finance/data/contacts/remove_contact.yml +31 -0
  165. rasa/cli/project_templates/finance/data/data.md +14 -0
  166. rasa/cli/project_templates/finance/data/general/bot_challenge.yml +6 -0
  167. rasa/cli/project_templates/finance/data/general/feedback.yml +20 -0
  168. rasa/cli/project_templates/finance/data/general/goodbye.yml +6 -0
  169. rasa/cli/project_templates/finance/data/general/hello.yml +6 -0
  170. rasa/cli/project_templates/finance/data/general/help.yml +9 -0
  171. rasa/cli/project_templates/finance/data/general/human_handoff.yml +16 -0
  172. rasa/cli/project_templates/finance/data/general/welcome.yml +9 -0
  173. rasa/cli/project_templates/finance/data/system/patterns/pattern_completed.yml +7 -0
  174. rasa/cli/project_templates/finance/data/system/patterns/pattern_correction.yml +7 -0
  175. rasa/cli/project_templates/finance/data/system/patterns/pattern_search.yml +8 -0
  176. rasa/cli/project_templates/finance/data/system/patterns/pattern_session_start.yml +8 -0
  177. rasa/cli/project_templates/finance/data/transfers/check_transfer_limit.yml +18 -0
  178. rasa/cli/project_templates/finance/data/transfers/list_transactions.yml +46 -0
  179. rasa/cli/project_templates/finance/data/transfers/move_money_between_accounts.yml +51 -0
  180. rasa/cli/project_templates/finance/data/transfers/transfer_money.yml +34 -0
  181. rasa/cli/project_templates/finance/data/transfers/transfer_money_to_a_third_party.yml +175 -0
  182. rasa/cli/project_templates/finance/db/cards.json +18 -0
  183. rasa/cli/project_templates/finance/db/contacts.json +10 -0
  184. rasa/cli/project_templates/finance/db/my_account.json +6 -0
  185. rasa/cli/project_templates/finance/db/transactions.json +22 -0
  186. rasa/cli/project_templates/finance/docs/docs.md +8 -0
  187. rasa/cli/project_templates/finance/docs/fenlo_banking_faq/account_features/budgeting_analytics.txt +22 -0
  188. rasa/cli/project_templates/finance/docs/fenlo_banking_faq/account_features/multi_currency_accounts.txt +19 -0
  189. rasa/cli/project_templates/finance/docs/fenlo_banking_faq/account_features/premium_benefits.txt +19 -0
  190. rasa/cli/project_templates/finance/docs/fenlo_banking_faq/card_management/contactless_limits.txt +16 -0
  191. rasa/cli/project_templates/finance/docs/fenlo_banking_faq/card_management/freeze_unfreeze_card.txt +16 -0
  192. rasa/cli/project_templates/finance/docs/fenlo_banking_faq/card_management/lost_stolen_card.txt +19 -0
  193. rasa/cli/project_templates/finance/docs/fenlo_banking_faq/money_transfers/instant_payments.txt +19 -0
  194. rasa/cli/project_templates/finance/docs/fenlo_banking_faq/money_transfers/international_transfers.txt +19 -0
  195. rasa/cli/project_templates/finance/docs/fenlo_banking_faq/security_fraud/fraud_protection.txt +22 -0
  196. rasa/cli/project_templates/finance/docs/fenlo_banking_faq/security_fraud/secure_payments.txt +22 -0
  197. rasa/cli/project_templates/finance/domain/accounts/check_balance.yml +15 -0
  198. rasa/cli/project_templates/finance/domain/accounts/download_statements.yml +40 -0
  199. rasa/cli/project_templates/finance/domain/bills/bill_pay_reminder.yml +49 -0
  200. rasa/cli/project_templates/finance/domain/cards/activate_card.yml +24 -0
  201. rasa/cli/project_templates/finance/domain/cards/block_card.yml +44 -0
  202. rasa/cli/project_templates/finance/domain/cards/list_cards.yml +16 -0
  203. rasa/cli/project_templates/finance/domain/cards/replace_card.yml +43 -0
  204. rasa/cli/project_templates/finance/domain/cards/shared.yml +15 -0
  205. rasa/cli/project_templates/finance/domain/contacts/add_contact.yml +37 -0
  206. rasa/cli/project_templates/finance/domain/contacts/list_contacts.yml +16 -0
  207. rasa/cli/project_templates/finance/domain/contacts/remove_contact.yml +32 -0
  208. rasa/cli/project_templates/finance/domain/domain.md +18 -0
  209. rasa/cli/project_templates/finance/domain/general/_shared.yml +39 -0
  210. rasa/cli/project_templates/finance/domain/general/bot_challenge.yml +4 -0
  211. rasa/cli/project_templates/finance/domain/general/cannot_handle.yml +8 -0
  212. rasa/cli/project_templates/finance/domain/general/feedback.yml +25 -0
  213. rasa/cli/project_templates/finance/domain/general/goodbye.yml +7 -0
  214. rasa/cli/project_templates/finance/domain/general/help.yml +0 -0
  215. rasa/cli/project_templates/finance/domain/general/human_handoff.yml +31 -0
  216. rasa/cli/project_templates/finance/domain/general/welcome.yml +39 -0
  217. rasa/cli/project_templates/finance/domain/transfers/check_transfer_limit.yml +32 -0
  218. rasa/cli/project_templates/finance/domain/transfers/list_transactions.yml +44 -0
  219. rasa/cli/project_templates/finance/domain/transfers/shared.yml +17 -0
  220. rasa/cli/project_templates/finance/domain/transfers/transfer_money.yml +221 -0
  221. rasa/cli/project_templates/finance/endpoints.yml +67 -0
  222. rasa/cli/project_templates/finance/prompts/rephraser_demo_personality_prompt.jinja2 +38 -0
  223. rasa/cli/project_templates/finance/tests/e2e_test_cases/without_stub/accounts/check_balance.yml +9 -0
  224. rasa/cli/project_templates/finance/tests/e2e_test_cases/without_stub/accounts/download_statements.yml +43 -0
  225. rasa/cli/project_templates/finance/tests/e2e_test_cases/without_stub/cards/block_card.yml +55 -0
  226. rasa/cli/project_templates/finance/tests/e2e_test_cases/without_stub/general/bot_challenge.yml +8 -0
  227. rasa/cli/project_templates/finance/tests/e2e_test_cases/without_stub/general/feedback.yml +46 -0
  228. rasa/cli/project_templates/finance/tests/e2e_test_cases/without_stub/general/goodbye.yml +9 -0
  229. rasa/cli/project_templates/finance/tests/e2e_test_cases/without_stub/general/hello.yml +8 -0
  230. rasa/cli/project_templates/finance/tests/e2e_test_cases/without_stub/general/human_handoff.yml +35 -0
  231. rasa/cli/project_templates/finance/tests/e2e_test_cases/without_stub/general/patterns.yml +22 -0
  232. rasa/cli/project_templates/finance/tests/e2e_test_cases/without_stub/transfers/transfer_money.yml +56 -0
  233. rasa/cli/project_templates/telco/README.md +25 -0
  234. rasa/cli/project_templates/telco/actions/__init__.py +0 -0
  235. rasa/cli/project_templates/telco/actions/actions.md +12 -0
  236. rasa/cli/project_templates/telco/actions/billing/__init__.py +0 -0
  237. rasa/cli/project_templates/telco/actions/billing/actions_billing.py +204 -0
  238. rasa/cli/project_templates/telco/actions/general/__init__.py +0 -0
  239. rasa/cli/project_templates/telco/actions/general/action_human_handoff.py +49 -0
  240. rasa/cli/project_templates/telco/actions/network/__init__.py +0 -0
  241. rasa/cli/project_templates/telco/actions/network/actions_get_data_from_db.py +48 -0
  242. rasa/cli/project_templates/telco/actions/network/actions_run_diagnostics.py +28 -0
  243. rasa/cli/project_templates/telco/actions/network/actions_session_start.py +18 -0
  244. rasa/cli/project_templates/telco/config.yml +29 -0
  245. rasa/cli/project_templates/telco/credentials.yml +33 -0
  246. rasa/cli/project_templates/telco/csvs/billing.csv +19 -0
  247. rasa/cli/project_templates/telco/csvs/customers.csv +5 -0
  248. rasa/cli/project_templates/telco/data/billing/flow_understand_bill.yml +45 -0
  249. rasa/cli/project_templates/telco/data/data.md +11 -0
  250. rasa/cli/project_templates/telco/data/general/bot_challenge.yml +6 -0
  251. rasa/cli/project_templates/telco/data/general/feedback.yml +20 -0
  252. rasa/cli/project_templates/telco/data/general/goodbye.yml +6 -0
  253. rasa/cli/project_templates/telco/data/general/hello.yml +6 -0
  254. rasa/cli/project_templates/telco/data/general/human_handoff.yml +16 -0
  255. rasa/cli/project_templates/telco/data/general/patterns.yml +30 -0
  256. rasa/cli/project_templates/telco/data/network/flow_reboot_router.yml +8 -0
  257. rasa/cli/project_templates/telco/data/network/flow_reset_router.yml +7 -0
  258. rasa/cli/project_templates/telco/data/network/flow_solve_internet_issue.yml +73 -0
  259. rasa/cli/project_templates/telco/docs/docs.md +8 -0
  260. rasa/cli/project_templates/telco/docs/network/reset_vs_rboot_router.txt +1 -0
  261. rasa/cli/project_templates/telco/docs/network/restart_router.txt +6 -0
  262. rasa/cli/project_templates/telco/docs/network/run_speed_test.txt +6 -0
  263. rasa/cli/project_templates/telco/domain/billing/understand_bill.yml +102 -0
  264. rasa/cli/project_templates/telco/domain/domain.md +13 -0
  265. rasa/cli/project_templates/telco/domain/general/bot_challenge.yml +4 -0
  266. rasa/cli/project_templates/telco/domain/general/feedback.yml +25 -0
  267. rasa/cli/project_templates/telco/domain/general/goodbye.yml +7 -0
  268. rasa/cli/project_templates/telco/domain/general/hello.yml +5 -0
  269. rasa/cli/project_templates/telco/domain/general/human_handoff.yml +26 -0
  270. rasa/cli/project_templates/telco/domain/general/patterns.yml +33 -0
  271. rasa/cli/project_templates/telco/domain/network/reboot_router.yml +21 -0
  272. rasa/cli/project_templates/telco/domain/network/reset_router.yml +12 -0
  273. rasa/cli/project_templates/telco/domain/network/run_speed_test.yml +25 -0
  274. rasa/cli/project_templates/telco/domain/network/solve_internet_issue.yml +74 -0
  275. rasa/cli/project_templates/telco/domain/shared.yml +129 -0
  276. rasa/cli/project_templates/telco/endpoints.yml +67 -0
  277. rasa/cli/project_templates/telco/prompts/rephraser_demo_personality_prompt.jinja2 +40 -0
  278. rasa/cli/project_templates/telco/tests/e2e_test_cases/with_stub/network/solve_internet_not_slow.yml +33 -0
  279. rasa/cli/project_templates/telco/tests/e2e_test_cases/with_stub/network/solve_internet_slow.yml +47 -0
  280. rasa/cli/project_templates/telco/tests/e2e_test_cases/without_stub/billing/understand_bill.yml +67 -0
  281. rasa/cli/project_templates/telco/tests/e2e_test_cases/without_stub/general/bot_challenge.yml +8 -0
  282. rasa/cli/project_templates/telco/tests/e2e_test_cases/without_stub/general/feedback.yml +46 -0
  283. rasa/cli/project_templates/telco/tests/e2e_test_cases/without_stub/general/goodbye.yml +9 -0
  284. rasa/cli/project_templates/telco/tests/e2e_test_cases/without_stub/general/hello.yml +8 -0
  285. rasa/cli/project_templates/telco/tests/e2e_test_cases/without_stub/general/human_handoff.yml +35 -0
  286. rasa/cli/project_templates/telco/tests/e2e_test_cases/without_stub/general/patterns.yml +23 -0
  287. rasa/cli/project_templates/tutorial/config.yml +2 -1
  288. rasa/cli/project_templates/tutorial/credentials.yml +10 -0
  289. rasa/cli/run.py +8 -10
  290. rasa/cli/scaffold.py +50 -6
  291. rasa/cli/shell.py +10 -5
  292. rasa/cli/studio/studio.py +1 -1
  293. rasa/cli/test.py +34 -14
  294. rasa/cli/train.py +44 -30
  295. rasa/cli/utils.py +1 -393
  296. rasa/cli/validation/__init__.py +0 -0
  297. rasa/cli/validation/bot_config.py +232 -0
  298. rasa/cli/validation/config_path_validation.py +257 -0
  299. rasa/cli/x.py +8 -4
  300. rasa/constants.py +7 -1
  301. rasa/core/actions/action.py +53 -13
  302. rasa/core/actions/action_exceptions.py +1 -1
  303. rasa/core/actions/action_run_slot_rejections.py +1 -1
  304. rasa/core/actions/grpc_custom_action_executor.py +1 -1
  305. rasa/core/agent.py +22 -2
  306. rasa/core/available_agents.py +239 -0
  307. rasa/core/brokers/broker.py +1 -1
  308. rasa/core/brokers/kafka.py +56 -8
  309. rasa/core/channels/__init__.py +82 -35
  310. rasa/core/channels/channel.py +4 -3
  311. rasa/core/channels/constants.py +3 -0
  312. rasa/core/channels/development_inspector.py +29 -16
  313. rasa/core/channels/hangouts.py +2 -2
  314. rasa/core/channels/inspector/README.md +25 -13
  315. rasa/core/channels/inspector/dist/assets/{arc-0b11fe30.js → arc-6177260a.js} +1 -1
  316. rasa/core/channels/inspector/dist/assets/{blockDiagram-38ab4fdb-9eef30a7.js → blockDiagram-38ab4fdb-b054f038.js} +1 -1
  317. rasa/core/channels/inspector/dist/assets/{c4Diagram-3d4e48cf-03e94f28.js → c4Diagram-3d4e48cf-f25427d5.js} +1 -1
  318. rasa/core/channels/inspector/dist/assets/channel-bf9cbb34.js +1 -0
  319. rasa/core/channels/inspector/dist/assets/{classDiagram-70f12bd4-95c09eba.js → classDiagram-70f12bd4-c7a2af53.js} +1 -1
  320. rasa/core/channels/inspector/dist/assets/{classDiagram-v2-f2320105-38e8446c.js → classDiagram-v2-f2320105-58db65c0.js} +1 -1
  321. rasa/core/channels/inspector/dist/assets/clone-8f9083bb.js +1 -0
  322. rasa/core/channels/inspector/dist/assets/{createText-2e5e7dd3-57dc3038.js → createText-2e5e7dd3-088372e2.js} +1 -1
  323. rasa/core/channels/inspector/dist/assets/{edges-e0da2a9e-4bac0545.js → edges-e0da2a9e-58676240.js} +1 -1
  324. rasa/core/channels/inspector/dist/assets/{erDiagram-9861fffd-81795c90.js → erDiagram-9861fffd-0c14d7c6.js} +1 -1
  325. rasa/core/channels/inspector/dist/assets/{flowDb-956e92f1-89489ae6.js → flowDb-956e92f1-ea63f85c.js} +1 -1
  326. rasa/core/channels/inspector/dist/assets/{flowDiagram-66a62f08-cd152627.js → flowDiagram-66a62f08-a2af48cd.js} +1 -1
  327. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-9ecd5b59.js +1 -0
  328. rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-4a651766-3da369bc.js → flowchart-elk-definition-4a651766-6937abe7.js} +1 -1
  329. rasa/core/channels/inspector/dist/assets/{ganttDiagram-c361ad54-85ec16f8.js → ganttDiagram-c361ad54-7473f357.js} +1 -1
  330. rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-72cf32ee-495bc140.js → gitGraphDiagram-72cf32ee-d0c9405e.js} +1 -1
  331. rasa/core/channels/inspector/dist/assets/{graph-1ec4d266.js → graph-0a6f8466.js} +1 -1
  332. rasa/core/channels/inspector/dist/assets/{index-3862675e-0a0e97c9.js → index-3862675e-7610671a.js} +1 -1
  333. rasa/core/channels/inspector/dist/assets/index-74e01d94.js +1354 -0
  334. rasa/core/channels/inspector/dist/assets/{infoDiagram-f8f76790-4d54bcde.js → infoDiagram-f8f76790-be397dc7.js} +1 -1
  335. rasa/core/channels/inspector/dist/assets/{journeyDiagram-49397b02-dc097114.js → journeyDiagram-49397b02-4cefbf62.js} +1 -1
  336. rasa/core/channels/inspector/dist/assets/{layout-1a08981e.js → layout-e7fbc2bf.js} +1 -1
  337. rasa/core/channels/inspector/dist/assets/{line-95f7f1d3.js → line-a8aa457c.js} +1 -1
  338. rasa/core/channels/inspector/dist/assets/{linear-97e69543.js → linear-3351e0d2.js} +1 -1
  339. rasa/core/channels/inspector/dist/assets/{mindmap-definition-fc14e90a-8c71ff03.js → mindmap-definition-fc14e90a-b8cbf605.js} +1 -1
  340. rasa/core/channels/inspector/dist/assets/{pieDiagram-8a3498a8-f14c71c7.js → pieDiagram-8a3498a8-f327f774.js} +1 -1
  341. rasa/core/channels/inspector/dist/assets/{quadrantDiagram-120e2f19-f1d3c9ff.js → quadrantDiagram-120e2f19-2854c591.js} +1 -1
  342. rasa/core/channels/inspector/dist/assets/{requirementDiagram-deff3bca-bfa2412f.js → requirementDiagram-deff3bca-964985d5.js} +1 -1
  343. rasa/core/channels/inspector/dist/assets/{sankeyDiagram-04a897e0-53f2c97b.js → sankeyDiagram-04a897e0-edeb4f33.js} +1 -1
  344. rasa/core/channels/inspector/dist/assets/{sequenceDiagram-704730f1-319d7c0e.js → sequenceDiagram-704730f1-fcf70125.js} +1 -1
  345. rasa/core/channels/inspector/dist/assets/{stateDiagram-587899a1-76a09418.js → stateDiagram-587899a1-0e770395.js} +1 -1
  346. rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-d93cdb3a-a67f15d4.js → stateDiagram-v2-d93cdb3a-af8dcd22.js} +1 -1
  347. rasa/core/channels/inspector/dist/assets/{styles-6aaf32cf-0654e7c3.js → styles-6aaf32cf-36a9e70d.js} +1 -1
  348. rasa/core/channels/inspector/dist/assets/{styles-9a916d00-1394bb9d.js → styles-9a916d00-884a8b5b.js} +1 -1
  349. rasa/core/channels/inspector/dist/assets/{styles-c10674c1-e4c5bdae.js → styles-c10674c1-dc097813.js} +1 -1
  350. rasa/core/channels/inspector/dist/assets/{svgDrawCommon-08f97a94-50957104.js → svgDrawCommon-08f97a94-5a2c7eed.js} +1 -1
  351. rasa/core/channels/inspector/dist/assets/{timeline-definition-85554ec2-b0885a6a.js → timeline-definition-85554ec2-e89c4f6e.js} +1 -1
  352. rasa/core/channels/inspector/dist/assets/{xychartDiagram-e933f94c-79e6541a.js → xychartDiagram-e933f94c-afb6fe56.js} +1 -1
  353. rasa/core/channels/inspector/dist/index.html +1 -1
  354. rasa/core/channels/inspector/package.json +18 -18
  355. rasa/core/channels/inspector/src/App.tsx +56 -12
  356. rasa/core/channels/inspector/src/components/DiagramFlow.tsx +1 -1
  357. rasa/core/channels/inspector/src/components/DialogueAgentStack.tsx +108 -0
  358. rasa/core/channels/inspector/src/components/{DialogueStack.tsx → DialogueHistoryStack.tsx} +4 -2
  359. rasa/core/channels/inspector/src/components/DialogueInformation.tsx +20 -3
  360. rasa/core/channels/inspector/src/components/LatencyDisplay.tsx +296 -0
  361. rasa/core/channels/inspector/src/components/LoadingSpinner.tsx +6 -2
  362. rasa/core/channels/inspector/src/helpers/audio/audiostream.ts +26 -4
  363. rasa/core/channels/inspector/src/helpers/formatters.test.ts +4 -0
  364. rasa/core/channels/inspector/src/helpers/formatters.ts +24 -3
  365. rasa/core/channels/inspector/src/helpers/utils.test.ts +127 -0
  366. rasa/core/channels/inspector/src/helpers/utils.ts +66 -1
  367. rasa/core/channels/inspector/src/theme/base/styles.ts +19 -1
  368. rasa/core/channels/inspector/src/types.ts +55 -1
  369. rasa/core/channels/inspector/yarn.lock +336 -189
  370. rasa/core/channels/socketio.py +212 -51
  371. rasa/core/channels/studio_chat.py +82 -32
  372. rasa/core/channels/telegram.py +4 -9
  373. rasa/core/channels/voice_ready/twilio_voice.py +1 -1
  374. rasa/core/channels/voice_stream/asr/asr_event.py +1 -1
  375. rasa/core/channels/voice_stream/asr/azure.py +6 -3
  376. rasa/core/channels/voice_stream/asr/deepgram.py +1 -1
  377. rasa/core/channels/voice_stream/audiocodes.py +11 -6
  378. rasa/core/channels/voice_stream/browser_audio.py +91 -4
  379. rasa/core/channels/voice_stream/call_state.py +13 -2
  380. rasa/core/channels/voice_stream/genesys.py +19 -15
  381. rasa/core/channels/voice_stream/jambonz.py +22 -12
  382. rasa/core/channels/voice_stream/tts/deepgram.py +140 -0
  383. rasa/core/channels/voice_stream/twilio_media_streams.py +35 -14
  384. rasa/core/channels/voice_stream/util.py +11 -1
  385. rasa/core/channels/voice_stream/voice_channel.py +170 -32
  386. rasa/core/concurrent_lock_store.py +83 -16
  387. rasa/core/config/__init__.py +0 -0
  388. rasa/core/{available_endpoints.py → config/available_endpoints.py} +56 -18
  389. rasa/core/config/configuration.py +295 -0
  390. rasa/core/config/credentials.py +19 -0
  391. rasa/core/config/message_procesing_config.py +34 -0
  392. rasa/core/constants.py +17 -0
  393. rasa/core/exceptions.py +1 -1
  394. rasa/core/featurizers/tracker_featurizers.py +3 -2
  395. rasa/core/iam_credentials_providers/__init__.py +0 -0
  396. rasa/core/iam_credentials_providers/aws_iam_credentials_providers.py +291 -0
  397. rasa/core/iam_credentials_providers/credentials_provider_protocol.py +91 -0
  398. rasa/core/lock_store.py +50 -10
  399. rasa/core/nlg/contextual_response_rephraser.py +5 -0
  400. rasa/core/nlg/generator.py +1 -1
  401. rasa/core/persistor.py +7 -7
  402. rasa/core/policies/enterprise_search_policy.py +9 -10
  403. rasa/core/policies/flow_policy.py +4 -4
  404. rasa/core/policies/flows/agent_executor.py +720 -0
  405. rasa/core/policies/flows/flow_exceptions.py +5 -2
  406. rasa/core/policies/flows/flow_executor.py +146 -77
  407. rasa/core/policies/flows/mcp_tool_executor.py +304 -0
  408. rasa/core/policies/intentless_policy.py +1 -1
  409. rasa/core/policies/rule_policy.py +1 -1
  410. rasa/core/policies/ted_policy.py +20 -12
  411. rasa/core/policies/unexpected_intent_policy.py +6 -0
  412. rasa/core/processor.py +100 -44
  413. rasa/core/redis_connection_factory.py +474 -0
  414. rasa/core/run.py +49 -10
  415. rasa/core/test.py +4 -0
  416. rasa/core/tracker_stores/redis_tracker_store.py +36 -14
  417. rasa/core/tracker_stores/sql_tracker_store.py +59 -1
  418. rasa/core/tracker_stores/tracker_store.py +3 -7
  419. rasa/core/train.py +1 -1
  420. rasa/core/training/interactive.py +20 -18
  421. rasa/core/training/story_conflict.py +5 -5
  422. rasa/core/utils.py +22 -23
  423. rasa/dialogue_understanding/commands/__init__.py +8 -0
  424. rasa/dialogue_understanding/commands/cancel_flow_command.py +20 -6
  425. rasa/dialogue_understanding/commands/chit_chat_answer_command.py +21 -2
  426. rasa/dialogue_understanding/commands/clarify_command.py +20 -2
  427. rasa/dialogue_understanding/commands/continue_agent_command.py +91 -0
  428. rasa/dialogue_understanding/commands/knowledge_answer_command.py +21 -2
  429. rasa/dialogue_understanding/commands/restart_agent_command.py +162 -0
  430. rasa/dialogue_understanding/commands/start_flow_command.py +75 -7
  431. rasa/dialogue_understanding/commands/utils.py +135 -2
  432. rasa/dialogue_understanding/generator/command_parser.py +4 -0
  433. rasa/dialogue_understanding/generator/flow_retrieval.py +0 -9
  434. rasa/dialogue_understanding/generator/llm_based_command_generator.py +52 -12
  435. rasa/dialogue_understanding/generator/llm_command_generator.py +1 -1
  436. rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +1 -1
  437. rasa/dialogue_understanding/generator/prompt_templates/agent_command_prompt_v2_claude_3_5_sonnet_20240620_template.jinja2 +66 -0
  438. rasa/dialogue_understanding/generator/prompt_templates/agent_command_prompt_v2_gpt_4o_2024_11_20_template.jinja2 +66 -0
  439. rasa/dialogue_understanding/generator/prompt_templates/agent_command_prompt_v3_claude_3_5_sonnet_20240620_template.jinja2 +89 -0
  440. rasa/dialogue_understanding/generator/prompt_templates/agent_command_prompt_v3_gpt_4o_2024_11_20_template.jinja2 +88 -0
  441. rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +42 -7
  442. rasa/dialogue_understanding/generator/single_step/search_ready_llm_command_generator.py +40 -3
  443. rasa/dialogue_understanding/generator/single_step/single_step_based_llm_command_generator.py +20 -3
  444. rasa/dialogue_understanding/patterns/cancel.py +27 -6
  445. rasa/dialogue_understanding/patterns/clarify.py +3 -14
  446. rasa/dialogue_understanding/patterns/continue_interrupted.py +239 -6
  447. rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +49 -9
  448. rasa/dialogue_understanding/processor/command_processor.py +136 -15
  449. rasa/dialogue_understanding/stack/dialogue_stack.py +98 -2
  450. rasa/dialogue_understanding/stack/frames/flow_stack_frame.py +57 -0
  451. rasa/dialogue_understanding/stack/utils.py +57 -3
  452. rasa/dialogue_understanding/utils.py +24 -4
  453. rasa/dialogue_understanding_test/du_test_runner.py +8 -3
  454. rasa/e2e_test/e2e_test_runner.py +13 -3
  455. rasa/engine/caching.py +2 -2
  456. rasa/engine/constants.py +1 -1
  457. rasa/engine/graph.py +5 -1
  458. rasa/engine/loader.py +12 -0
  459. rasa/engine/recipes/default_components.py +138 -49
  460. rasa/engine/recipes/default_recipe.py +108 -11
  461. rasa/engine/runner/dask.py +8 -5
  462. rasa/engine/validation.py +25 -8
  463. rasa/graph_components/validators/default_recipe_validator.py +86 -28
  464. rasa/hooks.py +5 -5
  465. rasa/llm_fine_tuning/utils.py +2 -2
  466. rasa/model_manager/model_api.py +4 -5
  467. rasa/model_manager/runner_service.py +2 -2
  468. rasa/model_manager/socket_bridge.py +21 -17
  469. rasa/model_manager/trainer_service.py +12 -9
  470. rasa/model_manager/utils.py +1 -29
  471. rasa/model_manager/warm_rasa_process.py +13 -3
  472. rasa/model_training.py +60 -47
  473. rasa/nlu/classifiers/diet_classifier.py +198 -98
  474. rasa/nlu/classifiers/logistic_regression_classifier.py +1 -4
  475. rasa/nlu/classifiers/mitie_intent_classifier.py +3 -0
  476. rasa/nlu/classifiers/sklearn_intent_classifier.py +1 -3
  477. rasa/nlu/extractors/crf_entity_extractor.py +9 -10
  478. rasa/nlu/extractors/mitie_entity_extractor.py +3 -0
  479. rasa/nlu/extractors/spacy_entity_extractor.py +3 -0
  480. rasa/nlu/featurizers/dense_featurizer/convert_featurizer.py +4 -0
  481. rasa/nlu/featurizers/dense_featurizer/lm_featurizer.py +5 -0
  482. rasa/nlu/featurizers/dense_featurizer/mitie_featurizer.py +2 -0
  483. rasa/nlu/featurizers/dense_featurizer/spacy_featurizer.py +3 -0
  484. rasa/nlu/featurizers/sparse_featurizer/count_vectors_featurizer.py +4 -2
  485. rasa/nlu/featurizers/sparse_featurizer/lexical_syntactic_featurizer.py +4 -0
  486. rasa/nlu/selectors/response_selector.py +10 -2
  487. rasa/nlu/tokenizers/jieba_tokenizer.py +3 -4
  488. rasa/nlu/tokenizers/mitie_tokenizer.py +3 -2
  489. rasa/nlu/tokenizers/spacy_tokenizer.py +3 -2
  490. rasa/nlu/utils/mitie_utils.py +3 -0
  491. rasa/nlu/utils/spacy_utils.py +3 -2
  492. rasa/plugin.py +8 -8
  493. rasa/privacy/privacy_config.py +1 -1
  494. rasa/privacy/privacy_manager.py +12 -3
  495. rasa/server.py +15 -3
  496. rasa/shared/agents/__init__.py +0 -0
  497. rasa/shared/agents/auth/__init__.py +0 -0
  498. rasa/shared/agents/auth/agent_auth_factory.py +105 -0
  499. rasa/shared/agents/auth/agent_auth_manager.py +92 -0
  500. rasa/shared/agents/auth/auth_strategy/__init__.py +19 -0
  501. rasa/shared/agents/auth/auth_strategy/agent_auth_strategy.py +52 -0
  502. rasa/shared/agents/auth/auth_strategy/api_key_auth_strategy.py +42 -0
  503. rasa/shared/agents/auth/auth_strategy/bearer_token_auth_strategy.py +28 -0
  504. rasa/shared/agents/auth/auth_strategy/oauth2_auth_strategy.py +170 -0
  505. rasa/shared/agents/auth/constants.py +13 -0
  506. rasa/shared/agents/auth/types.py +12 -0
  507. rasa/shared/agents/auth/utils.py +85 -0
  508. rasa/shared/agents/utils.py +35 -0
  509. rasa/shared/constants.py +11 -0
  510. rasa/shared/core/constants.py +17 -1
  511. rasa/shared/core/domain.py +62 -22
  512. rasa/shared/core/events.py +329 -0
  513. rasa/shared/core/flows/constants.py +5 -0
  514. rasa/shared/core/flows/flow.py +1 -1
  515. rasa/shared/core/flows/flow_step.py +7 -1
  516. rasa/shared/core/flows/flows_list.py +21 -5
  517. rasa/shared/core/flows/flows_yaml_schema.json +119 -184
  518. rasa/shared/core/flows/steps/call.py +57 -6
  519. rasa/shared/core/flows/steps/collect.py +98 -13
  520. rasa/shared/core/flows/validation.py +372 -8
  521. rasa/shared/core/flows/yaml_flows_io.py +19 -10
  522. rasa/shared/core/slots.py +6 -2
  523. rasa/shared/core/trackers.py +5 -2
  524. rasa/shared/core/training_data/story_reader/story_reader.py +1 -1
  525. rasa/shared/exceptions.py +39 -2
  526. rasa/shared/importers/importer.py +6 -0
  527. rasa/shared/importers/rasa.py +1 -1
  528. rasa/shared/importers/utils.py +86 -4
  529. rasa/shared/nlu/training_data/schemas/responses.yml +3 -0
  530. rasa/shared/providers/llm/_base_litellm_client.py +41 -9
  531. rasa/shared/providers/llm/litellm_router_llm_client.py +10 -6
  532. rasa/shared/providers/llm/llm_client.py +7 -3
  533. rasa/shared/providers/llm/llm_response.py +66 -0
  534. rasa/shared/providers/llm/self_hosted_llm_client.py +8 -4
  535. rasa/shared/utils/common.py +26 -1
  536. rasa/shared/utils/health_check/health_check.py +7 -3
  537. rasa/shared/utils/llm.py +92 -19
  538. rasa/shared/utils/mcp/__init__.py +0 -0
  539. rasa/shared/utils/mcp/server_connection.py +250 -0
  540. rasa/shared/utils/mcp/utils.py +20 -0
  541. rasa/shared/utils/schemas/events.py +42 -0
  542. rasa/shared/utils/yaml.py +3 -1
  543. rasa/studio/download.py +3 -0
  544. rasa/studio/prompts.py +1 -0
  545. rasa/studio/pull/pull.py +3 -2
  546. rasa/studio/train.py +8 -7
  547. rasa/studio/upload.py +19 -52
  548. rasa/telemetry.py +166 -28
  549. rasa/tracing/config.py +45 -12
  550. rasa/tracing/constants.py +14 -0
  551. rasa/tracing/instrumentation/attribute_extractors.py +142 -9
  552. rasa/tracing/instrumentation/instrumentation.py +626 -21
  553. rasa/tracing/instrumentation/intentless_policy_instrumentation.py +4 -4
  554. rasa/tracing/instrumentation/metrics.py +32 -0
  555. rasa/tracing/metric_instrument_provider.py +68 -0
  556. rasa/utils/common.py +92 -1
  557. rasa/utils/endpoints.py +11 -2
  558. rasa/utils/io.py +27 -9
  559. rasa/utils/json_utils.py +6 -1
  560. rasa/utils/log_utils.py +121 -7
  561. rasa/utils/ml_utils.py +1 -1
  562. rasa/utils/openapi.py +144 -0
  563. rasa/utils/plotting.py +1 -1
  564. rasa/utils/pypred.py +45 -0
  565. rasa/utils/tensorflow/__init__.py +7 -0
  566. rasa/utils/tensorflow/callback.py +136 -101
  567. rasa/utils/tensorflow/crf.py +1 -1
  568. rasa/utils/tensorflow/data_generator.py +21 -8
  569. rasa/utils/tensorflow/layers.py +21 -11
  570. rasa/utils/tensorflow/metrics.py +7 -3
  571. rasa/utils/tensorflow/models.py +56 -8
  572. rasa/utils/tensorflow/rasa_layers.py +8 -6
  573. rasa/utils/tensorflow/transformer.py +2 -3
  574. rasa/utils/train_utils.py +54 -24
  575. rasa/validator.py +149 -16
  576. rasa/version.py +1 -1
  577. rasa_pro-3.14.0.dist-info/METADATA +212 -0
  578. {rasa_pro-3.13.12.dist-info → rasa_pro-3.14.0.dist-info}/RECORD +581 -269
  579. rasa/core/channels/inspector/dist/assets/channel-51d02e9e.js +0 -1
  580. rasa/core/channels/inspector/dist/assets/clone-cc738fa6.js +0 -1
  581. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-0c716443.js +0 -1
  582. rasa/core/channels/inspector/dist/assets/index-c804b295.js +0 -1335
  583. rasa_pro-3.13.12.dist-info/METADATA +0 -192
  584. {rasa_pro-3.13.12.dist-info → rasa_pro-3.14.0.dist-info}/NOTICE +0 -0
  585. {rasa_pro-3.13.12.dist-info → rasa_pro-3.14.0.dist-info}/WHEEL +0 -0
  586. {rasa_pro-3.13.12.dist-info → rasa_pro-3.14.0.dist-info}/entry_points.txt +0 -0
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import asyncio
2
4
  import audioop
3
5
  import base64
@@ -14,6 +16,7 @@ from typing import (
14
16
  Optional,
15
17
  Text,
16
18
  Tuple,
19
+ Union,
17
20
  )
18
21
 
19
22
  import structlog
@@ -43,14 +46,13 @@ if TYPE_CHECKING:
43
46
  from sanic import Sanic, Websocket # type: ignore[attr-defined]
44
47
  from socketio import AsyncServer
45
48
 
46
- from rasa.core.channels.channel import InputChannel, UserMessage
47
49
  from rasa.shared.core.trackers import DialogueStateTracker
48
50
 
49
51
 
50
52
  structlogger = structlog.get_logger()
51
53
 
52
54
 
53
- def tracker_as_dump(tracker: "DialogueStateTracker") -> str:
55
+ def tracker_as_dump(tracker: "DialogueStateTracker") -> Dict[str, Any]:
54
56
  """Create a dump of the tracker state."""
55
57
  from rasa.shared.core.trackers import get_trackers_for_conversation_sessions
56
58
 
@@ -62,7 +64,7 @@ def tracker_as_dump(tracker: "DialogueStateTracker") -> str:
62
64
  last_tracker = multiple_tracker_sessions[-1]
63
65
 
64
66
  state = last_tracker.current_state(EventVerbosity.AFTER_RESTART)
65
- return json.dumps(state)
67
+ return state
66
68
 
67
69
 
68
70
  def does_need_action_prediction(tracker: "DialogueStateTracker") -> bool:
@@ -91,12 +93,12 @@ class StudioTrackerUpdatePlugin:
91
93
  """Remove tasks that have already completed."""
92
94
  self.tasks = [task for task in self.tasks if not task.done()]
93
95
 
94
- @hookimpl # type: ignore[misc]
96
+ @hookimpl
95
97
  def after_new_user_message(self, tracker: "DialogueStateTracker") -> None:
96
98
  """Triggers a tracker update notification after a new user message."""
97
99
  self.handle_tracker_update(tracker)
98
100
 
99
- @hookimpl # type: ignore[misc]
101
+ @hookimpl
100
102
  def after_action_executed(self, tracker: "DialogueStateTracker") -> None:
101
103
  """Triggers a tracker update notification after an action is executed."""
102
104
  self.handle_tracker_update(tracker)
@@ -116,7 +118,7 @@ class StudioTrackerUpdatePlugin:
116
118
  self.tasks.append(task)
117
119
  self._cleanup_tasks()
118
120
 
119
- @hookimpl # type: ignore[misc]
121
+ @hookimpl
120
122
  def after_server_stop(self) -> None:
121
123
  """Cancels all remaining tasks when the server stops."""
122
124
  self._cancel_tasks()
@@ -144,6 +146,7 @@ class StudioChatInput(SocketIOInput, VoiceInputChannel):
144
146
  jwt_key: Optional[Text] = None,
145
147
  jwt_method: Optional[Text] = "HS256",
146
148
  metadata_key: Optional[Text] = "metadata",
149
+ enable_silence_timeout: bool = False,
147
150
  ) -> None:
148
151
  """Creates a `StudioChatInput` object."""
149
152
  from rasa.core.agent import Agent
@@ -161,6 +164,7 @@ class StudioChatInput(SocketIOInput, VoiceInputChannel):
161
164
  jwt_key=jwt_key,
162
165
  jwt_method=jwt_method,
163
166
  metadata_key=metadata_key,
167
+ enable_silence_timeout=enable_silence_timeout,
164
168
  )
165
169
 
166
170
  # Initialize the Voice Input Channel
@@ -176,11 +180,14 @@ class StudioChatInput(SocketIOInput, VoiceInputChannel):
176
180
  # `background_tasks` holds the asyncio tasks for voice streaming
177
181
  self.active_connections: Dict[str, SocketIOVoiceWebsocketAdapter] = {}
178
182
  self.background_tasks: Dict[str, asyncio.Task] = {}
183
+ self._turn_start_times: Dict[Text, float] = {}
179
184
 
180
185
  self._register_tracker_update_hook()
181
186
 
182
187
  @classmethod
183
- def from_credentials(cls, credentials: Optional[Dict[Text, Any]]) -> "InputChannel":
188
+ def from_credentials(
189
+ cls, credentials: Optional[Dict[Text, Any]]
190
+ ) -> "StudioChatInput":
184
191
  """Creates a StudioChatInput channel from credentials."""
185
192
  credentials = credentials or {}
186
193
 
@@ -198,14 +205,15 @@ class StudioChatInput(SocketIOInput, VoiceInputChannel):
198
205
  jwt_key=credentials.get("jwt_key"),
199
206
  jwt_method=credentials.get("jwt_method", "HS256"),
200
207
  metadata_key=credentials.get("metadata_key", "metadata"),
208
+ enable_silence_timeout=credentials.get("enable_silence_timeout", False),
201
209
  )
202
210
 
203
- async def emit(self, event: str, data: Dict, room: str) -> None:
211
+ async def emit(self, event: str, data: Union[Dict, str], room: str) -> None:
204
212
  """Emits an event to the websocket."""
205
- if not self.sio:
213
+ if not self.sio_server:
206
214
  structlogger.error("studio_chat.emit.sio_not_initialized")
207
215
  return
208
- await self.sio.emit(event, data, room=room)
216
+ await self.sio_server.emit(event, data, room=room)
209
217
 
210
218
  def _register_tracker_update_hook(self) -> None:
211
219
  plugin_manager().register(StudioTrackerUpdatePlugin(self))
@@ -214,20 +222,29 @@ class StudioChatInput(SocketIOInput, VoiceInputChannel):
214
222
  """Triggers a tracker update notification after a change to the tracker."""
215
223
  await self.publish_tracker_update(tracker.sender_id, tracker_as_dump(tracker))
216
224
 
217
- async def publish_tracker_update(self, sender_id: str, tracker_dump: Dict) -> None:
225
+ async def publish_tracker_update(
226
+ self, sender_id: str, tracker_dump: Dict[str, Any]
227
+ ) -> None:
218
228
  """Publishes a tracker update notification to the websocket."""
219
229
  await self.emit("tracker", tracker_dump, room=sender_id)
220
230
 
221
231
  async def on_message_proxy(
222
232
  self,
223
- on_new_message: Callable[["UserMessage"], Awaitable[Any]],
224
- message: "UserMessage",
233
+ on_new_message: Callable[[UserMessage], Awaitable[Any]],
234
+ message: UserMessage,
225
235
  ) -> None:
226
236
  """Proxies the on_new_message call to the underlying channel.
227
237
 
228
238
  Triggers a tracker update notification after processing the message.
229
239
  """
230
- await on_new_message(message)
240
+ try:
241
+ await on_new_message(message)
242
+ except Exception as e:
243
+ structlogger.exception(
244
+ "studio_chat.on_new_message.error",
245
+ error=str(e),
246
+ sender_id=message.sender_id,
247
+ )
231
248
 
232
249
  if not self.agent or not self.agent.is_ready():
233
250
  structlogger.error("studio_chat.on_message_proxy.agent_not_initialized")
@@ -335,26 +352,33 @@ class StudioChatInput(SocketIOInput, VoiceInputChannel):
335
352
  elif "marker" in message:
336
353
  if message["marker"] == call_state.latest_bot_audio_id:
337
354
  # Just finished streaming last audio bytes
338
- call_state.is_bot_speaking = False # type: ignore[attr-defined]
355
+ call_state.is_bot_speaking = False
339
356
  if call_state.should_hangup:
340
357
  structlogger.debug(
341
358
  "studio_chat.hangup", marker=call_state.latest_bot_audio_id
342
359
  )
343
360
  return EndConversationAction()
344
361
  else:
345
- call_state.is_bot_speaking = True # type: ignore[attr-defined]
362
+ call_state.is_bot_speaking = True
346
363
  return ContinueConversationAction()
347
364
 
348
365
  def create_output_channel(
349
366
  self, voice_websocket: "Websocket", tts_engine: TTSEngine
350
367
  ) -> VoiceOutputChannel:
351
- """Create a voice output channel."""
368
+ """Create a voice output channel. This is used by VoiceInputChannel."""
352
369
  return StudioVoiceOutputChannel(
353
370
  voice_websocket,
354
371
  tts_engine,
355
372
  self.tts_cache,
356
373
  )
357
374
 
375
+ async def interrupt_playback(
376
+ self, ws: Websocket, call_parameters: CallParameters
377
+ ) -> None:
378
+ """Interrupt the current playback of audio."""
379
+ structlogger.debug("studio_chat.interrupt_playback")
380
+ await ws.send(json.dumps({"interruptPlayback": True}))
381
+
358
382
  def _start_voice_session(
359
383
  self,
360
384
  session_id: str,
@@ -375,7 +399,7 @@ class StudioChatInput(SocketIOInput, VoiceInputChannel):
375
399
 
376
400
  # Create a websocket adapter for this connection
377
401
  ws_adapter = SocketIOVoiceWebsocketAdapter(
378
- sio=self.sio,
402
+ sio_server=self.sio_server,
379
403
  session_id=session_id,
380
404
  sid=sid,
381
405
  bot_message_evt=self.bot_message_evt,
@@ -414,7 +438,7 @@ class StudioChatInput(SocketIOInput, VoiceInputChannel):
414
438
  if sid in self.active_connections:
415
439
  del self.active_connections[sid]
416
440
 
417
- @hookimpl # type: ignore[misc]
441
+ @hookimpl
418
442
  def after_server_stop(self) -> None:
419
443
  """Cleanup background tasks and active connections when the server stops."""
420
444
  structlogger.info("studio_chat.after_server_stop.cleanup")
@@ -423,13 +447,13 @@ class StudioChatInput(SocketIOInput, VoiceInputChannel):
423
447
  task.cancel()
424
448
 
425
449
  def blueprint(
426
- self, on_new_message: Callable[["UserMessage"], Awaitable[Any]]
450
+ self, on_new_message: Callable[[UserMessage], Awaitable[Any]]
427
451
  ) -> SocketBlueprint:
428
452
  socket_blueprint = super().blueprint(
429
453
  partial(self.on_message_proxy, on_new_message)
430
454
  )
431
455
 
432
- if not self.sio:
456
+ if not self.sio_server:
433
457
  structlogger.error("studio_chat.blueprint.sio_not_initialized")
434
458
  return socket_blueprint
435
459
 
@@ -437,14 +461,15 @@ class StudioChatInput(SocketIOInput, VoiceInputChannel):
437
461
  async def after_server_start(
438
462
  app: "Sanic", _: asyncio.AbstractEventLoop
439
463
  ) -> None:
440
- self.agent = app.ctx.agent
464
+ if hasattr(app.ctx, "agent"):
465
+ self.agent = app.ctx.agent
441
466
 
442
- @self.sio.on("disconnect", namespace=self.namespace)
467
+ @self.sio_server.on("disconnect", namespace=self.namespace)
443
468
  async def disconnect(sid: Text) -> None:
444
469
  structlogger.debug("studio_chat.sio.disconnect", sid=sid)
445
470
  self._cleanup_tasks_for_sid(sid)
446
471
 
447
- @self.sio.on("session_request", namespace=self.namespace)
472
+ @self.sio_server.on("session_request", namespace=self.namespace)
448
473
  async def session_request(sid: Text, data: Optional[Dict]) -> None:
449
474
  """Overrides the base SocketIOInput session_request handler.
450
475
 
@@ -464,7 +489,7 @@ class StudioChatInput(SocketIOInput, VoiceInputChannel):
464
489
  if data and data.get("is_voice", False):
465
490
  self._start_voice_session(data["session_id"], sid, on_new_message)
466
491
 
467
- @self.sio.on(self.user_message_evt, namespace=self.namespace)
492
+ @self.sio_server.on(self.user_message_evt, namespace=self.namespace)
468
493
  async def handle_message(sid: Text, data: Dict) -> None:
469
494
  """Overrides the base SocketIOInput handle_message handler."""
470
495
  # Handle voice messages
@@ -475,10 +500,18 @@ class StudioChatInput(SocketIOInput, VoiceInputChannel):
475
500
  ws.put_message(data)
476
501
  return
477
502
 
478
- # Handle text messages
479
- await self.handle_user_message(sid, data, on_new_message)
503
+ try:
504
+ # Handle text messages
505
+ await self.handle_user_message(sid, data, on_new_message)
506
+ except Exception as e:
507
+ structlogger.exception(
508
+ "studio_chat.sio.handle_message.error",
509
+ error=str(e),
510
+ sid=sid,
511
+ )
512
+ await self.emit("error", str(e), room=sid)
480
513
 
481
- @self.sio.on("update_tracker", namespace=self.namespace)
514
+ @self.sio_server.on("update_tracker", namespace=self.namespace)
482
515
  async def on_update_tracker(sid: Text, data: Dict) -> None:
483
516
  await self.handle_tracker_update(sid, data)
484
517
 
@@ -500,16 +533,33 @@ class StudioVoiceOutputChannel(VoiceOutputChannel):
500
533
 
501
534
  def create_marker_message(self, recipient_id: str) -> Tuple[str, str]:
502
535
  message_id = uuid.uuid4().hex
503
- return json.dumps({"marker": message_id}), message_id
536
+ marker_data: Dict[str, Any] = {"marker": message_id}
537
+
538
+ # Include comprehensive latency information if available
539
+ latency_data = {
540
+ "asr_latency_ms": call_state.asr_latency_ms,
541
+ "rasa_processing_latency_ms": call_state.rasa_processing_latency_ms,
542
+ "tts_first_byte_latency_ms": call_state.tts_first_byte_latency_ms,
543
+ "tts_complete_latency_ms": call_state.tts_complete_latency_ms,
544
+ }
545
+
546
+ # Filter out None values from latency data
547
+ latency_data = {k: v for k, v in latency_data.items() if v is not None}
548
+
549
+ # Add latency data to marker if any metrics are available
550
+ if latency_data:
551
+ marker_data["latency"] = latency_data
552
+
553
+ return json.dumps(marker_data), message_id
504
554
 
505
555
 
506
556
  class SocketIOVoiceWebsocketAdapter:
507
557
  """Adapter to make Socket.IO work like a Sanic WebSocket for voice channels."""
508
558
 
509
559
  def __init__(
510
- self, sio: "AsyncServer", session_id: str, sid: str, bot_message_evt: str
560
+ self, sio_server: "AsyncServer", session_id: str, sid: str, bot_message_evt: str
511
561
  ) -> None:
512
- self.sio = sio
562
+ self.sio_server = sio_server
513
563
  self.bot_message_evt = bot_message_evt
514
564
  self._closed = False
515
565
  self._receive_queue: asyncio.Queue[Any] = asyncio.Queue()
@@ -528,7 +578,7 @@ class SocketIOVoiceWebsocketAdapter:
528
578
  async def send(self, data: Any) -> None:
529
579
  """Send data to the client."""
530
580
  if not self.closed:
531
- await self.sio.emit(self.bot_message_evt, data, room=self.sid)
581
+ await self.sio_server.emit(self.bot_message_evt, data, room=self.sid)
532
582
 
533
583
  async def recv(self) -> Any:
534
584
  """Receive data from the client."""
@@ -4,6 +4,9 @@ import typing
4
4
  from copy import deepcopy
5
5
  from typing import Any, Awaitable, Callable, Dict, List, Optional, Text
6
6
 
7
+ # Import aiogram at module level to raise error if not installed
8
+ from aiogram import Bot
9
+ from aiogram.types import Message, Update
7
10
  from sanic import Blueprint, response
8
11
  from sanic.request import Request
9
12
  from sanic.response import HTTPResponse
@@ -28,15 +31,7 @@ class TelegramOutput(OutputChannel):
28
31
  return "telegram"
29
32
 
30
33
  def __init__(self, access_token: Optional[Text]) -> None:
31
- try:
32
- from aiogram import Bot
33
-
34
- self.bot = Bot(access_token)
35
- except ImportError:
36
- raise ImportError(
37
- "To use the Telegram channel, please install the aiogram package "
38
- "with 'pip install aiogram'"
39
- )
34
+ self.bot = Bot(access_token)
40
35
 
41
36
  async def send_text_message(
42
37
  self, recipient_id: Text, text: Text, **kwargs: Any
@@ -30,7 +30,7 @@ TWILIO_VOICE_PATH = "webhooks/twilio_voice/webhook"
30
30
 
31
31
 
32
32
  def map_call_params(form: RequestParameters) -> CallParameters:
33
- """Map the Audiocodes parameters to the CallParameters dataclass."""
33
+ """Map the Twilio Voice parameters to the CallParameters dataclass."""
34
34
  return CallParameters(
35
35
  call_id=form.get("CallSid"),
36
36
  user_phone=form.get("Caller"),
@@ -15,7 +15,7 @@ class NewTranscript(ASREvent):
15
15
 
16
16
  @dataclass
17
17
  class UserIsSpeaking(ASREvent):
18
- pass
18
+ text: str
19
19
 
20
20
 
21
21
  @dataclass
@@ -1,7 +1,7 @@
1
1
  import asyncio
2
2
  import os
3
3
  from dataclasses import dataclass
4
- from typing import Any, AsyncIterator, Dict, Optional
4
+ from typing import TYPE_CHECKING, Any, AsyncIterator, Dict, Optional
5
5
 
6
6
  import structlog
7
7
 
@@ -15,6 +15,9 @@ from rasa.core.channels.voice_stream.audio_bytes import HERTZ, RasaAudioBytes
15
15
  from rasa.shared.constants import AZURE_SPEECH_API_KEY_ENV_VAR
16
16
  from rasa.shared.exceptions import ConnectionException
17
17
 
18
+ if TYPE_CHECKING:
19
+ from azure.cognitiveservices.speech import SpeechRecognitionEventArgs
20
+
18
21
  logger = structlog.get_logger(__name__)
19
22
 
20
23
 
@@ -43,9 +46,9 @@ class AzureASR(ASREngine[AzureASRConfig]):
43
46
  )
44
47
  self.main_loop = asyncio.get_running_loop()
45
48
 
46
- def signal_user_is_speaking(self, event: Any) -> None:
49
+ def signal_user_is_speaking(self, event: "SpeechRecognitionEventArgs") -> None:
47
50
  """Replace the azure event with a generic is speaking event."""
48
- self.fill_queue(UserIsSpeaking())
51
+ self.fill_queue(UserIsSpeaking(event.result.text))
49
52
 
50
53
  def fill_queue(self, event: Any) -> None:
51
54
  """Either puts the event or a dedicated ASR Event into the queue."""
@@ -117,7 +117,7 @@ class DeepgramASR(ASREngine[DeepgramASRConfig]):
117
117
  self.accumulated_transcript, transcript
118
118
  )
119
119
  elif transcript:
120
- return UserIsSpeaking()
120
+ return UserIsSpeaking(transcript)
121
121
  # event that comes after utterance_end_ms of no new transcript
122
122
  elif data_type == "UtteranceEnd":
123
123
  if self.accumulated_transcript:
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import asyncio
2
4
  import base64
3
5
  import hmac
@@ -21,6 +23,7 @@ from rasa.core.channels.voice_stream.call_state import (
21
23
  call_state,
22
24
  )
23
25
  from rasa.core.channels.voice_stream.tts.tts_engine import TTSEngine
26
+ from rasa.core.channels.voice_stream.util import repack_voice_credentials
24
27
  from rasa.core.channels.voice_stream.voice_channel import (
25
28
  ContinueConversationAction,
26
29
  EndConversationAction,
@@ -85,7 +88,7 @@ class AudiocodesVoiceOutputChannel(VoiceOutputChannel):
85
88
  # however, Audiocodes does not have an event to indicate that.
86
89
  # This is an approximation, as the bot will be sent the audio chunks next
87
90
  # which are played to the user immediately.
88
- call_state.is_bot_speaking = True # type: ignore[attr-defined]
91
+ call_state.is_bot_speaking = True
89
92
  VoiceInputChannel._cancel_silence_timeout_watcher()
90
93
 
91
94
  async def send_intermediate_marker(self, recipient_id: str) -> None:
@@ -114,6 +117,7 @@ class AudiocodesVoiceInputChannel(VoiceInputChannel):
114
117
  server_url: str,
115
118
  asr_config: Dict,
116
119
  tts_config: Dict,
120
+ interruptions: Optional[Dict[str, int]] = None,
117
121
  token: Optional[Text] = None,
118
122
  ):
119
123
  mark_as_beta_feature("Audiocodes (audiocodes_stream) Channel")
@@ -121,6 +125,7 @@ class AudiocodesVoiceInputChannel(VoiceInputChannel):
121
125
  server_url=server_url,
122
126
  asr_config=asr_config,
123
127
  tts_config=tts_config,
128
+ interruptions=interruptions,
124
129
  )
125
130
  self.token = token
126
131
 
@@ -128,10 +133,10 @@ class AudiocodesVoiceInputChannel(VoiceInputChannel):
128
133
  def from_credentials(
129
134
  cls,
130
135
  credentials: Optional[Dict[str, Any]],
131
- ) -> "AudiocodesVoiceInputChannel":
132
- channel = super().from_credentials(credentials)
133
- channel.token = credentials.get("token") # type: ignore[attr-defined, union-attr]
134
- return channel # type: ignore[return-value]
136
+ ) -> AudiocodesVoiceInputChannel:
137
+ cls.validate_basic_credentials(credentials)
138
+ new_creds = repack_voice_credentials(credentials)
139
+ return cls(**new_creds)
135
140
 
136
141
  def channel_bytes_to_rasa_audio_bytes(self, input_bytes: bytes) -> RasaAudioBytes:
137
142
  return RasaAudioBytes(base64.b64decode(input_bytes))
@@ -185,7 +190,7 @@ class AudiocodesVoiceInputChannel(VoiceInputChannel):
185
190
  pass
186
191
  elif activity["name"] == "playFinished":
187
192
  logger.debug("audiocodes_stream.playFinished", data=activity)
188
- call_state.is_bot_speaking = False # type: ignore[attr-defined]
193
+ call_state.is_bot_speaking = False
189
194
  if call_state.should_hangup:
190
195
  logger.info("audiocodes_stream.hangup")
191
196
  self._send_hangup(ws, data)
@@ -1,8 +1,12 @@
1
+ from __future__ import annotations
2
+
1
3
  import audioop
2
4
  import base64
3
5
  import json
6
+ import os
4
7
  import uuid
5
- from typing import Any, Awaitable, Callable, Optional, Tuple
8
+ import wave
9
+ from typing import Any, Awaitable, Callable, Dict, Optional, Tuple
6
10
 
7
11
  import structlog
8
12
  from sanic import ( # type: ignore[attr-defined]
@@ -18,6 +22,7 @@ from rasa.core.channels.voice_ready.utils import CallParameters
18
22
  from rasa.core.channels.voice_stream.audio_bytes import RasaAudioBytes
19
23
  from rasa.core.channels.voice_stream.call_state import call_state
20
24
  from rasa.core.channels.voice_stream.tts.tts_engine import TTSEngine
25
+ from rasa.core.channels.voice_stream.util import repack_voice_credentials
21
26
  from rasa.core.channels.voice_stream.voice_channel import (
22
27
  ContinueConversationAction,
23
28
  EndConversationAction,
@@ -45,10 +50,70 @@ class BrowserAudioOutputChannel(VoiceOutputChannel):
45
50
 
46
51
  def create_marker_message(self, recipient_id: str) -> Tuple[str, str]:
47
52
  message_id = uuid.uuid4().hex
48
- return json.dumps({"marker": message_id}), message_id
53
+ marker_data = {"marker": message_id}
54
+
55
+ # Include comprehensive latency information if available
56
+ latency_data = {
57
+ "asr_latency_ms": call_state.asr_latency_ms,
58
+ "rasa_processing_latency_ms": call_state.rasa_processing_latency_ms,
59
+ "tts_first_byte_latency_ms": call_state.tts_first_byte_latency_ms,
60
+ "tts_complete_latency_ms": call_state.tts_complete_latency_ms,
61
+ }
62
+
63
+ # Filter out None values from latency data
64
+ latency_data = {k: v for k, v in latency_data.items() if v is not None}
65
+
66
+ # Add latency data to marker if any metrics are available
67
+ if latency_data:
68
+ marker_data["latency"] = latency_data # type: ignore[assignment]
69
+
70
+ return json.dumps(marker_data), message_id
49
71
 
50
72
 
51
73
  class BrowserAudioInputChannel(VoiceInputChannel):
74
+ requires_voice_license = False
75
+
76
+ def __init__(
77
+ self,
78
+ server_url: str,
79
+ asr_config: Dict[str, Any],
80
+ tts_config: Dict[str, Any],
81
+ recording: bool = False,
82
+ interruptions: Optional[Dict[str, int]] = None,
83
+ ) -> None:
84
+ """Initializes the browser audio input channel."""
85
+ super().__init__(server_url, asr_config, tts_config, interruptions)
86
+
87
+ # For debugging, recording of user audio might be useful
88
+ # to identify audio quality issues or transcription errors
89
+ self._recording_enabled = recording
90
+ self._wav_file: Optional[wave.Wave_write] = None
91
+
92
+ def _start_recording(self, call_id: str, user_id: str) -> None:
93
+ os.makedirs("recordings", exist_ok=True)
94
+ filename = f"{user_id}_{call_id}.wav"
95
+ file_path = os.path.join("recordings", filename)
96
+
97
+ if not self._recording_enabled:
98
+ return
99
+
100
+ self._wav_file = wave.open(file_path, "wb")
101
+ self._wav_file.setnchannels(1) # Mono audio
102
+ self._wav_file.setsampwidth(4) # 32-bit audio (4 bytes)
103
+ self._wav_file.setframerate(8000) # 8kHz sample rate
104
+ logger.info("voice_channel.user_audio_recording.started", file_path=file_path)
105
+
106
+ def _append_audio_to_recording(self, audio_bytes: bytes) -> None:
107
+ if self._wav_file and self._recording_enabled:
108
+ self._wav_file.writeframes(audio_bytes)
109
+
110
+ def _stop_recording(self) -> None:
111
+ """Close the recording file if it's open."""
112
+ if self._wav_file:
113
+ self._wav_file.close()
114
+ self._wav_file = None
115
+ logger.debug("voice_channel.user_audio_recording.stopped")
116
+
52
117
  @classmethod
53
118
  def name(cls) -> str:
54
119
  return "browser_audio"
@@ -62,6 +127,15 @@ class BrowserAudioInputChannel(VoiceInputChannel):
62
127
  call_id = f"inspect-{uuid.uuid4()}"
63
128
  return CallParameters(call_id, "local", "local", stream_id=call_id)
64
129
 
130
+ @classmethod
131
+ def from_credentials(
132
+ cls,
133
+ credentials: Optional[Dict[str, Any]],
134
+ ) -> BrowserAudioInputChannel:
135
+ cls.validate_basic_credentials(credentials)
136
+ new_creds = repack_voice_credentials(credentials or {})
137
+ return cls(**new_creds)
138
+
65
139
  def map_input_message(
66
140
  self,
67
141
  message: Any,
@@ -70,21 +144,29 @@ class BrowserAudioInputChannel(VoiceInputChannel):
70
144
  data = json.loads(message)
71
145
  if "audio" in data:
72
146
  channel_bytes = base64.b64decode(data["audio"])
147
+ self._append_audio_to_recording(channel_bytes)
73
148
  audio_bytes = self.channel_bytes_to_rasa_audio_bytes(channel_bytes)
74
149
  return NewAudioAction(audio_bytes)
75
150
  elif "marker" in data:
76
151
  if data["marker"] == call_state.latest_bot_audio_id:
77
152
  # Just finished streaming last audio bytes
78
- call_state.is_bot_speaking = False # type: ignore[attr-defined]
153
+ call_state.is_bot_speaking = False
79
154
  if call_state.should_hangup:
80
155
  logger.debug(
81
156
  "browser_audio.hangup", marker=call_state.latest_bot_audio_id
82
157
  )
83
158
  return EndConversationAction()
84
159
  else:
85
- call_state.is_bot_speaking = True # type: ignore[attr-defined]
160
+ call_state.is_bot_speaking = True
86
161
  return ContinueConversationAction()
87
162
 
163
+ async def interrupt_playback(
164
+ self, ws: Websocket, call_parameters: CallParameters
165
+ ) -> None:
166
+ """Interrupt the current playback of audio."""
167
+ logger.debug("browser_audio.interrupt_playback")
168
+ await ws.send(json.dumps({"interruptPlayback": True}))
169
+
88
170
  def create_output_channel(
89
171
  self, voice_websocket: Websocket, tts_engine: TTSEngine
90
172
  ) -> VoiceOutputChannel:
@@ -107,8 +189,13 @@ class BrowserAudioInputChannel(VoiceInputChannel):
107
189
  @blueprint.websocket("/websocket") # type: ignore
108
190
  async def handle_message(request: Request, ws: Websocket) -> None:
109
191
  try:
192
+ call_parameters = await self.collect_call_parameters(ws)
193
+ if call_parameters and call_parameters.call_id:
194
+ self._start_recording(call_parameters.call_id, "local")
110
195
  await self.run_audio_streaming(on_new_message, ws)
111
196
  except Exception as e:
112
197
  logger.error("browser_audio.handle_message.error", error=e)
198
+ finally:
199
+ self._stop_recording()
113
200
 
114
201
  return blueprint
@@ -1,7 +1,7 @@
1
1
  import asyncio
2
2
  from contextvars import ContextVar
3
3
  from dataclasses import dataclass, field
4
- from typing import Any, Dict, Optional
4
+ from typing import Any, Dict, Optional, cast
5
5
 
6
6
  from werkzeug.local import LocalProxy
7
7
 
@@ -19,9 +19,20 @@ class CallState:
19
19
  should_hangup: bool = False
20
20
  connection_failed: bool = False
21
21
 
22
+ # Latency tracking - start times only
23
+ user_speech_start_time: Optional[float] = None
24
+ rasa_processing_start_time: Optional[float] = None
25
+ tts_start_time: Optional[float] = None
26
+
27
+ # Calculated latencies (used by channels like browser_audio)
28
+ asr_latency_ms: Optional[float] = None
29
+ rasa_processing_latency_ms: Optional[float] = None
30
+ tts_first_byte_latency_ms: Optional[float] = None
31
+ tts_complete_latency_ms: Optional[float] = None
32
+
22
33
  # Generic field for channel-specific state data
23
34
  channel_data: Dict[str, Any] = field(default_factory=dict)
24
35
 
25
36
 
26
37
  _call_state: ContextVar[CallState] = ContextVar("call_state")
27
- call_state = LocalProxy(_call_state)
38
+ call_state: CallState = cast(CallState, LocalProxy(_call_state))