rasa-pro 3.11.0a4.dev3__py3-none-any.whl → 3.11.0rc2__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 (184) hide show
  1. rasa/__main__.py +22 -12
  2. rasa/api.py +1 -1
  3. rasa/cli/arguments/default_arguments.py +1 -2
  4. rasa/cli/arguments/shell.py +5 -1
  5. rasa/cli/e2e_test.py +1 -1
  6. rasa/cli/evaluate.py +8 -8
  7. rasa/cli/inspect.py +6 -4
  8. rasa/cli/llm_fine_tuning.py +1 -1
  9. rasa/cli/project_templates/calm/config.yml +5 -7
  10. rasa/cli/project_templates/calm/endpoints.yml +8 -0
  11. rasa/cli/project_templates/tutorial/config.yml +8 -5
  12. rasa/cli/project_templates/tutorial/data/flows.yml +1 -1
  13. rasa/cli/project_templates/tutorial/data/patterns.yml +5 -0
  14. rasa/cli/project_templates/tutorial/domain.yml +14 -0
  15. rasa/cli/project_templates/tutorial/endpoints.yml +7 -7
  16. rasa/cli/run.py +1 -1
  17. rasa/cli/scaffold.py +4 -2
  18. rasa/cli/studio/studio.py +18 -8
  19. rasa/cli/utils.py +5 -0
  20. rasa/cli/x.py +8 -8
  21. rasa/constants.py +1 -1
  22. rasa/core/actions/action_repeat_bot_messages.py +17 -0
  23. rasa/core/channels/channel.py +20 -0
  24. rasa/core/channels/inspector/dist/assets/{arc-6852c607.js → arc-bc141fb2.js} +1 -1
  25. rasa/core/channels/inspector/dist/assets/{c4Diagram-d0fbc5ce-acc952b2.js → c4Diagram-d0fbc5ce-be2db283.js} +1 -1
  26. rasa/core/channels/inspector/dist/assets/{classDiagram-936ed81e-848a7597.js → classDiagram-936ed81e-55366915.js} +1 -1
  27. rasa/core/channels/inspector/dist/assets/{classDiagram-v2-c3cb15f1-a73d3e68.js → classDiagram-v2-c3cb15f1-bb529518.js} +1 -1
  28. rasa/core/channels/inspector/dist/assets/{createText-62fc7601-e5ee049d.js → createText-62fc7601-b0ec81d6.js} +1 -1
  29. rasa/core/channels/inspector/dist/assets/{edges-f2ad444c-771e517e.js → edges-f2ad444c-6166330c.js} +1 -1
  30. rasa/core/channels/inspector/dist/assets/{erDiagram-9d236eb7-aa347178.js → erDiagram-9d236eb7-5ccc6a8e.js} +1 -1
  31. rasa/core/channels/inspector/dist/assets/{flowDb-1972c806-651fc57d.js → flowDb-1972c806-fca3bfe4.js} +1 -1
  32. rasa/core/channels/inspector/dist/assets/{flowDiagram-7ea5b25a-ca67804f.js → flowDiagram-7ea5b25a-4739080f.js} +1 -1
  33. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-855bc5b3-736177bf.js +1 -0
  34. rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-abe16c3d-2dbc568d.js → flowchart-elk-definition-abe16c3d-7c1b0e0f.js} +1 -1
  35. rasa/core/channels/inspector/dist/assets/{ganttDiagram-9b5ea136-25a65bd8.js → ganttDiagram-9b5ea136-772fd050.js} +1 -1
  36. rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-99d0ae7c-fdc7378d.js → gitGraphDiagram-99d0ae7c-8eae1dc9.js} +1 -1
  37. rasa/core/channels/inspector/dist/assets/{index-2c4b9a3b-6f1fd606.js → index-2c4b9a3b-f55afcdf.js} +1 -1
  38. rasa/core/channels/inspector/dist/assets/{index-efdd30c1.js → index-e7cef9de.js} +68 -68
  39. rasa/core/channels/inspector/dist/assets/{infoDiagram-736b4530-cb1a041a.js → infoDiagram-736b4530-124d4a14.js} +1 -1
  40. rasa/core/channels/inspector/dist/assets/{journeyDiagram-df861f2b-14609879.js → journeyDiagram-df861f2b-7c4fae44.js} +1 -1
  41. rasa/core/channels/inspector/dist/assets/{layout-2490f52b.js → layout-b9885fb6.js} +1 -1
  42. rasa/core/channels/inspector/dist/assets/{line-40186f1f.js → line-7c59abb6.js} +1 -1
  43. rasa/core/channels/inspector/dist/assets/{linear-08814e93.js → linear-4776f780.js} +1 -1
  44. rasa/core/channels/inspector/dist/assets/{mindmap-definition-beec6740-1a534584.js → mindmap-definition-beec6740-2332c46c.js} +1 -1
  45. rasa/core/channels/inspector/dist/assets/{pieDiagram-dbbf0591-72397b61.js → pieDiagram-dbbf0591-8fb39303.js} +1 -1
  46. rasa/core/channels/inspector/dist/assets/{quadrantDiagram-4d7f4fd6-3bb0b6a3.js → quadrantDiagram-4d7f4fd6-3c7180a2.js} +1 -1
  47. rasa/core/channels/inspector/dist/assets/{requirementDiagram-6fc4c22a-57334f61.js → requirementDiagram-6fc4c22a-e910bcb8.js} +1 -1
  48. rasa/core/channels/inspector/dist/assets/{sankeyDiagram-8f13d901-111e1297.js → sankeyDiagram-8f13d901-ead16c89.js} +1 -1
  49. rasa/core/channels/inspector/dist/assets/{sequenceDiagram-b655622a-10bcfe62.js → sequenceDiagram-b655622a-29a02a19.js} +1 -1
  50. rasa/core/channels/inspector/dist/assets/{stateDiagram-59f0c015-acaf7513.js → stateDiagram-59f0c015-042b3137.js} +1 -1
  51. rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-2b26beab-3ec2a235.js → stateDiagram-v2-2b26beab-2178c0f3.js} +1 -1
  52. rasa/core/channels/inspector/dist/assets/{styles-080da4f6-62730289.js → styles-080da4f6-23ffa4fc.js} +1 -1
  53. rasa/core/channels/inspector/dist/assets/{styles-3dcbcfbf-5284ee76.js → styles-3dcbcfbf-94f59763.js} +1 -1
  54. rasa/core/channels/inspector/dist/assets/{styles-9c745c82-642435e3.js → styles-9c745c82-78a6bebc.js} +1 -1
  55. rasa/core/channels/inspector/dist/assets/{svgDrawCommon-4835440b-b250a350.js → svgDrawCommon-4835440b-eae2a6f6.js} +1 -1
  56. rasa/core/channels/inspector/dist/assets/{timeline-definition-5b62e21b-c2b147ed.js → timeline-definition-5b62e21b-5c968d92.js} +1 -1
  57. rasa/core/channels/inspector/dist/assets/{xychartDiagram-2b33534f-f92cfea9.js → xychartDiagram-2b33534f-fd3db0d5.js} +1 -1
  58. rasa/core/channels/inspector/dist/index.html +1 -1
  59. rasa/core/channels/inspector/src/App.tsx +1 -1
  60. rasa/core/channels/inspector/src/helpers/audiostream.ts +77 -16
  61. rasa/core/channels/socketio.py +2 -1
  62. rasa/core/channels/telegram.py +1 -1
  63. rasa/core/channels/twilio.py +1 -1
  64. rasa/core/channels/voice_ready/audiocodes.py +12 -0
  65. rasa/core/channels/voice_ready/jambonz.py +15 -4
  66. rasa/core/channels/voice_ready/twilio_voice.py +6 -21
  67. rasa/core/channels/voice_stream/asr/asr_event.py +5 -0
  68. rasa/core/channels/voice_stream/asr/azure.py +122 -0
  69. rasa/core/channels/voice_stream/asr/deepgram.py +16 -6
  70. rasa/core/channels/voice_stream/audio_bytes.py +1 -0
  71. rasa/core/channels/voice_stream/browser_audio.py +31 -8
  72. rasa/core/channels/voice_stream/call_state.py +23 -0
  73. rasa/core/channels/voice_stream/tts/azure.py +6 -2
  74. rasa/core/channels/voice_stream/tts/cartesia.py +10 -6
  75. rasa/core/channels/voice_stream/tts/tts_engine.py +1 -0
  76. rasa/core/channels/voice_stream/twilio_media_streams.py +27 -18
  77. rasa/core/channels/voice_stream/util.py +4 -4
  78. rasa/core/channels/voice_stream/voice_channel.py +189 -39
  79. rasa/core/featurizers/single_state_featurizer.py +22 -1
  80. rasa/core/featurizers/tracker_featurizers.py +115 -18
  81. rasa/core/nlg/contextual_response_rephraser.py +32 -30
  82. rasa/core/persistor.py +86 -39
  83. rasa/core/policies/enterprise_search_policy.py +119 -60
  84. rasa/core/policies/flows/flow_executor.py +7 -4
  85. rasa/core/policies/intentless_policy.py +78 -22
  86. rasa/core/policies/ted_policy.py +58 -33
  87. rasa/core/policies/unexpected_intent_policy.py +15 -7
  88. rasa/core/processor.py +25 -0
  89. rasa/core/training/interactive.py +34 -35
  90. rasa/core/utils.py +8 -3
  91. rasa/dialogue_understanding/coexistence/llm_based_router.py +39 -12
  92. rasa/dialogue_understanding/commands/change_flow_command.py +6 -0
  93. rasa/dialogue_understanding/commands/user_silence_command.py +59 -0
  94. rasa/dialogue_understanding/commands/utils.py +5 -0
  95. rasa/dialogue_understanding/generator/constants.py +2 -0
  96. rasa/dialogue_understanding/generator/flow_retrieval.py +49 -4
  97. rasa/dialogue_understanding/generator/llm_based_command_generator.py +37 -23
  98. rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +57 -10
  99. rasa/dialogue_understanding/generator/nlu_command_adapter.py +19 -1
  100. rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +71 -11
  101. rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +39 -0
  102. rasa/dialogue_understanding/patterns/user_silence.py +37 -0
  103. rasa/dialogue_understanding/processor/command_processor.py +21 -1
  104. rasa/e2e_test/e2e_test_case.py +85 -6
  105. rasa/e2e_test/e2e_test_runner.py +4 -2
  106. rasa/e2e_test/utils/io.py +1 -1
  107. rasa/engine/validation.py +316 -10
  108. rasa/model_manager/config.py +15 -3
  109. rasa/model_manager/model_api.py +15 -7
  110. rasa/model_manager/runner_service.py +8 -6
  111. rasa/model_manager/socket_bridge.py +6 -3
  112. rasa/model_manager/trainer_service.py +7 -5
  113. rasa/model_manager/utils.py +28 -7
  114. rasa/model_service.py +9 -2
  115. rasa/model_training.py +2 -0
  116. rasa/nlu/classifiers/diet_classifier.py +38 -25
  117. rasa/nlu/classifiers/logistic_regression_classifier.py +22 -9
  118. rasa/nlu/classifiers/sklearn_intent_classifier.py +37 -16
  119. rasa/nlu/extractors/crf_entity_extractor.py +93 -50
  120. rasa/nlu/featurizers/sparse_featurizer/count_vectors_featurizer.py +45 -16
  121. rasa/nlu/featurizers/sparse_featurizer/lexical_syntactic_featurizer.py +52 -17
  122. rasa/nlu/featurizers/sparse_featurizer/regex_featurizer.py +5 -3
  123. rasa/nlu/tokenizers/whitespace_tokenizer.py +3 -14
  124. rasa/server.py +3 -1
  125. rasa/shared/constants.py +36 -3
  126. rasa/shared/core/constants.py +7 -0
  127. rasa/shared/core/domain.py +26 -0
  128. rasa/shared/core/flows/flow.py +5 -0
  129. rasa/shared/core/flows/flows_list.py +5 -1
  130. rasa/shared/core/flows/flows_yaml_schema.json +10 -0
  131. rasa/shared/core/flows/utils.py +39 -0
  132. rasa/shared/core/flows/validation.py +96 -0
  133. rasa/shared/core/slots.py +5 -0
  134. rasa/shared/nlu/training_data/features.py +120 -2
  135. rasa/shared/providers/_configs/azure_openai_client_config.py +5 -3
  136. rasa/shared/providers/_configs/litellm_router_client_config.py +200 -0
  137. rasa/shared/providers/_configs/model_group_config.py +167 -0
  138. rasa/shared/providers/_configs/openai_client_config.py +1 -1
  139. rasa/shared/providers/_configs/rasa_llm_client_config.py +73 -0
  140. rasa/shared/providers/_configs/self_hosted_llm_client_config.py +1 -0
  141. rasa/shared/providers/_configs/utils.py +16 -0
  142. rasa/shared/providers/embedding/_base_litellm_embedding_client.py +18 -29
  143. rasa/shared/providers/embedding/azure_openai_embedding_client.py +54 -21
  144. rasa/shared/providers/embedding/litellm_router_embedding_client.py +135 -0
  145. rasa/shared/providers/llm/_base_litellm_client.py +37 -31
  146. rasa/shared/providers/llm/azure_openai_llm_client.py +50 -29
  147. rasa/shared/providers/llm/litellm_router_llm_client.py +127 -0
  148. rasa/shared/providers/llm/rasa_llm_client.py +112 -0
  149. rasa/shared/providers/llm/self_hosted_llm_client.py +1 -1
  150. rasa/shared/providers/mappings.py +19 -0
  151. rasa/shared/providers/router/__init__.py +0 -0
  152. rasa/shared/providers/router/_base_litellm_router_client.py +149 -0
  153. rasa/shared/providers/router/router_client.py +73 -0
  154. rasa/shared/utils/common.py +8 -0
  155. rasa/shared/utils/health_check/__init__.py +0 -0
  156. rasa/shared/utils/health_check/embeddings_health_check_mixin.py +31 -0
  157. rasa/shared/utils/health_check/health_check.py +256 -0
  158. rasa/shared/utils/health_check/llm_health_check_mixin.py +31 -0
  159. rasa/shared/utils/io.py +28 -6
  160. rasa/shared/utils/llm.py +353 -46
  161. rasa/shared/utils/yaml.py +111 -73
  162. rasa/studio/auth.py +3 -5
  163. rasa/studio/config.py +13 -4
  164. rasa/studio/constants.py +1 -0
  165. rasa/studio/data_handler.py +10 -3
  166. rasa/studio/upload.py +81 -26
  167. rasa/telemetry.py +92 -17
  168. rasa/tracing/config.py +2 -0
  169. rasa/tracing/instrumentation/attribute_extractors.py +94 -17
  170. rasa/tracing/instrumentation/instrumentation.py +121 -0
  171. rasa/utils/common.py +5 -0
  172. rasa/utils/io.py +7 -81
  173. rasa/utils/log_utils.py +9 -2
  174. rasa/utils/sanic_error_handler.py +32 -0
  175. rasa/utils/tensorflow/feature_array.py +366 -0
  176. rasa/utils/tensorflow/model_data.py +2 -193
  177. rasa/validator.py +70 -0
  178. rasa/version.py +1 -1
  179. {rasa_pro-3.11.0a4.dev3.dist-info → rasa_pro-3.11.0rc2.dist-info}/METADATA +11 -10
  180. {rasa_pro-3.11.0a4.dev3.dist-info → rasa_pro-3.11.0rc2.dist-info}/RECORD +183 -163
  181. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-855bc5b3-587d82d8.js +0 -1
  182. {rasa_pro-3.11.0a4.dev3.dist-info → rasa_pro-3.11.0rc2.dist-info}/NOTICE +0 -0
  183. {rasa_pro-3.11.0a4.dev3.dist-info → rasa_pro-3.11.0rc2.dist-info}/WHEEL +0 -0
  184. {rasa_pro-3.11.0a4.dev3.dist-info → rasa_pro-3.11.0rc2.dist-info}/entry_points.txt +0 -0
