rasa-pro 3.11.0__py3-none-any.whl → 3.11.0a1__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 (220) hide show
  1. README.md +396 -17
  2. rasa/__main__.py +15 -31
  3. rasa/api.py +1 -5
  4. rasa/cli/arguments/default_arguments.py +2 -1
  5. rasa/cli/arguments/shell.py +1 -5
  6. rasa/cli/arguments/train.py +0 -14
  7. rasa/cli/e2e_test.py +1 -1
  8. rasa/cli/evaluate.py +8 -8
  9. rasa/cli/inspect.py +7 -15
  10. rasa/cli/interactive.py +0 -1
  11. rasa/cli/llm_fine_tuning.py +1 -1
  12. rasa/cli/project_templates/calm/config.yml +7 -5
  13. rasa/cli/project_templates/calm/endpoints.yml +2 -15
  14. rasa/cli/project_templates/tutorial/config.yml +5 -8
  15. rasa/cli/project_templates/tutorial/data/flows.yml +1 -1
  16. rasa/cli/project_templates/tutorial/data/patterns.yml +0 -5
  17. rasa/cli/project_templates/tutorial/domain.yml +0 -14
  18. rasa/cli/project_templates/tutorial/endpoints.yml +0 -5
  19. rasa/cli/run.py +1 -1
  20. rasa/cli/scaffold.py +2 -4
  21. rasa/cli/studio/studio.py +8 -18
  22. rasa/cli/studio/upload.py +15 -0
  23. rasa/cli/train.py +0 -3
  24. rasa/cli/utils.py +1 -6
  25. rasa/cli/x.py +8 -8
  26. rasa/constants.py +1 -3
  27. rasa/core/actions/action.py +33 -75
  28. rasa/core/actions/e2e_stub_custom_action_executor.py +1 -5
  29. rasa/core/actions/http_custom_action_executor.py +0 -4
  30. rasa/core/channels/__init__.py +0 -2
  31. rasa/core/channels/channel.py +0 -20
  32. rasa/core/channels/development_inspector.py +3 -10
  33. rasa/core/channels/inspector/dist/assets/{arc-bc141fb2.js → arc-86942a71.js} +1 -1
  34. rasa/core/channels/inspector/dist/assets/{c4Diagram-d0fbc5ce-be2db283.js → c4Diagram-d0fbc5ce-b0290676.js} +1 -1
  35. rasa/core/channels/inspector/dist/assets/{classDiagram-936ed81e-55366915.js → classDiagram-936ed81e-f6405f6e.js} +1 -1
  36. rasa/core/channels/inspector/dist/assets/{classDiagram-v2-c3cb15f1-bb529518.js → classDiagram-v2-c3cb15f1-ef61ac77.js} +1 -1
  37. rasa/core/channels/inspector/dist/assets/{createText-62fc7601-b0ec81d6.js → createText-62fc7601-f0411e58.js} +1 -1
  38. rasa/core/channels/inspector/dist/assets/{edges-f2ad444c-6166330c.js → edges-f2ad444c-7dcc4f3b.js} +1 -1
  39. rasa/core/channels/inspector/dist/assets/{erDiagram-9d236eb7-5ccc6a8e.js → erDiagram-9d236eb7-e0c092d7.js} +1 -1
  40. rasa/core/channels/inspector/dist/assets/{flowDb-1972c806-fca3bfe4.js → flowDb-1972c806-fba2e3ce.js} +1 -1
  41. rasa/core/channels/inspector/dist/assets/{flowDiagram-7ea5b25a-4739080f.js → flowDiagram-7ea5b25a-7a70b71a.js} +1 -1
  42. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-855bc5b3-24a5f41a.js +1 -0
  43. rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-abe16c3d-7c1b0e0f.js → flowchart-elk-definition-abe16c3d-00a59b68.js} +1 -1
  44. rasa/core/channels/inspector/dist/assets/{ganttDiagram-9b5ea136-772fd050.js → ganttDiagram-9b5ea136-293c91fa.js} +1 -1
  45. rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-99d0ae7c-8eae1dc9.js → gitGraphDiagram-99d0ae7c-07b2d68c.js} +1 -1
  46. rasa/core/channels/inspector/dist/assets/{index-2c4b9a3b-f55afcdf.js → index-2c4b9a3b-bc959fbd.js} +1 -1
  47. rasa/core/channels/inspector/dist/assets/{index-e7cef9de.js → index-3a8a5a28.js} +143 -143
  48. rasa/core/channels/inspector/dist/assets/{infoDiagram-736b4530-124d4a14.js → infoDiagram-736b4530-4a350f72.js} +1 -1
  49. rasa/core/channels/inspector/dist/assets/{journeyDiagram-df861f2b-7c4fae44.js → journeyDiagram-df861f2b-af464fb7.js} +1 -1
  50. rasa/core/channels/inspector/dist/assets/{layout-b9885fb6.js → layout-0071f036.js} +1 -1
  51. rasa/core/channels/inspector/dist/assets/{line-7c59abb6.js → line-2f73cc83.js} +1 -1
  52. rasa/core/channels/inspector/dist/assets/{linear-4776f780.js → linear-f014b4cc.js} +1 -1
  53. rasa/core/channels/inspector/dist/assets/{mindmap-definition-beec6740-2332c46c.js → mindmap-definition-beec6740-d2426fb6.js} +1 -1
  54. rasa/core/channels/inspector/dist/assets/{pieDiagram-dbbf0591-8fb39303.js → pieDiagram-dbbf0591-776f01a2.js} +1 -1
  55. rasa/core/channels/inspector/dist/assets/{quadrantDiagram-4d7f4fd6-3c7180a2.js → quadrantDiagram-4d7f4fd6-82e00b57.js} +1 -1
  56. rasa/core/channels/inspector/dist/assets/{requirementDiagram-6fc4c22a-e910bcb8.js → requirementDiagram-6fc4c22a-ea13c6bb.js} +1 -1
  57. rasa/core/channels/inspector/dist/assets/{sankeyDiagram-8f13d901-ead16c89.js → sankeyDiagram-8f13d901-1feca7e9.js} +1 -1
  58. rasa/core/channels/inspector/dist/assets/{sequenceDiagram-b655622a-29a02a19.js → sequenceDiagram-b655622a-070c61d2.js} +1 -1
  59. rasa/core/channels/inspector/dist/assets/{stateDiagram-59f0c015-042b3137.js → stateDiagram-59f0c015-24f46263.js} +1 -1
  60. rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-2b26beab-2178c0f3.js → stateDiagram-v2-2b26beab-c9056051.js} +1 -1
  61. rasa/core/channels/inspector/dist/assets/{styles-080da4f6-23ffa4fc.js → styles-080da4f6-08abc34a.js} +1 -1
  62. rasa/core/channels/inspector/dist/assets/{styles-3dcbcfbf-94f59763.js → styles-3dcbcfbf-bc74c25a.js} +1 -1
  63. rasa/core/channels/inspector/dist/assets/{styles-9c745c82-78a6bebc.js → styles-9c745c82-4e5d66de.js} +1 -1
  64. rasa/core/channels/inspector/dist/assets/{svgDrawCommon-4835440b-eae2a6f6.js → svgDrawCommon-4835440b-849c4517.js} +1 -1
  65. rasa/core/channels/inspector/dist/assets/{timeline-definition-5b62e21b-5c968d92.js → timeline-definition-5b62e21b-d0fb1598.js} +1 -1
  66. rasa/core/channels/inspector/dist/assets/{xychartDiagram-2b33534f-fd3db0d5.js → xychartDiagram-2b33534f-04d115e2.js} +1 -1
  67. rasa/core/channels/inspector/dist/index.html +1 -1
  68. rasa/core/channels/inspector/src/App.tsx +1 -1
  69. rasa/core/channels/inspector/src/components/LoadingSpinner.tsx +3 -6
  70. rasa/core/channels/socketio.py +2 -7
  71. rasa/core/channels/telegram.py +1 -1
  72. rasa/core/channels/twilio.py +1 -1
  73. rasa/core/channels/voice_ready/audiocodes.py +4 -15
  74. rasa/core/channels/voice_ready/jambonz.py +4 -15
  75. rasa/core/channels/voice_ready/twilio_voice.py +21 -6
  76. rasa/core/channels/voice_ready/utils.py +5 -6
  77. rasa/core/channels/voice_stream/asr/asr_engine.py +1 -19
  78. rasa/core/channels/voice_stream/asr/asr_event.py +0 -5
  79. rasa/core/channels/voice_stream/asr/deepgram.py +15 -28
  80. rasa/core/channels/voice_stream/audio_bytes.py +0 -1
  81. rasa/core/channels/voice_stream/tts/azure.py +3 -9
  82. rasa/core/channels/voice_stream/tts/cartesia.py +8 -12
  83. rasa/core/channels/voice_stream/tts/tts_engine.py +1 -11
  84. rasa/core/channels/voice_stream/twilio_media_streams.py +19 -28
  85. rasa/core/channels/voice_stream/util.py +4 -4
  86. rasa/core/channels/voice_stream/voice_channel.py +42 -222
  87. rasa/core/featurizers/single_state_featurizer.py +1 -22
  88. rasa/core/featurizers/tracker_featurizers.py +18 -115
  89. rasa/core/information_retrieval/qdrant.py +0 -1
  90. rasa/core/nlg/contextual_response_rephraser.py +25 -44
  91. rasa/core/persistor.py +34 -191
  92. rasa/core/policies/enterprise_search_policy.py +60 -119
  93. rasa/core/policies/flows/flow_executor.py +4 -7
  94. rasa/core/policies/intentless_policy.py +22 -82
  95. rasa/core/policies/ted_policy.py +33 -58
  96. rasa/core/policies/unexpected_intent_policy.py +7 -15
  97. rasa/core/processor.py +13 -89
  98. rasa/core/run.py +2 -2
  99. rasa/core/training/interactive.py +35 -34
  100. rasa/core/utils.py +22 -58
  101. rasa/dialogue_understanding/coexistence/llm_based_router.py +12 -39
  102. rasa/dialogue_understanding/commands/__init__.py +0 -4
  103. rasa/dialogue_understanding/commands/change_flow_command.py +0 -6
  104. rasa/dialogue_understanding/commands/utils.py +0 -5
  105. rasa/dialogue_understanding/generator/constants.py +0 -2
  106. rasa/dialogue_understanding/generator/flow_retrieval.py +4 -49
  107. rasa/dialogue_understanding/generator/llm_based_command_generator.py +23 -37
  108. rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +10 -57
  109. rasa/dialogue_understanding/generator/nlu_command_adapter.py +1 -19
  110. rasa/dialogue_understanding/generator/single_step/command_prompt_template.jinja2 +0 -3
  111. rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +10 -90
  112. rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +0 -53
  113. rasa/dialogue_understanding/processor/command_processor.py +1 -21
  114. rasa/e2e_test/assertions.py +16 -133
  115. rasa/e2e_test/assertions_schema.yml +0 -23
  116. rasa/e2e_test/e2e_test_case.py +6 -85
  117. rasa/e2e_test/e2e_test_runner.py +4 -6
  118. rasa/e2e_test/utils/io.py +1 -3
  119. rasa/engine/loader.py +0 -12
  120. rasa/engine/validation.py +11 -541
  121. rasa/keys +1 -0
  122. rasa/llm_fine_tuning/notebooks/unsloth_finetuning.ipynb +407 -0
  123. rasa/model_training.py +7 -29
  124. rasa/nlu/classifiers/diet_classifier.py +25 -38
  125. rasa/nlu/classifiers/logistic_regression_classifier.py +9 -22
  126. rasa/nlu/classifiers/sklearn_intent_classifier.py +16 -37
  127. rasa/nlu/extractors/crf_entity_extractor.py +50 -93
  128. rasa/nlu/featurizers/sparse_featurizer/count_vectors_featurizer.py +16 -45
  129. rasa/nlu/featurizers/sparse_featurizer/lexical_syntactic_featurizer.py +17 -52
  130. rasa/nlu/featurizers/sparse_featurizer/regex_featurizer.py +3 -5
  131. rasa/nlu/tokenizers/whitespace_tokenizer.py +14 -3
  132. rasa/server.py +1 -3
  133. rasa/shared/constants.py +0 -61
  134. rasa/shared/core/constants.py +0 -9
  135. rasa/shared/core/domain.py +5 -8
  136. rasa/shared/core/flows/flow.py +0 -5
  137. rasa/shared/core/flows/flows_list.py +1 -5
  138. rasa/shared/core/flows/flows_yaml_schema.json +0 -10
  139. rasa/shared/core/flows/validation.py +0 -96
  140. rasa/shared/core/flows/yaml_flows_io.py +4 -13
  141. rasa/shared/core/slots.py +0 -5
  142. rasa/shared/importers/importer.py +2 -19
  143. rasa/shared/importers/rasa.py +1 -5
  144. rasa/shared/nlu/training_data/features.py +2 -120
  145. rasa/shared/nlu/training_data/formats/rasa_yaml.py +3 -18
  146. rasa/shared/providers/_configs/azure_openai_client_config.py +3 -5
  147. rasa/shared/providers/_configs/openai_client_config.py +1 -1
  148. rasa/shared/providers/_configs/self_hosted_llm_client_config.py +0 -1
  149. rasa/shared/providers/_configs/utils.py +0 -16
  150. rasa/shared/providers/embedding/_base_litellm_embedding_client.py +29 -18
  151. rasa/shared/providers/embedding/azure_openai_embedding_client.py +21 -54
  152. rasa/shared/providers/embedding/default_litellm_embedding_client.py +0 -24
  153. rasa/shared/providers/llm/_base_litellm_client.py +31 -63
  154. rasa/shared/providers/llm/azure_openai_llm_client.py +29 -50
  155. rasa/shared/providers/llm/default_litellm_llm_client.py +0 -24
  156. rasa/shared/providers/llm/self_hosted_llm_client.py +29 -17
  157. rasa/shared/providers/mappings.py +0 -19
  158. rasa/shared/utils/common.py +2 -37
  159. rasa/shared/utils/io.py +6 -28
  160. rasa/shared/utils/llm.py +46 -353
  161. rasa/shared/utils/yaml.py +82 -181
  162. rasa/studio/auth.py +5 -3
  163. rasa/studio/config.py +4 -13
  164. rasa/studio/constants.py +0 -1
  165. rasa/studio/data_handler.py +4 -13
  166. rasa/studio/upload.py +80 -175
  167. rasa/telemetry.py +17 -94
  168. rasa/tracing/config.py +1 -3
  169. rasa/tracing/instrumentation/attribute_extractors.py +17 -94
  170. rasa/tracing/instrumentation/instrumentation.py +0 -121
  171. rasa/utils/common.py +0 -5
  172. rasa/utils/endpoints.py +1 -27
  173. rasa/utils/io.py +81 -7
  174. rasa/utils/log_utils.py +2 -9
  175. rasa/utils/tensorflow/model_data.py +193 -2
  176. rasa/validator.py +4 -110
  177. rasa/version.py +1 -1
  178. rasa_pro-3.11.0a1.dist-info/METADATA +576 -0
  179. {rasa_pro-3.11.0.dist-info → rasa_pro-3.11.0a1.dist-info}/RECORD +182 -216
  180. rasa/core/actions/action_repeat_bot_messages.py +0 -89
  181. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-855bc5b3-736177bf.js +0 -1
  182. rasa/core/channels/inspector/src/helpers/audiostream.ts +0 -165
  183. rasa/core/channels/voice_stream/asr/azure.py +0 -129
  184. rasa/core/channels/voice_stream/browser_audio.py +0 -107
  185. rasa/core/channels/voice_stream/call_state.py +0 -23
  186. rasa/dialogue_understanding/commands/repeat_bot_messages_command.py +0 -60
  187. rasa/dialogue_understanding/commands/user_silence_command.py +0 -59
  188. rasa/dialogue_understanding/patterns/repeat.py +0 -37
  189. rasa/dialogue_understanding/patterns/user_silence.py +0 -37
  190. rasa/model_manager/__init__.py +0 -0
  191. rasa/model_manager/config.py +0 -40
  192. rasa/model_manager/model_api.py +0 -559
  193. rasa/model_manager/runner_service.py +0 -286
  194. rasa/model_manager/socket_bridge.py +0 -146
  195. rasa/model_manager/studio_jwt_auth.py +0 -86
  196. rasa/model_manager/trainer_service.py +0 -325
  197. rasa/model_manager/utils.py +0 -87
  198. rasa/model_manager/warm_rasa_process.py +0 -187
  199. rasa/model_service.py +0 -112
  200. rasa/shared/core/flows/utils.py +0 -39
  201. rasa/shared/providers/_configs/litellm_router_client_config.py +0 -220
  202. rasa/shared/providers/_configs/model_group_config.py +0 -167
  203. rasa/shared/providers/_configs/rasa_llm_client_config.py +0 -73
  204. rasa/shared/providers/_utils.py +0 -79
  205. rasa/shared/providers/embedding/litellm_router_embedding_client.py +0 -135
  206. rasa/shared/providers/llm/litellm_router_llm_client.py +0 -182
  207. rasa/shared/providers/llm/rasa_llm_client.py +0 -112
  208. rasa/shared/providers/router/__init__.py +0 -0
  209. rasa/shared/providers/router/_base_litellm_router_client.py +0 -183
  210. rasa/shared/providers/router/router_client.py +0 -73
  211. rasa/shared/utils/health_check/__init__.py +0 -0
  212. rasa/shared/utils/health_check/embeddings_health_check_mixin.py +0 -31
  213. rasa/shared/utils/health_check/health_check.py +0 -258
  214. rasa/shared/utils/health_check/llm_health_check_mixin.py +0 -31
  215. rasa/utils/sanic_error_handler.py +0 -32
  216. rasa/utils/tensorflow/feature_array.py +0 -366
  217. rasa_pro-3.11.0.dist-info/METADATA +0 -198
  218. {rasa_pro-3.11.0.dist-info → rasa_pro-3.11.0a1.dist-info}/NOTICE +0 -0
  219. {rasa_pro-3.11.0.dist-info → rasa_pro-3.11.0a1.dist-info}/WHEEL +0 -0
  220. {rasa_pro-3.11.0.dist-info → rasa_pro-3.11.0a1.dist-info}/entry_points.txt +0 -0
