rasa-pro 3.12.22__py3-none-any.whl → 3.13.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 (354) hide show
  1. rasa/__main__.py +3 -4
  2. rasa/api.py +1 -1
  3. rasa/cli/dialogue_understanding_test.py +1 -1
  4. rasa/cli/e2e_test.py +1 -8
  5. rasa/cli/evaluate.py +2 -2
  6. rasa/cli/export.py +5 -3
  7. rasa/cli/inspect.py +7 -0
  8. rasa/cli/llm_fine_tuning.py +1 -1
  9. rasa/cli/project_templates/default/config.yml +5 -32
  10. rasa/cli/project_templates/{calm → default}/e2e_tests/cancelations/user_cancels_during_a_correction.yml +1 -1
  11. rasa/cli/project_templates/{calm → default}/e2e_tests/cancelations/user_changes_mind_on_a_whim.yml +1 -1
  12. rasa/cli/project_templates/{calm → default}/e2e_tests/corrections/user_corrects_contact_handle.yml +1 -1
  13. rasa/cli/project_templates/{calm → default}/e2e_tests/corrections/user_corrects_contact_name.yml +1 -1
  14. rasa/cli/project_templates/{calm → default}/e2e_tests/happy_paths/user_adds_contact_to_their_list.yml +1 -1
  15. rasa/cli/project_templates/{calm → default}/e2e_tests/happy_paths/user_lists_contacts.yml +1 -1
  16. rasa/cli/project_templates/{calm → default}/e2e_tests/happy_paths/user_removes_contact.yml +1 -1
  17. rasa/cli/project_templates/{calm → default}/e2e_tests/happy_paths/user_removes_contact_from_list.yml +1 -1
  18. rasa/cli/project_templates/default/endpoints.yml +18 -2
  19. rasa/cli/project_templates/defaults.py +133 -0
  20. rasa/cli/project_templates/tutorial/config.yml +1 -1
  21. rasa/cli/project_templates/tutorial/endpoints.yml +1 -1
  22. rasa/cli/run.py +1 -1
  23. rasa/cli/scaffold.py +2 -3
  24. rasa/cli/shell.py +6 -1
  25. rasa/cli/studio/download.py +0 -22
  26. rasa/cli/studio/link.py +36 -0
  27. rasa/cli/studio/pull.py +79 -0
  28. rasa/cli/studio/push.py +78 -0
  29. rasa/cli/studio/studio.py +12 -0
  30. rasa/cli/studio/train.py +1 -5
  31. rasa/cli/studio/upload.py +6 -4
  32. rasa/cli/train.py +5 -1
  33. rasa/cli/utils.py +1 -1
  34. rasa/cli/x.py +1 -1
  35. rasa/constants.py +2 -0
  36. rasa/core/__init__.py +0 -16
  37. rasa/core/actions/action.py +43 -29
  38. rasa/core/actions/action_repeat_bot_messages.py +18 -22
  39. rasa/core/actions/action_run_slot_rejections.py +1 -2
  40. rasa/core/agent.py +24 -3
  41. rasa/core/available_endpoints.py +146 -0
  42. rasa/core/brokers/kafka.py +4 -0
  43. rasa/core/brokers/pika.py +5 -2
  44. rasa/core/brokers/sql.py +1 -1
  45. rasa/core/channels/__init__.py +3 -0
  46. rasa/core/channels/botframework.py +2 -2
  47. rasa/core/channels/channel.py +2 -2
  48. rasa/core/channels/development_inspector.py +1 -1
  49. rasa/core/channels/facebook.py +1 -4
  50. rasa/core/channels/hangouts.py +8 -5
  51. rasa/core/channels/inspector/.eslintrc.cjs +12 -6
  52. rasa/core/channels/inspector/.prettierrc +5 -0
  53. rasa/core/channels/inspector/README.md +11 -5
  54. rasa/core/channels/inspector/dist/assets/{arc-9f75cc3b.js → arc-371401b1.js} +1 -1
  55. rasa/core/channels/inspector/dist/assets/{blockDiagram-38ab4fdb-7f34db23.js → blockDiagram-38ab4fdb-3f126156.js} +1 -1
  56. rasa/core/channels/inspector/dist/assets/{c4Diagram-3d4e48cf-948bab2c.js → c4Diagram-3d4e48cf-12f22eb7.js} +1 -1
  57. rasa/core/channels/inspector/dist/assets/channel-f1efda17.js +1 -0
  58. rasa/core/channels/inspector/dist/assets/{classDiagram-70f12bd4-53b0dd0e.js → classDiagram-70f12bd4-03b1d386.js} +1 -1
  59. rasa/core/channels/inspector/dist/assets/{classDiagram-v2-f2320105-fdf789e7.js → classDiagram-v2-f2320105-84f69d63.js} +1 -1
  60. rasa/core/channels/inspector/dist/assets/clone-fdf164e2.js +1 -0
  61. rasa/core/channels/inspector/dist/assets/{createText-2e5e7dd3-87c4ece5.js → createText-2e5e7dd3-ca47fd38.js} +1 -1
  62. rasa/core/channels/inspector/dist/assets/{edges-e0da2a9e-5a8b0749.js → edges-e0da2a9e-f837ca8a.js} +1 -1
  63. rasa/core/channels/inspector/dist/assets/{erDiagram-9861fffd-66da90e2.js → erDiagram-9861fffd-8717ac54.js} +1 -1
  64. rasa/core/channels/inspector/dist/assets/{flowDb-956e92f1-10044f05.js → flowDb-956e92f1-94f38b83.js} +1 -1
  65. rasa/core/channels/inspector/dist/assets/{flowDiagram-66a62f08-f338f66a.js → flowDiagram-66a62f08-b616f9fb.js} +1 -1
  66. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-7d7a1629.js +1 -0
  67. rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-4a651766-b13140aa.js → flowchart-elk-definition-4a651766-f5d24bb8.js} +1 -1
  68. rasa/core/channels/inspector/dist/assets/{ganttDiagram-c361ad54-f2b4a55a.js → ganttDiagram-c361ad54-b43ba8d9.js} +1 -1
  69. rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-72cf32ee-dedc298d.js → gitGraphDiagram-72cf32ee-c3aafaa5.js} +1 -1
  70. rasa/core/channels/inspector/dist/assets/{graph-4ede11ff.js → graph-0d0a2c10.js} +1 -1
  71. rasa/core/channels/inspector/dist/assets/{index-3862675e-65549d37.js → index-3862675e-58ea0305.js} +1 -1
  72. rasa/core/channels/inspector/dist/assets/{index-3a23e736.js → index-cce6f8a1.js} +123 -123
  73. rasa/core/channels/inspector/dist/assets/{infoDiagram-f8f76790-65439671.js → infoDiagram-f8f76790-b8f60461.js} +1 -1
  74. rasa/core/channels/inspector/dist/assets/{journeyDiagram-49397b02-56d03d98.js → journeyDiagram-49397b02-95be5545.js} +1 -1
  75. rasa/core/channels/inspector/dist/assets/{layout-dd48f7f4.js → layout-da885b9b.js} +1 -1
  76. rasa/core/channels/inspector/dist/assets/{line-1569ad2c.js → line-f1c817d3.js} +1 -1
  77. rasa/core/channels/inspector/dist/assets/{linear-48bf4935.js → linear-d42801e6.js} +1 -1
  78. rasa/core/channels/inspector/dist/assets/{mindmap-definition-fc14e90a-688504c1.js → mindmap-definition-fc14e90a-a38923a6.js} +1 -1
  79. rasa/core/channels/inspector/dist/assets/{pieDiagram-8a3498a8-78b6d7e6.js → pieDiagram-8a3498a8-ca6e71e9.js} +1 -1
  80. rasa/core/channels/inspector/dist/assets/{quadrantDiagram-120e2f19-048b84b3.js → quadrantDiagram-120e2f19-b290dae9.js} +1 -1
  81. rasa/core/channels/inspector/dist/assets/{requirementDiagram-deff3bca-dd67f107.js → requirementDiagram-deff3bca-03f02ceb.js} +1 -1
  82. rasa/core/channels/inspector/dist/assets/{sankeyDiagram-04a897e0-8128436e.js → sankeyDiagram-04a897e0-c49eee40.js} +1 -1
  83. rasa/core/channels/inspector/dist/assets/{sequenceDiagram-704730f1-1a0d1461.js → sequenceDiagram-704730f1-b2cd6a3d.js} +1 -1
  84. rasa/core/channels/inspector/dist/assets/{stateDiagram-587899a1-46d388ed.js → stateDiagram-587899a1-e53a2028.js} +1 -1
  85. rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-d93cdb3a-ea42951a.js → stateDiagram-v2-d93cdb3a-e1982a03.js} +1 -1
  86. rasa/core/channels/inspector/dist/assets/{styles-6aaf32cf-7427ed0c.js → styles-6aaf32cf-d0226ca5.js} +1 -1
  87. rasa/core/channels/inspector/dist/assets/{styles-9a916d00-ff5e5a16.js → styles-9a916d00-0e21dc00.js} +1 -1
  88. rasa/core/channels/inspector/dist/assets/{styles-c10674c1-7b3680cf.js → styles-c10674c1-9588494e.js} +1 -1
  89. rasa/core/channels/inspector/dist/assets/{svgDrawCommon-08f97a94-f860f2ad.js → svgDrawCommon-08f97a94-be478d4f.js} +1 -1
  90. rasa/core/channels/inspector/dist/assets/{timeline-definition-85554ec2-2eebf0c8.js → timeline-definition-85554ec2-74631749.js} +1 -1
  91. rasa/core/channels/inspector/dist/assets/{xychartDiagram-e933f94c-5d7f4e96.js → xychartDiagram-e933f94c-a043552f.js} +1 -1
  92. rasa/core/channels/inspector/dist/index.html +1 -1
  93. rasa/core/channels/inspector/package.json +3 -1
  94. rasa/core/channels/inspector/src/App.tsx +91 -90
  95. rasa/core/channels/inspector/src/components/Chat.tsx +45 -41
  96. rasa/core/channels/inspector/src/components/DiagramFlow.tsx +40 -40
  97. rasa/core/channels/inspector/src/components/DialogueInformation.tsx +57 -57
  98. rasa/core/channels/inspector/src/components/DialogueStack.tsx +36 -27
  99. rasa/core/channels/inspector/src/components/ExpandIcon.tsx +4 -4
  100. rasa/core/channels/inspector/src/components/FullscreenButton.tsx +7 -7
  101. rasa/core/channels/inspector/src/components/LoadingSpinner.tsx +28 -12
  102. rasa/core/channels/inspector/src/components/NoActiveFlow.tsx +9 -9
  103. rasa/core/channels/inspector/src/components/RasaLogo.tsx +5 -5
  104. rasa/core/channels/inspector/src/components/RecruitmentPanel.tsx +55 -60
  105. rasa/core/channels/inspector/src/components/SaraDiagrams.tsx +5 -5
  106. rasa/core/channels/inspector/src/components/Slots.tsx +22 -22
  107. rasa/core/channels/inspector/src/components/Welcome.tsx +28 -31
  108. rasa/core/channels/inspector/src/helpers/audio/audiostream.ts +245 -0
  109. rasa/core/channels/inspector/src/helpers/audio/microphone-processor.js +12 -0
  110. rasa/core/channels/inspector/src/helpers/audio/playback-processor.js +36 -0
  111. rasa/core/channels/inspector/src/helpers/conversation.ts +7 -7
  112. rasa/core/channels/inspector/src/helpers/formatters.test.ts +181 -181
  113. rasa/core/channels/inspector/src/helpers/formatters.ts +111 -111
  114. rasa/core/channels/inspector/src/helpers/utils.ts +78 -61
  115. rasa/core/channels/inspector/src/main.tsx +8 -8
  116. rasa/core/channels/inspector/src/theme/Button/Button.ts +8 -8
  117. rasa/core/channels/inspector/src/theme/Heading/Heading.ts +7 -7
  118. rasa/core/channels/inspector/src/theme/Input/Input.ts +9 -9
  119. rasa/core/channels/inspector/src/theme/Link/Link.ts +6 -6
  120. rasa/core/channels/inspector/src/theme/Modal/Modal.ts +13 -13
  121. rasa/core/channels/inspector/src/theme/Table/Table.tsx +10 -10
  122. rasa/core/channels/inspector/src/theme/Tooltip/Tooltip.ts +5 -5
  123. rasa/core/channels/inspector/src/theme/base/breakpoints.ts +7 -7
  124. rasa/core/channels/inspector/src/theme/base/colors.ts +64 -64
  125. rasa/core/channels/inspector/src/theme/base/fonts/fontFaces.css +21 -18
  126. rasa/core/channels/inspector/src/theme/base/radii.ts +8 -8
  127. rasa/core/channels/inspector/src/theme/base/shadows.ts +5 -5
  128. rasa/core/channels/inspector/src/theme/base/sizes.ts +5 -5
  129. rasa/core/channels/inspector/src/theme/base/space.ts +12 -12
  130. rasa/core/channels/inspector/src/theme/base/styles.ts +5 -5
  131. rasa/core/channels/inspector/src/theme/base/typography.ts +12 -12
  132. rasa/core/channels/inspector/src/theme/base/zIndices.ts +3 -3
  133. rasa/core/channels/inspector/src/theme/index.ts +38 -38
  134. rasa/core/channels/inspector/src/types.ts +56 -50
  135. rasa/core/channels/inspector/yarn.lock +5 -0
  136. rasa/core/channels/mattermost.py +1 -1
  137. rasa/core/channels/rasa_chat.py +2 -4
  138. rasa/core/channels/rest.py +5 -4
  139. rasa/core/channels/socketio.py +56 -41
  140. rasa/core/channels/studio_chat.py +329 -68
  141. rasa/core/channels/vier_cvg.py +1 -2
  142. rasa/core/channels/voice_ready/audiocodes.py +4 -11
  143. rasa/core/channels/voice_ready/jambonz.py +5 -6
  144. rasa/core/channels/voice_ready/twilio_voice.py +13 -12
  145. rasa/core/channels/voice_ready/utils.py +22 -0
  146. rasa/core/channels/voice_stream/audiocodes.py +13 -16
  147. rasa/core/channels/voice_stream/browser_audio.py +1 -1
  148. rasa/core/channels/voice_stream/genesys.py +37 -18
  149. rasa/core/channels/voice_stream/jambonz.py +232 -0
  150. rasa/core/channels/voice_stream/tts/__init__.py +8 -0
  151. rasa/core/channels/voice_stream/twilio_media_streams.py +15 -12
  152. rasa/core/channels/voice_stream/voice_channel.py +71 -27
  153. rasa/core/concurrent_lock_store.py +24 -10
  154. rasa/core/evaluation/marker_tracker_loader.py +1 -1
  155. rasa/core/exporter.py +37 -1
  156. rasa/core/http_interpreter.py +3 -7
  157. rasa/core/information_retrieval/faiss.py +18 -11
  158. rasa/core/information_retrieval/ingestion/faq_parser.py +158 -0
  159. rasa/core/jobs.py +2 -1
  160. rasa/core/lock_store.py +151 -60
  161. rasa/core/nlg/contextual_response_rephraser.py +17 -7
  162. rasa/core/nlg/generator.py +5 -22
  163. rasa/core/nlg/interpolator.py +2 -3
  164. rasa/core/nlg/response.py +6 -43
  165. rasa/core/nlg/summarize.py +1 -1
  166. rasa/core/nlg/translate.py +0 -8
  167. rasa/core/policies/enterprise_search_policy.py +305 -189
  168. rasa/core/policies/enterprise_search_policy_config.py +241 -0
  169. rasa/core/policies/enterprise_search_prompt_with_relevancy_check_and_citation_template.jinja2 +67 -0
  170. rasa/core/policies/flow_policy.py +1 -1
  171. rasa/core/policies/flows/flow_executor.py +102 -17
  172. rasa/core/policies/intentless_policy.py +56 -17
  173. rasa/core/processor.py +70 -49
  174. rasa/core/run.py +33 -11
  175. rasa/core/tracker_stores/__init__.py +0 -0
  176. rasa/core/{auth_retry_tracker_store.py → tracker_stores/auth_retry_tracker_store.py} +66 -1
  177. rasa/core/tracker_stores/dynamo_tracker_store.py +256 -0
  178. rasa/core/tracker_stores/mongo_tracker_store.py +223 -0
  179. rasa/core/tracker_stores/redis_tracker_store.py +252 -0
  180. rasa/core/tracker_stores/sql_tracker_store.py +582 -0
  181. rasa/core/tracker_stores/tracker_store.py +839 -0
  182. rasa/core/training/interactive.py +1 -1
  183. rasa/core/utils.py +24 -95
  184. rasa/dialogue_understanding/coexistence/intent_based_router.py +2 -1
  185. rasa/dialogue_understanding/coexistence/llm_based_router.py +13 -11
  186. rasa/dialogue_understanding/commands/can_not_handle_command.py +2 -0
  187. rasa/dialogue_understanding/commands/cancel_flow_command.py +3 -1
  188. rasa/dialogue_understanding/commands/chit_chat_answer_command.py +2 -0
  189. rasa/dialogue_understanding/commands/clarify_command.py +6 -2
  190. rasa/dialogue_understanding/commands/command_syntax_manager.py +1 -0
  191. rasa/dialogue_understanding/commands/correct_slots_command.py +5 -6
  192. rasa/dialogue_understanding/commands/error_command.py +1 -1
  193. rasa/dialogue_understanding/commands/human_handoff_command.py +3 -3
  194. rasa/dialogue_understanding/commands/knowledge_answer_command.py +2 -0
  195. rasa/dialogue_understanding/commands/repeat_bot_messages_command.py +2 -0
  196. rasa/dialogue_understanding/commands/set_slot_command.py +8 -4
  197. rasa/dialogue_understanding/commands/skip_question_command.py +3 -3
  198. rasa/dialogue_understanding/commands/start_flow_command.py +7 -3
  199. rasa/dialogue_understanding/generator/__init__.py +7 -1
  200. rasa/dialogue_understanding/generator/command_generator.py +4 -2
  201. rasa/dialogue_understanding/generator/command_parser.py +2 -2
  202. rasa/dialogue_understanding/generator/command_parser_validator.py +63 -0
  203. rasa/dialogue_understanding/generator/llm_based_command_generator.py +1 -2
  204. rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +3 -2
  205. rasa/dialogue_understanding/generator/nlu_command_adapter.py +2 -2
  206. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_template.jinja2 +0 -2
  207. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_claude_3_5_sonnet_20240620_template.jinja2 +1 -0
  208. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_gpt_4o_2024_11_20_template.jinja2 +1 -0
  209. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v3_claude_3_5_sonnet_20240620_template.jinja2 +79 -0
  210. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v3_gpt_4o_2024_11_20_template.jinja2 +79 -0
  211. rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +26 -461
  212. rasa/dialogue_understanding/generator/single_step/search_ready_llm_command_generator.py +147 -0
  213. rasa/dialogue_understanding/generator/single_step/single_step_based_llm_command_generator.py +461 -0
  214. rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +20 -64
  215. rasa/dialogue_understanding/patterns/cancel.py +1 -2
  216. rasa/dialogue_understanding/patterns/clarify.py +1 -1
  217. rasa/dialogue_understanding/patterns/correction.py +2 -2
  218. rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +42 -27
  219. rasa/dialogue_understanding/patterns/domain_for_patterns.py +190 -0
  220. rasa/dialogue_understanding/processor/command_processor.py +6 -7
  221. rasa/dialogue_understanding_test/command_metric_calculation.py +7 -40
  222. rasa/dialogue_understanding_test/command_metrics.py +38 -0
  223. rasa/dialogue_understanding_test/du_test_case.py +58 -25
  224. rasa/dialogue_understanding_test/du_test_result.py +228 -132
  225. rasa/dialogue_understanding_test/du_test_runner.py +11 -2
  226. rasa/dialogue_understanding_test/du_test_schema.yml +3 -3
  227. rasa/dialogue_understanding_test/io.py +35 -8
  228. rasa/e2e_test/constants.py +1 -1
  229. rasa/e2e_test/e2e_test_runner.py +1 -1
  230. rasa/e2e_test/e2e_test_schema.yml +3 -3
  231. rasa/engine/constants.py +1 -1
  232. rasa/engine/graph.py +2 -2
  233. rasa/engine/recipes/default_recipe.py +1 -1
  234. rasa/engine/validation.py +3 -2
  235. rasa/hooks.py +2 -30
  236. rasa/llm_fine_tuning/paraphrasing/conversation_rephraser.py +2 -6
  237. rasa/model_manager/model_api.py +89 -1
  238. rasa/model_manager/runner_service.py +20 -4
  239. rasa/model_manager/socket_bridge.py +0 -7
  240. rasa/model_manager/trainer_service.py +10 -4
  241. rasa/plugin.py +2 -15
  242. rasa/privacy/__init__.py +0 -0
  243. rasa/privacy/constants.py +83 -0
  244. rasa/privacy/event_broker_utils.py +77 -0
  245. rasa/privacy/privacy_config.py +281 -0
  246. rasa/privacy/privacy_config_schema.json +86 -0
  247. rasa/privacy/privacy_filter.py +393 -0
  248. rasa/privacy/privacy_manager.py +594 -0
  249. rasa/server.py +23 -2
  250. rasa/shared/constants.py +17 -0
  251. rasa/shared/core/command_payload_reader.py +1 -5
  252. rasa/shared/core/constants.py +4 -3
  253. rasa/shared/core/domain.py +172 -11
  254. rasa/shared/core/events.py +100 -6
  255. rasa/shared/core/flows/flow.py +30 -5
  256. rasa/shared/core/flows/flow_step.py +19 -3
  257. rasa/shared/core/flows/flow_step_links.py +15 -0
  258. rasa/shared/core/flows/flow_step_sequence.py +6 -0
  259. rasa/shared/core/flows/flows_yaml_schema.json +3 -0
  260. rasa/shared/core/flows/nlu_trigger.py +13 -0
  261. rasa/shared/core/flows/steps/action.py +7 -4
  262. rasa/shared/core/flows/steps/call.py +11 -4
  263. rasa/shared/core/flows/steps/collect.py +71 -6
  264. rasa/shared/core/flows/steps/internal.py +6 -1
  265. rasa/shared/core/flows/steps/link.py +7 -4
  266. rasa/shared/core/flows/steps/no_operation.py +7 -4
  267. rasa/shared/core/flows/steps/set_slots.py +8 -4
  268. rasa/shared/core/flows/validation.py +25 -5
  269. rasa/shared/core/flows/yaml_flows_io.py +106 -5
  270. rasa/shared/core/slots.py +29 -1
  271. rasa/shared/core/trackers.py +21 -10
  272. rasa/shared/core/training_data/story_reader/yaml_story_reader.py +1 -4
  273. rasa/shared/importers/importer.py +8 -0
  274. rasa/shared/providers/_configs/azure_openai_client_config.py +2 -2
  275. rasa/shared/providers/_configs/default_litellm_client_config.py +1 -1
  276. rasa/shared/providers/_configs/huggingface_local_embedding_client_config.py +1 -1
  277. rasa/shared/providers/_configs/openai_client_config.py +1 -1
  278. rasa/shared/providers/_configs/rasa_llm_client_config.py +1 -1
  279. rasa/shared/providers/_configs/self_hosted_llm_client_config.py +1 -1
  280. rasa/shared/providers/_configs/utils.py +0 -99
  281. rasa/shared/providers/llm/default_litellm_llm_client.py +2 -2
  282. rasa/shared/utils/common.py +43 -1
  283. rasa/shared/utils/configs.py +110 -0
  284. rasa/shared/utils/constants.py +0 -3
  285. rasa/shared/utils/llm.py +245 -8
  286. rasa/shared/utils/pykwalify_extensions.py +0 -9
  287. rasa/shared/utils/yaml.py +32 -0
  288. rasa/studio/constants.py +1 -0
  289. rasa/studio/data_handler.py +33 -12
  290. rasa/studio/download.py +117 -435
  291. rasa/studio/link.py +211 -0
  292. rasa/studio/prompts.py +221 -0
  293. rasa/studio/pull/__init__.py +0 -0
  294. rasa/studio/pull/data.py +222 -0
  295. rasa/studio/pull/domains.py +60 -0
  296. rasa/studio/pull/pull.py +239 -0
  297. rasa/studio/push.py +138 -0
  298. rasa/studio/results_logger.py +6 -1
  299. rasa/studio/train.py +1 -1
  300. rasa/studio/upload.py +243 -72
  301. rasa/studio/utils.py +33 -0
  302. rasa/telemetry.py +83 -26
  303. rasa/tracing/config.py +4 -5
  304. rasa/tracing/constants.py +19 -1
  305. rasa/tracing/instrumentation/attribute_extractors.py +68 -16
  306. rasa/tracing/instrumentation/instrumentation.py +54 -3
  307. rasa/tracing/instrumentation/metrics.py +98 -15
  308. rasa/tracing/metric_instrument_provider.py +75 -3
  309. rasa/utils/common.py +43 -22
  310. rasa/utils/endpoints.py +22 -1
  311. rasa/utils/licensing.py +2 -3
  312. rasa/utils/log_utils.py +1 -45
  313. rasa/validator.py +2 -8
  314. rasa/version.py +1 -1
  315. {rasa_pro-3.12.22.dist-info → rasa_pro-3.13.0.dist-info}/METADATA +11 -12
  316. {rasa_pro-3.12.22.dist-info → rasa_pro-3.13.0.dist-info}/RECORD +333 -309
  317. rasa/anonymization/__init__.py +0 -2
  318. rasa/anonymization/anonymisation_rule_yaml_reader.py +0 -91
  319. rasa/anonymization/anonymization_pipeline.py +0 -286
  320. rasa/anonymization/anonymization_rule_executor.py +0 -266
  321. rasa/anonymization/anonymization_rule_orchestrator.py +0 -119
  322. rasa/anonymization/schemas/config.yml +0 -47
  323. rasa/anonymization/utils.py +0 -118
  324. rasa/cli/project_templates/calm/config.yml +0 -10
  325. rasa/cli/project_templates/calm/credentials.yml +0 -33
  326. rasa/cli/project_templates/calm/endpoints.yml +0 -58
  327. rasa/cli/project_templates/default/actions/actions.py +0 -27
  328. rasa/cli/project_templates/default/data/nlu.yml +0 -91
  329. rasa/cli/project_templates/default/data/rules.yml +0 -13
  330. rasa/cli/project_templates/default/data/stories.yml +0 -30
  331. rasa/cli/project_templates/default/domain.yml +0 -34
  332. rasa/cli/project_templates/default/tests/test_stories.yml +0 -91
  333. rasa/core/channels/inspector/dist/assets/channel-dfa68278.js +0 -1
  334. rasa/core/channels/inspector/dist/assets/clone-edb7f119.js +0 -1
  335. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-65e7c670.js +0 -1
  336. rasa/core/channels/inspector/src/helpers/audiostream.ts +0 -191
  337. rasa/core/tracker_store.py +0 -1792
  338. /rasa/cli/project_templates/{calm → default}/actions/action_template.py +0 -0
  339. /rasa/cli/project_templates/{calm → default}/actions/add_contact.py +0 -0
  340. /rasa/cli/project_templates/{calm → default}/actions/db.py +0 -0
  341. /rasa/cli/project_templates/{calm → default}/actions/list_contacts.py +0 -0
  342. /rasa/cli/project_templates/{calm → default}/actions/remove_contact.py +0 -0
  343. /rasa/cli/project_templates/{calm → default}/data/flows/add_contact.yml +0 -0
  344. /rasa/cli/project_templates/{calm → default}/data/flows/list_contacts.yml +0 -0
  345. /rasa/cli/project_templates/{calm → default}/data/flows/remove_contact.yml +0 -0
  346. /rasa/cli/project_templates/{calm → default}/db/contacts.json +0 -0
  347. /rasa/cli/project_templates/{calm → default}/domain/add_contact.yml +0 -0
  348. /rasa/cli/project_templates/{calm → default}/domain/list_contacts.yml +0 -0
  349. /rasa/cli/project_templates/{calm → default}/domain/remove_contact.yml +0 -0
  350. /rasa/cli/project_templates/{calm → default}/domain/shared.yml +0 -0
  351. /rasa/{cli/project_templates/calm/actions → core/information_retrieval/ingestion}/__init__.py +0 -0
  352. {rasa_pro-3.12.22.dist-info → rasa_pro-3.13.0.dist-info}/NOTICE +0 -0
  353. {rasa_pro-3.12.22.dist-info → rasa_pro-3.13.0.dist-info}/WHEEL +0 -0
  354. {rasa_pro-3.12.22.dist-info → rasa_pro-3.13.0.dist-info}/entry_points.txt +0 -0