rasa/shared/constants.py CHANGED
@@ -183,7 +183,34 @@ STREAM_CONFIG_KEY = "stream"
183
183
  N_REPHRASES_CONFIG_KEY = "n"
184
184
  USE_CHAT_COMPLETIONS_ENDPOINT_CONFIG_KEY = "use_chat_completions_endpoint"
185
185
 
186
+ ROUTER_CONFIG_KEY = "router"
187
+ ROUTER_STRATEGY_CONFIG_KEY = "router_strategy"
188
+ REDIS_HOST_CONFIG_KEY = "redis_host"
189
+ ROUTER_STRATEGIES_REQUIRING_REDIS_CACHE = [
190
+ "cost-based-routing",
191
+ "usage-based-routing",
192
+ ]
193
+ ROUTER_STRATEGIES_NOT_REQUIRING_CACHE = [
194
+ "latency-based-routing",
195
+ "least-busy",
196
+ "simple-shuffle",
197
+ ]
198
+ VALID_ROUTER_STRATEGIES = (
199
+ ROUTER_STRATEGIES_REQUIRING_REDIS_CACHE + ROUTER_STRATEGIES_NOT_REQUIRING_CACHE
200
+ )
201
+
202
+ MODELS_CONFIG_KEY = "models"
203
+ MODEL_GROUPS_CONFIG_KEY = "model_groups"
204
+ MODEL_GROUP_CONFIG_KEY = "model_group"
205
+ MODEL_GROUP_ID_CONFIG_KEY = "id"
206
+
207
+ EXTRA_PARAMETERS_KEY = "extra_parameters"
208
+ MODEL_GROUP_ID_KEY = "model_group_id"
209
+ MODEL_LIST_KEY = "model_list"
210
+ LITELLM_PARAMS_KEY = "litellm_params"
211
+
186
212
  LLM_API_HEALTH_CHECK_ENV_VAR = "LLM_API_HEALTH_CHECK"