@@ -1,9 +1,8 @@
1
1
  import base64
2
2
  import json
3
- import uuid
4
-
5
3
  import structlog
6
- from typing import Any, Awaitable, Callable, Dict, Optional, Text, Tuple
4
+ from typing import Any, Awaitable, Callable, Dict, List, Optional, Text
5
+ import uuid
7
6
 
8
7
  from sanic import Blueprint, HTTPResponse, Request, response
9
8
  from sanic import Websocket # type: ignore
@@ -11,7 +10,6 @@ from sanic import Websocket # type: ignore
11
10
 
12
11
  from rasa.core.channels import UserMessage
13
12
  from rasa.core.channels.voice_ready.utils import CallParameters
14
- from rasa.core.channels.voice_stream.call_state import call_state
15
13
  from rasa.core.channels.voice_stream.tts.tts_engine import TTSEngine
16
14
  from rasa.core.channels.voice_stream.audio_bytes import RasaAudioBytes
17
15
  from rasa.core.channels.voice_stream.voice_channel import (
@@ -23,7 +21,7 @@ from rasa.core.channels.voice_stream.voice_channel import (
23
21
  VoiceOutputChannel,
24
22
  )
25
23
 
26
- logger = structlog.get_logger(__name__)
24
+ structlogger = structlog.get_logger()
27
25
 
28
26
 
29
27
  def map_call_params(data: Dict[Text, Any]) -> CallParameters:
@@ -49,18 +47,10 @@ class TwilioMediaStreamsOutputChannel(VoiceOutputChannel):
49
47
  ) -> bytes:
50
48
  return base64.b64encode(rasa_audio_bytes)
51
49
 
52
- def create_marker_message(self, recipient_id: str) -> Tuple[str, str]:
50
+ def channel_bytes_to_messages(
51
+ self, recipient_id: str, channel_bytes: bytes
52
+ ) -> List[Any]:
53
53
  message_id = uuid.uuid4().hex
54
- mark_message = json.dumps(
55
- {
56
- "event": "mark",
57
- "streamSid": recipient_id,
58
- "mark": {"name": message_id},
59
- }
60
- )
61
- return mark_message, message_id
62
-
63
- def channel_bytes_to_message(self, recipient_id: str, channel_bytes: bytes) -> str:
64
54
  media_message = json.dumps(
65
55
  {
66
56
  "event": "media",
@@ -70,7 +60,15 @@ class TwilioMediaStreamsOutputChannel(VoiceOutputChannel):
70
60
  },
71
61
  }
72
62
  )
73
- return media_message
63
+ mark_message = json.dumps(
64
+ {
65
+ "event": "mark",
66
+ "streamSid": recipient_id,
67
+ "mark": {"name": message_id},
68
+ }
69
+ )
70
+ self.latest_message_id = message_id
71
+ return [media_message, mark_message]
74
72
 
75
73
 