@@ -104,30 +104,27 @@ class AudiocodesVoiceInputChannel(VoiceInputChannel):
104
104
 
105
105
  def __init__(
106
106
  self,
107
- token: Optional[Text],
108
107
  server_url: str,
109
108
  asr_config: Dict,
110
109
  tts_config: Dict,
111
- monitor_silence: bool = False,
110
+ token: Optional[Text] = None,
112
111
  ):
113
112
  mark_as_beta_feature("Audiocodes (audiocodes_stream) Channel")
114
- super().__init__(server_url, asr_config, tts_config, monitor_silence)
113
+ super().__init__(
114
+ server_url=server_url,
115
+ asr_config=asr_config,
116
+ tts_config=tts_config,
117
+ )
115
118
  self.token = token
116
119
 
117
120
  @classmethod
118
121
  def from_credentials(
119
- cls, credentials: Optional[Dict[str, Any]]
120
- ) -> VoiceInputChannel:
121
- if not credentials:
122
- raise ValueError("No credentials given for Audiocodes voice channel.")
123
-
124
- return cls(
125
- token=credentials.get("token"),
126
- server_url=credentials["server_url"],
127
- asr_config=credentials["asr"],
128
- tts_config=credentials["tts"],
129
- monitor_silence=credentials.get("monitor_silence", False),
130
- )
122
+ cls,
123
+ 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]
131
128
 
