rasa-pro 3.13.1a18__py3-none-any.whl → 3.13.1a20__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 (273) hide show
  1. rasa/__main__.py +8 -0
  2. rasa/builder/auth.py +71 -0
  3. rasa/builder/config.py +16 -0
  4. rasa/builder/copilot/constants.py +15 -0
  5. rasa/builder/copilot/copilot.py +342 -0
  6. rasa/builder/copilot/copilot_response_handler.py +471 -0
  7. rasa/builder/copilot/exceptions.py +20 -0
  8. rasa/builder/copilot/models.py +344 -0
  9. rasa/builder/copilot/prompts/copilot_system_prompt.jinja2 +495 -0
  10. rasa/builder/copilot/telemetry.py +195 -0
  11. rasa/builder/document_retrieval/__init__.py +0 -0
  12. rasa/builder/document_retrieval/constants.py +16 -0
  13. rasa/builder/{inkeep_document_retrieval.py → document_retrieval/inkeep_document_retrieval.py} +53 -44
  14. rasa/builder/document_retrieval/models.py +62 -0
  15. rasa/builder/download.py +140 -0
  16. rasa/builder/guardrails/__init__.py +1 -0
  17. rasa/builder/guardrails/constants.py +4 -0
  18. rasa/builder/guardrails/exceptions.py +4 -0
  19. rasa/builder/guardrails/lakera.py +188 -0
  20. rasa/builder/guardrails/models.py +199 -0
  21. rasa/builder/guardrails/utils.py +305 -0
  22. rasa/builder/job_manager.py +87 -0
  23. rasa/builder/jobs.py +232 -0
  24. rasa/builder/llm_service.py +89 -173
  25. rasa/builder/logging_utils.py +162 -4
  26. rasa/builder/main.py +29 -16
  27. rasa/builder/models.py +93 -121
  28. rasa/builder/project_generator.py +91 -7
  29. rasa/builder/scrape_rasa_docs.py +1 -1
  30. rasa/builder/service.py +650 -452
  31. rasa/builder/shared/tracker_context.py +212 -0
  32. rasa/builder/validation_service.py +4 -4
  33. rasa/cli/data.py +8 -3
  34. rasa/cli/project_templates/basic/actions/action_api.py +15 -0
  35. rasa/cli/project_templates/basic/actions/action_human_handoff.py +44 -0
  36. rasa/cli/project_templates/basic/config.yml +23 -0
  37. rasa/cli/project_templates/{plain → basic}/credentials.yml +8 -7
  38. rasa/cli/project_templates/basic/data/general/feedback.yml +20 -0
  39. rasa/cli/project_templates/basic/data/general/goodbye.yml +6 -0
  40. rasa/cli/project_templates/basic/data/general/hello.yml +7 -0
  41. rasa/cli/project_templates/basic/data/general/help.yml +6 -0
  42. rasa/cli/project_templates/basic/data/general/human_handoff.yml +16 -0
  43. rasa/cli/project_templates/basic/data/general/welcome.yml +9 -0
  44. rasa/cli/project_templates/{finance/data/patterns → basic/data/system}/pattern_completed.yml +2 -1
  45. rasa/cli/project_templates/basic/data/system/pattern_correction.yml +7 -0
  46. rasa/cli/project_templates/basic/data/system/pattern_search.yml +8 -0
  47. rasa/cli/project_templates/basic/data/system/pattern_session_start.yml +8 -0
  48. rasa/cli/project_templates/basic/docs/rasa_assistant_qa.txt +65 -0
  49. rasa/cli/project_templates/basic/docs/template.txt +7 -0
  50. rasa/cli/project_templates/basic/domain/general/assistant_details.yml +12 -0
  51. rasa/cli/project_templates/basic/domain/general/bot_identity.yml +5 -0
  52. rasa/cli/project_templates/basic/domain/general/cannot_handle.yml +5 -0
  53. rasa/cli/project_templates/basic/domain/general/feedback.yml +28 -0
  54. rasa/cli/project_templates/basic/domain/general/goodbye.yml +7 -0
  55. rasa/cli/project_templates/basic/domain/general/help.yml +5 -0
  56. rasa/cli/project_templates/basic/domain/general/human_handoff_domain.yml +35 -0
  57. rasa/cli/project_templates/{finance/domain/default_actions.yml → basic/domain/general/utils.yml} +0 -3
  58. rasa/cli/project_templates/basic/domain/general/welcome.yml +7 -0
  59. rasa/cli/project_templates/{plain → basic}/endpoints.yml +42 -27
  60. rasa/cli/project_templates/basic/prompts/rephraser_demo_personality_prompt.jinja2 +19 -0
  61. rasa/cli/project_templates/defaults.py +25 -3
  62. rasa/cli/project_templates/finance/actions/__init__.py +46 -0
  63. rasa/cli/project_templates/finance/actions/accounts/__init__.py +0 -0
  64. rasa/cli/project_templates/finance/actions/{action_ask_account.py → accounts/action_ask_account.py} +6 -9
  65. rasa/cli/project_templates/finance/actions/{action_check_balance.py → accounts/action_check_balance.py} +4 -4
  66. rasa/cli/project_templates/finance/actions/action_session_start.py +11 -6
  67. rasa/cli/project_templates/finance/actions/cards/__init__.py +0 -0
  68. rasa/cli/project_templates/finance/actions/{action_ask_card.py → cards/action_ask_card.py} +4 -3
  69. rasa/cli/project_templates/finance/actions/{action_check_card_existence.py → cards/action_check_card_existence.py} +4 -3
  70. rasa/cli/project_templates/finance/actions/{action_update_card_status.py → cards/action_update_card_status.py} +18 -9
  71. rasa/cli/project_templates/finance/actions/database.py +1 -0
  72. rasa/cli/project_templates/finance/actions/transfers/__init__.py +0 -0
  73. rasa/cli/project_templates/finance/actions/{action_add_payee.py → transfers/action_add_payee.py} +8 -3
  74. rasa/cli/project_templates/finance/actions/{action_ask_account_from.py → transfers/action_ask_account_from.py} +5 -4
  75. rasa/cli/project_templates/finance/actions/{action_check_payee_existence.py → transfers/action_check_payee_existence.py} +3 -3
  76. rasa/cli/project_templates/finance/actions/{action_check_sufficient_funds.py → transfers/action_check_sufficient_funds.py} +3 -4
  77. rasa/cli/project_templates/finance/actions/{action_list_payees.py → transfers/action_list_payees.py} +4 -3
  78. rasa/cli/project_templates/finance/actions/{action_remove_payee.py → transfers/action_remove_payee.py} +4 -4
  79. rasa/cli/project_templates/finance/config.yml +8 -19
  80. rasa/cli/project_templates/finance/credentials.yml +6 -7
  81. rasa/cli/project_templates/finance/csvs/cards.csv +10 -10
  82. rasa/cli/project_templates/finance/csvs/payees.csv +10 -9
  83. rasa/cli/project_templates/finance/data/{flows → accounts}/check_balance.yml +2 -1
  84. rasa/cli/project_templates/finance/data/general/bot_identity.yml +6 -0
  85. rasa/cli/project_templates/finance/data/general/feedback.yml +20 -0
  86. rasa/cli/project_templates/finance/data/general/goodbye.yml +6 -0
  87. rasa/cli/project_templates/finance/data/general/hello.yml +7 -0
  88. rasa/cli/project_templates/finance/data/{flows/welcome.yml → general/help.yml} +2 -7
  89. rasa/cli/project_templates/finance/data/general/human_handoff.yml +16 -0
  90. rasa/cli/project_templates/finance/data/general/welcome.yml +9 -0
  91. rasa/cli/project_templates/finance/data/{patterns → system/patterns}/pattern_chitchat.yml +0 -2
  92. rasa/cli/project_templates/finance/data/system/patterns/pattern_completed.yml +7 -0
  93. rasa/cli/project_templates/finance/data/system/patterns/pattern_correction.yml +7 -0
  94. rasa/cli/project_templates/finance/data/system/patterns/pattern_search.yml +8 -0
  95. rasa/cli/project_templates/finance/data/{patterns → system/patterns}/pattern_session_start.yml +0 -1
  96. rasa/cli/project_templates/finance/domain/{check_balance.yml → accounts/check_balance.yml} +2 -0
  97. rasa/cli/project_templates/finance/domain/general/assistant_details.yml +12 -0
  98. rasa/cli/project_templates/finance/domain/general/bot_identity.yml +5 -0
  99. rasa/cli/project_templates/finance/domain/general/cannot_handle.yml +5 -0
  100. rasa/cli/project_templates/finance/domain/general/defaults.yml +24 -0
  101. rasa/cli/project_templates/finance/domain/general/feedback.yml +28 -0
  102. rasa/cli/project_templates/finance/domain/general/goodbye.yml +7 -0
  103. rasa/cli/project_templates/finance/domain/general/help.yml +5 -0
  104. rasa/cli/project_templates/finance/domain/general/human_handoff.yml +30 -0
  105. rasa/cli/project_templates/finance/domain/general/utils.yml +13 -0
  106. rasa/cli/project_templates/finance/domain/general/welcome.yml +8 -0
  107. rasa/cli/project_templates/finance/endpoints.yml +1 -0
  108. rasa/cli/project_templates/finance/prompts/rephraser_demo_personality_prompt.jinja2 +3 -3
  109. rasa/cli/project_templates/telco/actions/actions_billing.py +24 -17
  110. rasa/cli/project_templates/telco/actions/actions_get_data_from_db.py +6 -1
  111. rasa/cli/project_templates/telco/actions/actions_run_diagnostics.py +6 -1
  112. rasa/cli/project_templates/telco/actions/actions_session_start.py +6 -1
  113. rasa/cli/project_templates/tutorial/config.yml +2 -1
  114. rasa/cli/scaffold.py +27 -2
  115. rasa/cli/train.py +8 -0
  116. rasa/cli/utils.py +31 -15
  117. rasa/core/actions/action.py +28 -41
  118. rasa/core/actions/action_run_slot_rejections.py +1 -1
  119. rasa/core/channels/development_inspector.py +47 -14
  120. rasa/core/channels/inspector/dist/assets/{arc-371401b1.js → arc-1ddec37b.js} +1 -1
  121. rasa/core/channels/inspector/dist/assets/{blockDiagram-38ab4fdb-3f126156.js → blockDiagram-38ab4fdb-18af387c.js} +1 -1
  122. rasa/core/channels/inspector/dist/assets/{c4Diagram-3d4e48cf-12f22eb7.js → c4Diagram-3d4e48cf-250127a3.js} +1 -1
  123. rasa/core/channels/inspector/dist/assets/channel-59f6d54b.js +1 -0
  124. rasa/core/channels/inspector/dist/assets/{classDiagram-70f12bd4-03b1d386.js → classDiagram-70f12bd4-c3388b34.js} +1 -1
  125. rasa/core/channels/inspector/dist/assets/{classDiagram-v2-f2320105-84f69d63.js → classDiagram-v2-f2320105-9c893a82.js} +1 -1
  126. rasa/core/channels/inspector/dist/assets/clone-26177ddb.js +1 -0
  127. rasa/core/channels/inspector/dist/assets/{createText-2e5e7dd3-ca47fd38.js → createText-2e5e7dd3-c111213b.js} +1 -1
  128. rasa/core/channels/inspector/dist/assets/{edges-e0da2a9e-f837ca8a.js → edges-e0da2a9e-812a729d.js} +1 -1
  129. rasa/core/channels/inspector/dist/assets/{erDiagram-9861fffd-8717ac54.js → erDiagram-9861fffd-fd5051bc.js} +1 -1
  130. rasa/core/channels/inspector/dist/assets/{flowDb-956e92f1-94f38b83.js → flowDb-956e92f1-3287ac02.js} +1 -1
  131. rasa/core/channels/inspector/dist/assets/{flowDiagram-66a62f08-b616f9fb.js → flowDiagram-66a62f08-692fb0b2.js} +1 -1
  132. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-29c03f5a.js +1 -0
  133. rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-4a651766-f5d24bb8.js → flowchart-elk-definition-4a651766-008376f1.js} +1 -1
  134. rasa/core/channels/inspector/dist/assets/{ganttDiagram-c361ad54-b43ba8d9.js → ganttDiagram-c361ad54-df330a69.js} +1 -1
  135. rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-72cf32ee-c3aafaa5.js → gitGraphDiagram-72cf32ee-e03676fb.js} +1 -1
  136. rasa/core/channels/inspector/dist/assets/{graph-0d0a2c10.js → graph-46fad2ba.js} +1 -1
  137. rasa/core/channels/inspector/dist/assets/{index-3862675e-58ea0305.js → index-3862675e-a484ac55.js} +1 -1
  138. rasa/core/channels/inspector/dist/assets/{index-cce6f8a1.js → index-a003633f.js} +179 -179
  139. rasa/core/channels/inspector/dist/assets/{infoDiagram-f8f76790-b8f60461.js → infoDiagram-f8f76790-3f9e6ec2.js} +1 -1
  140. rasa/core/channels/inspector/dist/assets/{journeyDiagram-49397b02-95be5545.js → journeyDiagram-49397b02-79f72383.js} +1 -1
  141. rasa/core/channels/inspector/dist/assets/{layout-da885b9b.js → layout-aad098e5.js} +1 -1
  142. rasa/core/channels/inspector/dist/assets/{line-f1c817d3.js → line-219ab7ae.js} +1 -1
  143. rasa/core/channels/inspector/dist/assets/{linear-d42801e6.js → linear-2cddbe62.js} +1 -1
  144. rasa/core/channels/inspector/dist/assets/{mindmap-definition-fc14e90a-a38923a6.js → mindmap-definition-fc14e90a-1d41ed99.js} +1 -1
  145. rasa/core/channels/inspector/dist/assets/{pieDiagram-8a3498a8-ca6e71e9.js → pieDiagram-8a3498a8-cc496ee8.js} +1 -1
  146. rasa/core/channels/inspector/dist/assets/{quadrantDiagram-120e2f19-b290dae9.js → quadrantDiagram-120e2f19-84d32884.js} +1 -1
  147. rasa/core/channels/inspector/dist/assets/{requirementDiagram-deff3bca-03f02ceb.js → requirementDiagram-deff3bca-c0deb984.js} +1 -1
  148. rasa/core/channels/inspector/dist/assets/{sankeyDiagram-04a897e0-c49eee40.js → sankeyDiagram-04a897e0-b9d7fd62.js} +1 -1
  149. rasa/core/channels/inspector/dist/assets/{sequenceDiagram-704730f1-b2cd6a3d.js → sequenceDiagram-704730f1-7d517565.js} +1 -1
  150. rasa/core/channels/inspector/dist/assets/{stateDiagram-587899a1-e53a2028.js → stateDiagram-587899a1-98ef9b27.js} +1 -1
  151. rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-d93cdb3a-e1982a03.js → stateDiagram-v2-d93cdb3a-cee70748.js} +1 -1
  152. rasa/core/channels/inspector/dist/assets/{styles-6aaf32cf-d0226ca5.js → styles-6aaf32cf-3f9d1c96.js} +1 -1
  153. rasa/core/channels/inspector/dist/assets/{styles-9a916d00-0e21dc00.js → styles-9a916d00-67471923.js} +1 -1
  154. rasa/core/channels/inspector/dist/assets/{styles-c10674c1-9588494e.js → styles-c10674c1-bd093fb7.js} +1 -1
  155. rasa/core/channels/inspector/dist/assets/{svgDrawCommon-08f97a94-be478d4f.js → svgDrawCommon-08f97a94-675794e8.js} +1 -1
  156. rasa/core/channels/inspector/dist/assets/{timeline-definition-85554ec2-74631749.js → timeline-definition-85554ec2-0ac67617.js} +1 -1
  157. rasa/core/channels/inspector/dist/assets/{xychartDiagram-e933f94c-a043552f.js → xychartDiagram-e933f94c-c018dc37.js} +1 -1
  158. rasa/core/channels/inspector/dist/index.html +2 -2
  159. rasa/core/channels/inspector/index.html +1 -1
  160. rasa/core/channels/inspector/package.json +4 -3
  161. rasa/core/channels/inspector/src/App.tsx +53 -7
  162. rasa/core/channels/inspector/src/components/Chat.tsx +3 -2
  163. rasa/core/channels/inspector/src/components/DiagramFlow.tsx +1 -1
  164. rasa/core/channels/inspector/src/components/LatencyDisplay.tsx +268 -0
  165. rasa/core/channels/inspector/src/components/LoadingSpinner.tsx +6 -2
  166. rasa/core/channels/inspector/src/helpers/audio/audiostream.ts +8 -3
  167. rasa/core/channels/inspector/src/types.ts +8 -0
  168. rasa/core/channels/inspector/yarn.lock +12 -12
  169. rasa/core/channels/studio_chat.py +119 -34
  170. rasa/core/channels/voice_ready/twilio_voice.py +1 -1
  171. rasa/core/channels/voice_stream/asr/asr_engine.py +5 -1
  172. rasa/core/channels/voice_stream/asr/deepgram.py +5 -0
  173. rasa/core/channels/voice_stream/audiocodes.py +16 -8
  174. rasa/core/channels/voice_stream/browser_audio.py +39 -4
  175. rasa/core/channels/voice_stream/call_state.py +13 -2
  176. rasa/core/channels/voice_stream/genesys.py +16 -13
  177. rasa/core/channels/voice_stream/jambonz.py +14 -12
  178. rasa/core/channels/voice_stream/twilio_media_streams.py +14 -13
  179. rasa/core/channels/voice_stream/util.py +11 -1
  180. rasa/core/channels/voice_stream/voice_channel.py +108 -29
  181. rasa/core/nlg/callback.py +1 -1
  182. rasa/core/nlg/contextual_response_rephraser.py +19 -9
  183. rasa/core/nlg/generator.py +21 -5
  184. rasa/core/nlg/response.py +43 -6
  185. rasa/core/nlg/translate.py +8 -0
  186. rasa/core/policies/enterprise_search_policy.py +16 -21
  187. rasa/dialogue_understanding/commands/correct_slots_command.py +38 -10
  188. rasa/dialogue_understanding/generator/command_generator.py +5 -5
  189. rasa/dialogue_understanding/generator/command_parser.py +9 -13
  190. rasa/dialogue_understanding/processor/command_processor.py +149 -55
  191. rasa/dialogue_understanding/stack/utils.py +13 -3
  192. rasa/dialogue_understanding_test/du_test_schema.yml +3 -3
  193. rasa/dialogue_understanding_test/validation.py +9 -10
  194. rasa/e2e_test/e2e_config.py +18 -11
  195. rasa/e2e_test/e2e_test_schema.yml +3 -3
  196. rasa/e2e_test/utils/validation.py +17 -19
  197. rasa/engine/validation.py +86 -91
  198. rasa/exceptions.py +26 -1
  199. rasa/model_manager/model_api.py +2 -2
  200. rasa/model_manager/socket_bridge.py +8 -2
  201. rasa/shared/providers/_configs/default_litellm_client_config.py +3 -7
  202. rasa/shared/utils/cli.py +2 -0
  203. rasa/shared/utils/common.py +2 -1
  204. rasa/shared/utils/health_check/health_check.py +10 -14
  205. rasa/studio/upload.py +6 -2
  206. rasa/studio/utils.py +33 -22
  207. rasa/telemetry.py +95 -22
  208. rasa/utils/licensing.py +21 -10
  209. rasa/utils/log_utils.py +1 -1
  210. rasa/utils/tensorflow/transformer.py +3 -3
  211. rasa/validator.py +7 -5
  212. rasa/version.py +1 -1
  213. {rasa_pro-3.13.1a18.dist-info → rasa_pro-3.13.1a20.dist-info}/METADATA +7 -7
  214. {rasa_pro-3.13.1a18.dist-info → rasa_pro-3.13.1a20.dist-info}/RECORD +242 -203
  215. rasa/builder/create_openai_vector_store.py +0 -228
  216. rasa/builder/llm-helper-schema.json +0 -69
  217. rasa/builder/llm_context.py +0 -81
  218. rasa/builder/llm_helper_prompt.jinja2 +0 -245
  219. rasa/cli/project_templates/finance/data/nlu.yml +0 -29
  220. rasa/cli/project_templates/finance/data/patterns/pattern_search.yml +0 -5
  221. rasa/cli/project_templates/finance/domain/default_flows.yml +0 -33
  222. rasa/cli/project_templates/finance/prompts/command-generator.jinja2 +0 -57
  223. rasa/cli/project_templates/finance/tests/conversation_repair/cancellations.yml +0 -12
  224. rasa/cli/project_templates/finance/tests/conversation_repair/cannot_handle.yml +0 -7
  225. rasa/cli/project_templates/finance/tests/conversation_repair/chitchat.yml +0 -7
  226. rasa/cli/project_templates/finance/tests/conversation_repair/clarification.yml +0 -9
  227. rasa/cli/project_templates/finance/tests/conversation_repair/completion.yml +0 -18
  228. rasa/cli/project_templates/finance/tests/conversation_repair/corrections.yml +0 -17
  229. rasa/cli/project_templates/finance/tests/conversation_repair/digressions.yml +0 -32
  230. rasa/cli/project_templates/finance/tests/conversation_repair/human_handoff.yml +0 -21
  231. rasa/cli/project_templates/finance/tests/conversation_repair/skipping_collect_steps.yml +0 -16
  232. rasa/cli/project_templates/finance/tests/demo_scripts/main.yml +0 -16
  233. rasa/cli/project_templates/finance/tests/happy_paths/balance_verification.yml +0 -15
  234. rasa/cli/project_templates/finance/tests/happy_paths/banking_questions.yml +0 -12
  235. rasa/cli/project_templates/finance/tests/happy_paths/card_blocking.yml +0 -52
  236. rasa/cli/project_templates/finance/tests/happy_paths/money_transfer.yml +0 -136
  237. rasa/cli/project_templates/finance/tests/happy_paths/payee_management.yml +0 -27
  238. rasa/cli/project_templates/finance/tests/happy_paths/user_greeted.yml +0 -5
  239. rasa/cli/project_templates/plain/config.yml +0 -17
  240. rasa/cli/project_templates/plain/data/patterns/pattern_session_start.yml +0 -7
  241. rasa/cli/project_templates/plain/domain.yml +0 -5
  242. rasa/core/channels/inspector/dist/assets/channel-f1efda17.js +0 -1
  243. rasa/core/channels/inspector/dist/assets/clone-fdf164e2.js +0 -1
  244. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-7d7a1629.js +0 -1
  245. rasa/shared/importers/static.py +0 -63
  246. /rasa/{cli/project_templates/plain/actions → builder/copilot}/__init__.py +0 -0
  247. /rasa/builder/{inkeep-rag-response-schema.json → document_retrieval/inkeep-rag-response-schema.json} +0 -0
  248. /rasa/cli/project_templates/finance/actions/{action_process_immediate_payment.py → transfers/action_process_immediate_payment.py} +0 -0
  249. /rasa/cli/project_templates/finance/actions/{action_schedule_payment.py → transfers/action_schedule_payment.py} +0 -0
  250. /rasa/cli/project_templates/finance/actions/{action_validate_payment_date.py → transfers/action_validate_payment_date.py} +0 -0
  251. /rasa/cli/project_templates/finance/data/{flows → cards}/block_card.yml +0 -0
  252. /rasa/cli/project_templates/finance/data/{flows → cards}/select_card.yml +0 -0
  253. /rasa/cli/project_templates/finance/data/{source → system/source}/accounts.json +0 -0
  254. /rasa/cli/project_templates/finance/data/{source → system/source}/advisors.json +0 -0
  255. /rasa/cli/project_templates/finance/data/{source → system/source}/appointments.json +0 -0
  256. /rasa/cli/project_templates/finance/data/{source → system/source}/branches.json +0 -0
  257. /rasa/cli/project_templates/finance/data/{source → system/source}/cards.json +0 -0
  258. /rasa/cli/project_templates/finance/data/{source → system/source}/payees.json +0 -0
  259. /rasa/cli/project_templates/finance/data/{source → system/source}/transactions.json +0 -0
  260. /rasa/cli/project_templates/finance/data/{source → system/source}/users.json +0 -0
  261. /rasa/cli/project_templates/finance/data/{flows → transfers}/add_payee.yml +0 -0
  262. /rasa/cli/project_templates/finance/data/{flows → transfers}/list_payees.yml +0 -0
  263. /rasa/cli/project_templates/finance/data/{flows → transfers}/remove_payee.yml +0 -0
  264. /rasa/cli/project_templates/finance/data/{flows → transfers}/transfer_money.yml +0 -0
  265. /rasa/cli/project_templates/finance/domain/{block_card.yml → cards/block_card.yml} +0 -0
  266. /rasa/cli/project_templates/finance/domain/{select_card.yml → cards/select_card.yml} +0 -0
  267. /rasa/cli/project_templates/finance/domain/{add_payee.yml → transfers/add_payee.yml} +0 -0
  268. /rasa/cli/project_templates/finance/domain/{list_payees.yml → transfers/list_payees.yml} +0 -0
  269. /rasa/cli/project_templates/finance/domain/{remove_payee.yml → transfers/remove_payee.yml} +0 -0
  270. /rasa/cli/project_templates/finance/domain/{transfer_money.yml → transfers/transfer_money.yml} +0 -0
  271. {rasa_pro-3.13.1a18.dist-info → rasa_pro-3.13.1a20.dist-info}/NOTICE +0 -0
  272. {rasa_pro-3.13.1a18.dist-info → rasa_pro-3.13.1a20.dist-info}/WHEEL +0 -0
  273. {rasa_pro-3.13.1a18.dist-info → rasa_pro-3.13.1a20.dist-info}/entry_points.txt +0 -0