76
74
  class TwilioMediaStreamsInputChannel(VoiceInputChannel):
@@ -105,16 +103,9 @@ class TwilioMediaStreamsInputChannel(VoiceInputChannel):
105
103
  elif data["event"] == "stop":
106
104
  return EndConversationAction()
107
105
  elif data["event"] == "mark":
108
- if data["mark"]["name"] == call_state.latest_bot_audio_id:
109
- # Just finished streaming last audio bytes
110
- call_state.is_bot_speaking = False # type: ignore[attr-defined]
111
- if call_state.should_hangup:
112
- logger.debug(
113
- "twilio_streams.hangup", marker=call_state.latest_bot_audio_id
114
- )
115
- return EndConversationAction()
116
- else:
117
- call_state.is_bot_speaking = True # type: ignore[attr-defined]
106
+ if data["mark"]["name"] == self.hangup_after:
107
+ structlogger.debug("twilio_streams.hangup", marker=self.hangup_after)
108
+ return EndConversationAction()
118
109
  return ContinueConversationAction()
119
110
 
120
111
  def create_output_channel(
@@ -140,7 +131,7 @@ class TwilioMediaStreamsInputChannel(VoiceInputChannel):
140
131
  self, on_new_message: Callable[[UserMessage], Awaitable[Any]]
141
132
  ) -> Blueprint:
142
133
  """Defines a Sanic bluelogger.debug."""
143
- blueprint = Blueprint("twilio_media_streams", __name__)
134
+ blueprint = Blueprint("socketio_webhook", __name__)
144
135
 
145
136
  @blueprint.route("/", methods=["GET"])
146
137
  async def health(_: Request) -> HTTPResponse:
@@ -5,7 +5,7 @@ from typing import Optional, Type, TypeVar
5
5
 
6
6
  import structlog
7
7
 
8
- from rasa.core.channels.voice_stream.audio_bytes import HERTZ, RasaAudioBytes
8
+ from rasa.core.channels.voice_stream.audio_bytes import RasaAudioBytes
9
9
  from rasa.shared.exceptions import RasaException
10
10
 
11
11
  structlogger = structlog.get_logger()
@@ -23,16 +23,16 @@ def read_wav_to_rasa_audio_bytes(file_name: str) -> Optional[RasaAudioBytes]:
23
23
  wave_data = audioop.lin2lin(wave_data, wave_object.getsampwidth(), 1)
24
24
  # 8 bit is unsigned
25
25
  # wave_data = audioop.bias(wave_data, 1, 128)
26
- if wave_object.getframerate() != HERTZ:
26
+ if wave_object.getframerate() != 8000:
27
27
  wave_data, _ = audioop.ratecv(
28
- wave_data, 1, 1, wave_object.getframerate(), HERTZ, None
28
+ wave_data, 1, 1, wave_object.getframerate(), 8000, None
29
29
  )
30
30
  wave_data = audioop.lin2ulaw(wave_data, 1)
31
31
  return RasaAudioBytes(wave_data)
32
32
 
33
33
 
34
34
  def generate_silence(length_in_seconds: float = 1.0) -> RasaAudioBytes:
35
- return RasaAudioBytes(b"\00" * int(length_in_seconds * HERTZ))
35
+ return RasaAudioBytes(b"\00" * int(length_in_seconds * 8000))
36
36
 
37
37
 
38
38
  T = TypeVar("T", bound="MergeableConfig")
@@ -1,45 +1,25 @@
1
1
  import asyncio
2
- import structlog
2
+ import logging
3
3
  import copy
4
4
  from dataclasses import asdict, dataclass
5
- from typing import Any, AsyncIterator, Awaitable, Callable, Dict, List, Optional, Tuple
6
-
7
- from rasa.core.channels.voice_stream.util import generate_silence
8
- from rasa.shared.core.constants import SLOT_SILENCE_TIMEOUT
9
- from rasa.shared.utils.common import (
10
- class_from_module_path,
11
- mark_as_beta_feature,
12
- )
13
- from rasa.shared.utils.cli import print_error_and_exit
5
+ from typing import Any, Awaitable, Callable, Dict, List, Optional
14
6
 
15
7
  from sanic.exceptions import ServerError, WebsocketClosed
16
8
 
17
9
  from rasa.core.channels import InputChannel, OutputChannel, UserMessage
18
10
  from rasa.core.channels.voice_ready.utils import CallParameters
19
- from rasa.core.channels.voice_ready.utils import validate_voice_license_scope
20
11
  from rasa.core.channels.voice_stream.asr.asr_engine import ASREngine
21
- from rasa.core.channels.voice_stream.asr.asr_event import (
22
- ASREvent,
23
- NewTranscript,
24
- UserIsSpeaking,
25
- )
12
+ from rasa.core.channels.voice_stream.asr.asr_event import ASREvent, NewTranscript
26
13
  from sanic import Websocket # type: ignore
27
14
 
28
15
  from rasa.core.channels.voice_stream.asr.deepgram import DeepgramASR
29
- from rasa.core.channels.voice_stream.asr.azure import AzureASR
30
- from rasa.core.channels.voice_stream.audio_bytes import HERTZ, RasaAudioBytes
31
- from rasa.core.channels.voice_stream.call_state import (
32
- CallState,
33
- _call_state,
34
- call_state,
35
- )
16
+ from rasa.core.channels.voice_stream.audio_bytes import RasaAudioBytes
36
17
  from rasa.core.channels.voice_stream.tts.azure import AzureTTS
37
18
  from rasa.core.channels.voice_stream.tts.tts_engine import TTSEngine, TTSError
38
19
  from rasa.core.channels.voice_stream.tts.cartesia import CartesiaTTS
39
20
  from rasa.core.channels.voice_stream.tts.tts_cache import TTSCache
40
- from rasa.utils.io import remove_emojis
41
21
 
42
- logger = structlog.get_logger(__name__)
22
+ logger = logging.getLogger(__name__)
43
23
 
44
24
 
45
25
  @dataclass
@@ -63,55 +43,25 @@ class ContinueConversationAction(VoiceChannelAction):
63
43
 
64
44
 
65
45
  def asr_engine_from_config(asr_config: Dict) -> ASREngine:
66
- name = str(asr_config["name"])
46
+ name = str(asr_config["name"]).lower()
67
47
  asr_config = copy.copy(asr_config)
68
48
  asr_config.pop("name")