132
129
  def channel_bytes_to_rasa_audio_bytes(self, input_bytes: bytes) -> RasaAudioBytes:
133
130
  return RasaAudioBytes(base64.b64decode(input_bytes))
@@ -292,7 +289,7 @@ class AudiocodesVoiceInputChannel(VoiceInputChannel):
292
289
  def blueprint(
293
290
  self, on_new_message: Callable[[UserMessage], Awaitable[Any]]
294
291
  ) -> Blueprint:
295
- """Defines a Sanic bluelogger.debug."""
292
+ """Defines a Sanic blueprint"""
296
293
  blueprint = Blueprint("audiocodes_stream", __name__)
297
294
 
298
295
  @blueprint.route("/", methods=["GET"])
@@ -97,7 +97,7 @@ class BrowserAudioInputChannel(VoiceInputChannel):
97
97
  def blueprint(
98
98
  self, on_new_message: Callable[[UserMessage], Awaitable[Any]]
99
99
  ) -> Blueprint:
100
- """Defines a Sanic bluelogger.debug."""
100
+ """Defines a Sanic blueprint"""
101
101
  blueprint = Blueprint("browser_audio", __name__)
102
102
 
103
103
  @blueprint.route("/", methods=["GET"])
@@ -29,6 +29,7 @@ from rasa.core.channels.voice_stream.voice_channel import (
29
29
  VoiceInputChannel,
30
30
  VoiceOutputChannel,
31
31
  )
