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
@@ -1,5 +1,8 @@
1
1
  import asyncio
2
+ import audioop
3
+ import base64
2
4
  import json
5
+ import uuid
3
6
  from functools import partial
4
7
  from typing import (
5
8
  TYPE_CHECKING,
@@ -10,13 +13,25 @@ from typing import (
10
13
  List,
11
14
  Optional,
12
15
  Text,
16
+ Tuple,
13
17
  )
14
18
 
15
19
  import structlog
16
- from sanic import Sanic
17
20
 
21
+ from rasa.core.channels import UserMessage
18
22
  from rasa.core.channels.socketio import SocketBlueprint, SocketIOInput
19
- from rasa.core.exceptions import AgentNotReady
23
+ from rasa.core.channels.voice_ready.utils import CallParameters
24
+ from rasa.core.channels.voice_stream.audio_bytes import RasaAudioBytes
25
+ from rasa.core.channels.voice_stream.call_state import call_state
26
+ from rasa.core.channels.voice_stream.tts import TTSEngine
27
+ from rasa.core.channels.voice_stream.voice_channel import (
28
+ ContinueConversationAction,
29
+ EndConversationAction,
30
+ NewAudioAction,
31
+ VoiceChannelAction,
32
+ VoiceInputChannel,
33
+ VoiceOutputChannel,
34
+ )
20
35
  from rasa.hooks import hookimpl
21
36
  from rasa.plugin import plugin_manager
22
37
  from rasa.shared.core.constants import ACTION_LISTEN_NAME
@@ -24,7 +39,10 @@ from rasa.shared.core.events import ActionExecuted
24
39
  from rasa.shared.core.trackers import EventVerbosity
25
40
 
26
41
  if TYPE_CHECKING:
27
- from rasa.core.channels.channel import UserMessage
42
+ from sanic import Sanic, Websocket # type: ignore[attr-defined]
43
+ from socketio import AsyncServer
44
+
45
+ from rasa.core.channels.channel import InputChannel, UserMessage
28
46
  from rasa.shared.core.trackers import DialogueStateTracker
29
47
 
30
48
 
@@ -84,7 +102,9 @@ class StudioTrackerUpdatePlugin:
84
102
 
85
103
  def handle_tracker_update(self, tracker: "DialogueStateTracker") -> None:
86
104
  """Handles a tracker update when triggered by a hook."""
87
- structlogger.info("studio_chat.after_tracker_update", tracker=tracker)
105
+ structlogger.info(
106
+ "studio_chat.after_tracker_update", sender_id=tracker.sender_id
107
+ )
88
108
  # directly create a dump to avoid the tracker getting modified by another
89
109
  # function before it gets published (since the publishing is scheduled
90
110
  # as an async task)
@@ -101,32 +121,83 @@ class StudioTrackerUpdatePlugin:
101
121
  self._cancel_tasks()
102
122
 
103
123
 
104
- class StudioChatInput(SocketIOInput):
124
+ class StudioChatInput(SocketIOInput, VoiceInputChannel):
105
125
  """Input channel for the communication between Rasa Studio and Rasa Pro."""
106
126
 
127
+ requires_voice_license = False
128
+
107
129
  @classmethod
108
130
  def name(cls) -> Text:
109
131
  return "studio_chat"
110
132
 
111
133
  def __init__(
112
134
  self,
113
- *args: Any,
114
- **kwargs: Any,
135
+ server_url: str,
136
+ asr_config: Dict,
137
+ tts_config: Dict,
138
+ user_message_evt: Text = "user_uttered",
139
+ bot_message_evt: Text = "bot_uttered",
140
+ namespace: Optional[Text] = None,
141
+ session_persistence: bool = False,
142
+ socketio_path: Optional[Text] = "/socket.io",
143
+ jwt_key: Optional[Text] = None,
144
+ jwt_method: Optional[Text] = "HS256",
145
+ metadata_key: Optional[Text] = "metadata",
115
146
  ) -> None:
116
- """Creates a ``SocketIOInput`` object."""
147
+ """Creates a `StudioChatInput` object."""
117
148
  from rasa.core.agent import Agent
118
149
 
119
- super().__init__(*args, **kwargs)
120
150
  self.agent: Optional[Agent] = None
121
151
 
152
+ # Initialize the SocketIO input channel
153
+ SocketIOInput.__init__(
154
+ self,
155
+ user_message_evt=user_message_evt,
156
+ bot_message_evt=bot_message_evt,
157
+ namespace=namespace,
158
+ session_persistence=session_persistence,
159
+ socketio_path=socketio_path,
160
+ jwt_key=jwt_key,
161
+ jwt_method=jwt_method,
162
+ metadata_key=metadata_key,
163
+ )
164
+
165
+ # Initialize the Voice Input Channel
166
+ VoiceInputChannel.__init__(
167
+ self,
168
+ server_url=server_url,
169
+ asr_config=asr_config,
170
+ tts_config=tts_config,
171
+ )
172
+
173
+ # Dictionaries to manage active connections and background tasks
174
+ # `active_connections` holds the active voice sessions
175
+ # `background_tasks` holds the asyncio tasks for voice streaming
176
+ self.active_connections: Dict[str, SocketIOVoiceWebsocketAdapter] = {}
177
+ self.background_tasks: Dict[str, asyncio.Task] = {}
178
+
122
179
  self._register_tracker_update_hook()
123
180
 
124
- async def emit(self, event: str, data: Dict, room: str) -> None:
125
- """Emits an event to the websocket."""
126
- if not self.sio:
127
- structlogger.error("studio_chat.emit.sio_not_initialized")
128
- return
129
- await self.sio.emit(event, data, room=room)
181
+ @classmethod
182
+ def from_credentials(cls, credentials: Optional[Dict[Text, Any]]) -> "InputChannel":
183
+ """Creates a StudioChatInput channel from credentials."""
184
+ credentials = credentials or {}
185
+
186
+ return cls(
187
+ # Voice specific parameters
188
+ server_url=credentials.get("server_url", ""),
189
+ asr_config=credentials.get("asr", {}),
190
+ tts_config=credentials.get("tts", {}),
191
+ # SocketIO parameters
192
+ user_message_evt=credentials.get("user_message_evt", "user_uttered"),
193
+ bot_message_evt=credentials.get("bot_message_evt", "bot_uttered"),
194
+ namespace=credentials.get("namespace"),
195
+ session_persistence=credentials.get("session_persistence", False),
196
+ socketio_path=credentials.get("socketio_path", "/socket.io"),
197
+ jwt_key=credentials.get("jwt_key"),
198
+ jwt_method=credentials.get("jwt_method", "HS256"),
199
+ metadata_key=credentials.get("metadata_key", "metadata"),
200
+ )
130
201
 
131
202
  def _register_tracker_update_hook(self) -> None:
132
203
  plugin_manager().register(StudioTrackerUpdatePlugin(self))
@@ -137,7 +208,10 @@ class StudioChatInput(SocketIOInput):
137
208
 
138
209
  async def publish_tracker_update(self, sender_id: str, tracker_dump: Dict) -> None:
139
210
  """Publishes a tracker update notification to the websocket."""
140
- await self.emit("tracker", tracker_dump, room=sender_id)
211
+ if not self.sio:
212
+ structlogger.error("studio_chat.on_tracker_updated.sio_not_initialized")
213
+ return
214
+ await self.sio.emit("tracker", tracker_dump, room=sender_id)
141
215
 
142
216
  async def on_message_proxy(
143
217
  self,
@@ -150,15 +224,8 @@ class StudioChatInput(SocketIOInput):
150
224
  """
151
225
  await on_new_message(message)
152
226
 
153
- if not self.agent or not self.agent.is_ready():
227
+ if not self.agent:
154
228
  structlogger.error("studio_chat.on_message_proxy.agent_not_initialized")
155
- await self.emit_error(
156
- "The Rasa Pro model could not be loaded. "
157
- "Please check the training and deployment logs "
158
- "for more information.",
159
- message.sender_id,
160
- AgentNotReady("The Rasa Pro model could not be loaded."),
161
- )
162
229
  return
163
230
 
164
231
  tracker = await self.agent.tracker_store.retrieve(message.sender_id)
@@ -168,17 +235,6 @@ class StudioChatInput(SocketIOInput):
168
235
 
169
236
  await self.on_tracker_updated(tracker)
170
237
 
171
- async def emit_error(self, message: str, room: str, e: Exception) -> None:
172
- await self.emit(
173
- "error",
174
- {
175
- "message": message,
176
- "error": str(e),
177
- "exception": str(type(e).__name__),
178
- },
179
- room=room,
180
- )
181
-
182
238
  async def handle_tracker_update(self, sid: str, data: Dict) -> None:
183
239
  from rasa.shared.core.trackers import DialogueStateTracker
184
240
 
@@ -195,44 +251,134 @@ class StudioChatInput(SocketIOInput):
195
251
  structlogger.error("studio_chat.sio.domain_not_initialized")
196
252
  return None
197
253
 
198
- tracker: Optional[DialogueStateTracker] = None
199
-
200
254
  async with self.agent.lock_store.lock(data["sender_id"]):
201
- try:
202
- tracker = DialogueStateTracker.from_dict(
203
- data["sender_id"], data["events"], domain.slots
204
- )
255
+ tracker = DialogueStateTracker.from_dict(
256
+ data["sender_id"], data["events"], domain.slots
257
+ )
205
258
 
206
- # will override an existing tracker with the same id!
259
+ # will override an existing tracker with the same id!
260
+ await self.agent.tracker_store.save(tracker)
261
+
262
+ processor = self.agent.processor
263
+ if processor and does_need_action_prediction(tracker):
264
+ output_channel = self.get_output_channel()
265
+
266
+ await processor._run_prediction_loop(output_channel, tracker)
207
267
  await self.agent.tracker_store.save(tracker)
208
268
 
209
- processor = self.agent.processor
210
- if processor and does_need_action_prediction(tracker):
211
- output_channel = self.get_output_channel()
212
-
213
- await processor._run_prediction_loop(output_channel, tracker)
214
- await processor.run_anonymization_pipeline(tracker)
215
- await self.agent.tracker_store.save(tracker)
216
- except Exception as e:
217
- structlogger.error(
218
- "studio_chat.sio.handle_tracker_update.error",
219
- error=e,
220
- sender_id=data["sender_id"],
221
- )
222
- await self.emit_error(
223
- "An error occurred while updating the conversation.",
224
- data["sender_id"],
225
- e,
226
- )
227
-
228
- if not tracker:
229
- # in case the tracker couldn't be updated, we retrieve the prior
230
- # version and use that to populate the update
231
- tracker = await self.agent.tracker_store.get_or_create_tracker(
232
- data["sender_id"]
233
- )
234
269
  await self.on_tracker_updated(tracker)
235
270
 
271
+ def channel_bytes_to_rasa_audio_bytes(self, input_bytes: bytes) -> RasaAudioBytes:
272
+ """Voice method to convert channel bytes to RasaAudioBytes."""
273
+ return RasaAudioBytes(audioop.lin2ulaw(input_bytes, 4))
274
+
275
+ async def collect_call_parameters(
276
+ self, channel_websocket: "Websocket"
277
+ ) -> Optional[CallParameters]:
278
+ """Voice method to collect call parameters"""
279
+ session_id = channel_websocket.session_id
280
+ return CallParameters(session_id, "local", "local", stream_id=session_id)
281
+
282
+ def map_input_message(
283
+ self,
284
+ message: Any,
285
+ ws: "Websocket",
286
+ ) -> VoiceChannelAction:
287
+ """Voice method to map websocket messages to actions."""
288
+ if "audio" in message:
289
+ channel_bytes = base64.b64decode(message["audio"])
290
+ audio_bytes = self.channel_bytes_to_rasa_audio_bytes(channel_bytes)
291
+ return NewAudioAction(audio_bytes)
292
+ elif "marker" in message:
293
+ if message["marker"] == call_state.latest_bot_audio_id:
294
+ # Just finished streaming last audio bytes
295
+ call_state.is_bot_speaking = False # type: ignore[attr-defined]
296
+ if call_state.should_hangup:
297
+ structlogger.debug(
298
+ "studio_chat.hangup", marker=call_state.latest_bot_audio_id
299
+ )
300
+ return EndConversationAction()
301
+ else:
302
+ call_state.is_bot_speaking = True # type: ignore[attr-defined]
303
+ return ContinueConversationAction()
304
+
305
+ def create_output_channel(
306
+ self, voice_websocket: "Websocket", tts_engine: TTSEngine
307
+ ) -> VoiceOutputChannel:
308
+ """Create a voice output channel"""
309
+ return StudioVoiceOutputChannel(
310
+ voice_websocket,
311
+ tts_engine,
312
+ self.tts_cache,
313
+ )
314
+
315
+ def _start_voice_session(
316
+ self,
317
+ session_id: str,
318
+ sid: str,
319
+ on_new_message: Callable[[UserMessage], Awaitable[Any]],
320
+ ) -> None:
321
+ """Create SocketIO WebSocket Adaptor & start async task for voice streaming."""
322
+ if sid in self.active_connections:
323
+ structlogger.warning(
324
+ "studio_chat._start_voice_session.session_already_active",
325
+ session_id=sid,
326
+ )
327
+ return
328
+
329
+ structlogger.info(
330
+ "studio_chat._start_voice_session.starting_session", session_id=sid
331
+ )
332
+
333
+ # Create a websocket adapter for this connection
334
+ ws_adapter = SocketIOVoiceWebsocketAdapter(
335
+ sio=self.sio,
336
+ session_id=session_id,
337
+ sid=sid,
338
+ bot_message_evt=self.bot_message_evt,
339
+ )
340
+ self.active_connections[sid] = ws_adapter
341
+
342
+ # Start voice streaming in an async task
343
+ task = asyncio.create_task(
344
+ self._handle_voice_streaming(on_new_message, ws_adapter, sid)
345
+ )
346
+ self.background_tasks[sid] = task
347
+ task.add_done_callback(lambda _: self._cleanup_tasks_for_sid(sid))
348
+
349
+ async def _handle_voice_streaming(
350
+ self,
351
+ on_new_message: Callable[[UserMessage], Awaitable[Any]],
352
+ ws_adapter: "Websocket",
353
+ sid: str,
354
+ ) -> None:
355
+ """Handle voice streaming for a Socket.IO connection."""
356
+ try:
357
+ await self.run_audio_streaming(on_new_message, ws_adapter)
358
+ except Exception as e:
359
+ structlogger.exception(
360
+ "studio_voice.voice_streaming.error",
361
+ error=str(e),
362
+ sid=sid,
363
+ )
364
+ if sid in self.active_connections:
365
+ del self.active_connections[sid]
366
+
367
+ def _cleanup_tasks_for_sid(self, sid: str) -> None:
368
+ if sid in self.background_tasks:
369
+ task = self.background_tasks.pop(sid)
370
+ task.cancel()
371
+ if sid in self.active_connections:
372
+ del self.active_connections[sid]
373
+
374
+ @hookimpl # type: ignore[misc]
375
+ def after_server_stop(self) -> None:
376
+ """Cleanup background tasks and active connections when the server stops."""
377
+ structlogger.info("studio_chat.after_server_stop.cleanup")
378
+ self.active_connections.clear()
379
+ for task in self.background_tasks.values():
380
+ task.cancel()
381
+
236
382
  def blueprint(
237
383
  self, on_new_message: Callable[["UserMessage"], Awaitable[Any]]
238
384
  ) -> SocketBlueprint:
@@ -245,11 +391,126 @@ class StudioChatInput(SocketIOInput):
245
391
  return socket_blueprint
246
392
 
247
393
  @socket_blueprint.listener("after_server_start") # type: ignore[misc]
248
- async def after_server_start(app: Sanic, _: asyncio.AbstractEventLoop) -> None:
394
+ async def after_server_start(
395
+ app: "Sanic", _: asyncio.AbstractEventLoop
396
+ ) -> None:
249
397
  self.agent = app.ctx.agent
250
398
 
399
+ @self.sio.on("disconnect", namespace=self.namespace)
400
+ async def disconnect(sid: Text) -> None:
401
+ structlogger.debug("studio_chat.sio.disconnect", sid=sid)
402
+ self._cleanup_tasks_for_sid(sid)
403
+
404
+ @self.sio.on("session_request", namespace=self.namespace)
405
+ async def session_request(sid: Text, data: Optional[Dict]) -> None:
406
+ """Overrides the base SocketIOInput session_request handler.
407
+
408
+ Args:
409
+ sid: ID of the session (from SocketIO).
410
+ data:
411
+ - session_id: Studio Chat channel is used with a Bridge Architecture
412
+ (Model Service's Socket Bridge), so we use session_id to remain
413
+ consistent across the bridge. Session ID becomes the sender_id
414
+ for the UserMessage.
415
+ - is_voice: Boolean indicating if its a voice session.
416
+ """
417
+ # Call parent session_request handler first
418
+ await self.handle_session_request(sid, data)
419
+
420
+ # start a voice session if requested
421
+ if data and data.get("is_voice", False):
422
+ self._start_voice_session(data["session_id"], sid, on_new_message)
423
+
424
+ @self.sio.on(self.user_message_evt, namespace=self.namespace)
425
+ async def handle_message(sid: Text, data: Dict) -> None:
426
+ """Overrides the base SocketIOInput handle_message handler."""
427
+ # Handle voice messages
428
+ if "audio" in data or "marker" in data:
429
+ if sid in self.active_connections:
430
+ # Route audio messages to the voice adapter queue
431
+ ws = self.active_connections[sid]
432
+ ws.put_message(data)
433
+ return
434
+
435
+ # Handle text messages
436
+ await self.handle_user_message(sid, data, on_new_message)
437
+
251
438
  @self.sio.on("update_tracker", namespace=self.namespace)
252
439
  async def on_update_tracker(sid: Text, data: Dict) -> None:
253
440
  await self.handle_tracker_update(sid, data)
254
441
 
255
442
  return socket_blueprint
443
+
444
+
445
+ class StudioVoiceOutputChannel(VoiceOutputChannel):
446
+ @classmethod
447
+ def name(cls) -> str:
448
+ return "studio_chat"
449
+
450
+ def rasa_audio_bytes_to_channel_bytes(
451
+ self, rasa_audio_bytes: RasaAudioBytes
452
+ ) -> bytes:
453
+ return audioop.ulaw2lin(rasa_audio_bytes, 4)
454
+
455
+ def channel_bytes_to_message(self, recipient_id: str, channel_bytes: bytes) -> str:
456
+ return json.dumps({"audio": base64.b64encode(channel_bytes).decode("utf-8")})
457
+
458
+ def create_marker_message(self, recipient_id: str) -> Tuple[str, str]:
459
+ message_id = uuid.uuid4().hex
460
+ return json.dumps({"marker": message_id}), message_id
461
+
462
+
463
+ class SocketIOVoiceWebsocketAdapter:
464
+ """Adapter to make Socket.IO work like a Sanic WebSocket for voice channels."""
465
+
466
+ def __init__(
467
+ self, sio: "AsyncServer", session_id: str, sid: str, bot_message_evt: str
468
+ ) -> None:
469
+ self.sio = sio
470
+ self.bot_message_evt = bot_message_evt
471
+ self._closed = False
472
+ self._receive_queue: asyncio.Queue[Any] = asyncio.Queue()
473
+
474
+ # the messages need to be emitted on room=sid
475
+ self.sid = sid
476
+
477
+ # used by collect_call_parameters
478
+ # ultimately, this becomes the sender_id
479
+ self.session_id = session_id
480
+
481
+ @property
482
+ def closed(self) -> bool:
483
+ return self._closed
484
+
485
+ async def send(self, data: Any) -> None:
486
+ """Send data to the client."""
487
+ if not self.closed:
488
+ await self.sio.emit(self.bot_message_evt, data, room=self.sid)
489
+
490
+ async def recv(self) -> Any:
491
+ """Receive data from the client."""
492
+ if self.closed:
493
+ raise ConnectionError("WebSocket is closed")
494
+ return await self._receive_queue.get()
495
+
496
+ def put_message(self, message: Any) -> None:
497
+ """Put message in the internal receive queue."""
498
+ self._receive_queue.put_nowait(message)
499
+
500
+ async def close(self, code: int = 1000, reason: str = "") -> None:
501
+ """Close the connection."""
502
+ self._closed = True
503
+ # at this point, the client should have disconnected
504
+
505
+ def __aiter__(self) -> "SocketIOVoiceWebsocketAdapter":
506
+ """Allow the adapter to be used in an async for loop."""
507
+ return self
508
+
509
+ async def __anext__(self) -> Any:
510
+ if self.closed:
511
+ raise StopAsyncIteration
512
+ try:
513
+ message = await self.recv()
514
+ return message
515
+ except Exception:
516
+ raise StopAsyncIteration
@@ -129,9 +129,8 @@ class CVGOutput(OutputChannel):
129
129
  )
130
130
 
131
131
  logger.info(
132
- "Creating incoming UserMessage: {text=%s, output_channel=%s, sender_id=%s, metadata=%s}" # noqa: E501
132
+ "Creating incoming UserMessage: {output_channel=%s, sender_id=%s, metadata=%s}" # noqa: E501
133
133
  % (
134
- user_message.text,
135
134
  user_message.output_channel,
136
135
  user_message.sender_id,
137
136
  user_message.metadata,
@@ -149,7 +149,7 @@ class Conversation:
149
149
  structlogger.warning(
150
150
  "audiocodes.handle.activities.empty_input_channel_name",
151
151
  event_info=(
152
- "Audiocodes input channel name is empty "
152
+ f"Audiocodes input channel name is empty "
153
153
  f"for conversation {self.conversation_id}"
154
154
  ),
155
155
  )
@@ -177,7 +177,7 @@ class Conversation:
177
177
  else:
178
178
  structlogger.warning(
179
179
  "audiocodes.handle.activities.unknown_activity_type",
180
- activity=activity,
180
+ activity_type=activity["type"],
181
181
  )
182
182
  continue
183
183
 
@@ -193,16 +193,9 @@ class Conversation:
193
193
  try:
194
194
  await on_new_message(user_msg)
195
195
  except Exception as e: # skipcq: PYL-W0703
196
- if isinstance(user_msg.text, dict):
197
- anonymized_info = json.dumps(user_msg.text)
198
- elif isinstance(user_msg.text, str):
199
- anonymized_info = user_msg.text
200
- else:
201
- anonymized_info = INFO_UNKNOWN
202
-
203
196
  structlogger.exception(
204
197
  "audiocodes.handle.activities.failure",
205
- user_message=copy.deepcopy(anonymized_info),
198
+ sender_id=self.conversation_id,
206
199
  error=e,
207
200
  exc_info=True,
208
201
  )
@@ -495,8 +488,8 @@ class AudiocodesInput(InputChannel):
495
488
  await on_new_message(
496
489
  UserMessage(
497
490
  text=f"{INTENT_MESSAGE_PREFIX}session_end",
498
- input_channel=self.name(),
499
491
  output_channel=None,
492
+ input_channel=self.name(),
500
493
  sender_id=conversation_id,
501
494
  metadata=reason,
502
495
  )
@@ -17,7 +17,10 @@ from rasa.core.channels.voice_ready.jambonz_protocol import (
17
17
  send_ws_text_message,
18
18
  websocket_message_handler,
19
19
  )
20
- from rasa.core.channels.voice_ready.utils import validate_voice_license_scope
20
+ from rasa.core.channels.voice_ready.utils import (
21
+ validate_username_password_credentials,
22
+ validate_voice_license_scope,
23
+ )
21
24
  from rasa.shared.exceptions import RasaException
22
25
  from rasa.shared.utils.common import mark_as_beta_feature
23
26
  from rasa.utils.io import remove_emojis
@@ -41,11 +44,7 @@ class JambonzVoiceReadyInput(InputChannel):
41
44
 
42
45
  username = credentials.get("username")
43
46
  password = credentials.get("password")
44
- if (username is None) != (password is None):
45
- raise RasaException(
46
- "In Jambonz channel, either both username and password "
47
- "or neither should be provided. "
48
- )
47
+ validate_username_password_credentials(username, password, "Jambonz")
49
48
 
50
49
  return cls(username, password)
51
50
 
@@ -16,9 +16,12 @@ from rasa.core.channels.channel import (
16
16
  create_auth_requested_response_provider,
17
17
  requires_basic_auth,
18
18
  )
19
- from rasa.core.channels.voice_ready.utils import CallParameters
19
+ from rasa.core.channels.voice_ready.utils import (
20
+ CallParameters,
21
+ validate_username_password_credentials,
22
+ )
20
23
  from rasa.shared.core.events import BotUttered
21
- from rasa.shared.exceptions import InvalidConfigException, RasaException
24
+ from rasa.shared.exceptions import InvalidConfigException
22
25
 
23
26
  logger = structlog.get_logger(__name__)
24
27
 
@@ -127,11 +130,7 @@ class TwilioVoiceInput(InputChannel):
127
130
 
128
131
  username = credentials.get("username")
129
132
  password = credentials.get("password")
130
- if (username is None) != (password is None):
131
- raise RasaException(
132
- "In TwilioVoice channel, either both username and password "
133
- "or neither should be provided. "
134
- )
133
+ validate_username_password_credentials(username, password, "TwilioVoice")
135
134
 
136
135
  return cls(
137
136
  credentials.get(
@@ -180,8 +179,9 @@ class TwilioVoiceInput(InputChannel):
180
179
  if self.assistant_voice not in self.SUPPORTED_VOICES:
181
180
  self._raise_invalid_voice_exception()
182
181
 
183
- if (self.username is None) != (self.password is None):
184
- self._raise_invalid_credentials_exception()
182
+ validate_username_password_credentials(
183
+ self.username, self.password, "TwilioVoice"
184
+ )
185
185
 
186
186
  try:
187
187
  int(self.speech_timeout)
@@ -389,9 +389,10 @@ class TwilioVoiceInput(InputChannel):
389
389
  return voice_response
390
390
 
391
391
  def _raise_invalid_credentials_exception(self) -> None:
392
- raise InvalidConfigException(
393
- "In TwilioVoice channel, either both username and password "
394
- "or neither should be provided. "
392
+ # This method is now redundant since we use the shared validation function
393
+ # but keeping it for backward compatibility if any external code calls it
394
+ validate_username_password_credentials(
395
+ self.username, self.password, "TwilioVoice"
395
396
  )
396
397
 
397
398
 
@@ -3,9 +3,31 @@ from typing import Optional
3
3
 
4
4
  import structlog
5
5
 
6
+ from rasa.shared.exceptions import InvalidConfigException
7
+
6
8
  structlogger = structlog.get_logger()
7
9
 
8
10
 
11
+ def validate_username_password_credentials(
12
+ username: Optional[str], password: Optional[str], channel_name: str
13
+ ) -> None:
14
+ """Validate that username and password are both provided or both None.
15
+
16
+ Args:
17
+ username: The username credential, or None
18
+ password: The password credential, or None
19
+ channel_name: The name of the channel for error message
20
+
21
+ Raises:
22
+ InvalidConfigException: If only one of username/password is provided
23
+ """
24
+ if (not username) != (not password):
25
+ raise InvalidConfigException(
26
+ f"In {channel_name} channel, either both username and password "
27
+ "or neither should be provided."
28
+ )
29
+
30
+
9
31
  def validate_voice_license_scope() -> None:
10
32
  from rasa.utils.licensing import (
11
33
  PRODUCT_AREA,