69
- if name.lower() == "deepgram":
49
+ if name == "deepgram":
70
50
  return DeepgramASR.from_config_dict(asr_config)
71
- if name == "azure":
72
- return AzureASR.from_config_dict(asr_config)
73
51
  else:
74
- mark_as_beta_feature("Custom ASR Engine")
75
- try:
76
- asr_engine_class = class_from_module_path(name)
77
- return asr_engine_class.from_config_dict(asr_config)
78
- except NameError:
79
- print_error_and_exit(
80
- f"Failed to initialize ASR Engine with type '{name}'. "
81
- f"Please make sure the method `from_config_dict`is implemented."
82
- )
83
- except TypeError as e:
84
- print_error_and_exit(
85
- f"Failed to initialize ASR Engine with type '{name}'. "
86
- f"Invalid configuration provided. "
87
- f"Error: {e}"
88
- )
52
+ raise NotImplementedError
89
53
 
90
54
 
91
55
  def tts_engine_from_config(tts_config: Dict) -> TTSEngine:
92
- name = str(tts_config["name"])
56
+ name = str(tts_config["name"]).lower()
93
57
  tts_config = copy.copy(tts_config)
94
58
  tts_config.pop("name")
95
- if name.lower() == "azure":
59
+ if name == "azure":
96
60
  return AzureTTS.from_config_dict(tts_config)
97
- elif name.lower() == "cartesia":
61
+ elif name == "cartesia":
98
62
  return CartesiaTTS.from_config_dict(tts_config)
99
63
  else:
100
- mark_as_beta_feature("Custom TTS Engine")
101
- try:
102
- tts_engine_class = class_from_module_path(name)
103
- return tts_engine_class.from_config_dict(tts_config)
104
- except NameError:
105
- print_error_and_exit(
106
- f"Failed to initialize TTS Engine with type '{name}'. "
107
- f"Please make sure the method `from_config_dict`is implemented."
108
- )
109
- except TypeError as e:
110
- print_error_and_exit(
111
- f"Failed to initialize ASR Engine with type '{name}'. "
112
- f"Invalid configuration provided. "
113
- f"Error: {e}"
114
- )
64
+ raise NotImplementedError(f"TTS engine {name} is not implemented")
115
65
 
116
66
 
117
67
  class VoiceOutputChannel(OutputChannel):
@@ -121,171 +71,75 @@ class VoiceOutputChannel(OutputChannel):
121
71
  tts_engine: TTSEngine,
122
72
  tts_cache: TTSCache,
123
73
  ):
124
- super().__init__()
125
74
  self.voice_websocket = voice_websocket
126
75
  self.tts_engine = tts_engine
127
76
  self.tts_cache = tts_cache
128
77
 
78
+ self.should_hangup = False
129
79
  self.latest_message_id: Optional[str] = None
130
80
 
131
81
  def rasa_audio_bytes_to_channel_bytes(
132
82
  self, rasa_audio_bytes: RasaAudioBytes
133
83
  ) -> bytes:
134
- """Turn rasa's audio byte format into the format for the channel."""
135
84
  raise NotImplementedError
136
85
 
137
- def channel_bytes_to_message(self, recipient_id: str, channel_bytes: bytes) -> str:
138
- """Wrap the bytes for the channel in the proper format."""
86
+ def channel_bytes_to_messages(
87
+ self, recipient_id: str, channel_bytes: bytes
88
+ ) -> List[Any]:
139
89
  raise NotImplementedError
140
90
 
141
- def create_marker_message(self, recipient_id: str) -> Tuple[str, str]:
142
- """Create a marker message for a specific channel."""
143
- raise NotImplementedError
144
-
145
- async def send_marker_message(self, recipient_id: str) -> None:
146
- """Send a message that marks positions in the audio stream."""
147
- marker_message, mark_id = self.create_marker_message(recipient_id)
148
- await self.voice_websocket.send(marker_message)
149
- self.latest_message_id = mark_id
150
-
151
- def update_silence_timeout(self) -> None:
152
- """Updates the silence timeout for the session."""
153
- if self.tracker_state:
154
- call_state.silence_timeout = ( # type: ignore[attr-defined]
155
- self.tracker_state["slots"][SLOT_SILENCE_TIMEOUT]
156
- )
157
-
158
- async def send_text_with_buttons(
159
- self,
160
- recipient_id: str,
161
- text: str,
162
- buttons: List[Dict[str, Any]],
163
- **kwargs: Any,
164
- ) -> None:
165
- """Uses the concise button output format for voice channels."""
166
- await self.send_text_with_buttons_concise(recipient_id, text, buttons, **kwargs)
167
-
168
91
  async def send_text_message(
169
92
  self, recipient_id: str, text: str, **kwargs: Any
170
93
  ) -> None:
171
- text = remove_emojis(text)
172
- self.update_silence_timeout()
173
94
  cached_audio_bytes = self.tts_cache.get(text)
174
- collected_audio_bytes = RasaAudioBytes(b"")
175
- seconds_marker = -1
176
- if cached_audio_bytes:
177
- audio_stream = self.chunk_audio(cached_audio_bytes)
178
- else:
179
- # Todo: make kwargs compatible with engine config
180
- synth_config = self.tts_engine.config.__class__.from_dict({})
181
- try:
182
- audio_stream = self.tts_engine.synthesize(text, synth_config)
183
- except TTSError:
184
- # TODO: add message that works without tts, e.g. loading from disc
185
- audio_stream = self.chunk_audio(generate_silence())
186
95
 