32
+ from rasa.shared.exceptions import InvalidConfigException
32
33
 
33
34
  """
34
35
  Genesys throws a rate limit error with too many audio messages.
@@ -91,30 +92,48 @@ class GenesysInputChannel(VoiceInputChannel):
91
92
  return "genesys"
92
93
 
93
94
  def __init__(
94
- self, api_key: Text, client_secret: Optional[Text], *args: Any, **kwargs: Any
95
+ self,
96
+ server_url: str,
97
+ asr_config: Dict,
98
+ tts_config: Dict,
99
+ api_key: Optional[Text] = None,
100
+ client_secret: Optional[Text] = None,
95
101
  ) -> None:
96
- super().__init__(*args, **kwargs)
102
+ super().__init__(server_url, asr_config, tts_config)
97
103
  self.api_key = api_key
98
104
  self.client_secret = client_secret
99
105
 
100
106
  @classmethod
101
107
  def from_credentials(
102
- cls, credentials: Optional[Dict[str, Any]]
103
- ) -> VoiceInputChannel:
104
- if not credentials:
105
- raise ValueError("No credentials given for Genesys voice channel.")
106
-
107
- if not credentials.get("api_key"):
108
- raise ValueError("No API key given for Genesys voice channel (api_key).")
109
-
110
- return cls(
111
- api_key=credentials["api_key"],
112
- client_secret=credentials.get("client_secret"),
113
- server_url=credentials["server_url"],
114
- asr_config=credentials["asr"],
115
- tts_config=credentials["tts"],
116
- monitor_silence=credentials.get("monitor_silence", False),
117
- )
108
+ cls,
109
+ credentials: Optional[Dict[str, Any]],
110
+ ) -> "GenesysInputChannel":
111
+ """Create a channel from credentials dictionary.
112
+
113
+ Args:
114
+ credentials: Dictionary containing the required credentials:
115
+ - server_url: URL where the server is hosted
116
+ - asr: ASR engine configuration
117
+ - tts: TTS engine configuration
118
+ - api_key: Required API key for Genesys authentication
119
+ - client_secret: Optional client secret for signature verification
120
+
121
+ Returns:
122
+ GenesysInputChannel instance
123
+ """
124
+ channel = super().from_credentials(credentials)
125
+
126
+ # Check required Genesys-specific credentials
127
+ if not credentials.get("api_key"): # type: ignore[union-attr]
128
+ raise InvalidConfigException(
129
+ "No API key given for Genesys voice channel (api_key)."
130
+ )
131
+
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]
118
137
 
119
138
  def _ensure_channel_data_initialized(self) -> None:
120
139
  """Initialize Genesys-specific channel data if not already present.