213
+ LLM_API_HEALTH_CHECK_DEFAULT_VALUE = "false"
187
214
 
188
215
  AZURE_API_KEY_ENV_VAR = "AZURE_API_KEY"
189
216
  AZURE_AD_TOKEN_ENV_VAR = "AZURE_AD_TOKEN"
@@ -191,6 +218,8 @@ AZURE_API_BASE_ENV_VAR = "AZURE_API_BASE"
191
218
  AZURE_API_VERSION_ENV_VAR = "AZURE_API_VERSION"
192
219
  AZURE_API_TYPE_ENV_VAR = "AZURE_API_TYPE"
193
220
 
221
+ AWS_REGION_NAME_CONFIG_KEY = "aws_region_name"
222
+
194
223
  HUGGINGFACE_MULTIPROCESS_CONFIG_KEY = "multi_process"
195
224
  HUGGINGFACE_CACHE_FOLDER_CONFIG_KEY = "cache_folder"
196
225
  HUGGINGFACE_SHOW_PROGRESS_CONFIG_KEY = "show_progress"
@@ -211,6 +240,13 @@ OPENAI_PROVIDER = "openai"
211
240
  AZURE_OPENAI_PROVIDER = "azure"
212
241
  SELF_HOSTED_PROVIDER = "self-hosted"