96
+ if cached_audio_bytes:
97
+ await self.send_audio_bytes(recipient_id, cached_audio_bytes)
98
+ return
99
+ collected_audio_bytes = RasaAudioBytes(b"")
100
+ # Todo: make kwargs compatible with engine config
101
+ synth_config = self.tts_engine.config.__class__.from_dict({})
102
+ try:
103
+ audio_stream = self.tts_engine.synthesize(text, synth_config)
104
+ except TTSError:
105
+ # TODO: add message that works without tts, e.g. loading from disc
106
+ pass
187
107
  async for audio_bytes in audio_stream:
188
108
  try:
189
109
  await self.send_audio_bytes(recipient_id, audio_bytes)
190
- full_seconds_of_audio = len(collected_audio_bytes) // HERTZ
191
- if full_seconds_of_audio > seconds_marker:
192
- await self.send_marker_message(recipient_id)
193
- seconds_marker = full_seconds_of_audio
194
-
195
110
  except (WebsocketClosed, ServerError):
196
111
  # ignore sending error, and keep collecting and caching audio bytes
197
- call_state.connection_failed = True # type: ignore[attr-defined]
112
+ self.should_hangup = True
113
+
198
114
  collected_audio_bytes = RasaAudioBytes(collected_audio_bytes + audio_bytes)
199
- try:
200
- await self.send_marker_message(recipient_id)
201
- except (WebsocketClosed, ServerError):
202
- # ignore sending error
203
- pass
204
- call_state.latest_bot_audio_id = self.latest_message_id # type: ignore[attr-defined]
205
115
 
206
- if not cached_audio_bytes:
207
- self.tts_cache.put(text, collected_audio_bytes)
116
+ self.tts_cache.put(text, collected_audio_bytes)
208
117
 
209
118
  async def send_audio_bytes(
210
119
  self, recipient_id: str, audio_bytes: RasaAudioBytes
211
120
  ) -> None:
212
121
  channel_bytes = self.rasa_audio_bytes_to_channel_bytes(audio_bytes)
213
- message = self.channel_bytes_to_message(recipient_id, channel_bytes)
214
- await self.voice_websocket.send(message)
215
-
216
- async def chunk_audio(
217
- self, audio_bytes: RasaAudioBytes, chunk_size: int = 2048
218
- ) -> AsyncIterator[RasaAudioBytes]:
219
- """Generate chunks from cached audio bytes."""
220
- offset = 0
221
- while offset < len(audio_bytes):
222
- chunk = audio_bytes[offset : offset + chunk_size]
223
- if len(chunk):
224
- yield RasaAudioBytes(chunk)
225
- offset += chunk_size
226
- return
122
+ for message in self.channel_bytes_to_messages(recipient_id, channel_bytes):
123
+ await self.voice_websocket.send(message)
227
124
 
228
125
  async def hangup(self, recipient_id: str, **kwargs: Any) -> None:
229
- call_state.should_hangup = True # type: ignore[attr-defined]
126
+ self.should_hangup = True
230
127
 
231
128
 
232
129
  class VoiceInputChannel(InputChannel):
233
- def __init__(
234
- self,
235
- server_url: str,
236
- asr_config: Dict,
237
- tts_config: Dict,
238
- monitor_silence: bool = False,
239
- ):
240
- validate_voice_license_scope()
130
+ def __init__(self, server_url: str, asr_config: Dict, tts_config: Dict):
241
131
  self.server_url = server_url
242
132
  self.asr_config = asr_config
243
133
  self.tts_config = tts_config
244
- self.monitor_silence = monitor_silence
245
134
  self.tts_cache = TTSCache(tts_config.get("cache_size", 1000))
246
135
 
247
- async def handle_silence_timeout(
248
- self,
249
- voice_websocket: Websocket,
250
- on_new_message: Callable[[UserMessage], Awaitable[Any]],
251
- tts_engine: TTSEngine,
252
- call_parameters: CallParameters,
253
- ) -> None:
254
- timeout = call_state.silence_timeout
255
- if not timeout:
256
- return
257
- if not self.monitor_silence:
258
- return
259
- logger.debug("voice_channel.silence_timeout_watch_started", timeout=timeout)
260
- await asyncio.sleep(timeout)
261
- logger.debug("voice_channel.silence_timeout_tripped")
262
- output_channel = self.create_output_channel(voice_websocket, tts_engine)
263
- message = UserMessage(
264
- "/silence_timeout",
265
- output_channel,
266
- call_parameters.stream_id,
267
- input_channel=self.name(),
268
- metadata=asdict(call_parameters),
269
- )
270
- await on_new_message(message)
271
-
272
- @staticmethod
273
- def _cancel_silence_timeout_watcher() -> None:
274
- """Cancels the silent timeout task if it exists."""
275
- if call_state.silence_timeout_watcher:
276
- logger.debug("voice_channel.cancelling_current_timeout_watcher_task")
277
- call_state.silence_timeout_watcher.cancel()
278
- call_state.silence_timeout_watcher = None # type: ignore[attr-defined]
136
+ # if set to a value, call will be hungup after marker is reached
137
+ self.hangup_after: Optional[str] = None
279
138
 
280
139
  @classmethod
281
140
  def from_credentials(cls, credentials: Optional[Dict[str, Any]]) -> InputChannel:
282
141
  credentials = credentials or {}
283
- return cls(
284
- credentials["server_url"],
285
- credentials["asr"],
286
- credentials["tts"],
287
- credentials.get("monitor_silence", False),
288
- )
142
+ return cls(credentials["server_url"], credentials["asr"], credentials["tts"])
289
143
 
290
144
  def channel_bytes_to_rasa_audio_bytes(self, input_bytes: bytes) -> RasaAudioBytes:
291
145
  raise NotImplementedError
@@ -325,7 +179,6 @@ class VoiceInputChannel(InputChannel):
325
179
  channel_websocket: Websocket,
326
180
  ) -> None:
327
181
  """Pipe input audio to ASR and consume ASR events simultaneously."""
328
- _call_state.set(CallState())
329
182
  asr_engine = asr_engine_from_config(self.asr_config)
330
183
  tts_engine = tts_engine_from_config(self.tts_config)
331
184
  await asr_engine.connect()
@@ -339,29 +192,7 @@ class VoiceInputChannel(InputChannel):
339
192
 
340
193
  async def consume_audio_bytes() -> None:
341
194
  async for message in channel_websocket:
342
- is_bot_speaking_before = call_state.is_bot_speaking
343
195
  channel_action = self.map_input_message(message)
344
- is_bot_speaking_after = call_state.is_bot_speaking
345
-
346
- if not is_bot_speaking_before and is_bot_speaking_after:
347
- logger.debug("voice_channel.bot_started_speaking")
348
- # relevant when the bot speaks multiple messages in one turn
349
- self._cancel_silence_timeout_watcher()
350
-
351
- # we just stopped speaking, starting a watcher for silence timeout
352
- if is_bot_speaking_before and not is_bot_speaking_after:
353
- logger.debug("voice_channel.bot_stopped_speaking")
354
- self._cancel_silence_timeout_watcher()
355
- call_state.silence_timeout_watcher = ( # type: ignore[attr-defined]
356
- asyncio.create_task(
357
- self.handle_silence_timeout(
358
- channel_websocket,
359
- on_new_message,
360
- tts_engine,
361
- call_parameters,
362
- )
363
- )
364
- )
365
196
  if isinstance(channel_action, NewAudioAction):
366
197
  await asr_engine.send_audio_chunks(channel_action.audio_bytes)
367
198
  elif isinstance(channel_action, EndConversationAction):
@@ -378,20 +209,12 @@ class VoiceInputChannel(InputChannel):
378
209
  call_parameters,
379
210
  )
380
211
 
381
- audio_forwarding_task = asyncio.create_task(consume_audio_bytes())
382
- asr_event_task = asyncio.create_task(consume_asr_events())
383
212
  await asyncio.wait(
384
- [audio_forwarding_task, asr_event_task],
213
+ [consume_audio_bytes(), consume_asr_events()],
385
214
  return_when=asyncio.FIRST_COMPLETED,
386
215
  )
387
- if not audio_forwarding_task.done():
388
- audio_forwarding_task.cancel()
389
- if not asr_event_task.done():
390
- asr_event_task.cancel()
391
216
  await tts_engine.close_connection()
392
217
  await asr_engine.close_connection()
393
- await channel_websocket.close()
394
- self._cancel_silence_timeout_watcher()
395
218
 
396
219
  def create_output_channel(
397
220
  self, voice_websocket: Websocket, tts_engine: TTSEngine
@@ -409,10 +232,7 @@ class VoiceInputChannel(InputChannel):
409
232
  ) -> None:
410
233
  """Handle a new event from the ASR system."""
411
234
  if isinstance(e, NewTranscript) and e.text:
412
- logger.debug(
413
- "VoiceInputChannel.handle_asr_event.new_transcript", transcript=e.text
414
- )
415
- call_state.is_user_speaking = False # type: ignore[attr-defined]
235
+ logger.info(f"New transcript: {e.text}")
416
236
  output_channel = self.create_output_channel(voice_websocket, tts_engine)
417
237
  message = UserMessage(
418
238
  e.text,
@@ -422,6 +242,6 @@ class VoiceInputChannel(InputChannel):
422
242
  metadata=asdict(call_parameters),
423
243
  )
424
244
  await on_new_message(message)
425
- elif isinstance(e, UserIsSpeaking):
426
- self._cancel_silence_timeout_watcher()
427
- call_state.is_user_speaking = True # type: ignore[attr-defined]
245
+
246
+ if output_channel.should_hangup:
247
+ self.hangup_after = output_channel.latest_message_id
@@ -1,8 +1,7 @@
1
1
  import logging
2
- from typing import List, Optional, Dict, Text, Set, Any
3
-
4
2
  import numpy as np
5
3
  import scipy.sparse
4
+ from typing import List, Optional, Dict, Text, Set, Any
6
5
 
7
6
  from rasa.core.featurizers.precomputation import MessageContainerForCoreFeaturization
8
7
  from rasa.nlu.extractors.extractor import EntityTagSpec
@@ -361,26 +360,6 @@ class SingleStateFeaturizer:
361
360
  for action in domain.action_names_or_texts
362
361
  ]
363
362
 
364
- def to_dict(self) -> Dict[str, Any]:
365
- return {
366
- "action_texts": self.action_texts,
367
- "entity_tag_specs": self.entity_tag_specs,
368
- "feature_states": self._default_feature_states,
369
- }
370
-
371
- @classmethod
372
- def create_from_dict(
373
- cls, data: Dict[str, Any]
374
- ) -> Optional["SingleStateFeaturizer"]:
375
- if not data:
376
- return None
377
-
378
- featurizer = SingleStateFeaturizer()
379
- featurizer.action_texts = data["action_texts"]
380
- featurizer._default_feature_states = data["feature_states"]
381
- featurizer.entity_tag_specs = data["entity_tag_specs"]
382
- return featurizer
383
-
384
363
 
385
364
  class IntentTokenizerSingleStateFeaturizer(SingleStateFeaturizer):
386
365
  """A SingleStateFeaturizer for use with policies that predict intent labels."""