@@ -0,0 +1,232 @@
1
+ import audioop
2
+ import json
3
+ import uuid
4
+ from typing import Any, Awaitable, Callable, Dict, Optional, Text, Tuple
5
+
6
+ import structlog
7
+ from sanic import ( # type: ignore[attr-defined]
8
+ Blueprint,
9
+ HTTPResponse,
10
+ Request,
11
+ Websocket,
12
+ response,
13
+ )
14
+
15
+ from rasa.core.channels import UserMessage, requires_basic_auth
16
+ from rasa.core.channels.voice_ready.utils import (
17
+ CallParameters,
18
+ validate_username_password_credentials,
19
+ )
20
+ from rasa.core.channels.voice_stream.audio_bytes import RasaAudioBytes
21
+ from rasa.core.channels.voice_stream.call_state import call_state
22
+ from rasa.core.channels.voice_stream.tts.tts_engine import TTSEngine
23
+ from rasa.core.channels.voice_stream.voice_channel import (
24
+ ContinueConversationAction,
25
+ EndConversationAction,
26
+ NewAudioAction,
27
+ VoiceChannelAction,
28
+ VoiceInputChannel,
29
+ VoiceOutputChannel,
30
+ )
31
+
32
+ logger = structlog.get_logger()
33
+
34
+ JAMBONZ_STREAMS_WEBSOCKET_PATH = "webhooks/jambonz_streams/websocket"
35
+
36
+
37
+ def map_call_params(data: Dict[Text, str]) -> CallParameters:
38
+ """Map the twilio stream parameters to the CallParameters dataclass."""
39
+ call_sid = data.get("callSid", "None")
40
+ from_number = data.get("from", "Unknown")
41
+ to_number = data.get("to")
42
+ return CallParameters(
43
+ call_id=call_sid,
44
+ user_phone=from_number,
45
+ bot_phone=to_number,
46
+ stream_id=call_sid,
47
+ )
48
+
49
+
50
+ class JambonzStreamOutputChannel(VoiceOutputChannel):
51
+ @classmethod
52
+ def name(cls) -> str:
53
+ return "jambonz_stream"
54
+
55
+ async def send_audio_bytes(
56
+ self, recipient_id: str, audio_bytes: RasaAudioBytes
57
+ ) -> None:
58
+ """Overridden to send binary websocket messages for Jambonz.
59
+
60
+ Converts 8kHz μ-law to 8kHz L16 PCM for Jambonz streaming.
61
+ """
62
+ pcm = audioop.ulaw2lin(audio_bytes, 2)
63
+ await self.voice_websocket.send(pcm)
64
+
65
+ def create_marker_message(self, recipient_id: str) -> Tuple[str, str]:
66
+ """Create a marker message to track audio stream position."""
67
+ marker_id = uuid.uuid4().hex
68
+ return json.dumps({"type": "mark", "data": {"name": marker_id}}), marker_id
69
+
70
+
71
+ class JambonzStreamInputChannel(VoiceInputChannel):
72
+ @classmethod
73
+ def name(cls) -> str:
74
+ return "jambonz_stream"
75
+
76
+ def __init__(
77
+ self,
78
+ server_url: str,
79
+ asr_config: Dict,
80
+ tts_config: Dict,
81
+ username: Optional[Text] = None,
82
+ password: Optional[Text] = None,
83
+ ) -> None:
84
+ """Initialize the channel.
85
+
86
+ Args:
87
+ username: Optional username for basic auth
88
+ password: Optional password for basic auth
89
+ """
90
+ super().__init__(server_url, asr_config, tts_config)
91
+ self.username = username
92
+ self.password = password
93
+
94
+ @classmethod
95
+ def from_credentials(
96
+ cls, credentials: Optional[Dict[Text, Any]]
97
+ ) -> "JambonzStreamInputChannel":
98
+ """Create a channel from credentials dictionary.
99
+
100
+ Args:
101
+ credentials: Dictionary containing the required credentials:
102
+ - server_url: URL where the server is hosted
103
+ - asr: ASR engine configuration
104
+ - tts: TTS engine configuration
105
+ - username: Optional username for basic auth
106
+ - password: Optional password for basic auth
107
+
108
+ Returns:
109
+ JambonzStreamInputChannel instance
110
+ """
111
+ # Get common credentials from parent
112
+ channel = super().from_credentials(credentials)
113
+
114
+ # Check optional basic auth credentials
115
+ username = credentials.get("username") # type: ignore[union-attr]
116
+ password = credentials.get("password") # type: ignore[union-attr]
117
+ validate_username_password_credentials(username, password, "Jambonz Stream")
118
+
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
+ def _websocket_stream_url(self) -> str:
126
+ """Returns the websocket stream URL."""
127
+ # depending on the config value, the url might contain http as a
128
+ # protocol or not - we'll make sure both work
129
+ if self.server_url.startswith("http"):
130
+ base_url = self.server_url.replace("http", "ws")
131
+ else:
132
+ base_url = f"wss://{self.server_url}"
133
+ return f"{base_url}/{JAMBONZ_STREAMS_WEBSOCKET_PATH}"
134
+
135
+ def channel_bytes_to_rasa_audio_bytes(self, input_bytes: bytes) -> RasaAudioBytes:
136
+ """Convert Jambonz audio bytes (L16 PCM) to Rasa audio bytes (μ-law)."""
137
+ ulaw = audioop.lin2ulaw(input_bytes, 2)
138
+ return RasaAudioBytes(ulaw)
139
+
140
+ async def collect_call_parameters(
141
+ self, channel_websocket: Websocket
142
+ ) -> Optional[CallParameters]:
143
+ # Wait for initial metadata message
144
+ message = await channel_websocket.recv()
145
+ logger.debug("jambonz.collect_call_parameters", message=message)
146
+ metadata = json.loads(message)
147
+ return map_call_params(metadata)
148
+
149
+ def map_input_message(self, message: Any, ws: Websocket) -> VoiceChannelAction:
150
+ # Handle binary audio frames
151
+ if isinstance(message, bytes):
152
+ channel_bytes = message
153
+ audio_bytes = self.channel_bytes_to_rasa_audio_bytes(channel_bytes)
154
+ return NewAudioAction(audio_bytes)
155
+
156
+ # Handle JSON messages
157
+ data = json.loads(message)
158
+ if data["type"] == "mark":
159
+ if data["data"]["name"] == call_state.latest_bot_audio_id:
160
+ # Just finished streaming last audio bytes
161
+ call_state.is_bot_speaking = False # type: ignore[attr-defined]
162
+ if call_state.should_hangup:
163
+ logger.debug(
164
+ "jambonz.hangup", marker=call_state.latest_bot_audio_id
165
+ )
166
+ return EndConversationAction()
167
+ else:
168
+ call_state.is_bot_speaking = True # type: ignore[attr-defined]
169
+ elif data["event"] == "dtmf":
170
+ # TODO: handle DTMF input
171
+ logger.debug("jambonz.dtmf.received", dtmf=data["dtmf"])
172
+ else:
173
+ logger.warning("jambonz.unexpected_message", message=data)
174
+
175
+ return ContinueConversationAction()
176
+
177
+ def create_output_channel(
178
+ self, voice_websocket: Websocket, tts_engine: TTSEngine
179
+ ) -> VoiceOutputChannel:
180
+ return JambonzStreamOutputChannel(
181
+ voice_websocket,
182
+ tts_engine,
183
+ self.tts_cache,
184
+ )
185
+
186
+ def blueprint(
187
+ self, on_new_message: Callable[[UserMessage], Awaitable[Any]]
188
+ ) -> Blueprint:
189
+ blueprint = Blueprint("jambonz_stream", __name__)
190
+
191
+ @blueprint.route("/", methods=["GET"])
192
+ async def health(_: Request) -> HTTPResponse:
193
+ return response.json({"status": "ok"})
194
+
195
+ @blueprint.route("/call_status", methods=["POST"])
196
+ @requires_basic_auth(self.username, self.password)
197
+ async def call_status(request: Request) -> HTTPResponse:
198
+ """Handle call status updates from Jambonz."""
199
+ data = request.json
200
+ logger.debug("jambonz.call_status.received", data=data)
201
+ return response.json({"status": "ok"})
202
+
203
+ @blueprint.route("/webhook", methods=["POST"])
204
+ @requires_basic_auth(self.username, self.password)
205
+ async def webhook(request: Request) -> HTTPResponse:
206
+ """Handle incoming webhook requests from Jambonz."""
207
+ data = request.json
208
+ logger.debug("jambonz.webhook.received", data=data)
209
+ return response.json(
210
+ [
211
+ {
212
+ "verb": "listen",
213
+ "url": self._websocket_stream_url(),
214
+ "sampleRate": 8000,
215
+ "passDtmf": True,
216
+ "bidirectionalAudio": {
217
+ "enabled": True,
218
+ "streaming": True,
219
+ "sampleRate": 8000,
220
+ },
221
+ }
222
+ ]
223
+ )
224
+
225
+ @blueprint.websocket("/websocket", subprotocols=["audio.jambonz.org"]) # type: ignore[misc]
226
+ async def handle_message(request: Request, ws: Websocket) -> None:
227
+ try:
228
+ await self.run_audio_streaming(on_new_message, ws)
229
+ except Exception as e:
230
+ logger.error("jambonz.handle_message.error", error=e)
231
+
232
+ return blueprint
@@ -0,0 +1,8 @@
1
+ from rasa.core.channels.voice_stream.tts.tts_cache import TTSCache
2
+ from rasa.core.channels.voice_stream.tts.tts_engine import (
3
+ TTSEngine,
4
+ TTSEngineConfig,
5
+ TTSError,
6
+ )
7
+
8
+ __all__ = ["TTSEngine", "TTSEngineConfig", "TTSError", "TTSCache"]
@@ -14,12 +14,15 @@ from sanic import ( # type: ignore[attr-defined]
14
14
  response,
15
15
  )
