rasa-pro 3.11.0a4.dev3__py3-none-any.whl → 3.11.0rc1__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 (163) 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 +4 -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/utils.py +5 -0
  19. rasa/cli/x.py +8 -8
  20. rasa/constants.py +1 -1
  21. rasa/core/channels/channel.py +3 -0
  22. rasa/core/channels/inspector/dist/assets/{arc-6852c607.js → arc-bc141fb2.js} +1 -1
  23. rasa/core/channels/inspector/dist/assets/{c4Diagram-d0fbc5ce-acc952b2.js → c4Diagram-d0fbc5ce-be2db283.js} +1 -1
  24. rasa/core/channels/inspector/dist/assets/{classDiagram-936ed81e-848a7597.js → classDiagram-936ed81e-55366915.js} +1 -1
  25. rasa/core/channels/inspector/dist/assets/{classDiagram-v2-c3cb15f1-a73d3e68.js → classDiagram-v2-c3cb15f1-bb529518.js} +1 -1
  26. rasa/core/channels/inspector/dist/assets/{createText-62fc7601-e5ee049d.js → createText-62fc7601-b0ec81d6.js} +1 -1
  27. rasa/core/channels/inspector/dist/assets/{edges-f2ad444c-771e517e.js → edges-f2ad444c-6166330c.js} +1 -1
  28. rasa/core/channels/inspector/dist/assets/{erDiagram-9d236eb7-aa347178.js → erDiagram-9d236eb7-5ccc6a8e.js} +1 -1
  29. rasa/core/channels/inspector/dist/assets/{flowDb-1972c806-651fc57d.js → flowDb-1972c806-fca3bfe4.js} +1 -1
  30. rasa/core/channels/inspector/dist/assets/{flowDiagram-7ea5b25a-ca67804f.js → flowDiagram-7ea5b25a-4739080f.js} +1 -1
  31. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-855bc5b3-736177bf.js +1 -0
  32. rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-abe16c3d-2dbc568d.js → flowchart-elk-definition-abe16c3d-7c1b0e0f.js} +1 -1
  33. rasa/core/channels/inspector/dist/assets/{ganttDiagram-9b5ea136-25a65bd8.js → ganttDiagram-9b5ea136-772fd050.js} +1 -1
  34. rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-99d0ae7c-fdc7378d.js → gitGraphDiagram-99d0ae7c-8eae1dc9.js} +1 -1
  35. rasa/core/channels/inspector/dist/assets/{index-2c4b9a3b-6f1fd606.js → index-2c4b9a3b-f55afcdf.js} +1 -1
  36. rasa/core/channels/inspector/dist/assets/{index-efdd30c1.js → index-e7cef9de.js} +68 -68
  37. rasa/core/channels/inspector/dist/assets/{infoDiagram-736b4530-cb1a041a.js → infoDiagram-736b4530-124d4a14.js} +1 -1
  38. rasa/core/channels/inspector/dist/assets/{journeyDiagram-df861f2b-14609879.js → journeyDiagram-df861f2b-7c4fae44.js} +1 -1
  39. rasa/core/channels/inspector/dist/assets/{layout-2490f52b.js → layout-b9885fb6.js} +1 -1
  40. rasa/core/channels/inspector/dist/assets/{line-40186f1f.js → line-7c59abb6.js} +1 -1
  41. rasa/core/channels/inspector/dist/assets/{linear-08814e93.js → linear-4776f780.js} +1 -1
  42. rasa/core/channels/inspector/dist/assets/{mindmap-definition-beec6740-1a534584.js → mindmap-definition-beec6740-2332c46c.js} +1 -1
  43. rasa/core/channels/inspector/dist/assets/{pieDiagram-dbbf0591-72397b61.js → pieDiagram-dbbf0591-8fb39303.js} +1 -1
  44. rasa/core/channels/inspector/dist/assets/{quadrantDiagram-4d7f4fd6-3bb0b6a3.js → quadrantDiagram-4d7f4fd6-3c7180a2.js} +1 -1
  45. rasa/core/channels/inspector/dist/assets/{requirementDiagram-6fc4c22a-57334f61.js → requirementDiagram-6fc4c22a-e910bcb8.js} +1 -1
  46. rasa/core/channels/inspector/dist/assets/{sankeyDiagram-8f13d901-111e1297.js → sankeyDiagram-8f13d901-ead16c89.js} +1 -1
  47. rasa/core/channels/inspector/dist/assets/{sequenceDiagram-b655622a-10bcfe62.js → sequenceDiagram-b655622a-29a02a19.js} +1 -1
  48. rasa/core/channels/inspector/dist/assets/{stateDiagram-59f0c015-acaf7513.js → stateDiagram-59f0c015-042b3137.js} +1 -1
  49. rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-2b26beab-3ec2a235.js → stateDiagram-v2-2b26beab-2178c0f3.js} +1 -1
  50. rasa/core/channels/inspector/dist/assets/{styles-080da4f6-62730289.js → styles-080da4f6-23ffa4fc.js} +1 -1
  51. rasa/core/channels/inspector/dist/assets/{styles-3dcbcfbf-5284ee76.js → styles-3dcbcfbf-94f59763.js} +1 -1
  52. rasa/core/channels/inspector/dist/assets/{styles-9c745c82-642435e3.js → styles-9c745c82-78a6bebc.js} +1 -1
  53. rasa/core/channels/inspector/dist/assets/{svgDrawCommon-4835440b-b250a350.js → svgDrawCommon-4835440b-eae2a6f6.js} +1 -1
  54. rasa/core/channels/inspector/dist/assets/{timeline-definition-5b62e21b-c2b147ed.js → timeline-definition-5b62e21b-5c968d92.js} +1 -1
  55. rasa/core/channels/inspector/dist/assets/{xychartDiagram-2b33534f-f92cfea9.js → xychartDiagram-2b33534f-fd3db0d5.js} +1 -1
  56. rasa/core/channels/inspector/dist/index.html +1 -1
  57. rasa/core/channels/inspector/src/App.tsx +1 -1
  58. rasa/core/channels/inspector/src/helpers/audiostream.ts +77 -16
  59. rasa/core/channels/socketio.py +2 -1
  60. rasa/core/channels/telegram.py +1 -1
  61. rasa/core/channels/twilio.py +1 -1
  62. rasa/core/channels/voice_ready/jambonz.py +2 -2
  63. rasa/core/channels/voice_stream/asr/asr_event.py +5 -0
  64. rasa/core/channels/voice_stream/asr/azure.py +122 -0
  65. rasa/core/channels/voice_stream/asr/deepgram.py +16 -6
  66. rasa/core/channels/voice_stream/audio_bytes.py +1 -0
  67. rasa/core/channels/voice_stream/browser_audio.py +31 -8
  68. rasa/core/channels/voice_stream/call_state.py +23 -0
  69. rasa/core/channels/voice_stream/tts/azure.py +6 -2
  70. rasa/core/channels/voice_stream/tts/cartesia.py +10 -6
  71. rasa/core/channels/voice_stream/tts/tts_engine.py +1 -0
  72. rasa/core/channels/voice_stream/twilio_media_streams.py +27 -18
  73. rasa/core/channels/voice_stream/util.py +4 -4
  74. rasa/core/channels/voice_stream/voice_channel.py +177 -39
  75. rasa/core/featurizers/single_state_featurizer.py +22 -1
  76. rasa/core/featurizers/tracker_featurizers.py +115 -18
  77. rasa/core/nlg/contextual_response_rephraser.py +16 -22
  78. rasa/core/persistor.py +86 -39
  79. rasa/core/policies/enterprise_search_policy.py +159 -60
  80. rasa/core/policies/flows/flow_executor.py +7 -4
  81. rasa/core/policies/intentless_policy.py +120 -22
  82. rasa/core/policies/ted_policy.py +58 -33
  83. rasa/core/policies/unexpected_intent_policy.py +15 -7
  84. rasa/core/processor.py +25 -0
  85. rasa/core/training/interactive.py +34 -35
  86. rasa/core/utils.py +8 -3
  87. rasa/dialogue_understanding/coexistence/llm_based_router.py +58 -16
  88. rasa/dialogue_understanding/commands/change_flow_command.py +6 -0
  89. rasa/dialogue_understanding/commands/user_silence_command.py +59 -0
  90. rasa/dialogue_understanding/commands/utils.py +5 -0
  91. rasa/dialogue_understanding/generator/constants.py +4 -0
  92. rasa/dialogue_understanding/generator/flow_retrieval.py +65 -3
  93. rasa/dialogue_understanding/generator/llm_based_command_generator.py +68 -26
  94. rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +57 -8
  95. rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +64 -7
  96. rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +39 -0
  97. rasa/dialogue_understanding/patterns/user_silence.py +37 -0
  98. rasa/e2e_test/e2e_test_runner.py +4 -2
  99. rasa/e2e_test/utils/io.py +1 -1
  100. rasa/engine/validation.py +297 -7
  101. rasa/model_manager/config.py +15 -3
  102. rasa/model_manager/model_api.py +15 -7
  103. rasa/model_manager/runner_service.py +8 -6
  104. rasa/model_manager/socket_bridge.py +6 -3
  105. rasa/model_manager/trainer_service.py +7 -5
  106. rasa/model_manager/utils.py +28 -7
  107. rasa/model_service.py +6 -2
  108. rasa/model_training.py +2 -0
  109. rasa/nlu/classifiers/diet_classifier.py +38 -25
  110. rasa/nlu/classifiers/logistic_regression_classifier.py +22 -9
  111. rasa/nlu/classifiers/sklearn_intent_classifier.py +37 -16
  112. rasa/nlu/extractors/crf_entity_extractor.py +93 -50
  113. rasa/nlu/featurizers/sparse_featurizer/count_vectors_featurizer.py +45 -16
  114. rasa/nlu/featurizers/sparse_featurizer/lexical_syntactic_featurizer.py +52 -17
  115. rasa/nlu/featurizers/sparse_featurizer/regex_featurizer.py +5 -3
  116. rasa/shared/constants.py +36 -3
  117. rasa/shared/core/constants.py +7 -0
  118. rasa/shared/core/domain.py +26 -0
  119. rasa/shared/core/flows/flow.py +5 -0
  120. rasa/shared/core/flows/flows_yaml_schema.json +10 -0
  121. rasa/shared/core/flows/utils.py +39 -0
  122. rasa/shared/core/flows/validation.py +96 -0
  123. rasa/shared/core/slots.py +5 -0
  124. rasa/shared/nlu/training_data/features.py +120 -2
  125. rasa/shared/providers/_configs/azure_openai_client_config.py +5 -3
  126. rasa/shared/providers/_configs/litellm_router_client_config.py +200 -0
  127. rasa/shared/providers/_configs/model_group_config.py +167 -0
  128. rasa/shared/providers/_configs/openai_client_config.py +1 -1
  129. rasa/shared/providers/_configs/rasa_llm_client_config.py +73 -0
  130. rasa/shared/providers/_configs/self_hosted_llm_client_config.py +1 -0
  131. rasa/shared/providers/_configs/utils.py +16 -0
  132. rasa/shared/providers/embedding/_base_litellm_embedding_client.py +12 -15
  133. rasa/shared/providers/embedding/azure_openai_embedding_client.py +54 -21
  134. rasa/shared/providers/embedding/litellm_router_embedding_client.py +135 -0
  135. rasa/shared/providers/llm/_base_litellm_client.py +31 -30
  136. rasa/shared/providers/llm/azure_openai_llm_client.py +50 -29
  137. rasa/shared/providers/llm/litellm_router_llm_client.py +127 -0
  138. rasa/shared/providers/llm/rasa_llm_client.py +112 -0
  139. rasa/shared/providers/llm/self_hosted_llm_client.py +1 -1
  140. rasa/shared/providers/mappings.py +19 -0
  141. rasa/shared/providers/router/__init__.py +0 -0
  142. rasa/shared/providers/router/_base_litellm_router_client.py +149 -0
  143. rasa/shared/providers/router/router_client.py +73 -0
  144. rasa/shared/utils/common.py +8 -0
  145. rasa/shared/utils/health_check.py +533 -0
  146. rasa/shared/utils/io.py +28 -6
  147. rasa/shared/utils/llm.py +350 -46
  148. rasa/shared/utils/yaml.py +11 -13
  149. rasa/studio/upload.py +64 -20
  150. rasa/telemetry.py +80 -17
  151. rasa/tracing/instrumentation/attribute_extractors.py +74 -17
  152. rasa/utils/io.py +0 -66
  153. rasa/utils/log_utils.py +9 -2
  154. rasa/utils/tensorflow/feature_array.py +366 -0
  155. rasa/utils/tensorflow/model_data.py +2 -193
  156. rasa/validator.py +70 -0
  157. rasa/version.py +1 -1
  158. {rasa_pro-3.11.0a4.dev3.dist-info → rasa_pro-3.11.0rc1.dist-info}/METADATA +10 -10
  159. {rasa_pro-3.11.0a4.dev3.dist-info → rasa_pro-3.11.0rc1.dist-info}/RECORD +162 -146
  160. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-855bc5b3-587d82d8.js +0 -1
  161. {rasa_pro-3.11.0a4.dev3.dist-info → rasa_pro-3.11.0rc1.dist-info}/NOTICE +0 -0
  162. {rasa_pro-3.11.0a4.dev3.dist-info → rasa_pro-3.11.0rc1.dist-info}/WHEEL +0 -0
  163. {rasa_pro-3.11.0a4.dev3.dist-info → rasa_pro-3.11.0rc1.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,533 @@
1
+ import os
2
+ from typing import Optional, Dict, Any
3
+
4
+ from rasa.shared.constants import (
5
+ LLM_API_HEALTH_CHECK_ENV_VAR,
6
+ MODELS_CONFIG_KEY,
7
+ MODEL_GROUP_ID_CONFIG_KEY,
8
+ LLM_API_HEALTH_CHECK_DEFAULT_VALUE,
9
+ )
10
+ from rasa.shared.exceptions import ProviderClientValidationError
11
+ from rasa.shared.providers.embedding.embedding_client import EmbeddingClient
12
+ from rasa.shared.providers.llm.llm_client import LLMClient
13
+ from rasa.shared.utils.cli import print_error_and_exit
14
+ from rasa.shared.utils.llm import llm_factory, structlogger, embedder_factory
15
+
16
+
17
+ def try_instantiate_llm_client(
18
+ custom_llm_config: Optional[Dict],
19
+ default_llm_config: Optional[Dict],
20
+ log_source_function: str,
21
+ log_source_component: str,
22
+ ) -> LLMClient:
23
+ """Validate llm configuration."""
24
+ try:
25
+ return llm_factory(custom_llm_config, default_llm_config)
26
+ except (ProviderClientValidationError, ValueError) as e:
27
+ structlogger.error(
28
+ f"{log_source_function}.llm_instantiation_failed",
29
+ message="Unable to instantiate LLM client.",
30
+ error=e,
31
+ )
32
+ print_error_and_exit(
33
+ f"Unable to create the LLM client for component - {log_source_component}. "
34
+ f"Please make sure you specified the required environment variables "
35
+ f"and configuration keys. "
36
+ f"Error: {e}"
37
+ )
38
+
39
+
40
+ def try_instantiate_embedder(
41
+ custom_embeddings_config: Optional[Dict],
42
+ default_embeddings_config: Optional[Dict],
43
+ log_source_function: str,
44
+ log_source_component: str,
45
+ ) -> EmbeddingClient:
46
+ """Validate embeddings configuration."""
47
+ try:
48
+ return embedder_factory(custom_embeddings_config, default_embeddings_config)
49
+ except (ProviderClientValidationError, ValueError) as e:
50
+ structlogger.error(
51
+ f"{log_source_function}.embedder_instantiation_failed",
52
+ message="Unable to instantiate Embedding client.",
53
+ error=e,
54
+ )
55
+ print_error_and_exit(
56
+ f"Unable to create the Embedding client for component - "
57
+ f"{log_source_component}. Please make sure you specified the required "
58
+ f"environment variables and configuration keys. Error: {e}"
59
+ )
60
+
61
+
62
+ def perform_training_time_llm_health_check(
63
+ custom_config: Optional[Dict[str, Any]],
64
+ default_config: Dict[str, Any],
65
+ log_source_function: str,
66
+ log_source_component: str,
67
+ ) -> Optional[str]:
68
+ """Try to instantiate the LLM Client to validate the provided config.
69
+ If the LLM_API_HEALTH_CHECK environment variable is true, perform a test call
70
+ to the LLM API. If config contains multiple models, perform a consistency
71
+ check for the model group.
72
+
73
+ This method supports both single model configurations and model group configurations
74
+ (configs that have the `models` key).
75
+
76
+ Returns:
77
+ model name from the API response or `None` if LLM_API_HEALTH_CHECK is false
78
+ """
79
+ llm_client = try_instantiate_llm_client(
80
+ custom_config, default_config, log_source_function, log_source_component
81
+ )
82
+
83
+ if (
84
+ os.getenv(
85
+ LLM_API_HEALTH_CHECK_ENV_VAR, LLM_API_HEALTH_CHECK_DEFAULT_VALUE
86
+ ).lower()
87
+ == "true"
88
+ ):
89
+ train_model_name: Optional[str] = None
90
+ if (
91
+ custom_config
92
+ and MODELS_CONFIG_KEY in custom_config
93
+ and len(custom_config[MODELS_CONFIG_KEY]) > 1
94
+ ):
95
+ train_model_name = perform_llm_model_group_consistency_check(
96
+ custom_config,
97
+ default_config,
98
+ log_source_function,
99
+ log_source_component,
100
+ )
101
+ else:
102
+ train_model_name = send_test_llm_api_request(
103
+ llm_client,
104
+ log_source_function,
105
+ log_source_component,
106
+ )
107
+ return train_model_name
108
+ else:
109
+ structlogger.warning(
110
+ f"{log_source_function}.perform_training_time_llm_health_check.disabled",
111
+ event_info=(
112
+ f"The {LLM_API_HEALTH_CHECK_ENV_VAR} environment variable is set "
113
+ f"to false, which will disable model consistency check. "
114
+ f"It is recommended to set this variable to true in production "
115
+ f"environments."
116
+ ),
117
+ )
118
+ return None
119
+
120
+
121
+ def perform_training_time_embeddings_health_check(
122
+ custom_config: Optional[Dict[str, Any]],
123
+ default_config: Dict[str, Any],
124
+ log_source_function: str,
125
+ log_source_component: str,
126
+ ) -> Optional[str]:
127
+ """Try to instantiate the Embedder to validate the provided config.
128
+ If the LLM_API_HEALTH_CHECK environment variable is true, perform a test call
129
+ to the Embeddings API. If config contains multiple models, perform a consistency
130
+ check for the model group.
131
+
132
+ This method supports both single model configurations and model group configurations
133
+ (configs that have the `models` key).
134
+
135
+ Returns:
136
+ model name from the API response or `None` if LLM_API_HEALTH_CHECK is false
137
+ """
138
+ if (
139
+ os.getenv(
140
+ LLM_API_HEALTH_CHECK_ENV_VAR, LLM_API_HEALTH_CHECK_DEFAULT_VALUE
141
+ ).lower()
142
+ == "true"
143
+ ):
144
+ train_model_name: Optional[str] = None
145
+ if (
146
+ custom_config
147
+ and MODELS_CONFIG_KEY in custom_config
148
+ and len(custom_config[MODELS_CONFIG_KEY]) > 1
149
+ ):
150
+ train_model_name = perform_embeddings_model_group_consistency_check(
151
+ custom_config,
152
+ default_config,
153
+ log_source_function,
154
+ log_source_component,
155
+ )
156
+ else:
157
+ embedder = try_instantiate_embedder(
158
+ custom_config, default_config, log_source_function, log_source_component
159
+ )
160
+ train_model_name = send_test_embeddings_api_request(
161
+ embedder, log_source_function, log_source_component
162
+ )
163
+ return train_model_name
164
+ else:
165
+ structlogger.warning(
166
+ f"{log_source_function}"
167
+ f".perform_training_time_embeddings_health_check.disabled",
168
+ event_info=(
169
+ f"The {LLM_API_HEALTH_CHECK_ENV_VAR} environment variable is set "
170
+ f"to false, which will disable model consistency check. "
171
+ f"It is recommended to set this variable to true in production "
172
+ f"environments."
173
+ ),
174
+ )
175
+ return None
176
+
177
+
178
+ def perform_inference_time_llm_health_check(
179
+ custom_config: Optional[Dict[str, Any]],
180
+ default_config: Dict[str, Any],
181
+ train_model_name: Optional[str],
182
+ log_source_function: str,
183
+ log_source_component: str,
184
+ ) -> None:
185
+ """If the LLM_API_HEALTH_CHECK environment variable is true, perform a test call
186
+ to the LLM API. If config contains multiple models, perform a consistency
187
+ check for the model group.
188
+ Compare the model name from the API response with the model name used for training
189
+ (if available) and raise an exception if they are different.
190
+
191
+ This method supports both single model configurations and model group configurations
192
+ (configs that have the `models` key).
193
+ """
194
+ if (
195
+ os.getenv(
196
+ LLM_API_HEALTH_CHECK_ENV_VAR, LLM_API_HEALTH_CHECK_DEFAULT_VALUE
197
+ ).lower()
198
+ == "true"
199
+ ):
200
+ structlogger.info(
201
+ f"{log_source_function}.perform_inference_time_llm_health_check",
202
+ event_info=(
203
+ f"Performing an inference-time health check on the LLM API for "
204
+ f"the component - {log_source_component}."
205
+ ),
206
+ config=custom_config,
207
+ )
208
+
209
+ inference_model_name: Optional[str] = None
210
+ if (
211
+ custom_config
212
+ and MODELS_CONFIG_KEY in custom_config
213
+ and len(custom_config[MODELS_CONFIG_KEY]) > 1
214
+ ):
215
+ inference_model_name = perform_llm_model_group_consistency_check(
216
+ custom_config,
217
+ default_config,
218
+ log_source_function,
219
+ log_source_component,
220
+ )
221
+ else:
222
+ llm_client = try_instantiate_llm_client(
223
+ custom_config,
224
+ default_config,
225
+ log_source_function,
226
+ log_source_component,
227
+ )
228
+ inference_model_name = send_test_llm_api_request(
229
+ llm_client,
230
+ log_source_function,
231
+ log_source_component,
232
+ )
233
+
234
+ if not inference_model_name:
235
+ structlogger.warning(
236
+ f"{log_source_function}"
237
+ f".perform_inference_time_llm_health_check.no_inference_model",
238
+ event_info=(
239
+ "Failed to perform model consistency check: "
240
+ "the API response does not contain a model name."
241
+ ),
242
+ )
243
+ elif not train_model_name:
244
+ structlogger.warning(
245
+ f"{log_source_function}"
246
+ f".perform_inference_time_llm_health_check.no_train_model",
247
+ event_info=(
248
+ f"The model was trained with {LLM_API_HEALTH_CHECK_ENV_VAR} "
249
+ f"environment variable set to false, so the model "
250
+ f"consistency check is not available."
251
+ ),
252
+ )
253
+ elif inference_model_name != train_model_name:
254
+ error_message = (
255
+ f"The LLM used to train the {log_source_component} "
256
+ f"({train_model_name}) is not the same as the LLM used for inference "
257
+ f"({inference_model_name}). Please verify your configuration."
258
+ )
259
+ structlogger.error(
260
+ f"{log_source_function}.train_inference_llm_model_mismatch",
261
+ event_info=error_message,
262
+ )
263
+ print_error_and_exit(error_message)
264
+ else:
265
+ structlogger.warning(
266
+ f"{log_source_function}.perform_inference_time_llm_health_check.disabled",
267
+ event_info=(
268
+ f"The {LLM_API_HEALTH_CHECK_ENV_VAR} environment variable is set "
269
+ f"to false, which will disable model consistency check. "
270
+ f"It is recommended to set this variable to true in production "
271
+ f"environments."
272
+ ),
273
+ )
274
+
275
+
276
+ def perform_inference_time_embeddings_health_check(
277
+ custom_config: Optional[Dict[str, Any]],
278
+ default_config: Dict[str, Any],
279
+ train_model_name: Optional[str],
280
+ log_source_function: str,
281
+ log_source_component: str,
282
+ ) -> None:
283
+ """If the LLM_API_HEALTH_CHECK environment variable is true, perform a test call
284
+ to the Embeddings API. If config contains multiple models, perform a consistency
285
+ check for the model group.
286
+ Compare the model name from the API response with the model name used for training
287
+ (if available) and raise an exception if they are different.
288
+
289
+ This method supports both single model configurations and model group configurations
290
+ (configs that have the `models` key).
291
+ """
292
+ if (
293
+ os.getenv(
294
+ LLM_API_HEALTH_CHECK_ENV_VAR, LLM_API_HEALTH_CHECK_DEFAULT_VALUE
295
+ ).lower()
296
+ == "true"
297
+ ):
298
+ structlogger.info(
299
+ f"{log_source_function}.perform_inference_time_embeddings_health_check",
300
+ event_info=(
301
+ f"Performing an inference-time health check on the Embeddings API for "
302
+ f"the component - {log_source_component}."
303
+ ),
304
+ config=custom_config,
305
+ )
306
+
307
+ inference_model_name: Optional[str] = None
308
+ if (
309
+ custom_config
310
+ and MODELS_CONFIG_KEY in custom_config
311
+ and len(custom_config[MODELS_CONFIG_KEY]) > 1
312
+ ):
313
+ inference_model_name = perform_embeddings_model_group_consistency_check(
314
+ custom_config,
315
+ default_config,
316
+ log_source_function,
317
+ log_source_component,
318
+ )
319
+ else:
320
+ embedder = try_instantiate_embedder(
321
+ custom_config, default_config, log_source_function, log_source_component
322
+ )
323
+ inference_model_name = send_test_embeddings_api_request(
324
+ embedder, log_source_function, log_source_component
325
+ )
326
+
327
+ if not inference_model_name:
328
+ structlogger.warning(
329
+ f"{log_source_function}"
330
+ f".perform_inference_time_embeddings_health_check.no_inference_model",
331
+ event_info=(
332
+ "Failed to perform embeddings model consistency check: "
333
+ "the API response does not contain a model name."
334
+ ),
335
+ )
336
+ elif not train_model_name:
337
+ structlogger.warning(
338
+ f"{log_source_function}"
339
+ f".perform_inference_time_embeddings_health_check.no_train_model",
340
+ event_info=(
341
+ f"The model was trained with {LLM_API_HEALTH_CHECK_ENV_VAR} "
342
+ f"environment variable set to false, so the model "
343
+ f"consistency check is not available."
344
+ ),
345
+ )
346
+ elif inference_model_name != train_model_name:
347
+ error_message = (
348
+ f"The Embeddings model used to train the {log_source_component} "
349
+ f"({train_model_name}) is not the same as the model used for inference "
350
+ f"({inference_model_name}). Please verify your configuration."
351
+ )
352
+ structlogger.error(
353
+ f"{log_source_function}.train_inference_embeddings_model_mismatch",
354
+ event_info=error_message,
355
+ )
356
+ print_error_and_exit(error_message)
357
+ else:
358
+ structlogger.warning(
359
+ f"{log_source_function}"
360
+ f".perform_inference_time_embeddings_health_check.disabled",
361
+ event_info=(
362
+ f"The {LLM_API_HEALTH_CHECK_ENV_VAR} environment variable is set "
363
+ f"to false, which will disable model consistency check. "
364
+ f"It is recommended to set this variable to true in production "
365
+ f"environments."
366
+ ),
367
+ )
368
+
369
+
370
+ def perform_llm_model_group_consistency_check(
371
+ custom_config: Dict[str, Any],
372
+ default_config: Dict[str, Any],
373
+ log_source_function: str,
374
+ log_source_component: str,
375
+ ) -> Optional[str]:
376
+ """Perform a consistency check for multiple models inside a model group.
377
+
378
+ This function checks if all models within a model group are consistent by verifying
379
+ that they all return the same model name from the LLM API health check.
380
+
381
+ This method supports only model group configuration (config that have the `models`
382
+ key) and will ignore single model configuration.
383
+
384
+ Returns:
385
+ The model name if all models are consistent, otherwise raises
386
+ an InvalidConfigException.
387
+
388
+ Raises:
389
+ InvalidConfigException: If the model group contains different models.
390
+ """
391
+ if not custom_config or MODELS_CONFIG_KEY not in custom_config:
392
+ return None
393
+
394
+ model_names = set()
395
+ for model_config in custom_config[MODELS_CONFIG_KEY]:
396
+ llm_client = try_instantiate_llm_client(
397
+ model_config, default_config, log_source_function, log_source_component
398
+ )
399
+ model_name = send_test_llm_api_request(
400
+ llm_client, log_source_function, log_source_component
401
+ )
402
+ model_names.add(model_name)
403
+
404
+ if len(model_names) > 1:
405
+ error_message = (
406
+ f"The model group {custom_config.get(MODEL_GROUP_ID_CONFIG_KEY)} used by "
407
+ f"{log_source_component} component is inconsistent. "
408
+ f"It contains different models: {model_names}. "
409
+ f"Please verify your configuration."
410
+ )
411
+ structlogger.error(
412
+ f"{log_source_function}.inconsistent_model_group", event_info=error_message
413
+ )
414
+ print_error_and_exit(error_message)
415
+
416
+ return model_names.pop() if len(model_names) > 0 else None
417
+
418
+
419
+ def send_test_llm_api_request(
420
+ llm_client: LLMClient, log_source_function: str, log_source_component: str
421
+ ) -> Optional[str]:
422
+ """Sends a test request to the LLM API to perform a health check.
423
+
424
+ Returns:
425
+ The model name from the API response.
426
+
427
+ Raises:
428
+ Exception: If the API call fails.
429
+ """
430
+ structlogger.info(
431
+ f"{log_source_function}.send_test_llm_api_request",
432
+ event_info=(
433
+ f"Sending a test LLM API request for the component - "
434
+ f"{log_source_component}."
435
+ ),
436
+ config=llm_client.config,
437
+ )
438
+ try:
439
+ response = llm_client.completion("hello")
440
+ return response.model
441
+ except Exception as e:
442
+ structlogger.error(
443
+ f"{log_source_function}.send_test_llm_api_request_failed",
444
+ event_info="Test call to the LLM API failed.",
445
+ error=e,
446
+ )
447
+ print_error_and_exit(
448
+ f"Test call to the LLM API failed for component - {log_source_component}. "
449
+ f"Error: {e}"
450
+ )
451
+
452
+
453
+ def send_test_embeddings_api_request(
454
+ embedder: EmbeddingClient, log_source_function: str, log_source_component: str
455
+ ) -> Optional[str]:
456
+ """Sends a test request to the Embeddings API to perform a health check.
457
+
458
+ Returns:
459
+ The model name from the API response.
460
+
461
+ Raises:
462
+ Exception: If the API call fails.
463
+ """
464
+ structlogger.info(
465
+ f"{log_source_function}.send_test_embeddings_api_request",
466
+ event_info=(
467
+ f"Sending a test Embeddings API request for the component - "
468
+ f"{log_source_component}."
469
+ ),
470
+ config=embedder.config,
471
+ )
472
+ try:
473
+ response = embedder.embed(["hello"])
474
+ return response.model
475
+ except Exception as e:
476
+ structlogger.error(
477
+ f"{log_source_function}.send_test_llm_api_request_failed",
478
+ event_info="Test call to the Embeddings API failed.",
479
+ error=e,
480
+ )
481
+ print_error_and_exit(
482
+ f"Test call to the Embeddings API failed for component - "
483
+ f"{log_source_component}. Error: {e}"
484
+ )
485
+
486
+
487
+ def perform_embeddings_model_group_consistency_check(
488
+ custom_config: Dict[str, Any],
489
+ default_config: Dict[str, Any],
490
+ log_source_function: str,
491
+ log_source_component: str,
492
+ ) -> Optional[str]:
493
+ """Perform a consistency check for multiple embeddings models inside a model group.
494
+
495
+ This function checks if all models within a model group are consistent by verifying
496
+ that they all return the same model name from the Embeddings API health check.
497
+
498
+ This method supports only model group configuration (config that have the `models`
499
+ key) and will ignore single model configuration.
500
+
501
+ Returns:
502
+ The model name if all models are consistent, otherwise raises
503
+ an InvalidConfigException.
504
+
505
+ Raises:
506
+ InvalidConfigException: If the model group contains different models.
507
+ """
508
+ if not custom_config or MODELS_CONFIG_KEY not in custom_config:
509
+ return None
510
+
511
+ model_names = set()
512
+ for model_config in custom_config[MODELS_CONFIG_KEY]:
513
+ embedder = try_instantiate_embedder(
514
+ custom_config, default_config, log_source_function, log_source_component
515
+ )
516
+ model_name = send_test_embeddings_api_request(
517
+ embedder, log_source_function, log_source_component
518
+ )
519
+ model_names.add(model_name)
520
+
521
+ if len(model_names) > 1:
522
+ error_message = (
523
+ f"The embeddings model group {custom_config.get(MODEL_GROUP_ID_CONFIG_KEY)}"
524
+ f" used by {log_source_component} component is inconsistent. "
525
+ f"It contains different models: {model_names}. "
526
+ f"Please verify your configuration."
527
+ )
528
+ structlogger.error(
529
+ f"{log_source_function}.inconsistent_embeddings", event_info=error_message
530
+ )
531
+ print_error_and_exit(error_message)
532
+
533
+ return model_names.pop() if len(model_names) > 0 else None
rasa/shared/utils/io.py CHANGED
@@ -1,18 +1,19 @@
1
- from collections import OrderedDict
2
- from functools import wraps
3
- from hashlib import md5
4
1
  import asyncio
5
2
  import errno
6
3
  import glob
7
4
  import json
8
5
  import logging
9
6
  import os
7
+ import random
8
+ import string
10
9
  import sys
10
+ import warnings
11
+ from collections import OrderedDict
12
+ from functools import wraps
13
+ from hashlib import md5
11
14
  from pathlib import Path
12
15
  from typing import Any, cast, Callable, Dict, List, Optional, Text, Type, TypeVar, Union
13
- import warnings
14
- import random
15
- import string
16
+
16
17
  import portalocker
17
18
 
18
19
  from rasa.shared.constants import (
@@ -475,3 +476,24 @@ def suppress_logs(log_level: int = logging.WARNING) -> Callable[[F], F]:
475
476
  return cast(F, sync_wrapper)
476
477
 
477
478
  return decorator
479
+
480
+
481
+ def resolve_environment_variables(
482
+ value: Union[str, List[Any], Dict[str, Any]],
483
+ ) -> Union[str, List[Any], Dict[str, Any]]:
484
+ """Resolve environment variables in a string, list, or dictionary.
485
+
486
+ Args:
487
+ value: The value to resolve environment variables in.
488
+
489
+ Returns:
490
+ The value with environment variables resolved.
491
+ """
492
+ if isinstance(value, str):
493
+ return os.path.expandvars(value)
494
+ elif isinstance(value, list):
495
+ return [resolve_environment_variables(item) for item in value]
496
+ elif isinstance(value, dict):
497
+ return {key: resolve_environment_variables(val) for key, val in value.items()}
498
+ else:
499
+ return value