@@ -1,7 +1,10 @@
1
+ from __future__ import annotations
2
+
1
3
  import asyncio
2
4
  import audioop
3
5
  import base64
4
6
  import json
7
+ import time
5
8
  import uuid
6
9
  from functools import partial
7
10
  from typing import (
@@ -14,6 +17,7 @@ from typing import (
14
17
  Optional,
15
18
  Text,
16
19
  Tuple,
20
+ Union,
17
21
  )
18
22
 
19
23
  import structlog
@@ -32,6 +36,7 @@ from rasa.core.channels.voice_stream.voice_channel import (
32
36
  VoiceInputChannel,
33
37
  VoiceOutputChannel,
34
38
  )
39
+ from rasa.core.exceptions import AgentNotReady
35
40
  from rasa.hooks import hookimpl
36
41
  from rasa.plugin import plugin_manager
37
42
  from rasa.shared.core.constants import ACTION_LISTEN_NAME
@@ -49,7 +54,9 @@ if TYPE_CHECKING:
49
54
  structlogger = structlog.get_logger()
50
55
 
51
56
 
52
- def tracker_as_dump(tracker: "DialogueStateTracker") -> Dict[str, Any]:
57
+ def tracker_as_dump(
58
+ tracker: "DialogueStateTracker", latency: Optional[float] = None
59
+ ) -> Dict[str, Any]:
53
60
  """Create a dump of the tracker state."""
54
61
  from rasa.shared.core.trackers import get_trackers_for_conversation_sessions
55
62
 
@@ -62,7 +69,12 @@ def tracker_as_dump(tracker: "DialogueStateTracker") -> Dict[str, Any]:
62
69
 
63
70
  # TODO: this is a bug: the bridge converts this back to json, but it
64
71
  # should be json in the first place
65
- return last_tracker.current_state(EventVerbosity.AFTER_RESTART)
72
+ state = last_tracker.current_state(EventVerbosity.AFTER_RESTART)
73
+
74
+ if latency is not None:
75
+ state["latency"] = {"rasa_processing_latency_ms": latency}
76
+
77
+ return state
66
78
 
67
79
 
68
80
  def does_need_action_prediction(tracker: "DialogueStateTracker") -> bool:
@@ -149,7 +161,6 @@ class StudioChatInput(SocketIOInput, VoiceInputChannel):
149
161
  from rasa.core.agent import Agent
150
162
 
151
163
  self.agent: Optional[Agent] = None
152
- self.latest_tracker_session_id = None
153
164
 
154
165
  # Initialize the SocketIO input channel
155
166
  SocketIOInput.__init__(
@@ -177,6 +188,7 @@ class StudioChatInput(SocketIOInput, VoiceInputChannel):
177
188
  # `background_tasks` holds the asyncio tasks for voice streaming
178
189
  self.active_connections: Dict[str, SocketIOVoiceWebsocketAdapter] = {}
179
190
  self.background_tasks: Dict[str, asyncio.Task] = {}
191
+ self._turn_start_times: Dict[Text, float] = {}
180
192
 
181
193
  self._register_tracker_update_hook()
182
194
 
@@ -203,24 +215,41 @@ class StudioChatInput(SocketIOInput, VoiceInputChannel):
203
215
  metadata_key=credentials.get("metadata_key", "metadata"),
204
216
  )
205
217
 
218
+ async def emit(self, event: str, data: Union[Dict, str], room: str) -> None:
219
+ """Emits an event to the websocket."""
220
+ if not self.sio:
221
+ structlogger.error("studio_chat.emit.sio_not_initialized")
222
+ return
223
+ await self.sio.emit(event, data, room=room)
224
+
206
225
  def _register_tracker_update_hook(self) -> None:
207
226
  plugin_manager().register(StudioTrackerUpdatePlugin(self))
208
227
 
209
- async def on_tracker_updated(self, tracker: "DialogueStateTracker") -> None:
228
+ async def on_tracker_updated(
229
+ self, tracker: "DialogueStateTracker", latency: Optional[float] = None
230
+ ) -> None:
210
231
  """Triggers a tracker update notification after a change to the tracker."""
211
- await self.publish_tracker_update(tracker.sender_id, tracker_as_dump(tracker))
232
+ await self.publish_tracker_update(
233
+ tracker.sender_id, tracker_as_dump(tracker, latency)
234
+ )
212
235
 
213
- async def publish_tracker_update(self, sender_id: str, tracker_dump: Dict) -> None:
236
+ async def publish_tracker_update(self, sender_id: str, tracker_dump: str) -> None:
214
237
  """Publishes a tracker update notification to the websocket."""
215
- if not self.sio:
216
- structlogger.error("studio_chat.on_tracker_updated.sio_not_initialized")
217
- return
238
+ await self.emit("tracker", tracker_dump, room=sender_id)
218
239
 
219
- # we need the latest session id to use it for the llm helper to get the
220
- # most recent conversation the user had with the bot.
221
- self.latest_tracker_session_id = sender_id
240
+ def _record_turn_start_time(self, sender_id: Text) -> None:
241
+ """Records the start time of a new turn."""
242
+ self._turn_start_times[sender_id] = time.time()
243
+
244
+ def _get_latency(self, sender_id: Text) -> Optional[float]:
245
+ """Returns the latency of the current turn in milliseconds."""
246
+ if sender_id not in self._turn_start_times:
247
+ return None
222
248
 
223
- await self.sio.emit("tracker", tracker_dump, room=sender_id)
249
+ latency = (time.time() - self._turn_start_times[sender_id]) * 1000
250
+ # The turn is over, so we can remove the start time
251
+ del self._turn_start_times[sender_id]
252
+ return latency
224
253
 
225
254
  async def on_message_proxy(
226
255
  self,
@@ -231,6 +260,7 @@ class StudioChatInput(SocketIOInput, VoiceInputChannel):
231
260
 
232
261
  Triggers a tracker update notification after processing the message.
233
262
  """
263
+ self._record_turn_start_time(message.sender_id)
234
264
  try:
235
265
  await on_new_message(message)
236
266
  except Exception as e:
@@ -240,8 +270,15 @@ class StudioChatInput(SocketIOInput, VoiceInputChannel):
240
270
  sender_id=message.sender_id,
241
271
  )
242
272
 
243
- if not self.agent:
273
+ if not self.agent or not self.agent.is_ready():
244
274
  structlogger.error("studio_chat.on_message_proxy.agent_not_initialized")
275
+ await self.emit_error(
276
+ "The Rasa Pro model could not be loaded. "
277
+ "Please check the training and deployment logs "
278
+ "for more information.",
279
+ message.sender_id,
280
+ AgentNotReady("The Rasa Pro model could not be loaded."),
281
+ )
245
282
  return
246
283
 
247
284
  tracker = await self.agent.tracker_store.retrieve(message.sender_id)
@@ -249,7 +286,19 @@ class StudioChatInput(SocketIOInput, VoiceInputChannel):
249
286
  structlogger.error("studio_chat.on_message_proxy.tracker_not_found")
250
287
  return
251
288
 
252
- await self.on_tracker_updated(tracker)
289
+ latency = self._get_latency(message.sender_id)
290
+ await self.on_tracker_updated(tracker, latency)
291
+
292
+ async def emit_error(self, message: str, room: str, e: Exception) -> None:
293
+ await self.emit(
294
+ "error",
295
+ {
296
+ "message": message,
297
+ "error": str(e),
298
+ "exception": str(type(e).__name__),
299
+ },
300
+ room=room,
301
+ )
253
302
 
254
303
  async def handle_tracker_update(self, sid: str, data: Dict) -> None:
255
304
  from rasa.shared.core.trackers import DialogueStateTracker
@@ -267,21 +316,41 @@ class StudioChatInput(SocketIOInput, VoiceInputChannel):
267
316
  structlogger.error("studio_chat.sio.domain_not_initialized")
268
317
  return None
269
318
 
319
+ tracker: Optional[DialogueStateTracker] = None
320
+
270
321
  async with self.agent.lock_store.lock(data["sender_id"]):
271
- tracker = DialogueStateTracker.from_dict(
272
- data["sender_id"], data["events"], domain.slots
273
- )
322
+ try:
323
+ tracker = DialogueStateTracker.from_dict(
324
+ data["sender_id"], data["events"], domain.slots
325
+ )
274
326
 
275
- # will override an existing tracker with the same id!
276
- await self.agent.tracker_store.save(tracker)
327
+ # will override an existing tracker with the same id!
328
+ await self.agent.tracker_store.save(tracker)
277
329
 
278
- processor = self.agent.processor
279
- if processor and does_need_action_prediction(tracker):
280
- output_channel = self.get_output_channel()
330
+ processor = self.agent.processor
331
+ if processor and does_need_action_prediction(tracker):
332
+ output_channel = self.get_output_channel()
281
333
 
282
- await processor._run_prediction_loop(output_channel, tracker)
283
- await self.agent.tracker_store.save(tracker)
334
+ await processor._run_prediction_loop(output_channel, tracker)
335
+ await self.agent.tracker_store.save(tracker)
336
+ except Exception as e:
337
+ structlogger.error(
338
+ "studio_chat.sio.handle_tracker_update.error",
339
+ error=e,
340
+ sender_id=data["sender_id"],
341
+ )
342
+ await self.emit_error(
343
+ "An error occurred while updating the conversation.",
344
+ data["sender_id"],
345
+ e,
346
+ )
284
347
 
348
+ if not tracker:
349
+ # in case the tracker couldn't be updated, we retrieve the prior
350
+ # version and use that to populate the update
351
+ tracker = await self.agent.tracker_store.get_or_create_tracker(
352
+ data["sender_id"]
353
+ )
285
354
  await self.on_tracker_updated(tracker)
286
355
 
287
356
  def channel_bytes_to_rasa_audio_bytes(self, input_bytes: bytes) -> RasaAudioBytes:
@@ -308,14 +377,14 @@ class StudioChatInput(SocketIOInput, VoiceInputChannel):
308
377
  elif "marker" in message:
309
378
  if message["marker"] == call_state.latest_bot_audio_id:
310
379
  # Just finished streaming last audio bytes
311
- call_state.is_bot_speaking = False # type: ignore[attr-defined]
380
+ call_state.is_bot_speaking = False
312
381
  if call_state.should_hangup:
313
382
  structlogger.debug(
314
383
  "studio_chat.hangup", marker=call_state.latest_bot_audio_id
315
384
  )
316
385
  return EndConversationAction()
317
386
  else:
318
- call_state.is_bot_speaking = True # type: ignore[attr-defined]
387
+ call_state.is_bot_speaking = True
319
388
  return ContinueConversationAction()
320
389
 
321
390
  def create_output_channel(
@@ -398,9 +467,8 @@ class StudioChatInput(SocketIOInput, VoiceInputChannel):
398
467
  def blueprint(
399
468
  self, on_new_message: Callable[["UserMessage"], Awaitable[Any]]
400
469
  ) -> SocketBlueprint:
401
- socket_blueprint = super().blueprint(
402
- partial(self.on_message_proxy, on_new_message)
403
- )
470
+ proxied_on_message = partial(self.on_message_proxy, on_new_message)
471
+ socket_blueprint = super().blueprint(proxied_on_message)
404
472
 
405
473
  if not self.sio:
406
474
  structlogger.error("studio_chat.blueprint.sio_not_initialized")
@@ -435,7 +503,7 @@ class StudioChatInput(SocketIOInput, VoiceInputChannel):
435
503
 
436
504
  # start a voice session if requested
437
505
  if data and data.get("is_voice", False):
438
- self._start_voice_session(data["session_id"], sid, on_new_message)
506
+ self._start_voice_session(data["session_id"], sid, proxied_on_message)
439
507
 
440
508
  @self.sio.on(self.user_message_evt, namespace=self.namespace)
441
509
  async def handle_message(sid: Text, data: Dict) -> None:
@@ -450,14 +518,14 @@ class StudioChatInput(SocketIOInput, VoiceInputChannel):
450
518
 
451
519
  try:
452
520
  # Handle text messages
453
- await self.handle_user_message(sid, data, on_new_message)
521
+ await self.handle_user_message(sid, data, proxied_on_message)
454
522
  except Exception as e:
455
523
  structlogger.exception(
456
524
  "studio_chat.sio.handle_message.error",
457
525
  error=str(e),
458
526
  sid=sid,
459
527
  )
460
- await self.sio.emit("error", str(e), room=sid)
528
+ await self.emit("error", str(e), room=sid)
461
529
 
462
530
  @self.sio.on("update_tracker", namespace=self.namespace)
463
531
  async def on_update_tracker(sid: Text, data: Dict) -> None:
@@ -481,7 +549,24 @@ class StudioVoiceOutputChannel(VoiceOutputChannel):
481
549
 
482
550
  def create_marker_message(self, recipient_id: str) -> Tuple[str, str]:
483
551
  message_id = uuid.uuid4().hex
484
- return json.dumps({"marker": message_id}), message_id
552
+ marker_data = {"marker": message_id}
553
+
554
+ # Include comprehensive latency information if available
555
+ latency_data = {
556
+ "asr_latency_ms": call_state.asr_latency_ms,
557
+ "rasa_processing_latency_ms": call_state.rasa_processing_latency_ms,
558
+ "tts_first_byte_latency_ms": call_state.tts_first_byte_latency_ms,
559
+ "tts_complete_latency_ms": call_state.tts_complete_latency_ms,
560
+ }
561
+
562
+ # Filter out None values from latency data
563
+ latency_data = {k: v for k, v in latency_data.items() if v is not None}
564
+
565
+ # Add latency data to marker if any metrics are available
566
+ if latency_data:
567
+ marker_data["latency"] = latency_data # type: ignore[assignment]
568
+
569
+ return json.dumps(marker_data), message_id
485
570
 
486
571
 
487
572
  class SocketIOVoiceWebsocketAdapter:
@@ -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"),
@@ -26,7 +26,7 @@ logger = structlog.get_logger(__name__)
26
26
 
27
27
  @dataclass
28
28
  class ASREngineConfig(MergeableConfig):
29
- pass
29
+ keep_alive_interval: int = 5 # seconds
30
30
 
31
31
 
32
32
  class ASREngine(Generic[T]):
@@ -93,3 +93,7 @@ class ASREngine(Generic[T]):
93
93
  def get_default_config() -> T:
94
94
  """Get the default config for this component."""
95
95
  raise NotImplementedError
96
+
97
+ async def send_keep_alive(self) -> None:
98
+ """Send a keep-alive message to the ASR system if supported."""
99
+ pass
@@ -145,3 +145,8 @@ class DeepgramASR(ASREngine[DeepgramASRConfig]):
145
145
  def concatenate_transcripts(t1: str, t2: str) -> str:
146
146
  """Concatenate two transcripts making sure there is a space between them."""
147
147
  return (t1.strip() + " " + t2.strip()).strip()
148
+
149
+ async def send_keep_alive(self) -> None:
150
+ """Send a keep-alive message to the Deepgram websocket connection."""
151
+ if self.asr_socket is not None:
152
+ await self.asr_socket.send(json.dumps({"type": "KeepAlive"}))
@@ -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,
@@ -81,6 +84,12 @@ class AudiocodesVoiceOutputChannel(VoiceOutputChannel):
81
84
  logger.debug("Sending start marker", stream_id=self._get_stream_id())
82
85
  await self.voice_websocket.send(media_message)
83
86
 
87
+ # This should be set when the bot actually starts speaking
88
+ # however, Audiocodes does not have an event to indicate that.
89
+ # This is an approximation, as the bot will be sent the audio chunks next
90
+ # which are played to the user immediately.
91
+ call_state.is_bot_speaking = True
92
+
84
93
  async def send_intermediate_marker(self, recipient_id: str) -> None:
85
94
  """Audiocodes doesn't need intermediate markers, so do nothing."""
86
95
  pass
@@ -121,10 +130,10 @@ class AudiocodesVoiceInputChannel(VoiceInputChannel):
121
130
  def from_credentials(
122
131
  cls,
123
132
  credentials: Optional[Dict[str, Any]],
124
- ) -> "AudiocodesVoiceInputChannel":
125
- channel = super().from_credentials(credentials)
126
- channel.token = credentials.get("token") # type: ignore[attr-defined, union-attr]
127
- return channel # type: ignore[return-value]
133
+ ) -> AudiocodesVoiceInputChannel:
134
+ cls.validate_basic_credentials(credentials)
135
+ new_creds = repack_voice_credentials(credentials)
136
+ return cls(**new_creds)
128
137
 
129
138
  def channel_bytes_to_rasa_audio_bytes(self, input_bytes: bytes) -> RasaAudioBytes:
130
139
  return RasaAudioBytes(base64.b64decode(input_bytes))
@@ -170,21 +179,20 @@ class AudiocodesVoiceInputChannel(VoiceInputChannel):
170
179
  if data["type"] == "activities":
171
180
  activities = data["activities"]
172
181
  for activity in activities:
173
- logger.debug("audiocodes_stream.activity", data=activity)
174
182
  if activity["name"] == "start":
175
- # already handled in collect_call_parameters
183
+ # handled in collect_call_parameters
176
184
  pass
177
185
  elif activity["name"] == "dtmf":
178
- # TODO: handle DTMF input
186
+ logger.info("audiocodes_stream.dtmf_ignored", data=activity)
179
187
  pass
180
188
  elif activity["name"] == "playFinished":
181
189
  logger.debug("audiocodes_stream.playFinished", data=activity)
190
+ call_state.is_bot_speaking = False
182
191
  if call_state.should_hangup:
183
192
  logger.info("audiocodes_stream.hangup")
184
193
  self._send_hangup(ws, data)
185
194
  # the conversation should continue until
186
195
  # we receive a end message from audiocodes
187
- pass
188
196
  else:
189
197
  logger.warning("audiocodes_stream.unknown_activity", data=activity)
190
198
  elif data["type"] == "userStream.start":
@@ -1,8 +1,10 @@
1
+ from __future__ import annotations
2
+
1
3
  import audioop
2
4
  import base64
3
5
  import json
4
6
  import uuid
5
- from typing import Any, Awaitable, Callable, Optional, Tuple
7
+ from typing import Any, Awaitable, Callable, Dict, Optional, Tuple
6
8
 
7
9
  import structlog
8
10
  from sanic import ( # type: ignore[attr-defined]
@@ -18,6 +20,7 @@ from rasa.core.channels.voice_ready.utils import CallParameters
18
20
  from rasa.core.channels.voice_stream.audio_bytes import RasaAudioBytes
19
21
  from rasa.core.channels.voice_stream.call_state import call_state
20
22
  from rasa.core.channels.voice_stream.tts.tts_engine import TTSEngine
23
+ from rasa.core.channels.voice_stream.util import repack_voice_credentials
21
24
  from rasa.core.channels.voice_stream.voice_channel import (
22
25
  ContinueConversationAction,
23
26
  EndConversationAction,
@@ -45,10 +48,33 @@ class BrowserAudioOutputChannel(VoiceOutputChannel):
45
48
 
46
49
  def create_marker_message(self, recipient_id: str) -> Tuple[str, str]:
47
50
  message_id = uuid.uuid4().hex
48
- return json.dumps({"marker": message_id}), message_id
51
+ marker_data = {"marker": message_id}
52
+
53
+ # Include comprehensive latency information if available
54
+ latency_data = {
55
+ "asr_latency_ms": call_state.asr_latency_ms,
56
+ "rasa_processing_latency_ms": call_state.rasa_processing_latency_ms,
57
+ "tts_first_byte_latency_ms": call_state.tts_first_byte_latency_ms,
58
+ "tts_complete_latency_ms": call_state.tts_complete_latency_ms,
59
+ }
60
+
61
+ # Filter out None values from latency data
62
+ latency_data = {k: v for k, v in latency_data.items() if v is not None}
63
+
64
+ # Add latency data to marker if any metrics are available
65
+ if latency_data:
66
+ marker_data["latency"] = latency_data # type: ignore[assignment]
67
+
68
+ return json.dumps(marker_data), message_id
49
69
 
50
70
 
51
71
  class BrowserAudioInputChannel(VoiceInputChannel):
72
+ def __init__(
73
+ self, server_url: str, asr_config: Dict[str, Any], tts_config: Dict[str, Any]
74
+ ) -> None:
75
+ """Initializes the browser audio input channel."""
76
+ super().__init__(server_url, asr_config, tts_config)
77
+
52
78
  @classmethod
53
79
  def name(cls) -> str:
54
80
  return "browser_audio"
@@ -62,6 +88,15 @@ class BrowserAudioInputChannel(VoiceInputChannel):
62
88
  call_id = f"inspect-{uuid.uuid4()}"
63
89
  return CallParameters(call_id, "local", "local", stream_id=call_id)
64
90
 
91
+ @classmethod
92
+ def from_credentials(
93
+ cls,
94
+ credentials: Optional[Dict[str, Any]],
95
+ ) -> BrowserAudioInputChannel:
96
+ cls.validate_basic_credentials(credentials)
97
+ new_creds = repack_voice_credentials(credentials)
98
+ return cls(**new_creds)
99
+
65
100
  def map_input_message(
66
101
  self,
67
102
  message: Any,
@@ -75,14 +110,14 @@ class BrowserAudioInputChannel(VoiceInputChannel):
75
110
  elif "marker" in data:
76
111
  if data["marker"] == call_state.latest_bot_audio_id:
77
112
  # Just finished streaming last audio bytes
78
- call_state.is_bot_speaking = False # type: ignore[attr-defined]
113
+ call_state.is_bot_speaking = False
79
114
  if call_state.should_hangup:
80
115
  logger.debug(
81
116
  "browser_audio.hangup", marker=call_state.latest_bot_audio_id
82
117
  )
83
118
  return EndConversationAction()
84
119
  else:
85
- call_state.is_bot_speaking = True # type: ignore[attr-defined]
120
+ call_state.is_bot_speaking = True
86
121
  return ContinueConversationAction()
87
122
 
88
123
  def create_output_channel(
@@ -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))
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import asyncio
2
4
  import base64
3
5
  import hashlib
@@ -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,
@@ -54,7 +57,7 @@ logger = structlog.get_logger(__name__)
54
57
 
55
58
 
56
59
  def map_call_params(data: Dict[Text, Any]) -> CallParameters:
57
- """Map the twilio stream parameters to the CallParameters dataclass."""
60
+ """Map the Genesys parameters to the CallParameters dataclass."""
58
61
  parameters = data["parameters"]
59
62
  participant = parameters["participant"]
60
63
  # sent as {"ani": "tel:+491604697810"}
@@ -107,7 +110,7 @@ class GenesysInputChannel(VoiceInputChannel):
107
110
  def from_credentials(
108
111
  cls,
109
112
  credentials: Optional[Dict[str, Any]],
110
- ) -> "GenesysInputChannel":
113
+ ) -> GenesysInputChannel:
111
114
  """Create a channel from credentials dictionary.
112
115
 
113
116
  Args:
@@ -121,21 +124,21 @@ class GenesysInputChannel(VoiceInputChannel):
121
124
  Returns:
122
125
  GenesysInputChannel instance
123
126
  """
124
- channel = super().from_credentials(credentials)
127
+ cls.validate_credentials(credentials)
128
+ new_creds = repack_voice_credentials(credentials)
129
+ return cls(**new_creds)
125
130
 
126
- # Check required Genesys-specific credentials
131
+ @classmethod
132
+ def validate_credentials(cls, credentials: Optional[Dict[str, Any]]) -> None:
133
+ """Validate the credentials for the Genesys voice channel."""
134
+ cls.validate_basic_credentials(credentials)
127
135
  if not credentials.get("api_key"): # type: ignore[union-attr]
128
136
  raise InvalidConfigException(
129
137
  "No API key given for Genesys voice channel (api_key)."
130
138
  )
131
139
 
132
- # Update channel with Genesys-specific credentials
133
- channel.api_key = credentials["api_key"] # type: ignore[index,attr-defined]
134
- channel.client_secret = credentials.get("client_secret") # type: ignore[union-attr,attr-defined]
135
-
136
- return channel # type: ignore[return-value]
137
-
138
- def _ensure_channel_data_initialized(self) -> None:
140
+ @staticmethod
141
+ def _ensure_channel_data_initialized() -> None:
139
142
  """Initialize Genesys-specific channel data if not already present.
140
143
 
141
144
  Genesys requires the server and client each maintain a
@@ -216,10 +219,10 @@ class GenesysInputChannel(VoiceInputChannel):
216
219
  self.handle_ping(ws, data)
217
220
  elif msg_type == "playback_started":
218
221
  logger.debug("genesys.handle_playback_started", message=data)
219
- call_state.is_bot_speaking = True # type: ignore[attr-defined]
222
+ call_state.is_bot_speaking = True
220
223
  elif msg_type == "playback_completed":
221
224
  logger.debug("genesys.handle_playback_completed", message=data)
222
- call_state.is_bot_speaking = False # type: ignore[attr-defined]
225
+ call_state.is_bot_speaking = False
223
226
  if call_state.should_hangup:
224
227
  logger.info("genesys.hangup")
225
228
  self.disconnect(ws, data)
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import audioop
2
4
  import json
3
5
  import uuid
@@ -20,6 +22,7 @@ from rasa.core.channels.voice_ready.utils import (
20
22
  from rasa.core.channels.voice_stream.audio_bytes import RasaAudioBytes
21
23
  from rasa.core.channels.voice_stream.call_state import call_state
22
24
  from rasa.core.channels.voice_stream.tts.tts_engine import TTSEngine
25
+ from rasa.core.channels.voice_stream.util import repack_voice_credentials
23
26
  from rasa.core.channels.voice_stream.voice_channel import (
24
27
  ContinueConversationAction,
25
28
  EndConversationAction,
@@ -31,11 +34,11 @@ from rasa.core.channels.voice_stream.voice_channel import (
31
34
 
32
35
  logger = structlog.get_logger()
33
36
 
34
- JAMBONZ_STREAMS_WEBSOCKET_PATH = "webhooks/jambonz_streams/websocket"
37
+ JAMBONZ_STREAMS_WEBSOCKET_PATH = "webhooks/jambonz_stream/websocket"
35
38
 
36
39
 
37
40
  def map_call_params(data: Dict[Text, str]) -> CallParameters:
38
- """Map the twilio stream parameters to the CallParameters dataclass."""
41
+ """Map the Jambonz stream parameters to the CallParameters dataclass."""
39
42
  call_sid = data.get("callSid", "None")
40
43
  from_number = data.get("from", "Unknown")
41
44
  to_number = data.get("to")
@@ -94,7 +97,7 @@ class JambonzStreamInputChannel(VoiceInputChannel):
94
97
  @classmethod
95
98
  def from_credentials(
96
99
  cls, credentials: Optional[Dict[Text, Any]]
97
- ) -> "JambonzStreamInputChannel":
100
+ ) -> JambonzStreamInputChannel:
98
101
  """Create a channel from credentials dictionary.
99
102
 
100
103
  Args:
@@ -109,19 +112,18 @@ class JambonzStreamInputChannel(VoiceInputChannel):
109
112
  JambonzStreamInputChannel instance
110
113
  """
111
114
  # Get common credentials from parent
112
- channel = super().from_credentials(credentials)
115
+ cls.validate_credentials(credentials)
116
+ new_creds = repack_voice_credentials(credentials)
117
+ return cls(**new_creds)
113
118
 
119
+ @classmethod
120
+ def validate_credentials(cls, credentials: Optional[Dict[Text, Any]]) -> None:
121
+ cls.validate_basic_credentials(credentials)
114
122
  # Check optional basic auth credentials
115
123
  username = credentials.get("username") # type: ignore[union-attr]
116
124
  password = credentials.get("password") # type: ignore[union-attr]
117
125
  validate_username_password_credentials(username, password, "Jambonz Stream")
118
126
 
119
- # Update channel with auth credentials
120
- channel.username = username # type: ignore[attr-defined]
121
- channel.password = password # type: ignore[attr-defined]
122
-
123
- return channel # type: ignore[return-value]
124
-
125
127
  def _websocket_stream_url(self) -> str:
126
128
  """Returns the websocket stream URL."""
127
129
  # depending on the config value, the url might contain http as a
@@ -158,14 +160,14 @@ class JambonzStreamInputChannel(VoiceInputChannel):
158
160
  if data["type"] == "mark":
159
161
  if data["data"]["name"] == call_state.latest_bot_audio_id:
160
162
  # Just finished streaming last audio bytes
161
- call_state.is_bot_speaking = False # type: ignore[attr-defined]
163
+ call_state.is_bot_speaking = False
162
164
  if call_state.should_hangup:
163
165
  logger.debug(
164
166
  "jambonz.hangup", marker=call_state.latest_bot_audio_id
165
167
  )
166
168
  return EndConversationAction()
167
169
  else:
168
- call_state.is_bot_speaking = True # type: ignore[attr-defined]
170
+ call_state.is_bot_speaking = True
169
171
  elif data["event"] == "dtmf":
170
172
  # TODO: handle DTMF input
171
173
  logger.debug("jambonz.dtmf.received", dtmf=data["dtmf"])