16
16
 
17
- from rasa.core.channels import InputChannel, UserMessage
17
+ from rasa.core.channels import UserMessage
18
18
  from rasa.core.channels.channel import (
19
19
  create_auth_requested_response_provider,
20
20
  requires_basic_auth,
21
21
  )
22
- from rasa.core.channels.voice_ready.utils import CallParameters
22
+ from rasa.core.channels.voice_ready.utils import (
23
+ CallParameters,
24
+ validate_username_password_credentials,
25
+ )
23
26
  from rasa.core.channels.voice_stream.audio_bytes import RasaAudioBytes
24
27
  from rasa.core.channels.voice_stream.call_state import call_state
25
28
  from rasa.core.channels.voice_stream.tts.tts_engine import TTSEngine
@@ -31,7 +34,6 @@ from rasa.core.channels.voice_stream.voice_channel import (
31
34
  VoiceInputChannel,
32
35
  VoiceOutputChannel,
33
36
  )
34
- from rasa.shared.exceptions import RasaException
35
37
 
36
38
  if TYPE_CHECKING:
37
39
  from twilio.twiml.voice_response import VoiceResponse
@@ -102,31 +104,32 @@ class TwilioMediaStreamsInputChannel(VoiceInputChannel):
102
104
  server_url: str,
103
105
  asr_config: Dict,