213
242
  HUGGINGFACE_LOCAL_EMBEDDING_PROVIDER = "huggingface_local"
243
+ RASA_PROVIDER = "rasa"
244
+
245
+ SELF_HOSTED_VLLM_PREFIX = "hosted_vllm"
246
+ SELF_HOSTED_VLLM_API_KEY_ENV_VAR = "HOSTED_VLLM_API_KEY"
247
+
248
+ SELF_HOSTED_VLLM_PREFIX = "hosted_vllm"
249
+ SELF_HOSTED_VLLM_API_KEY_ENV_VAR = "HOSTED_VLLM_API_KEY"
214
250
 
215
251
  SELF_HOSTED_VLLM_PREFIX = "hosted_vllm"
216
252
  SELF_HOSTED_VLLM_API_KEY_ENV_VAR = "HOSTED_VLLM_API_KEY"
@@ -244,6 +280,3 @@ RASA_PATTERN_CANNOT_HANDLE_INVALID_INTENT = (
244
280
  )
245
281
 
246
282
  ROUTE_TO_CALM_SLOT = "route_session_to_calm"
247
-
248
- ORIGINAL_VALUE = "original_value"
249
- RESOLVED_VALUE = "resolved_value"
@@ -11,6 +11,7 @@ USER_INTENT_BACK = "back"
11
11
  USER_INTENT_OUT_OF_SCOPE = "out_of_scope"
12
12
  USER_INTENT_SESSION_START = "session_start"
13
13
  USER_INTENT_SESSION_END = "session_end"
