rasa-pro 3.11.5__py3-none-any.whl → 3.12.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 (559) hide show
  1. README.md +10 -13
  2. rasa/__main__.py +7 -7
  3. rasa/anonymization/anonymisation_rule_yaml_reader.py +1 -1
  4. rasa/anonymization/anonymization_pipeline.py +3 -3
  5. rasa/anonymization/anonymization_rule_executor.py +17 -11
  6. rasa/anonymization/anonymization_rule_orchestrator.py +2 -3
  7. rasa/cli/arguments/data.py +2 -2
  8. rasa/cli/arguments/default_arguments.py +1 -1
  9. rasa/cli/arguments/evaluate.py +2 -1
  10. rasa/cli/arguments/interactive.py +1 -1
  11. rasa/cli/arguments/run.py +1 -1
  12. rasa/cli/arguments/test.py +7 -5
  13. rasa/cli/arguments/train.py +3 -3
  14. rasa/cli/arguments/visualize.py +2 -2
  15. rasa/cli/arguments/x.py +1 -0
  16. rasa/cli/data.py +20 -3
  17. rasa/cli/dialogue_understanding_test.py +386 -0
  18. rasa/cli/evaluate.py +1 -1
  19. rasa/cli/export.py +6 -6
  20. rasa/cli/inspect.py +20 -1
  21. rasa/cli/interactive.py +4 -5
  22. rasa/cli/llm_fine_tuning.py +51 -16
  23. rasa/cli/markers.py +1 -2
  24. rasa/cli/project_templates/calm/actions/add_contact.py +1 -1
  25. rasa/cli/project_templates/calm/config.yml +2 -2
  26. rasa/cli/project_templates/calm/domain/list_contacts.yml +1 -2
  27. rasa/cli/project_templates/calm/domain/remove_contact.yml +1 -2
  28. rasa/cli/project_templates/calm/domain/shared.yml +1 -4
  29. rasa/cli/project_templates/calm/endpoints.yml +2 -2
  30. rasa/cli/project_templates/tutorial/actions/actions.py +3 -2
  31. rasa/cli/shell.py +5 -6
  32. rasa/cli/studio/download.py +1 -2
  33. rasa/cli/studio/studio.py +2 -3
  34. rasa/cli/studio/train.py +0 -1
  35. rasa/cli/telemetry.py +2 -2
  36. rasa/cli/test.py +11 -11
  37. rasa/cli/train.py +3 -0
  38. rasa/cli/utils.py +25 -5
  39. rasa/constants.py +0 -1
  40. rasa/core/__init__.py +0 -1
  41. rasa/core/actions/action.py +135 -208
  42. rasa/core/actions/action_handle_digressions.py +164 -0
  43. rasa/core/actions/action_hangup.py +1 -1
  44. rasa/core/actions/action_repeat_bot_messages.py +2 -2
  45. rasa/core/actions/action_run_slot_rejections.py +18 -6
  46. rasa/core/actions/action_trigger_chitchat.py +1 -1
  47. rasa/core/actions/action_trigger_flow.py +5 -5
  48. rasa/core/actions/action_trigger_search.py +1 -1
  49. rasa/core/actions/custom_action_executor.py +1 -1
  50. rasa/core/actions/direct_custom_actions_executor.py +1 -0
  51. rasa/core/actions/forms.py +22 -15
  52. rasa/core/actions/http_custom_action_executor.py +8 -1
  53. rasa/core/actions/loops.py +3 -3
  54. rasa/core/actions/two_stage_fallback.py +13 -13
  55. rasa/core/auth_retry_tracker_store.py +1 -2
  56. rasa/core/brokers/broker.py +2 -1
  57. rasa/core/brokers/file.py +1 -1
  58. rasa/core/brokers/kafka.py +8 -8
  59. rasa/core/brokers/pika.py +8 -9
  60. rasa/core/brokers/sql.py +4 -3
  61. rasa/core/channels/__init__.py +7 -0
  62. rasa/core/channels/botframework.py +2 -2
  63. rasa/core/channels/callback.py +4 -4
  64. rasa/core/channels/channel.py +11 -11
  65. rasa/core/channels/console.py +0 -1
  66. rasa/core/channels/development_inspector.py +80 -24
  67. rasa/core/channels/facebook.py +5 -5
  68. rasa/core/channels/hangouts.py +7 -8
  69. rasa/core/channels/inspector/dist/assets/{arc-f0f8bd46.js → arc-9f1365dc.js} +1 -1
  70. rasa/core/channels/inspector/dist/assets/{blockDiagram-38ab4fdb-7162c77d.js → blockDiagram-38ab4fdb-e0f81b12.js} +1 -1
  71. rasa/core/channels/inspector/dist/assets/{c4Diagram-3d4e48cf-b1d0d098.js → c4Diagram-3d4e48cf-9deaee1c.js} +1 -1
  72. rasa/core/channels/inspector/dist/assets/channel-44956714.js +1 -0
  73. rasa/core/channels/inspector/dist/assets/{classDiagram-70f12bd4-807a1b27.js → classDiagram-70f12bd4-20450a96.js} +1 -1
  74. rasa/core/channels/inspector/dist/assets/{classDiagram-v2-f2320105-5238dcdb.js → classDiagram-v2-f2320105-749d2abf.js} +1 -1
  75. rasa/core/channels/inspector/dist/assets/clone-a9475142.js +1 -0
  76. rasa/core/channels/inspector/dist/assets/{createText-2e5e7dd3-75dfaa67.js → createText-2e5e7dd3-bef0b38c.js} +1 -1
  77. rasa/core/channels/inspector/dist/assets/{edges-e0da2a9e-df20501d.js → edges-e0da2a9e-943801a7.js} +1 -1
  78. rasa/core/channels/inspector/dist/assets/{erDiagram-9861fffd-13cf4797.js → erDiagram-9861fffd-d523a948.js} +1 -1
  79. rasa/core/channels/inspector/dist/assets/{flowDb-956e92f1-a4991264.js → flowDb-956e92f1-54e4cf19.js} +1 -1
  80. rasa/core/channels/inspector/dist/assets/{flowDiagram-66a62f08-ccecf773.js → flowDiagram-66a62f08-48bfbbe8.js} +1 -1
  81. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-43fa749a.js +1 -0
  82. rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-4a651766-b5801783.js → flowchart-elk-definition-4a651766-17c30827.js} +1 -1
  83. rasa/core/channels/inspector/dist/assets/{ganttDiagram-c361ad54-161e079a.js → ganttDiagram-c361ad54-43086f2d.js} +1 -1
  84. rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-72cf32ee-f38e86a4.js → gitGraphDiagram-72cf32ee-5c8b693e.js} +1 -1
  85. rasa/core/channels/inspector/dist/assets/{graph-be6ef5d8.js → graph-41a90d26.js} +1 -1
  86. rasa/core/channels/inspector/dist/assets/{index-3862675e-d9ce8994.js → index-3862675e-b43eeae9.js} +1 -1
  87. rasa/core/channels/inspector/dist/assets/{index-7794b245.js → index-e8affe45.js} +155 -155
  88. rasa/core/channels/inspector/dist/assets/{infoDiagram-f8f76790-5000a3dc.js → infoDiagram-f8f76790-0b20676b.js} +1 -1
  89. rasa/core/channels/inspector/dist/assets/{journeyDiagram-49397b02-8ef0a17a.js → journeyDiagram-49397b02-39bce7b5.js} +1 -1
  90. rasa/core/channels/inspector/dist/assets/{layout-d649bc98.js → layout-dc8eeea4.js} +1 -1
  91. rasa/core/channels/inspector/dist/assets/{line-95add810.js → line-c4d2e756.js} +1 -1
  92. rasa/core/channels/inspector/dist/assets/{linear-f6025094.js → linear-86f6f2d9.js} +1 -1
  93. rasa/core/channels/inspector/dist/assets/{mindmap-definition-fc14e90a-2e8531c4.js → mindmap-definition-fc14e90a-4216f771.js} +1 -1
  94. rasa/core/channels/inspector/dist/assets/{pieDiagram-8a3498a8-918adfdb.js → pieDiagram-8a3498a8-1a0cfa96.js} +1 -1
  95. rasa/core/channels/inspector/dist/assets/{quadrantDiagram-120e2f19-cbd01797.js → quadrantDiagram-120e2f19-f91e67cf.js} +1 -1
  96. rasa/core/channels/inspector/dist/assets/{requirementDiagram-deff3bca-6a8b877b.js → requirementDiagram-deff3bca-d4046bed.js} +1 -1
  97. rasa/core/channels/inspector/dist/assets/{sankeyDiagram-04a897e0-c377c3fe.js → sankeyDiagram-04a897e0-2cf6d1d7.js} +1 -1
  98. rasa/core/channels/inspector/dist/assets/{sequenceDiagram-704730f1-ab9e9b7f.js → sequenceDiagram-704730f1-751ac4f5.js} +1 -1
  99. rasa/core/channels/inspector/dist/assets/{stateDiagram-587899a1-5e6ae67d.js → stateDiagram-587899a1-f734f4d4.js} +1 -1
  100. rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-d93cdb3a-40643476.js → stateDiagram-v2-d93cdb3a-91c65710.js} +1 -1
  101. rasa/core/channels/inspector/dist/assets/{styles-6aaf32cf-afb8d108.js → styles-6aaf32cf-e0cff7be.js} +1 -1
  102. rasa/core/channels/inspector/dist/assets/{styles-9a916d00-7edc9423.js → styles-9a916d00-c8029e5d.js} +1 -1
  103. rasa/core/channels/inspector/dist/assets/{styles-c10674c1-c1d8f7e9.js → styles-c10674c1-114f312a.js} +1 -1
  104. rasa/core/channels/inspector/dist/assets/{svgDrawCommon-08f97a94-f494b2ef.js → svgDrawCommon-08f97a94-b7b9dc00.js} +1 -1
  105. rasa/core/channels/inspector/dist/assets/{timeline-definition-85554ec2-11c7cdd0.js → timeline-definition-85554ec2-9536d189.js} +1 -1
  106. rasa/core/channels/inspector/dist/assets/{xychartDiagram-e933f94c-3f191ec1.js → xychartDiagram-e933f94c-bf3b0f36.js} +1 -1
  107. rasa/core/channels/inspector/dist/index.html +1 -1
  108. rasa/core/channels/inspector/package.json +1 -0
  109. rasa/core/channels/inspector/src/App.tsx +15 -2
  110. rasa/core/channels/inspector/src/components/RasaLogo.tsx +31 -0
  111. rasa/core/channels/inspector/src/components/RecruitmentPanel.tsx +68 -0
  112. rasa/core/channels/inspector/src/components/Welcome.tsx +19 -13
  113. rasa/core/channels/inspector/yarn.lock +5 -0
  114. rasa/core/channels/mattermost.py +4 -4
  115. rasa/core/channels/rasa_chat.py +4 -4
  116. rasa/core/channels/rest.py +11 -12
  117. rasa/core/channels/rocketchat.py +4 -3
  118. rasa/core/channels/slack.py +6 -5
  119. rasa/core/channels/socketio.py +8 -28
  120. rasa/core/channels/studio_chat.py +212 -0
  121. rasa/core/channels/telegram.py +105 -55
  122. rasa/core/channels/twilio.py +3 -3
  123. rasa/core/channels/vier_cvg.py +2 -2
  124. rasa/core/channels/voice_ready/audiocodes.py +9 -9
  125. rasa/core/channels/voice_ready/jambonz.py +5 -5
  126. rasa/core/channels/voice_ready/jambonz_protocol.py +3 -4
  127. rasa/core/channels/voice_ready/twilio_voice.py +9 -8
  128. rasa/core/channels/voice_ready/utils.py +2 -2
  129. rasa/core/channels/voice_stream/asr/asr_engine.py +12 -6
  130. rasa/core/channels/voice_stream/asr/asr_event.py +5 -0
  131. rasa/core/channels/voice_stream/asr/azure.py +16 -3
  132. rasa/core/channels/voice_stream/asr/deepgram.py +76 -19
  133. rasa/core/channels/voice_stream/audiocodes.py +292 -0
  134. rasa/core/channels/voice_stream/browser_audio.py +14 -7
  135. rasa/core/channels/voice_stream/call_state.py +6 -2
  136. rasa/core/channels/voice_stream/genesys.py +320 -0
  137. rasa/core/channels/voice_stream/tts/azure.py +13 -5
  138. rasa/core/channels/voice_stream/tts/cartesia.py +34 -14
  139. rasa/core/channels/voice_stream/tts/tts_cache.py +3 -2
  140. rasa/core/channels/voice_stream/tts/tts_engine.py +1 -1
  141. rasa/core/channels/voice_stream/twilio_media_streams.py +12 -8
  142. rasa/core/channels/voice_stream/util.py +1 -1
  143. rasa/core/channels/voice_stream/voice_channel.py +100 -56
  144. rasa/core/channels/webexteams.py +3 -4
  145. rasa/core/constants.py +2 -0
  146. rasa/core/evaluation/marker.py +7 -6
  147. rasa/core/evaluation/marker_base.py +15 -16
  148. rasa/core/evaluation/marker_stats.py +3 -4
  149. rasa/core/evaluation/marker_tracker_loader.py +5 -4
  150. rasa/core/exporter.py +4 -4
  151. rasa/core/featurizers/precomputation.py +8 -8
  152. rasa/core/featurizers/single_state_featurizer.py +7 -7
  153. rasa/core/featurizers/tracker_featurizers.py +13 -13
  154. rasa/core/http_interpreter.py +3 -4
  155. rasa/core/information_retrieval/__init__.py +1 -1
  156. rasa/core/information_retrieval/faiss.py +4 -4
  157. rasa/core/information_retrieval/information_retrieval.py +2 -2
  158. rasa/core/information_retrieval/milvus.py +3 -3
  159. rasa/core/information_retrieval/qdrant.py +3 -3
  160. rasa/core/jobs.py +1 -0
  161. rasa/core/lock.py +2 -3
  162. rasa/core/lock_store.py +3 -3
  163. rasa/core/migrate.py +12 -9
  164. rasa/core/nlg/__init__.py +1 -1
  165. rasa/core/nlg/callback.py +2 -3
  166. rasa/core/nlg/contextual_response_rephraser.py +82 -14
  167. rasa/core/nlg/generator.py +85 -17
  168. rasa/core/nlg/interpolator.py +4 -3
  169. rasa/core/nlg/response.py +9 -7
  170. rasa/core/nlg/summarize.py +1 -0
  171. rasa/core/nlg/translate.py +55 -0
  172. rasa/core/persistor.py +3 -3
  173. rasa/core/policies/ensemble.py +10 -9
  174. rasa/core/policies/enterprise_search_policy.py +87 -21
  175. rasa/core/policies/enterprise_search_prompt_with_citation_template.jinja2 +1 -1
  176. rasa/core/policies/flow_policy.py +13 -14
  177. rasa/core/policies/flows/flow_executor.py +85 -55
  178. rasa/core/policies/intentless_policy.py +6 -7
  179. rasa/core/policies/memoization.py +22 -20
  180. rasa/core/policies/policy.py +24 -22
  181. rasa/core/policies/rule_policy.py +37 -36
  182. rasa/core/policies/ted_policy.py +87 -85
  183. rasa/core/policies/unexpected_intent_policy.py +77 -75
  184. rasa/core/processor.py +167 -74
  185. rasa/core/run.py +5 -4
  186. rasa/core/secrets_manager/endpoints.py +2 -3
  187. rasa/core/secrets_manager/factory.py +2 -3
  188. rasa/core/secrets_manager/secret_manager.py +2 -3
  189. rasa/core/secrets_manager/vault.py +2 -2
  190. rasa/core/test.py +30 -30
  191. rasa/core/tracker_store.py +138 -49
  192. rasa/core/train.py +1 -1
  193. rasa/core/training/__init__.py +2 -2
  194. rasa/core/training/converters/responses_prefix_converter.py +1 -2
  195. rasa/core/training/interactive.py +13 -13
  196. rasa/core/training/story_conflict.py +4 -5
  197. rasa/core/training/training.py +3 -5
  198. rasa/core/utils.py +5 -5
  199. rasa/core/visualize.py +1 -1
  200. rasa/dialogue_understanding/coexistence/intent_based_router.py +2 -2
  201. rasa/dialogue_understanding/coexistence/llm_based_router.py +5 -5
  202. rasa/dialogue_understanding/commands/__init__.py +22 -22
  203. rasa/dialogue_understanding/commands/can_not_handle_command.py +38 -1
  204. rasa/dialogue_understanding/commands/cancel_flow_command.py +96 -9
  205. rasa/dialogue_understanding/commands/change_flow_command.py +36 -2
  206. rasa/dialogue_understanding/commands/chit_chat_answer_command.py +36 -4
  207. rasa/dialogue_understanding/commands/clarify_command.py +46 -4
  208. rasa/dialogue_understanding/commands/command.py +3 -2
  209. rasa/dialogue_understanding/commands/command_syntax_manager.py +55 -0
  210. rasa/dialogue_understanding/commands/correct_slots_command.py +14 -5
  211. rasa/dialogue_understanding/commands/error_command.py +1 -1
  212. rasa/dialogue_understanding/commands/free_form_answer_command.py +2 -1
  213. rasa/dialogue_understanding/commands/handle_code_change_command.py +2 -2
  214. rasa/dialogue_understanding/commands/handle_digressions_command.py +144 -0
  215. rasa/dialogue_understanding/commands/human_handoff_command.py +34 -4
  216. rasa/dialogue_understanding/commands/knowledge_answer_command.py +36 -4
  217. rasa/dialogue_understanding/commands/noop_command.py +2 -1
  218. rasa/dialogue_understanding/commands/prompt_command.py +94 -0
  219. rasa/dialogue_understanding/commands/repeat_bot_messages_command.py +34 -4
  220. rasa/dialogue_understanding/commands/restart_command.py +2 -5
  221. rasa/dialogue_understanding/commands/session_end_command.py +3 -5
  222. rasa/dialogue_understanding/commands/session_start_command.py +3 -5
  223. rasa/dialogue_understanding/commands/set_slot_command.py +55 -16
  224. rasa/dialogue_understanding/commands/skip_question_command.py +34 -4
  225. rasa/dialogue_understanding/commands/start_flow_command.py +78 -2
  226. rasa/dialogue_understanding/commands/user_silence_command.py +3 -5
  227. rasa/dialogue_understanding/commands/utils.py +126 -43
  228. rasa/dialogue_understanding/constants.py +2 -0
  229. rasa/dialogue_understanding/generator/__init__.py +2 -0
  230. rasa/dialogue_understanding/generator/command_generator.py +120 -79
  231. rasa/dialogue_understanding/generator/command_parser.py +245 -0
  232. rasa/dialogue_understanding/generator/constants.py +12 -4
  233. rasa/dialogue_understanding/generator/flow_retrieval.py +7 -7
  234. rasa/dialogue_understanding/generator/llm_based_command_generator.py +187 -59
  235. rasa/dialogue_understanding/generator/llm_command_generator.py +6 -3
  236. rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +106 -110
  237. rasa/dialogue_understanding/generator/nlu_command_adapter.py +53 -11
  238. rasa/dialogue_understanding/generator/prompt_templates/__init__.py +0 -0
  239. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_claude_3_5_sonnet_20240620_template.jinja2 +58 -0
  240. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_gpt_4o_2024_11_20_template.jinja2 +57 -0
  241. rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +574 -0
  242. rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +41 -386
  243. rasa/dialogue_understanding/generator/utils.py +76 -0
  244. rasa/dialogue_understanding/patterns/cancel.py +2 -1
  245. rasa/dialogue_understanding/patterns/cannot_handle.py +1 -0
  246. rasa/dialogue_understanding/patterns/chitchat.py +1 -1
  247. rasa/dialogue_understanding/patterns/clarify.py +2 -1
  248. rasa/dialogue_understanding/patterns/code_change.py +2 -0
  249. rasa/dialogue_understanding/patterns/collect_information.py +7 -4
  250. rasa/dialogue_understanding/patterns/completed.py +1 -1
  251. rasa/dialogue_understanding/patterns/continue_interrupted.py +1 -1
  252. rasa/dialogue_understanding/patterns/correction.py +17 -3
  253. rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +78 -2
  254. rasa/dialogue_understanding/patterns/handle_digressions.py +81 -0
  255. rasa/dialogue_understanding/patterns/human_handoff.py +1 -1
  256. rasa/dialogue_understanding/patterns/internal_error.py +1 -0
  257. rasa/dialogue_understanding/patterns/search.py +1 -1
  258. rasa/dialogue_understanding/patterns/session_start.py +1 -1
  259. rasa/dialogue_understanding/patterns/skip_question.py +1 -0
  260. rasa/dialogue_understanding/patterns/user_silence.py +1 -1
  261. rasa/dialogue_understanding/patterns/validate_slot.py +65 -0
  262. rasa/dialogue_understanding/processor/command_processor.py +193 -43
  263. rasa/dialogue_understanding/processor/command_processor_component.py +1 -1
  264. rasa/dialogue_understanding/stack/dialogue_stack.py +4 -3
  265. rasa/dialogue_understanding/stack/frames/__init__.py +2 -2
  266. rasa/dialogue_understanding/stack/frames/chit_chat_frame.py +4 -1
  267. rasa/dialogue_understanding/stack/frames/dialogue_stack_frame.py +2 -3
  268. rasa/dialogue_understanding/stack/frames/flow_stack_frame.py +5 -2
  269. rasa/dialogue_understanding/stack/frames/search_frame.py +4 -1
  270. rasa/dialogue_understanding/stack/utils.py +56 -10
  271. rasa/dialogue_understanding/utils.py +164 -0
  272. rasa/dialogue_understanding_test/README.md +429 -0
  273. rasa/dialogue_understanding_test/__init__.py +0 -0
  274. rasa/dialogue_understanding_test/command_comparison.py +60 -0
  275. rasa/dialogue_understanding_test/command_metric_calculation.py +122 -0
  276. rasa/dialogue_understanding_test/constants.py +22 -0
  277. rasa/dialogue_understanding_test/du_test_case.py +448 -0
  278. rasa/dialogue_understanding_test/du_test_result.py +390 -0
  279. rasa/dialogue_understanding_test/du_test_runner.py +322 -0
  280. rasa/dialogue_understanding_test/du_test_schema.yml +161 -0
  281. rasa/dialogue_understanding_test/io.py +443 -0
  282. rasa/dialogue_understanding_test/test_case_simulation/__init__.py +0 -0
  283. rasa/dialogue_understanding_test/test_case_simulation/exception.py +28 -0
  284. rasa/dialogue_understanding_test/test_case_simulation/test_case_tracker_simulator.py +336 -0
  285. rasa/dialogue_understanding_test/utils.py +70 -0
  286. rasa/dialogue_understanding_test/validation.py +77 -0
  287. rasa/e2e_test/aggregate_test_stats_calculator.py +1 -1
  288. rasa/e2e_test/assertions.py +202 -175
  289. rasa/e2e_test/assertions_schema.yml +6 -0
  290. rasa/e2e_test/constants.py +16 -1
  291. rasa/e2e_test/e2e_config.py +102 -41
  292. rasa/e2e_test/e2e_config_schema.yml +28 -10
  293. rasa/e2e_test/e2e_test_case.py +5 -5
  294. rasa/e2e_test/e2e_test_converter.py +2 -3
  295. rasa/e2e_test/e2e_test_coverage_report.py +6 -6
  296. rasa/e2e_test/e2e_test_result.py +1 -1
  297. rasa/e2e_test/e2e_test_runner.py +143 -38
  298. rasa/e2e_test/llm_judge_prompts/answer_relevance_prompt_template.jinja2 +93 -0
  299. rasa/e2e_test/llm_judge_prompts/groundedness_prompt_template.jinja2 +169 -0
  300. rasa/e2e_test/stub_custom_action.py +1 -1
  301. rasa/e2e_test/utils/generative_assertions.py +243 -0
  302. rasa/e2e_test/utils/io.py +123 -93
  303. rasa/e2e_test/utils/validation.py +101 -3
  304. rasa/engine/caching.py +5 -7
  305. rasa/engine/constants.py +1 -1
  306. rasa/engine/graph.py +3 -2
  307. rasa/engine/language.py +182 -0
  308. rasa/engine/recipes/config_files/default_config.yml +4 -0
  309. rasa/engine/recipes/default_components.py +13 -15
  310. rasa/engine/recipes/default_recipe.py +65 -49
  311. rasa/engine/recipes/graph_recipe.py +10 -7
  312. rasa/engine/recipes/recipe.py +2 -2
  313. rasa/engine/runner/dask.py +2 -2
  314. rasa/engine/runner/interface.py +1 -0
  315. rasa/engine/storage/local_model_storage.py +6 -4
  316. rasa/engine/storage/resource.py +2 -1
  317. rasa/engine/storage/storage.py +8 -3
  318. rasa/engine/training/components.py +2 -1
  319. rasa/engine/training/fingerprinting.py +4 -2
  320. rasa/engine/training/graph_trainer.py +4 -4
  321. rasa/engine/training/hooks.py +2 -2
  322. rasa/engine/validation.py +36 -33
  323. rasa/exceptions.py +3 -2
  324. rasa/graph_components/converters/nlu_message_converter.py +3 -3
  325. rasa/graph_components/providers/domain_for_core_training_provider.py +3 -3
  326. rasa/graph_components/providers/domain_provider.py +3 -2
  327. rasa/graph_components/providers/flows_provider.py +2 -3
  328. rasa/graph_components/providers/forms_provider.py +4 -4
  329. rasa/graph_components/providers/nlu_training_data_provider.py +5 -3
  330. rasa/graph_components/providers/responses_provider.py +4 -4
  331. rasa/graph_components/providers/rule_only_provider.py +3 -2
  332. rasa/graph_components/providers/story_graph_provider.py +8 -8
  333. rasa/graph_components/providers/training_tracker_provider.py +3 -2
  334. rasa/graph_components/validators/default_recipe_validator.py +16 -16
  335. rasa/graph_components/validators/finetuning_validator.py +10 -8
  336. rasa/hooks.py +19 -14
  337. rasa/jupyter.py +2 -2
  338. rasa/llm_fine_tuning/annotation_module.py +4 -4
  339. rasa/llm_fine_tuning/conversations.py +5 -33
  340. rasa/llm_fine_tuning/llm_data_preparation_module.py +6 -4
  341. rasa/llm_fine_tuning/paraphrasing/conversation_rephraser.py +4 -4
  342. rasa/llm_fine_tuning/paraphrasing/rephrase_validator.py +18 -13
  343. rasa/llm_fine_tuning/paraphrasing_module.py +6 -2
  344. rasa/llm_fine_tuning/storage.py +3 -3
  345. rasa/llm_fine_tuning/train_test_split_module.py +27 -27
  346. rasa/llm_fine_tuning/utils.py +7 -0
  347. rasa/markers/marker.py +2 -3
  348. rasa/markers/marker_base.py +1 -2
  349. rasa/markers/upload.py +2 -2
  350. rasa/markers/validate.py +2 -3
  351. rasa/model.py +3 -5
  352. rasa/model_manager/config.py +1 -1
  353. rasa/model_manager/model_api.py +5 -4
  354. rasa/model_manager/runner_service.py +13 -10
  355. rasa/model_manager/socket_bridge.py +15 -9
  356. rasa/model_manager/studio_jwt_auth.py +1 -0
  357. rasa/model_manager/trainer_service.py +9 -7
  358. rasa/model_manager/utils.py +1 -1
  359. rasa/model_manager/warm_rasa_process.py +14 -9
  360. rasa/model_service.py +5 -6
  361. rasa/model_testing.py +13 -15
  362. rasa/model_training.py +29 -29
  363. rasa/nlu/classifiers/diet_classifier.py +72 -73
  364. rasa/nlu/classifiers/fallback_classifier.py +9 -8
  365. rasa/nlu/classifiers/keyword_intent_classifier.py +7 -6
  366. rasa/nlu/classifiers/logistic_regression_classifier.py +3 -3
  367. rasa/nlu/classifiers/mitie_intent_classifier.py +5 -4
  368. rasa/nlu/classifiers/regex_message_handler.py +3 -2
  369. rasa/nlu/classifiers/sklearn_intent_classifier.py +2 -2
  370. rasa/nlu/convert.py +2 -2
  371. rasa/nlu/emulators/dialogflow.py +3 -3
  372. rasa/nlu/emulators/luis.py +5 -5
  373. rasa/nlu/emulators/no_emulator.py +1 -0
  374. rasa/nlu/emulators/wit.py +4 -4
  375. rasa/nlu/extractors/crf_entity_extractor.py +11 -11
  376. rasa/nlu/extractors/duckling_entity_extractor.py +7 -6
  377. rasa/nlu/extractors/entity_synonyms.py +10 -9
  378. rasa/nlu/extractors/extractor.py +16 -16
  379. rasa/nlu/extractors/mitie_entity_extractor.py +10 -9
  380. rasa/nlu/extractors/regex_entity_extractor.py +11 -10
  381. rasa/nlu/extractors/spacy_entity_extractor.py +2 -2
  382. rasa/nlu/featurizers/dense_featurizer/convert_featurizer.py +15 -14
  383. rasa/nlu/featurizers/dense_featurizer/dense_featurizer.py +2 -1
  384. rasa/nlu/featurizers/dense_featurizer/lm_featurizer.py +10 -9
  385. rasa/nlu/featurizers/dense_featurizer/mitie_featurizer.py +9 -7
  386. rasa/nlu/featurizers/dense_featurizer/spacy_featurizer.py +13 -12
  387. rasa/nlu/featurizers/featurizer.py +5 -4
  388. rasa/nlu/featurizers/sparse_featurizer/count_vectors_featurizer.py +6 -6
  389. rasa/nlu/featurizers/sparse_featurizer/lexical_syntactic_featurizer.py +4 -4
  390. rasa/nlu/featurizers/sparse_featurizer/regex_featurizer.py +4 -4
  391. rasa/nlu/featurizers/sparse_featurizer/sparse_featurizer.py +2 -0
  392. rasa/nlu/model.py +0 -1
  393. rasa/nlu/selectors/response_selector.py +67 -68
  394. rasa/nlu/test.py +38 -38
  395. rasa/nlu/tokenizers/jieba_tokenizer.py +1 -2
  396. rasa/nlu/tokenizers/mitie_tokenizer.py +2 -2
  397. rasa/nlu/tokenizers/spacy_tokenizer.py +3 -3
  398. rasa/nlu/tokenizers/tokenizer.py +6 -7
  399. rasa/nlu/tokenizers/whitespace_tokenizer.py +1 -1
  400. rasa/nlu/utils/bilou_utils.py +7 -7
  401. rasa/nlu/utils/hugging_face/registry.py +22 -22
  402. rasa/nlu/utils/hugging_face/transformers_pre_post_processors.py +2 -1
  403. rasa/nlu/utils/mitie_utils.py +2 -1
  404. rasa/nlu/utils/pattern_utils.py +1 -1
  405. rasa/nlu/utils/spacy_utils.py +3 -3
  406. rasa/plugin.py +12 -1
  407. rasa/server.py +3 -2
  408. rasa/shared/constants.py +45 -18
  409. rasa/shared/core/command_payload_reader.py +15 -7
  410. rasa/shared/core/constants.py +34 -4
  411. rasa/shared/core/conversation.py +1 -2
  412. rasa/shared/core/domain.py +19 -20
  413. rasa/shared/core/events.py +60 -39
  414. rasa/shared/core/flows/__init__.py +0 -1
  415. rasa/shared/core/flows/constants.py +11 -0
  416. rasa/shared/core/flows/flow.py +107 -26
  417. rasa/shared/core/flows/flow_step.py +4 -3
  418. rasa/shared/core/flows/flow_step_links.py +1 -2
  419. rasa/shared/core/flows/flow_step_sequence.py +1 -1
  420. rasa/shared/core/flows/flows_list.py +3 -3
  421. rasa/shared/core/flows/flows_yaml_schema.json +69 -3
  422. rasa/shared/core/flows/nlu_trigger.py +1 -1
  423. rasa/shared/core/flows/steps/__init__.py +2 -2
  424. rasa/shared/core/flows/steps/action.py +1 -1
  425. rasa/shared/core/flows/steps/call.py +1 -1
  426. rasa/shared/core/flows/steps/collect.py +22 -40
  427. rasa/shared/core/flows/steps/internal.py +1 -1
  428. rasa/shared/core/flows/steps/link.py +1 -1
  429. rasa/shared/core/flows/steps/no_operation.py +2 -2
  430. rasa/shared/core/flows/steps/set_slots.py +1 -1
  431. rasa/shared/core/flows/utils.py +44 -4
  432. rasa/shared/core/flows/validation.py +4 -6
  433. rasa/shared/core/generator.py +20 -21
  434. rasa/shared/core/slot_mappings.py +360 -121
  435. rasa/shared/core/slots.py +163 -6
  436. rasa/shared/core/trackers.py +108 -33
  437. rasa/shared/core/training_data/loading.py +1 -1
  438. rasa/shared/core/training_data/story_reader/story_reader.py +3 -3
  439. rasa/shared/core/training_data/story_reader/story_step_builder.py +4 -4
  440. rasa/shared/core/training_data/story_reader/yaml_story_reader.py +29 -31
  441. rasa/shared/core/training_data/story_writer/yaml_story_writer.py +22 -24
  442. rasa/shared/core/training_data/structures.py +11 -12
  443. rasa/shared/core/training_data/visualization.py +10 -10
  444. rasa/shared/data.py +6 -6
  445. rasa/shared/engine/caching.py +0 -1
  446. rasa/shared/exceptions.py +2 -2
  447. rasa/shared/importers/importer.py +58 -2
  448. rasa/shared/importers/rasa.py +5 -6
  449. rasa/shared/importers/utils.py +1 -1
  450. rasa/shared/nlu/constants.py +9 -0
  451. rasa/shared/nlu/training_data/entities_parser.py +6 -6
  452. rasa/shared/nlu/training_data/features.py +3 -3
  453. rasa/shared/nlu/training_data/formats/__init__.py +1 -1
  454. rasa/shared/nlu/training_data/formats/dialogflow.py +4 -5
  455. rasa/shared/nlu/training_data/formats/luis.py +7 -8
  456. rasa/shared/nlu/training_data/formats/rasa.py +4 -5
  457. rasa/shared/nlu/training_data/formats/rasa_yaml.py +17 -16
  458. rasa/shared/nlu/training_data/formats/readerwriter.py +8 -11
  459. rasa/shared/nlu/training_data/formats/wit.py +3 -4
  460. rasa/shared/nlu/training_data/loading.py +4 -4
  461. rasa/shared/nlu/training_data/lookup_tables_parser.py +1 -1
  462. rasa/shared/nlu/training_data/message.py +13 -14
  463. rasa/shared/nlu/training_data/schemas/data_schema.py +1 -1
  464. rasa/shared/nlu/training_data/schemas/responses.yml +19 -11
  465. rasa/shared/nlu/training_data/synonyms_parser.py +3 -3
  466. rasa/shared/nlu/training_data/training_data.py +12 -13
  467. rasa/shared/nlu/training_data/util.py +11 -10
  468. rasa/shared/providers/_configs/azure_entra_id_config.py +541 -0
  469. rasa/shared/providers/_configs/azure_openai_client_config.py +150 -15
  470. rasa/shared/providers/_configs/client_config.py +3 -1
  471. rasa/shared/providers/_configs/default_litellm_client_config.py +9 -7
  472. rasa/shared/providers/_configs/huggingface_local_embedding_client_config.py +13 -11
  473. rasa/shared/providers/_configs/litellm_router_client_config.py +12 -10
  474. rasa/shared/providers/_configs/model_group_config.py +8 -5
  475. rasa/shared/providers/_configs/oauth_config.py +33 -0
  476. rasa/shared/providers/_configs/openai_client_config.py +14 -12
  477. rasa/shared/providers/_configs/rasa_llm_client_config.py +5 -3
  478. rasa/shared/providers/_configs/self_hosted_llm_client_config.py +12 -11
  479. rasa/shared/providers/_configs/utils.py +1 -0
  480. rasa/shared/providers/_ssl_verification_utils.py +5 -6
  481. rasa/shared/providers/_utils.py +5 -5
  482. rasa/shared/providers/constants.py +6 -0
  483. rasa/shared/providers/embedding/_base_litellm_embedding_client.py +1 -1
  484. rasa/shared/providers/embedding/azure_openai_embedding_client.py +32 -7
  485. rasa/shared/providers/embedding/embedding_client.py +1 -1
  486. rasa/shared/providers/embedding/litellm_router_embedding_client.py +5 -2
  487. rasa/shared/providers/llm/_base_litellm_client.py +43 -18
  488. rasa/shared/providers/llm/azure_openai_llm_client.py +90 -34
  489. rasa/shared/providers/llm/default_litellm_llm_client.py +4 -2
  490. rasa/shared/providers/llm/litellm_router_llm_client.py +32 -9
  491. rasa/shared/providers/llm/llm_client.py +24 -8
  492. rasa/shared/providers/llm/llm_response.py +61 -2
  493. rasa/shared/providers/llm/openai_llm_client.py +11 -5
  494. rasa/shared/providers/llm/rasa_llm_client.py +17 -14
  495. rasa/shared/providers/llm/self_hosted_llm_client.py +35 -15
  496. rasa/shared/providers/mappings.py +18 -19
  497. rasa/shared/providers/router/_base_litellm_router_client.py +48 -15
  498. rasa/shared/providers/router/router_client.py +3 -1
  499. rasa/shared/utils/cli.py +1 -1
  500. rasa/shared/utils/common.py +15 -1
  501. rasa/shared/utils/constants.py +3 -0
  502. rasa/shared/utils/health_check/embeddings_health_check_mixin.py +1 -1
  503. rasa/shared/utils/health_check/health_check.py +3 -3
  504. rasa/shared/utils/health_check/llm_health_check_mixin.py +1 -1
  505. rasa/shared/utils/io.py +1 -1
  506. rasa/shared/utils/llm.py +100 -18
  507. rasa/shared/utils/pykwalify_extensions.py +25 -1
  508. rasa/shared/utils/schemas/domain.yml +26 -1
  509. rasa/shared/utils/schemas/events.py +1 -1
  510. rasa/shared/utils/yaml.py +24 -20
  511. rasa/studio/auth.py +3 -3
  512. rasa/studio/config.py +1 -2
  513. rasa/studio/data_handler.py +3 -3
  514. rasa/studio/download.py +1 -1
  515. rasa/studio/results_logger.py +3 -3
  516. rasa/studio/upload.py +21 -5
  517. rasa/telemetry.py +127 -48
  518. rasa/tracing/config.py +5 -3
  519. rasa/tracing/constants.py +12 -0
  520. rasa/tracing/instrumentation/attribute_extractors.py +92 -14
  521. rasa/tracing/instrumentation/instrumentation.py +61 -5
  522. rasa/tracing/instrumentation/intentless_policy_instrumentation.py +1 -1
  523. rasa/tracing/instrumentation/metrics.py +52 -11
  524. rasa/tracing/metric_instrument_provider.py +54 -14
  525. rasa/utils/common.py +12 -24
  526. rasa/utils/endpoints.py +1 -1
  527. rasa/utils/io.py +7 -7
  528. rasa/utils/licensing.py +3 -4
  529. rasa/utils/log_utils.py +7 -6
  530. rasa/utils/ml_utils.py +1 -0
  531. rasa/utils/plotting.py +3 -3
  532. rasa/utils/sanic_error_handler.py +1 -1
  533. rasa/utils/tensorflow/callback.py +2 -2
  534. rasa/utils/tensorflow/crf.py +2 -2
  535. rasa/utils/tensorflow/data_generator.py +5 -5
  536. rasa/utils/tensorflow/environment.py +3 -3
  537. rasa/utils/tensorflow/feature_array.py +2 -3
  538. rasa/utils/tensorflow/layers.py +18 -12
  539. rasa/utils/tensorflow/layers_utils.py +2 -1
  540. rasa/utils/tensorflow/metrics.py +2 -2
  541. rasa/utils/tensorflow/model_data.py +7 -7
  542. rasa/utils/tensorflow/model_data_utils.py +10 -9
  543. rasa/utils/tensorflow/models.py +31 -32
  544. rasa/utils/tensorflow/rasa_layers.py +20 -19
  545. rasa/utils/tensorflow/types.py +2 -1
  546. rasa/utils/train_utils.py +23 -21
  547. rasa/utils/url_tools.py +1 -1
  548. rasa/validator.py +594 -115
  549. rasa/version.py +1 -1
  550. {rasa_pro-3.11.5.dist-info → rasa_pro-3.12.0.dist-info}/METADATA +23 -26
  551. rasa_pro-3.12.0.dist-info/RECORD +829 -0
  552. rasa/core/channels/inspector/dist/assets/channel-e265ea59.js +0 -1
  553. rasa/core/channels/inspector/dist/assets/clone-21f8a43d.js +0 -1
  554. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-5c8ce12d.js +0 -1
  555. rasa_pro-3.11.5.dist-info/RECORD +0 -785
  556. /rasa/dialogue_understanding/generator/{single_step → prompt_templates}/command_prompt_template.jinja2 +0 -0
  557. {rasa_pro-3.11.5.dist-info → rasa_pro-3.12.0.dist-info}/NOTICE +0 -0
  558. {rasa_pro-3.11.5.dist-info → rasa_pro-3.12.0.dist-info}/WHEEL +0 -0
  559. {rasa_pro-3.11.5.dist-info → rasa_pro-3.12.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,320 @@
1
+ import asyncio
2
+ import json
3
+ from typing import Any, Awaitable, Callable, Dict, Optional, Text
4
+
5
+ import structlog
6
+ from sanic import ( # type: ignore[attr-defined]
7
+ Blueprint,
8
+ HTTPResponse,
9
+ Request,
10
+ Websocket,
11
+ response,
12
+ )
13
+
14
+ from rasa.core.channels import UserMessage
15
+ from rasa.core.channels.voice_ready.utils import CallParameters
16
+ from rasa.core.channels.voice_stream.audio_bytes import RasaAudioBytes
17
+ from rasa.core.channels.voice_stream.call_state import (
18
+ call_state,
19
+ )
20
+ from rasa.core.channels.voice_stream.tts.tts_engine import TTSEngine
21
+ from rasa.core.channels.voice_stream.voice_channel import (
22
+ ContinueConversationAction,
23
+ EndConversationAction,
24
+ NewAudioAction,
25
+ VoiceChannelAction,
26
+ VoiceInputChannel,
27
+ VoiceOutputChannel,
28
+ )
29
+
30
+ """
31
+ Genesys throws a rate limit error with too many audio messages.
32
+ To avoid this, we buffer the audio messages and send them in chunks.
33
+
34
+ - global.inbound.binary.average.rate.per.second: 5
35
+ The allowed average rate per second of inbound binary data
36
+
37
+ - global.inbound.binary.max: 25
38
+ The maximum number of inbound binary data messages
39
+ that can be sent instantaneously
40
+
41
+ https://developer.genesys.cloud/organization/organization/limits#audiohook
42
+
43
+ The maximum binary message size is not mentioned
44
+ in the documentation but observed in their example app
45
+ https://github.com/GenesysCloudBlueprints/audioconnector-server-reference-implementation
46
+ """
47
+ MAXIMUM_BINARY_MESSAGE_SIZE = 64000 # 64KB
48
+ logger = structlog.get_logger(__name__)
49
+
50
+
51
+ def map_call_params(data: Dict[Text, Any]) -> CallParameters:
52
+ """Map the twilio stream parameters to the CallParameters dataclass."""
53
+ parameters = data["parameters"]
54
+ participant = parameters["participant"]
55
+ # sent as {"ani": "tel:+491604697810"}
56
+ ani = participant.get("ani", "")
57
+ user_phone = ani.split(":")[-1] if ani else ""
58
+
59
+ return CallParameters(
60
+ call_id=parameters.get("conversationId", ""),
61
+ user_phone=user_phone,
62
+ bot_phone=participant.get("dnis", ""),
63
+ )
64
+
65
+
66
+ class GenesysOutputChannel(VoiceOutputChannel):
67
+ @classmethod
68
+ def name(cls) -> str:
69
+ return "genesys"
70
+
71
+ async def send_audio_bytes(
72
+ self, recipient_id: str, audio_bytes: RasaAudioBytes
73
+ ) -> None:
74
+ await self.voice_websocket.send(audio_bytes)
75
+
76
+ async def send_marker_message(self, recipient_id: str) -> None:
77
+ """
78
+ Send a message that marks positions in the audio stream.
79
+ Genesys does not support this feature, so we do nothing here.
80
+ """
81
+ pass
82
+
83
+
84
+ class GenesysInputChannel(VoiceInputChannel):
85
+ @classmethod
86
+ def name(cls) -> str:
87
+ return "genesys"
88
+
89
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
90
+ super().__init__(*args, **kwargs)
91
+
92
+ def _ensure_channel_data_initialized(self) -> None:
93
+ """Initialize Genesys-specific channel data if not already present.
94
+
95
+ Genesys requires the server and client each maintain a
96
+ monotonically increasing message sequence number.
97
+ """
98
+ if "server_sequence_number" not in call_state.channel_data:
99
+ call_state.channel_data["server_sequence_number"] = 0
100
+ if "client_sequence_number" not in call_state.channel_data:
101
+ call_state.channel_data["client_sequence_number"] = 0
102
+
103
+ def _get_next_sequence(self) -> int:
104
+ """
105
+ Get the next message sequence number
106
+ Rasa == Server
107
+ Genesys == Client
108
+
109
+ Genesys requires the server and client each maintain a
110
+ monotonically increasing message sequence number.
111
+ """
112
+ self._ensure_channel_data_initialized()
113
+ call_state.channel_data["server_sequence_number"] += 1
114
+ return call_state.channel_data["server_sequence_number"]
115
+
116
+ def _get_last_client_sequence(self) -> int:
117
+ """Get the last client(Genesys) sequence number."""
118
+ self._ensure_channel_data_initialized()
119
+ return call_state.channel_data["client_sequence_number"]
120
+
121
+ def _update_client_sequence(self, seq: int) -> None:
122
+ """Update the client(Genesys) sequence number."""
123
+ self._ensure_channel_data_initialized()
124
+
125
+ if seq - call_state.channel_data["client_sequence_number"] != 1:
126
+ logger.warning(
127
+ "genesys.update_client_sequence.sequence_gap",
128
+ received_seq=seq,
129
+ last_seq=call_state.channel_data["client_sequence_number"],
130
+ )
131
+ call_state.channel_data["client_sequence_number"] = seq
132
+
133
+ def channel_bytes_to_rasa_audio_bytes(self, input_bytes: bytes) -> RasaAudioBytes:
134
+ return RasaAudioBytes(input_bytes)
135
+
136
+ async def collect_call_parameters(
137
+ self, channel_websocket: Websocket
138
+ ) -> Optional[CallParameters]:
139
+ """Call Parameters are collected during the open event."""
140
+ async for message in channel_websocket:
141
+ data = json.loads(message)
142
+ self._update_client_sequence(data["seq"])
143
+ if data.get("type") == "open":
144
+ call_params = await self.handle_open(channel_websocket, data)
145
+ return call_params
146
+ else:
147
+ logger.error("genesys.receive.unexpected_initial_message", message=data)
148
+
149
+ return None
150
+
151
+ def map_input_message(
152
+ self,
153
+ message: Any,
154
+ ws: Websocket,
155
+ ) -> VoiceChannelAction:
156
+ # if message is binary, it's audio
157
+ if isinstance(message, bytes):
158
+ return NewAudioAction(self.channel_bytes_to_rasa_audio_bytes(message))
159
+ else:
160
+ # process text message
161
+ data = json.loads(message)
162
+ self._update_client_sequence(data["seq"])
163
+ msg_type = data.get("type")
164
+ if msg_type == "close":
165
+ logger.info("genesys.handle_close", message=data)
166
+ self.handle_close(ws, data)
167
+ return EndConversationAction()
168
+ elif msg_type == "ping":
169
+ logger.info("genesys.handle_ping", message=data)
170
+ self.handle_ping(ws, data)
171
+ elif msg_type == "playback_started":
172
+ logger.debug("genesys.handle_playback_started", message=data)
173
+ call_state.is_bot_speaking = True # type: ignore[attr-defined]
174
+ elif msg_type == "playback_completed":
175
+ logger.debug("genesys.handle_playback_completed", message=data)
176
+ call_state.is_bot_speaking = False # type: ignore[attr-defined]
177
+ if call_state.should_hangup:
178
+ logger.info("genesys.hangup")
179
+ self.disconnect(ws, data)
180
+ # the conversation should continue until
181
+ # we receive a close message from Genesys
182
+ elif msg_type == "dtmf":
183
+ logger.info("genesys.handle_dtmf", message=data)
184
+ elif msg_type == "error":
185
+ logger.warning("genesys.handle_error", message=data)
186
+ else:
187
+ logger.warning("genesys.map_input_message.unknown_type", message=data)
188
+
189
+ return ContinueConversationAction()
190
+
191
+ def create_output_channel(
192
+ self, voice_websocket: Websocket, tts_engine: TTSEngine
193
+ ) -> VoiceOutputChannel:
194
+ return GenesysOutputChannel(
195
+ voice_websocket,
196
+ tts_engine,
197
+ self.tts_cache,
198
+ min_buffer_size=MAXIMUM_BINARY_MESSAGE_SIZE // 2,
199
+ )
200
+
201
+ async def handle_open(self, ws: Websocket, message: dict) -> CallParameters:
202
+ """Handle initial open transaction from Genesys."""
203
+ call_parameters = map_call_params(message)
204
+ params = message["parameters"]
205
+ media_options = params.get("media", [])
206
+
207
+ # Send opened response
208
+ if media_options:
209
+ logger.info("genesys.handle_open", media_parameter=media_options[0])
210
+ response = {
211
+ "version": "2",
212
+ "type": "opened",
213
+ "seq": self._get_next_sequence(),
214
+ "clientseq": self._get_last_client_sequence(),
215
+ "id": message.get("id"),
216
+ "parameters": {"startPaused": False, "media": [media_options[0]]},
217
+ }
218
+ logger.debug("genesys.handle_open.opened", response=response)
219
+ await ws.send(json.dumps(response))
220
+ else:
221
+ logger.warning(
222
+ "genesys.handle_open.no_media_formats", client_message=message
223
+ )
224
+ return call_parameters
225
+
226
+ def handle_ping(self, ws: Websocket, message: dict) -> None:
227
+ """Handle ping message from Genesys."""
228
+ response = {
229
+ "version": "2",
230
+ "type": "pong",
231
+ "seq": self._get_next_sequence(),
232
+ "clientseq": message.get("seq"),
233
+ "id": message.get("id"),
234
+ "parameters": {},
235
+ }
236
+ logger.debug("genesys.handle_ping.pong", response=response)
237
+ _schedule_ws_task(ws.send(json.dumps(response)))
238
+
239
+ def handle_close(self, ws: Websocket, message: dict) -> None:
240
+ """Handle close message from Genesys."""
241
+ response = {
242
+ "version": "2",
243
+ "type": "closed",
244
+ "seq": self._get_next_sequence(),
245
+ "clientseq": self._get_last_client_sequence(),
246
+ "id": message.get("id"),
247
+ "parameters": message.get("parameters", {}),
248
+ }
249
+ logger.debug("genesys.handle_close.closed", response=response)
250
+
251
+ _schedule_ws_task(ws.send(json.dumps(response)))
252
+
253
+ def disconnect(self, ws: Websocket, data: dict) -> None:
254
+ """
255
+ Send disconnect message to Genesys.
256
+
257
+ https://developer.genesys.cloud/devapps/audiohook/protocol-reference#disconnect
258
+ It should be used to hangup the call.
259
+ Genesys will respond with a "close" message to us
260
+ that is handled by the handle_close method.
261
+ """
262
+ message = {
263
+ "version": "2",
264
+ "type": "disconnect",
265
+ "seq": self._get_next_sequence(),
266
+ "clientseq": self._get_last_client_sequence(),
267
+ "id": data.get("id"),
268
+ "parameters": {
269
+ "reason": "completed",
270
+ # arbitrary values can be sent here
271
+ },
272
+ }
273
+ logger.debug("genesys.disconnect", message=message)
274
+ _schedule_ws_task(ws.send(json.dumps(message)))
275
+
276
+ def blueprint(
277
+ self, on_new_message: Callable[[UserMessage], Awaitable[Any]]
278
+ ) -> Blueprint:
279
+ """Defines a Sanic blueprint for the voice input channel."""
280
+ blueprint = Blueprint("genesys", __name__)
281
+
282
+ @blueprint.route("/", methods=["GET"])
283
+ async def health(_: Request) -> HTTPResponse:
284
+ return response.json({"status": "ok"})
285
+
286
+ @blueprint.websocket("/websocket") # type: ignore[misc]
287
+ async def receive(request: Request, ws: Websocket) -> None:
288
+ logger.debug(
289
+ "genesys.receive",
290
+ audiohook_session_id=request.headers.get("audiohook-session-id"),
291
+ )
292
+ # validate required headers
293
+ required_headers = [
294
+ "audiohook-organization-id",
295
+ "audiohook-correlation-id",
296
+ "audiohook-session-id",
297
+ "x-api-key",
298
+ ]
299
+
300
+ for header in required_headers:
301
+ if header not in request.headers:
302
+ await ws.close(1008, f"Missing required header: {header}")
303
+ return
304
+
305
+ # TODO: validate API key header
306
+ # process audio streaming
307
+ logger.info("genesys.receive", message="Starting audio streaming")
308
+ await self.run_audio_streaming(on_new_message, ws)
309
+
310
+ return blueprint
311
+
312
+
313
+ def _schedule_ws_task(coro: Awaitable[Any]) -> None:
314
+ """Helper function to schedule a coroutine in the event loop.
315
+
316
+ Args:
317
+ coro: The coroutine to schedule
318
+ """
319
+ loop = asyncio.get_running_loop()
320
+ loop.call_soon_threadsafe(lambda: loop.create_task(coro))
@@ -1,6 +1,6 @@
1
1
  import os
2
- from typing import AsyncIterator, Dict, Optional
3
2
  from dataclasses import dataclass
3
+ from typing import AsyncIterator, Dict, Optional
4
4
 
5
5
  import aiohttp
6
6
  import structlog
@@ -15,13 +15,13 @@ from rasa.core.channels.voice_stream.tts.tts_engine import (
15
15
  from rasa.shared.constants import AZURE_SPEECH_API_KEY_ENV_VAR
16
16
  from rasa.shared.exceptions import ConnectionException
17
17
 
18
-
19
18
  structlogger = structlog.get_logger()
20
19
 
21
20
 
22
21
  @dataclass
23
22
  class AzureTTSConfig(TTSEngineConfig):
24
23
  speech_region: Optional[str] = None
24
+ endpoint: Optional[str] = None
25
25
 
26
26
 
27
27
  class AzureTTS(TTSEngine[AzureTTSConfig]):
@@ -77,12 +77,19 @@ class AzureTTS(TTSEngine[AzureTTSConfig]):
77
77
 
78
78
  @staticmethod
79
79
  def get_tts_endpoint(config: AzureTTSConfig) -> str:
80
- return f"https://{config.speech_region}.tts.speech.microsoft.com/cognitiveservices/v1"
80
+ if config.endpoint is not None:
81
+ return config.endpoint
82
+ else:
83
+ return (
84
+ f"https://{config.speech_region}.tts.speech.microsoft.com/"
85
+ f"cognitiveservices/v1"
86
+ )
81
87
 
82
88
  @staticmethod
83
89
  def create_request_body(text: str, conf: AzureTTSConfig) -> str:
84
90
  return f"""
85
- <speak version='1.0' xml:lang='{conf.language}'>
91
+ <speak version='1.0' xml:lang='{conf.language}' xmlns:mstts='http://www.w3.org/2001/mstts'
92
+ xmlns='http://www.w3.org/2001/10/synthesis'>
86
93
  <voice xml:lang='{conf.language}' name='{conf.voice}'>
87
94
  {text}
88
95
  </voice>
@@ -98,7 +105,8 @@ class AzureTTS(TTSEngine[AzureTTSConfig]):
98
105
  language="en-US",
99
106
  voice="en-US-JennyNeural",
100
107
  timeout=10,
101
- speech_region="germanywestcentral",
108
+ speech_region="eastus",
109
+ endpoint=None,
102
110
  )
103
111
 
104
112
  @classmethod
@@ -1,16 +1,19 @@
1
+ import base64
2
+ import json
3
+ import os
1
4
  from dataclasses import dataclass
2
5
  from typing import AsyncIterator, Dict, Optional
3
- import os
6
+
4
7
  import aiohttp
5
8
  import structlog
6
9
  from aiohttp import ClientConnectorError, ClientTimeout
7
10
 
11
+ from rasa.core.channels.voice_stream.audio_bytes import HERTZ, RasaAudioBytes
8
12
  from rasa.core.channels.voice_stream.tts.tts_engine import (
13
+ TTSEngine,
9
14
  TTSEngineConfig,
15
+ TTSError,
10
16
  )
11
-
12
- from rasa.core.channels.voice_stream.audio_bytes import HERTZ, RasaAudioBytes
13
- from rasa.core.channels.voice_stream.tts.tts_engine import TTSEngine, TTSError
14
17
  from rasa.shared.constants import CARTESIA_API_KEY_ENV_VAR
15
18
  from rasa.shared.exceptions import ConnectionException
16
19
 
@@ -21,6 +24,7 @@ structlogger = structlog.get_logger()
21
24
  class CartesiaTTSConfig(TTSEngineConfig):
22
25
  model_id: Optional[str] = None
23
26
  version: Optional[str] = None
27
+ endpoint: Optional[str] = None
24
28
 
25
29
 
26
30
  class CartesiaTTS(TTSEngine[CartesiaTTSConfig]):
@@ -35,11 +39,6 @@ class CartesiaTTS(TTSEngine[CartesiaTTSConfig]):
35
39
  if self.__class__.session is None or self.__class__.session.closed:
36
40
  self.__class__.session = aiohttp.ClientSession(timeout=timeout)
37
41
 
38
- @staticmethod
39
- def get_tts_endpoint() -> str:
40
- """Create the endpoint string for cartesia."""
41
- return "https://api.cartesia.ai/tts/bytes"
42
-
43
42
  @staticmethod
44
43
  def get_request_body(text: str, config: CartesiaTTSConfig) -> Dict:
45
44
  """Create the request body for cartesia."""
@@ -76,7 +75,7 @@ class CartesiaTTS(TTSEngine[CartesiaTTSConfig]):
76
75
  config = self.config.merge(config)
77
76
  payload = self.get_request_body(text, config)
78
77
  headers = self.get_request_headers(config)
79
- url = self.get_tts_endpoint()
78
+ url = self.config.endpoint
80
79
  if self.session is None:
81
80
  raise ConnectionException("Client session is not initialized")
82
81
  try:
@@ -84,16 +83,36 @@ class CartesiaTTS(TTSEngine[CartesiaTTSConfig]):
84
83
  url, headers=headers, json=payload, chunked=True
85
84
  ) as response:
86
85
  if 200 <= response.status < 300:
87
- async for data in response.content.iter_chunked(1024):
88
- yield self.engine_bytes_to_rasa_audio_bytes(data)
86
+ async for chunk in response.content:
87
+ # we are looking for chunks in the response that look like
88
+ # b"data: {..., data: <base64 encoded audio bytes> ...}"
89
+ # and extract the audio bytes from that
90
+ if chunk.startswith(b"data: "):
91
+ json_bytes = chunk[5:-1]
92
+ json_data = json.loads(json_bytes.decode())
93
+ if "data" in json_data:
94
+ base64_encoded_bytes = json_data["data"]
95
+ channel_bytes = base64.b64decode(base64_encoded_bytes)
96
+ yield self.engine_bytes_to_rasa_audio_bytes(
97
+ channel_bytes
98
+ )
89
99
  return
100
+ elif response.status == 401:
101
+ structlogger.error(
102
+ "cartesia.synthesize.rest.unauthorized",
103
+ status_code=response.status,
104
+ )
105
+ raise TTSError(
106
+ "Unauthorized. Please make sure you have the correct API key."
107
+ )
90
108
  else:
109
+ response_text = await response.text()
91
110
  structlogger.error(
92
111
  "cartesia.synthesize.rest.failed",
93
112
  status_code=response.status,
94
- msg=response.text(),
113
+ msg=response_text,
95
114
  )
96
- raise TTSError(f"TTS failed: {response.text()}")
115
+ raise TTSError(f"TTS failed: {response_text}")
97
116
  except ClientConnectorError as e:
98
117
  raise TTSError(e)
99
118
  except TimeoutError as e:
@@ -111,6 +130,7 @@ class CartesiaTTS(TTSEngine[CartesiaTTSConfig]):
111
130
  timeout=10,
112
131
  model_id="sonic-english",
113
132
  version="2024-06-10",
133
+ endpoint="https://api.cartesia.ai/tts/sse",
114
134
  )
115
135
 
116
136
  @classmethod
@@ -1,6 +1,7 @@
1
- from typing import Optional
2
- from collections import OrderedDict
3
1
  import logging
2
+ from collections import OrderedDict
3
+ from typing import Optional
4
+
4
5
  from rasa.core.channels.voice_stream.audio_bytes import RasaAudioBytes
5
6
 
6
7
  logger = logging.getLogger(__name__)
@@ -1,5 +1,5 @@
1
- from typing import AsyncIterator, Dict, Generic, Optional, Tuple, Type, TypeVar
2
1
  from dataclasses import dataclass
2
+ from typing import AsyncIterator, Dict, Generic, Optional, Tuple, Type, TypeVar
3
3
 
4
4
  from rasa.core.channels.voice_stream.audio_bytes import RasaAudioBytes
5
5
  from rasa.core.channels.voice_stream.util import MergeableConfig
@@ -1,24 +1,27 @@
1
1
  import base64
2
2
  import json
3
3
  import uuid
4
-
5
- import structlog
6
4
  from typing import Any, Awaitable, Callable, Dict, Optional, Text, Tuple
7
5
 
8
- from sanic import Blueprint, HTTPResponse, Request, response
9
- from sanic import Websocket # type: ignore
10
-
6
+ import structlog
7
+ from sanic import ( # type: ignore[attr-defined]
8
+ Blueprint,
9
+ HTTPResponse,
10
+ Request,
11
+ Websocket,
12
+ response,
13
+ )
11
14
 
12
15
  from rasa.core.channels import UserMessage
13
16
  from rasa.core.channels.voice_ready.utils import CallParameters
17
+ from rasa.core.channels.voice_stream.audio_bytes import RasaAudioBytes
14
18
  from rasa.core.channels.voice_stream.call_state import call_state
15
19
  from rasa.core.channels.voice_stream.tts.tts_engine import TTSEngine
16
- from rasa.core.channels.voice_stream.audio_bytes import RasaAudioBytes
17
20
  from rasa.core.channels.voice_stream.voice_channel import (
21
+ ContinueConversationAction,
18
22
  EndConversationAction,
19
23
  NewAudioAction,
20
24
  VoiceChannelAction,
21
- ContinueConversationAction,
22
25
  VoiceInputChannel,
23
26
  VoiceOutputChannel,
24
27
  )
@@ -95,6 +98,7 @@ class TwilioMediaStreamsInputChannel(VoiceInputChannel):
95
98
  def map_input_message(
96
99
  self,
97
100
  message: Any,
101
+ ws: Websocket,
98
102
  ) -> VoiceChannelAction:
99
103
  data = json.loads(message)
100
104
  if data["event"] == "media":
@@ -139,7 +143,7 @@ class TwilioMediaStreamsInputChannel(VoiceInputChannel):
139
143
  def blueprint(
140
144
  self, on_new_message: Callable[[UserMessage], Awaitable[Any]]
141
145
  ) -> Blueprint:
142
- """Defines a Sanic bluelogger.debug."""
146
+ """Defines a Sanic blueprint for the voice input channel."""
143
147
  blueprint = Blueprint("twilio_media_streams", __name__)
144
148
 
145
149
  @blueprint.route("/", methods=["GET"])
@@ -1,5 +1,5 @@
1
- import wave
2
1
  import audioop
2
+ import wave
3
3
  from dataclasses import asdict, dataclass
4
4
  from typing import Optional, Type, TypeVar
5
5