104
106
  tts_config: Dict,
105
- monitor_silence: bool = False,
106
107
  username: Optional[Text] = None,
107
108
  password: Optional[Text] = None,
108
109
  ):
109
- super().__init__(server_url, asr_config, tts_config, monitor_silence)
110
+ super().__init__(
111
+ server_url=server_url,
112
+ asr_config=asr_config,
113
+ tts_config=tts_config,
114
+ )
110
115
  self.username = username
111
116
  self.password = password
112
117
 
113
118
  @classmethod
114
- def from_credentials(cls, credentials: Optional[Dict[str, Any]]) -> InputChannel:
119
+ def from_credentials(
120
+ cls,
121
+ credentials: Optional[Dict[str, Any]],
122
+ ) -> VoiceInputChannel:
115
123
  credentials = credentials or {}
116
124
 
117
125
  username = credentials.get("username")
118
126
  password = credentials.get("password")
119
- if (username is None) != (password is None):
120
- raise RasaException(
121
- "In TwilioMediaStreams channel, either both username and password "
122
- "or neither should be provided. "
123
- )
127
+ validate_username_password_credentials(username, password, "TwilioMediaStreams")
124
128
 
125
129
  return cls(
126
130
  credentials["server_url"],
127
131
  credentials["asr"],
128
132
  credentials["tts"],
129
- credentials.get("monitor_silence", False),
130
133
  username=username,
131
134
  password=password,
132
135
  )
@@ -31,9 +31,11 @@ from rasa.core.channels.voice_stream.tts.azure import AzureTTS
31
31
  from rasa.core.channels.voice_stream.tts.cartesia import CartesiaTTS
32
32
  from rasa.core.channels.voice_stream.tts.tts_cache import TTSCache
33
33
  from rasa.core.channels.voice_stream.tts.tts_engine import TTSEngine, TTSError