14
+ USER_INTENT_SILENCE_TIMEOUT = "silence_timeout"
14
15
  SESSION_START_METADATA_SLOT = "session_started_metadata"
15
16
 
16
17
  DEFAULT_INTENTS = [
@@ -19,6 +20,7 @@ DEFAULT_INTENTS = [
19
20
  USER_INTENT_OUT_OF_SCOPE,
20
21
  USER_INTENT_SESSION_START,
21
22
  USER_INTENT_SESSION_END,
23
+ USER_INTENT_SILENCE_TIMEOUT,
22
24
  constants.DEFAULT_NLU_FALLBACK_INTENT_NAME,
23
25
  ]
24
26
 
@@ -106,6 +108,11 @@ FLOW_HASHES_SLOT = "flow_hashes"
106
108
 
107
109
  FLOW_SLOT_NAMES = [FLOW_HASHES_SLOT]
108
110
 
111
+ # slots for audio timeout
112
+ SLOT_SILENCE_TIMEOUT = "silence_timeout"
113
+ SILENCE_TIMEOUT_DEFAULT_VALUE = 6.0
114
+ SLOT_CONSECUTIVE_SILENCE_TIMEOUTS = "consecutive_silence_timeouts"
115
+ SILENCE_SLOTS = [SLOT_SILENCE_TIMEOUT, SLOT_CONSECUTIVE_SILENCE_TIMEOUTS]
109
116
  # slots for knowledge base
110
117
  SLOT_LISTED_ITEMS = "knowledge_base_listed_objects"
111
118
  SLOT_LAST_OBJECT = "knowledge_base_last_object"
@@ -3,6 +3,7 @@ from __future__ import annotations
3
3
  import collections
4
4
  import copy
5
5
  import json
6
+ import math
6
7
  import os
7
8
  from dataclasses import dataclass
8
9
  from functools import cached_property
@@ -57,6 +58,7 @@ from rasa.shared.core.events import SlotSet, UserUttered
57
58
  from rasa.shared.core.slots import (
58
59
  AnySlot,
59
60
  CategoricalSlot,
61
+ FloatSlot,
60
62
  ListSlot,
61
63
  Slot,
62
64
  TextSlot,
@@ -1082,6 +1084,7 @@ class Domain:
1082
1084
  self._add_knowledge_base_slots()
1083
1085
  self._add_categorical_slot_default_value()
1084
1086
  self._add_session_metadata_slot()
1087
+ self._add_audio_slots()
1085
1088
 
1086
1089
  def _add_categorical_slot_default_value(self) -> None:
1087
1090
  """Add a default value to all categorical slots.
@@ -1136,6 +1139,29 @@ class Domain:
1136
1139
  )
1137
1140
  )
1138
1141
 
1142
+ def _add_audio_slots(self) -> None:
1143
+ """Add slots relevant for audio channels."""
1144
+ self.slots.append(
1145
+ FloatSlot(
1146
+ rasa.shared.core.constants.SLOT_SILENCE_TIMEOUT,
1147
+ mappings=[],
1148
+ influence_conversation=False,
1149
+ is_builtin=True,
1150
+ initial_value=rasa.shared.core.constants.SILENCE_TIMEOUT_DEFAULT_VALUE,
1151
+ max_value=math.inf,
1152
+ )
1153
+ )
1154
+ self.slots.append(
1155
+ FloatSlot(
1156
+ rasa.shared.core.constants.SLOT_CONSECUTIVE_SILENCE_TIMEOUTS,
1157
+ mappings=[],
1158
+ influence_conversation=False,
1159
+ is_builtin=True,
1160
+ initial_value=0.0,
1161
+ max_value=math.inf,
1162
+ )
1163
+ )
1164
+
1139
1165
  def _add_knowledge_base_slots(self) -> None:
1140
1166
  """Add slots for the knowledge base action to slots.
1141
1167
 
@@ -60,6 +60,8 @@ class Flow:
60
60
  """
61
61
  file_path: Optional[str] = None
62
62
  """The path to the file where the flow is stored."""
63
+ persisted_slots: List[str] = field(default_factory=list)
64
+ """The list of slots that should be persisted after the flow ends."""
63
65
 
64
66
  @staticmethod
65
67
  def from_json(
@@ -95,6 +97,7 @@ class Flow:
95
97
  # If we are reading the flows in after training the file_path is part of
96
98
  # data. When the model is trained, take the provided file_path.
97
99
  file_path=data.get("file_path") if "file_path" in data else file_path,
100
+ persisted_slots=data.get("persisted_slots", []),
98
101
  )
99
102
 
100
103
  def get_full_name(self) -> str:
@@ -167,6 +170,8 @@ class Flow:
167
170
  data["nlu_trigger"] = self.nlu_triggers.as_json()
168
171
  if self.file_path:
169
172
  data["file_path"] = self.file_path
173
+ if self.persisted_slots:
174
+ data["persisted_slots"] = self.persisted_slots
170
175
 
171
176
  return data
172
177
 
@@ -234,12 +234,16 @@ class FlowsList:
234
234
  [f for f in self.underlying_flows if not f.is_startable_only_via_link()]
235
235
  )
236
236
 
237
- def available_slot_names(self) -> Set[str]:
237
+ def available_slot_names(
238
+ self, ask_before_filling: Optional[bool] = None
239
+ ) -> Set[str]:
238
240
  """Get all slot names collected by flows."""
239
241
  return {
240
242
  step.collect
241
243
  for flow in self.underlying_flows
242
244
  for step in flow.get_collect_steps()
245
+ if ask_before_filling is None
246
+ or step.ask_before_filling == ask_before_filling
243
247
  }
244
248
 
245
249
  def available_custom_actions(self) -> Set[str]:
@@ -279,6 +279,9 @@
279
279
  },
280
280
  "steps": {
281
281
  "$ref": "#/$defs/steps"
282
+ },
283
+ "persisted_slots": {
284
+ "$ref": "#/$defs/persisted_slots"
282
285
  }
283
286
  }
284
287
  },
@@ -288,6 +291,13 @@
288
291
  "items": {
289
292
  "type": "object"
290
293
  }
294
+ },
295
+ "persisted_slots": {
296
+ "type": "array",
297
+ "schema_name": "list of slots",
298
+ "items": {
299
+ "type": "string"
300
+ }
291
301
  }
292
302
  }
293
303
  }
@@ -0,0 +1,39 @@
1
+ from typing import Set
2
+ from rasa.shared.utils.io import raise_deprecation_warning
3
+
4
+ RESET_PROPERTY_NAME = "reset_after_flow_ends"
5
+ PERSIST_PROPERTY_NAME = "persisted_slots"
6
+
7
+
8
+ def warn_deprecated_collect_step_config(flow_id: str, collect_step: str) -> None:
9
+ """Warns about deprecated reset_after_flow_ends usage in collect steps."""
10
+ raise_deprecation_warning(
11
+ f"Configuring '{RESET_PROPERTY_NAME}' in collect step '{collect_step}' is "
12
+ f"deprecated and will be removed in Rasa Pro 4.0.0. In flow id '{flow_id}', "
13
+ f"please use the '{PERSIST_PROPERTY_NAME}' "
14
+ "property at the flow level instead."
15
+ )
16
+
17
+
18
+ def get_duplicate_slot_persistence_config_error_message(
19
+ flow_id: str, collect_step: str
20
+ ) -> str:
21
+ """Returns an error message for duplicate slot persistence configuration."""
22
+ return (
23
+ f"Flow with id '{flow_id}' uses the '{RESET_PROPERTY_NAME}' property "
24
+ f"in collect step '{collect_step}' and also the "
25
+ f"'{PERSIST_PROPERTY_NAME}' property at the flow level. "
26
+ "Please use only one of the two configuration methods."
27
+ )
28
+
29
+
30
+ def get_invalid_slot_persistence_config_error_message(
31
+ flow_id: str, invalid_slots: Set[str]
32
+ ) -> str:
33
+ """Returns an error message for invalid slot persistence configuration."""
34
+ return (
35
+ f"Flow with id '{flow_id}' lists slot(s) '{invalid_slots}' in the "
36
+ f"'{PERSIST_PROPERTY_NAME}' property. However these slots "
37
+ f"are neither used in a collect step nor a set_slot step of the flow. "
38
+ f"Please remove such slots from the '{PERSIST_PROPERTY_NAME}' property."
39
+ )
@@ -3,6 +3,7 @@ from __future__ import annotations
3
3
  import re
4
4
  import typing
5
5
  from collections import defaultdict
6
+ from dataclasses import dataclass
6
7
  from typing import Optional, Set, Text, List
7
8
 
8
9
  from rasa.shared.constants import (
@@ -26,6 +27,12 @@ from rasa.shared.core.flows.steps.call import CallFlowStep
26
27
  from rasa.shared.core.flows.steps.collect import CollectInformationFlowStep
27
28
  from rasa.shared.core.flows.steps.constants import CONTINUE_STEP_PREFIX, DEFAULT_STEPS
28
29
  from rasa.shared.core.flows.steps.link import LinkFlowStep
30
+ from rasa.shared.core.flows.steps.set_slots import SetSlotsFlowStep
31
+ from rasa.shared.core.flows.utils import (
32
+ warn_deprecated_collect_step_config,
33
+ get_duplicate_slot_persistence_config_error_message,
34
+ get_invalid_slot_persistence_config_error_message,
35
+ )
29
36
  from rasa.shared.exceptions import RasaException
30
37
 
31
38
  if typing.TYPE_CHECKING:
@@ -379,6 +386,42 @@ class FlowIdNamingException(RasaException):
379
386
  )
380
387
 
381
388
 
389
+ class DuplicateSlotPersistConfigException(RasaException):
390
+ """Raised when a slot persist configuration is duplicated."""
391
+
392
+ def __init__(self, flow_id: str, collect_step: str) -> None:
393
+ """Initializes the exception."""
394
+ self.flow_id = flow_id
395
+ self.collect_step = collect_step
396
+
397
+ def __str__(self) -> str:
398
+ """Return a string representation of the exception."""
399
+ return get_duplicate_slot_persistence_config_error_message(
400
+ self.flow_id, self.collect_step
401
+ )
402
+
403
+
404
+ class InvalidPersistSlotsException(RasaException):
405
+ """Raised when a slot persist configuration is duplicated."""
406
+
407
+ def __init__(self, flow_id: str, invalid_slots: Set[str]) -> None:
408
+ """Initializes the exception."""
409
+ self.flow_id = flow_id
410
+ self.invalid_slots = invalid_slots
411
+
412
+ def __str__(self) -> str:
413
+ """Return a string representation of the exception."""
414
+ return get_invalid_slot_persistence_config_error_message(
415
+ self.flow_id, self.invalid_slots
416
+ )
417
+
418
+
419
+ @dataclass
420
+ class ValidationResult:
421
+ is_valid: bool
422
+ invalid_slots: Set[str]
423
+
424
+
382
425
  def validate_flow(flow: Flow) -> None:
383
426
  """Validates the flow configuration.
384
427
 
@@ -387,6 +430,8 @@ def validate_flow(flow: Flow) -> None:
387
430
  - whether all next links point to existing steps
388
431
  - whether all steps can be reached from the start step
389
432
  """
433
+ from rasa.cli.utils import is_skip_validation_flag_set
434
+
390
435
  validate_flow_not_empty(flow)
391
436
  validate_no_empty_step_sequences(flow)
392
437
  validate_all_steps_next_property(flow)
@@ -397,6 +442,12 @@ def validate_flow(flow: Flow) -> None:
397
442
  validate_slot_names_to_be_collected(flow)
398
443
  validate_flow_id(flow)
399
444
 
445
+ if is_skip_validation_flag_set():
446
+ # we only want to run this validation if the --skip-validation flag is used
447
+ # during training because Flow Validation exceptions are raised one by one
448
+ # as opposed to all at once with the Validator class
449
+ validate_slot_persistence_configuration(flow)
450
+
400
451
 
401
452
  def validate_flow_not_empty(flow: Flow) -> None:
402
453
  """Validate that the flow is not empty."""
@@ -637,3 +688,48 @@ def validate_flow_id(flow: Flow) -> None:
637
688
  flow_re = re.compile(FLOW_ID_REGEX)
638
689
  if not flow_re.search(flow.id):
639
690
  raise FlowIdNamingException(flow.id)
691
+
692
+
693
+ def validate_slot_persistence_configuration(flow: Flow) -> None:
694
+ """Validates that slot persistence configuration is valid.
695
+
696
+ Only slots used in either a collect step or a set_slot step can be persisted
697
+ and the configuration can either be set at the flow level or the collect step level,
698
+ but not both.
699
+
700
+ Args:
701
+ flow: The flow to validate.
702
+
703
+ Raises:
704
+ DuplicateSlotPersistConfigException: If slot persist config is duplicated.
705
+ """
706
+
707
+ def _is_persist_slots_valid(
708
+ persist_slots: List[str], flow_slots: Set[str]
709
+ ) -> ValidationResult:
710
+ """Validates that the slots that should be persisted are used in the flow."""
711
+ invalid_slots = set(persist_slots) - flow_slots
712
+ is_valid = False if invalid_slots else True
713
+
714
+ return ValidationResult(is_valid, invalid_slots)
715
+
716
+ flow_id = flow.id
717
+ persist_slots = flow.persisted_slots
718
+ has_flow_level_persistence = True if persist_slots else False
719
+ flow_slots = set()
720
+
721
+ for step in flow.steps_with_calls_resolved:
722
+ if isinstance(step, SetSlotsFlowStep):
723
+ flow_slots.update([slot["key"] for slot in step.slots])
724
+ elif isinstance(step, CollectInformationFlowStep):
725
+ flow_slots.add(step.collect)
726
+ if not step.reset_after_flow_ends:
727
+ collect_step = step.collect
728
+ warn_deprecated_collect_step_config(flow_id, collect_step)
729
+ if has_flow_level_persistence:
730
+ raise DuplicateSlotPersistConfigException(flow_id, collect_step)
731
+
732
+ if has_flow_level_persistence:
733
+ result = _is_persist_slots_valid(persist_slots, flow_slots)
734
+ if not result.is_valid:
735
+ raise InvalidPersistSlotsException(flow_id, result.invalid_slots)
rasa/shared/core/slots.py CHANGED
@@ -193,6 +193,11 @@ class Slot(ABC):
193
193
  data.update(self.persistence_info())
194
194
  return rasa.shared.utils.io.get_dictionary_fingerprint(data)
195
195
 
196
+ def __eq__(self, other: Any) -> bool:
197
+ if not isinstance(other, Slot):
198
+ return False
199
+ return self.name == other.name and self.value == other.value
200
+
196
201
 
197
202
  class FloatSlot(Slot):
198
203
  """A slot storing a float value."""
@@ -1,15 +1,133 @@
1
1
  from __future__ import annotations
2
- from typing import Iterable, Union, Text, Optional, List, Any, Tuple, Dict, Set
2
+
3
3
  import itertools
4
+ from dataclasses import dataclass
5
+ from typing import Iterable, Union, Text, Optional, List, Any, Tuple, Dict, Set
4
6
 
5
7
  import numpy as np
6
8
  import scipy.sparse
9
+ from safetensors.numpy import save_file, load_file
7
10
 
8
- import rasa.shared.utils.io
9
11
  import rasa.shared.nlu.training_data.util
12
+ import rasa.shared.utils.io
10
13
  from rasa.shared.nlu.constants import FEATURE_TYPE_SEQUENCE, FEATURE_TYPE_SENTENCE
11
14
 
12
15
 
16
+ @dataclass
17
+ class FeatureMetadata:
18
+ data_type: str
19
+ attribute: str
20
+ origin: Union[str, List[str]]
21
+ is_sparse: bool
22
+ shape: tuple
23
+ safetensors_key: str
24
+
25
+
26
+ def save_features(
27
+ features_dict: Dict[Text, List[Features]], file_name: str
28
+ ) -> Dict[str, Any]:
29
+ """Save a dictionary of Features lists to disk using safetensors.
30
+
31
+ Args:
32
+ features_dict: Dictionary mapping strings to lists of Features objects
33
+ file_name: File to save the features to
34
+
35
+ Returns:
36
+ The metadata to reconstruct the features.
37
+ """
38
+ # All tensors are stored in a single safetensors file
39
+ tensors_to_save = {}
40
+ # Metadata will be stored separately
41
+ metadata = {}
42
+
43
+ for key, features_list in features_dict.items():
44
+ feature_metadata_list = []
45
+
46
+ for idx, feature in enumerate(features_list):
47
+ # Create a unique key for this tensor in the safetensors file
48
+ safetensors_key = f"{key}_{idx}"
49
+
50
+ # Convert sparse matrices to dense if needed
51
+ if feature.is_sparse():
52
+ # For sparse matrices, use the COO format
53
+ coo = feature.features.tocoo() # type:ignore[union-attr]
54
+ # Save data, row indices and col indices separately
55
+ tensors_to_save[f"{safetensors_key}_data"] = coo.data
56
+ tensors_to_save[f"{safetensors_key}_row"] = coo.row
57
+ tensors_to_save[f"{safetensors_key}_col"] = coo.col
58
+ else:
59
+ tensors_to_save[safetensors_key] = feature.features
60
+
61
+ # Store metadata
62
+ metadata_item = FeatureMetadata(
63
+ data_type=feature.type,
64
+ attribute=feature.attribute,
65
+ origin=feature.origin,
66
+ is_sparse=feature.is_sparse(),
67
+ shape=feature.features.shape,
68
+ safetensors_key=safetensors_key,
69
+ )
70
+ feature_metadata_list.append(vars(metadata_item))
71
+
72
+ metadata[key] = feature_metadata_list
73
+
74
+ # Save tensors
75
+ save_file(tensors_to_save, file_name)
76
+
77
+ return metadata
78
+
79
+
80
+ def load_features(
81
+ filename: str, metadata: Dict[str, Any]
82
+ ) -> Dict[Text, List[Features]]:
83
+ """Load Features dictionary from disk.
84
+
85
+ Args:
86
+ filename: File name of the safetensors file.
87
+ metadata: Metadata to reconstruct the features.
88
+
89
+ Returns:
90
+ Dictionary mapping strings to lists of Features objects
91
+ """
92
+ # Load tensors
93
+ tensors = load_file(filename)
94
+
95
+ # Reconstruct the features dictionary
96
+ features_dict: Dict[Text, List[Features]] = {}
97
+
98
+ for key, feature_metadata_list in metadata.items():
99
+ features_list = []
100
+
101
+ for meta in feature_metadata_list:
102
+ safetensors_key = meta["safetensors_key"]
103
+
104
+ if meta["is_sparse"]:
105
+ # Reconstruct sparse matrix from COO format
106
+ data = tensors[f"{safetensors_key}_data"]
107
+ row = tensors[f"{safetensors_key}_row"]
108
+ col = tensors[f"{safetensors_key}_col"]
109
+
110
+ features_matrix = scipy.sparse.coo_matrix(
111
+ (data, (row, col)), shape=tuple(meta["shape"])
112
+ ).tocsr() # Convert back to CSR format
113
+ else:
114
+ features_matrix = tensors[safetensors_key]
115
+
116
+ # Reconstruct Features object
117
+ features = Features(
118
+ features=features_matrix,
119
+ feature_type=meta["data_type"],
120
+ attribute=meta["attribute"],
121
+ origin=meta["origin"],
122
+ )
123
+
124
+ features_list.append(features)
125
+
126
+ features_dict[key] = features_list
127
+
128
+ return features_dict
129
+
130
+
13
131
  class Features:
14
132
  """Stores the features produced by any featurizer."""
15
133
 
@@ -107,8 +107,7 @@ class AzureOpenAIClientConfig:
107
107
 
108
108
  @classmethod
109
109
  def from_dict(cls, config: dict) -> "AzureOpenAIClientConfig":
110
- """
111
- Initializes a dataclass from the passed config.
110
+ """Initializes a dataclass from the passed config.
112
111
 
113
112
  Args:
114
113
  config: (dict) The config from which to initialize.
@@ -175,7 +174,10 @@ def is_azure_openai_config(config: dict) -> bool:
175
174
 
176
175
  # Case: Configuration contains `deployment` key
177
176
  # (specific to Azure OpenAI configuration)
178
- if config.get(DEPLOYMENT_CONFIG_KEY) is not None:
177
+ if (
178
+ config.get(DEPLOYMENT_CONFIG_KEY) is not None
179
+ and config.get(PROVIDER_CONFIG_KEY) is None
180
+ ):
179
181
  return True
180
182
 
181
183
  return False