34
- from rasa.core.channels.voice_stream.util import generate_silence
35
- from rasa.shared.core.constants import SLOT_SILENCE_TIMEOUT
36
- from rasa.shared.utils.cli import print_error_and_exit
34
+ from rasa.core.channels.voice_stream.util import (
35
+ generate_silence,
36
+ )
37
+ from rasa.shared.core.constants import SILENCE_TIMEOUT_SLOT
38
+ from rasa.shared.exceptions import InvalidConfigException
37
39
  from rasa.shared.utils.common import (
38
40
  class_from_module_path,
39
41
  mark_as_beta_feature,
@@ -42,6 +44,11 @@ from rasa.utils.io import remove_emojis
42
44
 
43
45
  logger = structlog.get_logger(__name__)
44
46
 
47
+ # define constants for the voice channel
48
+ USER_CONVERSATION_SESSION_END = "/session_end"
49
+ USER_CONVERSATION_SESSION_START = "/session_start"
50
+ USER_CONVERSATION_SILENCE_TIMEOUT = "/silence_timeout"
51
+
45
52
 
46
53
  @dataclass
47
54
  class VoiceChannelAction:
@@ -64,6 +71,14 @@ class ContinueConversationAction(VoiceChannelAction):
64
71
 
65
72
 
66
73
  def asr_engine_from_config(asr_config: Dict) -> ASREngine:
74
+ if not asr_config:
75
+ raise ValueError("ASR configuration dictionary cannot be empty")
76
+
77
+ if "name" not in asr_config:
78
+ raise ValueError(
79
+ "ASR configuration must contain 'name' key specifying the engine type"
80
+ )
81
+
67
82
  name = str(asr_config["name"])
68
83
  asr_config = copy.copy(asr_config)
69
84
  asr_config.pop("name")
@@ -77,12 +92,12 @@ def asr_engine_from_config(asr_config: Dict) -> ASREngine:
77
92
  asr_engine_class = class_from_module_path(name)
78
93
  return asr_engine_class.from_config_dict(asr_config)
79
94
  except NameError:
80
- print_error_and_exit(
95
+ raise InvalidConfigException(
81
96
  f"Failed to initialize ASR Engine with type '{name}'. "
82
97
  f"Please make sure the method `from_config_dict`is implemented."
83
98
  )
84
99
  except TypeError as e:
85
- print_error_and_exit(
100
+ raise InvalidConfigException(
86
101
  f"Failed to initialize ASR Engine with type '{name}'. "
87
102
  f"Invalid configuration provided. "
88
103
  f"Error: {e}"
@@ -90,6 +105,14 @@ def asr_engine_from_config(asr_config: Dict) -> ASREngine:
90
105
 
91
106
 
92
107
  def tts_engine_from_config(tts_config: Dict) -> TTSEngine:
108
+ if not tts_config:
109
+ raise ValueError("TTS configuration dictionary cannot be empty")
110
+
111
+ if "name" not in tts_config:
112
+ raise ValueError(
113
+ "TTS configuration must contain 'name' key specifying the engine type"
114
+ )
115
+
93
116
  name = str(tts_config["name"])
94
117
  tts_config = copy.copy(tts_config)
95
118
  tts_config.pop("name")
@@ -103,13 +126,13 @@ def tts_engine_from_config(tts_config: Dict) -> TTSEngine:
103
126
  tts_engine_class = class_from_module_path(name)
104
127
  return tts_engine_class.from_config_dict(tts_config)
105
128
  except NameError:
106
- print_error_and_exit(
129
+ raise InvalidConfigException(
107
130
  f"Failed to initialize TTS Engine with type '{name}'. "
108
131
  f"Please make sure the method `from_config_dict`is implemented."
109
132
  )
110
133
  except TypeError as e:
111
- print_error_and_exit(
112
- f"Failed to initialize ASR Engine with type '{name}'. "
134
+ raise InvalidConfigException(
135
+ f"Failed to initialize TTS Engine with type '{name}'. "
113
136
  f"Invalid configuration provided. "
114
137
  f"Error: {e}"
115
138
  )
@@ -166,8 +189,12 @@ class VoiceOutputChannel(OutputChannel):
166
189
  def update_silence_timeout(self) -> None:
167
190
  """Updates the silence timeout for the session."""
168
191
  if self.tracker_state:
169
- call_state.silence_timeout = ( # type: ignore[attr-defined]
170
- self.tracker_state["slots"][SLOT_SILENCE_TIMEOUT]
192
+ call_state.silence_timeout = self.tracker_state["slots"][ # type: ignore[attr-defined]
193
+ SILENCE_TIMEOUT_SLOT
194
+ ]
195
+ logger.debug(
196
+ "voice_channel.silence_timeout_updated",
197
+ silence_timeout=call_state.silence_timeout,
171
198
  )
172
199
 
173
200
  async def send_text_with_buttons(
@@ -189,6 +216,7 @@ class VoiceOutputChannel(OutputChannel):
189
216
  collected_audio_bytes = RasaAudioBytes(b"")
190
217
  seconds_marker = -1
191
218
  last_sent_offset = 0
219
+ logger.debug("voice_channel.sending_audio", text=text)
192
220
 
193
221
  # Send start marker before first chunk
194
222
  try:
@@ -274,18 +302,21 @@ class VoiceOutputChannel(OutputChannel):
274
302
 
275
303
 
276
304
  class VoiceInputChannel(InputChannel):
305
+ # All children of this class require a voice license to be used.
306
+ requires_voice_license = True
307
+
277
308
  def __init__(
278
309
  self,
279
310
  server_url: str,
280
311
  asr_config: Dict,
281
312
  tts_config: Dict,
282
- monitor_silence: bool = False,
283
313
  ):
284
- validate_voice_license_scope()
314
+ if self.requires_voice_license:
315
+ validate_voice_license_scope()
316
+
285
317
  self.server_url = server_url
286
318
  self.asr_config = asr_config
287
319
  self.tts_config = tts_config
288
- self.monitor_silence = monitor_silence
289
320
  self.tts_cache = TTSCache(tts_config.get("cache_size", 1000))
290
321
 
291
322
  logger.info(
@@ -303,8 +334,6 @@ class VoiceInputChannel(InputChannel):
303
334
  timeout = call_state.silence_timeout
304
335
  if not timeout:
305
336
  return
306
- if not self.monitor_silence:
307
- return
308
337
  logger.debug("voice_channel.silence_timeout_watch_started", timeout=timeout)
309
338
  await asyncio.sleep(timeout)
310
339
  await asr_event_queue.put(UserSilence())
@@ -319,13 +348,28 @@ class VoiceInputChannel(InputChannel):
319
348
  call_state.silence_timeout_watcher = None # type: ignore[attr-defined]
320
349
 
321
350
  @classmethod
322
- def from_credentials(cls, credentials: Optional[Dict[str, Any]]) -> InputChannel:
323
- credentials = credentials or {}
351
+ def from_credentials(
352
+ cls,
353
+ credentials: Optional[Dict[str, Any]],
354
+ ) -> InputChannel:
355
+ if not credentials:
356
+ cls.raise_missing_credentials_exception()
357
+
358
+ if not credentials.get("server_url"):
359
+ raise InvalidConfigException("No server_url provided in credentials.")
360
+ if not credentials.get("asr"):
361
+ raise InvalidConfigException(
362
+ "No ASR configuration provided in credentials."
363
+ )
364
+ if not credentials.get("tts"):
365
+ raise InvalidConfigException(
366
+ "No TTS configuration provided in credentials."
367
+ )
368
+
324
369
  return cls(
325
- credentials["server_url"],
326
- credentials["asr"],
327
- credentials["tts"],
328
- credentials.get("monitor_silence", False),
370
+ server_url=credentials["server_url"],
371
+ asr_config=credentials["asr"],
372
+ tts_config=credentials["tts"],
329
373
  )
330
374
 
331
375
  def channel_bytes_to_rasa_audio_bytes(self, input_bytes: bytes) -> RasaAudioBytes:
@@ -345,7 +389,7 @@ class VoiceInputChannel(InputChannel):
345
389
  ) -> None:
346
390
  output_channel = self.create_output_channel(channel_websocket, tts_engine)
347
391
  message = UserMessage(
348
- text="/session_start",
392
+ text=USER_CONVERSATION_SESSION_START,
349
393
  output_channel=output_channel,
350
394
  sender_id=self.get_sender_id(call_parameters),
351
395
  input_channel=self.name(),
@@ -382,17 +426,17 @@ class VoiceInputChannel(InputChannel):
382
426
 
383
427
  async def consume_audio_bytes() -> None:
384
428
  async for message in channel_websocket:
385
- is_bot_speaking_before = call_state.is_bot_speaking
429
+ was_bot_speaking_before = call_state.is_bot_speaking
386
430
  channel_action = self.map_input_message(message, channel_websocket)
387
431
  is_bot_speaking_after = call_state.is_bot_speaking
388
432
 
389
- if not is_bot_speaking_before and is_bot_speaking_after:
433
+ if not was_bot_speaking_before and is_bot_speaking_after:
390
434
  logger.debug("voice_channel.bot_started_speaking")
391
435
  # relevant when the bot speaks multiple messages in one turn
392
436
  self._cancel_silence_timeout_watcher()
393
437
 
394
438
  # we just stopped speaking, starting a watcher for silence timeout
395
- if is_bot_speaking_before and not is_bot_speaking_after:
439
+ if was_bot_speaking_before and not is_bot_speaking_after:
396
440
  logger.debug("voice_channel.bot_stopped_speaking")
397
441
  self._cancel_silence_timeout_watcher()
398
442
  call_state.silence_timeout_watcher = ( # type: ignore[attr-defined]
@@ -476,7 +520,7 @@ class VoiceInputChannel(InputChannel):
476
520
  elif isinstance(e, UserSilence):
477
521
  output_channel = self.create_output_channel(voice_websocket, tts_engine)
478
522
  message = UserMessage(
479
- text="/silence_timeout",
523
+ text=USER_CONVERSATION_SILENCE_TIMEOUT,
480
524
  output_channel=output_channel,
481
525
  sender_id=self.get_sender_id(call_parameters),
482
526
  input_channel=self.name(),
@@ -494,7 +538,7 @@ class VoiceInputChannel(InputChannel):
494
538
  """Handle disconnection from the channel."""
495
539
  output_channel = self.create_output_channel(channel_websocket, tts_engine)
496
540
  message = UserMessage(
497
- text="/session_end",
541
+ text=USER_CONVERSATION_SESSION_END,
498
542
  output_channel=output_channel,
499
543
  sender_id=self.get_sender_id(call_parameters),
500
544
  input_channel=self.name(),