rasa-pro 3.11.0rc1__py3-none-any.whl → 3.11.0rc3__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 (66) hide show
  1. rasa/cli/inspect.py +2 -0
  2. rasa/cli/studio/studio.py +18 -8
  3. rasa/core/actions/action_repeat_bot_messages.py +17 -0
  4. rasa/core/channels/channel.py +17 -0
  5. rasa/core/channels/development_inspector.py +4 -1
  6. rasa/core/channels/voice_ready/audiocodes.py +15 -4
  7. rasa/core/channels/voice_ready/jambonz.py +13 -2
  8. rasa/core/channels/voice_ready/twilio_voice.py +6 -21
  9. rasa/core/channels/voice_stream/asr/asr_event.py +1 -1
  10. rasa/core/channels/voice_stream/asr/azure.py +5 -7
  11. rasa/core/channels/voice_stream/asr/deepgram.py +13 -11
  12. rasa/core/channels/voice_stream/voice_channel.py +61 -19
  13. rasa/core/nlg/contextual_response_rephraser.py +20 -12
  14. rasa/core/policies/enterprise_search_policy.py +32 -72
  15. rasa/core/policies/intentless_policy.py +34 -72
  16. rasa/dialogue_understanding/coexistence/llm_based_router.py +18 -33
  17. rasa/dialogue_understanding/generator/constants.py +0 -2
  18. rasa/dialogue_understanding/generator/flow_retrieval.py +33 -50
  19. rasa/dialogue_understanding/generator/llm_based_command_generator.py +12 -40
  20. rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +18 -20
  21. rasa/dialogue_understanding/generator/nlu_command_adapter.py +19 -1
  22. rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +26 -22
  23. rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +9 -0
  24. rasa/dialogue_understanding/processor/command_processor.py +21 -1
  25. rasa/e2e_test/e2e_test_case.py +85 -6
  26. rasa/engine/validation.py +88 -60
  27. rasa/model_service.py +3 -0
  28. rasa/nlu/tokenizers/whitespace_tokenizer.py +3 -14
  29. rasa/server.py +3 -1
  30. rasa/shared/constants.py +5 -5
  31. rasa/shared/core/constants.py +1 -1
  32. rasa/shared/core/domain.py +0 -26
  33. rasa/shared/core/flows/flows_list.py +5 -1
  34. rasa/shared/providers/_configs/litellm_router_client_config.py +29 -9
  35. rasa/shared/providers/embedding/_base_litellm_embedding_client.py +6 -14
  36. rasa/shared/providers/embedding/litellm_router_embedding_client.py +1 -1
  37. rasa/shared/providers/llm/_base_litellm_client.py +32 -1
  38. rasa/shared/providers/llm/litellm_router_llm_client.py +56 -1
  39. rasa/shared/providers/llm/self_hosted_llm_client.py +4 -28
  40. rasa/shared/providers/router/_base_litellm_router_client.py +35 -1
  41. rasa/shared/utils/common.py +1 -1
  42. rasa/shared/utils/health_check/__init__.py +0 -0
  43. rasa/shared/utils/health_check/embeddings_health_check_mixin.py +31 -0
  44. rasa/shared/utils/health_check/health_check.py +256 -0
  45. rasa/shared/utils/health_check/llm_health_check_mixin.py +31 -0
  46. rasa/shared/utils/llm.py +5 -2
  47. rasa/shared/utils/yaml.py +102 -62
  48. rasa/studio/auth.py +3 -5
  49. rasa/studio/config.py +13 -4
  50. rasa/studio/constants.py +1 -0
  51. rasa/studio/data_handler.py +10 -3
  52. rasa/studio/upload.py +21 -10
  53. rasa/telemetry.py +15 -1
  54. rasa/tracing/config.py +3 -1
  55. rasa/tracing/instrumentation/attribute_extractors.py +20 -0
  56. rasa/tracing/instrumentation/instrumentation.py +121 -0
  57. rasa/utils/common.py +5 -0
  58. rasa/utils/io.py +8 -16
  59. rasa/utils/sanic_error_handler.py +32 -0
  60. rasa/version.py +1 -1
  61. {rasa_pro-3.11.0rc1.dist-info → rasa_pro-3.11.0rc3.dist-info}/METADATA +3 -2
  62. {rasa_pro-3.11.0rc1.dist-info → rasa_pro-3.11.0rc3.dist-info}/RECORD +65 -61
  63. rasa/shared/utils/health_check.py +0 -533
  64. {rasa_pro-3.11.0rc1.dist-info → rasa_pro-3.11.0rc3.dist-info}/NOTICE +0 -0
  65. {rasa_pro-3.11.0rc1.dist-info → rasa_pro-3.11.0rc3.dist-info}/WHEEL +0 -0
  66. {rasa_pro-3.11.0rc1.dist-info → rasa_pro-3.11.0rc3.dist-info}/entry_points.txt +0 -0
@@ -27,12 +27,9 @@ from langchain.schema.embeddings import Embeddings
27
27
  from langchain_community.vectorstores.faiss import FAISS
28
28
  from langchain_community.vectorstores.utils import DistanceStrategy
29
29
 
30
- from rasa.dialogue_understanding.generator.constants import (
31
- TRAINED_EMBEDDINGS_CONFIG_KEY,
32
- )
33
30
  from rasa.engine.storage.resource import Resource
34
31
  from rasa.engine.storage.storage import ModelStorage
35
-
32
+ import rasa.shared.utils.io
36
33
  from rasa.shared.constants import (
37
34
  EMBEDDINGS_CONFIG_KEY,
38
35
  PROVIDER_CONFIG_KEY,
@@ -41,12 +38,15 @@ from rasa.shared.constants import (
41
38
  from rasa.shared.core.domain import Domain
42
39
  from rasa.shared.core.flows import FlowsList
43
40
  from rasa.shared.core.trackers import DialogueStateTracker
44
- from rasa.shared.exceptions import ProviderClientAPIException, FileIOException
41
+ from rasa.shared.exceptions import ProviderClientAPIException
45
42
  from rasa.shared.nlu.constants import TEXT, FLOWS_FROM_SEMANTIC_SEARCH
46
43
  from rasa.shared.nlu.training_data.message import Message
47
44
  from rasa.shared.providers.embedding._langchain_embedding_client_adapter import (
48
45
  _LangchainEmbeddingClientAdapter,
49
46
  )
47
+ from rasa.shared.utils.health_check.embeddings_health_check_mixin import (
48
+ EmbeddingsHealthCheckMixin,
49
+ )
50
50
  from rasa.shared.utils.llm import (
51
51
  tracker_as_readable_transcript,
52
52
  embedder_factory,
@@ -56,11 +56,6 @@ from rasa.shared.utils.llm import (
56
56
  allowed_values_for_slot,
57
57
  resolve_model_client_config,
58
58
  )
59
- from rasa.shared.utils.health_check import (
60
- perform_training_time_embeddings_health_check,
61
- perform_inference_time_embeddings_health_check,
62
- )
63
- from rasa.shared.utils.io import dump_obj_as_json_to_file, read_json_file
64
59
 
65
60
  DEFAULT_FLOW_DOCUMENT_TEMPLATE = importlib.resources.read_text(
66
61
  "rasa.dialogue_understanding.generator", "flow_document_template.jinja2"
@@ -85,7 +80,7 @@ DEFAULT_SHOULD_EMBED_SLOTS = True
85
80
  structlogger = structlog.get_logger()
86
81
 
87
82
 
88
- class FlowRetrieval:
83
+ class FlowRetrieval(EmbeddingsHealthCheckMixin):
89
84
  @classmethod
90
85
  def get_default_config(cls) -> Dict[str, Any]:
91
86
  """The default config for the flow retrieval."""
@@ -94,7 +89,6 @@ class FlowRetrieval:
94
89
  MAX_FLOWS_FROM_SEMANTIC_SEARCH_KEY: DEFAULT_MAX_FLOWS_FROM_SEMANTIC_SEARCH,
95
90
  TURNS_TO_EMBED_KEY: DEFAULT_TURNS_TO_EMBED,
96
91
  SHOULD_EMBED_SLOTS_KEY: DEFAULT_SHOULD_EMBED_SLOTS,
97
- TRAINED_EMBEDDINGS_CONFIG_KEY: None,
98
92
  }
99
93
 
100
94
  def __init__(
@@ -147,16 +141,6 @@ class FlowRetrieval:
147
141
 
148
142
  return config
149
143
 
150
- def train(self) -> None:
151
- self.config[TRAINED_EMBEDDINGS_CONFIG_KEY] = (
152
- perform_training_time_embeddings_health_check(
153
- self.config.get(EMBEDDINGS_CONFIG_KEY),
154
- DEFAULT_EMBEDDINGS_CONFIG,
155
- "flow_retrieval.train",
156
- FlowRetrieval.__name__,
157
- )
158
- )
159
-
160
144
  @classmethod
161
145
  def load(
162
146
  cls,
@@ -166,6 +150,18 @@ class FlowRetrieval:
166
150
  **kwargs: Any,
167
151
  ) -> "FlowRetrieval":
168
152
  """Load flow retrieval with previously populated FAISS vector store."""
153
+
154
+ # Perform health check on resolved embedding client config
155
+ embeddings_config = resolve_model_client_config(
156
+ config.get(EMBEDDINGS_CONFIG_KEY, {})
157
+ )
158
+ cls.perform_embeddings_health_check(
159
+ embeddings_config,
160
+ DEFAULT_EMBEDDINGS_CONFIG,
161
+ "flow_retrieval.load",
162
+ FlowRetrieval.__name__,
163
+ )
164
+
169
165
  # initialize base flow retrieval
170
166
  flow_retrieval = FlowRetrieval(config, model_storage, resource)
171
167
  # load vector store
@@ -174,30 +170,6 @@ class FlowRetrieval:
174
170
  )
175
171
  flow_retrieval.vector_store = vector_store
176
172
 
177
- persisted_config = None
178
- try:
179
- with model_storage.read_from(resource) as path:
180
- persisted_config = read_json_file(
181
- path / FLOW_RETRIEVAL_CONFIG_FILE_NAME
182
- )
183
- except (FileNotFoundError, FileIOException) as e:
184
- structlogger.warning(
185
- "flow_retrieval.load.failed", error=e, resource=resource.name
186
- )
187
-
188
- train_embeddings_name = (
189
- persisted_config.get(TRAINED_EMBEDDINGS_CONFIG_KEY, None)
190
- if persisted_config
191
- else None
192
- )
193
- perform_inference_time_embeddings_health_check(
194
- flow_retrieval.config.get(EMBEDDINGS_CONFIG_KEY),
195
- DEFAULT_EMBEDDINGS_CONFIG,
196
- train_embeddings_name,
197
- "flow_retrieval.load",
198
- FlowRetrieval.__name__,
199
- )
200
-
201
173
  return flow_retrieval
202
174
 
203
175
  @classmethod
@@ -243,10 +215,7 @@ class FlowRetrieval:
243
215
 
244
216
  def persist(self) -> None:
245
217
  self._persist_vector_store()
246
- with self._model_storage.write_to(self._resource) as path:
247
- dump_obj_as_json_to_file(
248
- path / FLOW_RETRIEVAL_CONFIG_FILE_NAME, self.config
249
- )
218
+ self._persist_config()
250
219
 
251
220
  def _persist_vector_store(self) -> None:
252
221
  """Persists the FAISS vector store."""
@@ -259,6 +228,12 @@ class FlowRetrieval:
259
228
  event_info="Vector store is None, not persisted.",
260
229
  )
261
230
 
231
+ def _persist_config(self) -> None:
232
+ with self._model_storage.write_to(self._resource) as path:
233
+ rasa.shared.utils.io.dump_obj_as_json_to_file(
234
+ path / FLOW_RETRIEVAL_CONFIG_FILE_NAME, self.config
235
+ )
236
+
262
237
  def populate(self, flows: FlowsList, domain: Domain) -> None:
263
238
  """Populates the vector store with embeddings generated from
264
239
  documents based on the flow descriptions, and flow slots
@@ -268,6 +243,14 @@ class FlowRetrieval:
268
243
  flows: List of flows to populate the vector store with.
269
244
  domain: The domain containing relevant slot information.
270
245
  """
246
+ # Perform health check before populating the vector store with flows
247
+ self.perform_embeddings_health_check(
248
+ self.config.get(EMBEDDINGS_CONFIG_KEY),
249
+ DEFAULT_EMBEDDINGS_CONFIG,
250
+ "flow_retrieval.train",
251
+ FlowRetrieval.__name__,
252
+ )
253
+
271
254
  flows_to_embedd = flows.exclude_link_only_flows()
272
255
  embeddings = self._create_embedder(self.config)
273
256
  documents = self._generate_flow_documents(flows_to_embedd, domain)
@@ -17,7 +17,6 @@ from rasa.dialogue_understanding.generator.constants import (
17
17
  FLOW_RETRIEVAL_KEY,
18
18
  FLOW_RETRIEVAL_ACTIVE_KEY,
19
19
  FLOW_RETRIEVAL_FLOW_THRESHOLD,
20
- TRAINED_MODEL_NAME_CONFIG_KEY,
21
20
  )
22
21
  from rasa.dialogue_understanding.generator.flow_retrieval import FlowRetrieval
23
22
  from rasa.engine.graph import GraphComponent, ExecutionContext
@@ -33,27 +32,26 @@ from rasa.shared.exceptions import ProviderClientAPIException
33
32
  from rasa.shared.nlu.constants import FLOWS_IN_PROMPT
34
33
  from rasa.shared.nlu.training_data.message import Message
35
34
  from rasa.shared.nlu.training_data.training_data import TrainingData
35
+ from rasa.shared.utils.health_check.llm_health_check_mixin import LLMHealthCheckMixin
36
36
  from rasa.shared.utils.llm import (
37
37
  allowed_values_for_slot,
38
38
  llm_factory,
39
39
  resolve_model_client_config,
40
40
  )
41
- from rasa.shared.utils.health_check import perform_training_time_llm_health_check
42
41
  from rasa.utils.log_utils import log_llm
43
42
 
44
43
  structlogger = structlog.get_logger()
45
44
 
46
45
 
47
- LLM_BASED_COMMAND_GENERATOR_CONFIG_FILE = "config.json"
48
-
49
-
50
46
  @DefaultV1Recipe.register(
51
47
  [
52
48
  DefaultV1Recipe.ComponentType.COMMAND_GENERATOR,
53
49
  ],
54
50
  is_trainable=True,
55
51
  )
56
- class LLMBasedCommandGenerator(GraphComponent, CommandGenerator, ABC):
52
+ class LLMBasedCommandGenerator(
53
+ LLMHealthCheckMixin, GraphComponent, CommandGenerator, ABC
54
+ ):
57
55
  """An abstract class defining interface and common functionality
58
56
  of an LLM-based command generators.
59
57
  """
@@ -106,11 +104,7 @@ class LLMBasedCommandGenerator(GraphComponent, CommandGenerator, ABC):
106
104
  @abstractmethod
107
105
  def persist(self) -> None:
108
106
  """Persist the component to disk for future loading."""
109
- # persist the config to store the resolved llm and embedding config
110
- with self._model_storage.write_to(self._resource) as path:
111
- rasa.shared.utils.io.dump_obj_as_json_to_file(
112
- path / LLM_BASED_COMMAND_GENERATOR_CONFIG_FILE, self.config
113
- )
107
+ pass
114
108
 
115
109
  @abstractmethod
116
110
  async def predict_commands(
@@ -173,13 +167,11 @@ class LLMBasedCommandGenerator(GraphComponent, CommandGenerator, ABC):
173
167
  """Train the llm based command generator. Stores all flows into a vector
174
168
  store.
175
169
  """
176
- self.config[TRAINED_MODEL_NAME_CONFIG_KEY] = (
177
- perform_training_time_llm_health_check(
178
- self.config.get(LLM_CONFIG_KEY),
179
- DEFAULT_LLM_CONFIG,
180
- "llm_based_command_generator.train",
181
- LLMBasedCommandGenerator.__name__,
182
- )
170
+ self.perform_llm_health_check(
171
+ self.config.get(LLM_CONFIG_KEY),
172
+ DEFAULT_LLM_CONFIG,
173
+ "llm_based_command_generator.train",
174
+ LLMBasedCommandGenerator.__name__,
183
175
  )
184
176
 
185
177
  if (
@@ -210,12 +202,11 @@ class LLMBasedCommandGenerator(GraphComponent, CommandGenerator, ABC):
210
202
  except Exception as e:
211
203
  structlogger.error(
212
204
  "llm_based_command_generator.train.failed",
213
- event_info="Flow retrieval store isinaccessible.",
205
+ event_info="Flow retrieval store is inaccessible.",
214
206
  error=e,
215
207
  )
216
208
  raise
217
- if self.flow_retrieval is not None:
218
- self.flow_retrieval.train()
209
+
219
210
  self.persist()
220
211
  return self._resource
221
212
 
@@ -251,25 +242,6 @@ class LLMBasedCommandGenerator(GraphComponent, CommandGenerator, ABC):
251
242
  )
252
243
  return None
253
244
 
254
- @classmethod
255
- def load_config_from_model_storage(
256
- cls,
257
- model_storage: ModelStorage,
258
- resource: Resource,
259
- ) -> Optional[Text]:
260
- try:
261
- with model_storage.read_from(resource) as path:
262
- return rasa.shared.utils.io.read_json_file(
263
- path / LLM_BASED_COMMAND_GENERATOR_CONFIG_FILE
264
- )
265
- except (FileNotFoundError, FileIOException) as e:
266
- structlogger.warning(
267
- "llm_based_command_generator.load_config.failed",
268
- error=e,
269
- resource=resource.name,
270
- )
271
- return None
272
-
273
245
  @classmethod
274
246
  def load_flow_retrival(
275
247
  cls,
@@ -24,7 +24,6 @@ from rasa.dialogue_understanding.generator.constants import (
24
24
  LLM_CONFIG_KEY,
25
25
  USER_INPUT_CONFIG_KEY,
26
26
  FLOW_RETRIEVAL_KEY,
27
- TRAINED_MODEL_NAME_CONFIG_KEY,
28
27
  DEFAULT_LLM_CONFIG,
29
28
  )
30
29
  from rasa.dialogue_understanding.generator.flow_retrieval import FlowRetrieval
@@ -60,7 +59,6 @@ from rasa.shared.utils.llm import (
60
59
  allowed_values_for_slot,
61
60
  resolve_model_client_config,
62
61
  )
63
- from rasa.shared.utils.health_check import perform_inference_time_llm_health_check
64
62
 
65
63
  # multistep template keys
66
64
  HANDLE_FLOWS_KEY = "handle_flows"
@@ -77,6 +75,7 @@ DEFAULT_HANDLE_FLOWS_TEMPLATE = importlib.resources.read_text(
77
75
  DEFAULT_FILL_SLOTS_TEMPLATE = importlib.resources.read_text(
78
76
  "rasa.dialogue_understanding.generator.multi_step", "fill_slots_prompt.jinja2"
79
77
  ).strip()
78
+ MULTI_STEP_LLM_COMMAND_GENERATOR_CONFIG_FILE = "config.json"
80
79
 
81
80
  # dictionary of template names and associated file names and default values
82
81
  PROMPT_TEMPLATES = {
@@ -145,15 +144,18 @@ class MultiStepLLMCommandGenerator(LLMBasedCommandGenerator):
145
144
  **kwargs: Any,
146
145
  ) -> "MultiStepLLMCommandGenerator":
147
146
  """Loads trained component (see parent class for full docstring)."""
148
- prompts = cls._load_prompt_templates(model_storage, resource)
149
147
 
150
- persisted_config = cls.load_config_from_model_storage(model_storage, resource)
151
- train_model_name = (
152
- persisted_config.get(TRAINED_MODEL_NAME_CONFIG_KEY, None)
153
- if persisted_config
154
- else None
148
+ # Perform health check of the LLM client config
149
+ llm_config = resolve_model_client_config(config.get(LLM_CONFIG_KEY, {}))
150
+ cls.perform_llm_health_check(
151
+ llm_config,
152
+ DEFAULT_LLM_CONFIG,
153
+ "multi_step_llm_command_generator.load",
154
+ MultiStepLLMCommandGenerator.__name__,
155
155
  )
156
156
 
157
+ prompts = cls._load_prompt_templates(model_storage, resource)
158
+
157
159
  # init base command generator
158
160
  command_generator = cls(config, model_storage, resource, prompts)
159
161
  # load flow retrieval if enabled
@@ -162,23 +164,12 @@ class MultiStepLLMCommandGenerator(LLMBasedCommandGenerator):
162
164
  command_generator.config, model_storage, resource
163
165
  )
164
166
 
165
- perform_inference_time_llm_health_check(
166
- command_generator.config.get(LLM_CONFIG_KEY),
167
- DEFAULT_LLM_CONFIG,
168
- train_model_name,
169
- "multi_step_llm_command_generator.load",
170
- MultiStepLLMCommandGenerator.__name__,
171
- )
172
-
173
167
  return command_generator
174
168
 
175
169
  def persist(self) -> None:
176
170
  """Persist this component to disk for future loading."""
177
- super().persist()
178
-
179
- # persist prompt template
180
171
  self._persist_prompt_templates()
181
- # persist flow retrieval
172
+ self._persist_config()
182
173
  if self.flow_retrieval is not None:
183
174
  self.flow_retrieval.persist()
184
175
 
@@ -411,6 +402,13 @@ class MultiStepLLMCommandGenerator(LLMBasedCommandGenerator):
411
402
  file_path = path / file_name
412
403
  rasa.shared.utils.io.write_text_file(template, file_path)
413
404
 
405
+ def _persist_config(self) -> None:
406
+ """Persist config as a source of truth for resolved clients."""
407
+ with self._model_storage.write_to(self._resource) as path:
408
+ rasa.shared.utils.io.dump_obj_as_json_to_file(
409
+ path / MULTI_STEP_LLM_COMMAND_GENERATOR_CONFIG_FILE, self.config
410
+ )
411
+
414
412
  async def _predict_commands_with_multi_step(
415
413
  self,
416
414
  message: Message,
@@ -19,6 +19,7 @@ from rasa.engine.storage.storage import ModelStorage
19
19
  from rasa.shared.constants import ROUTE_TO_CALM_SLOT
20
20
  from rasa.shared.core.domain import Domain
21
21
  from rasa.shared.core.flows.flows_list import FlowsList
22
+ from rasa.shared.core.flows.steps import CollectInformationFlowStep
22
23
  from rasa.shared.core.slot_mappings import (
23
24
  SlotFillingManager,
24
25
  extract_slot_value,
@@ -217,7 +218,24 @@ def _issue_set_slot_commands(
217
218
  commands: List[Command] = []
218
219
  domain = domain if domain else Domain.empty()
219
220
  slot_filling_manager = SlotFillingManager(domain, tracker, message)
220
- available_slot_names = flows.available_slot_names()
221
+
222
+ # only use slots that don't have ask_before_filling set to True
223
+ available_slot_names = flows.available_slot_names(ask_before_filling=False)
224
+
225
+ # check if the current step is a CollectInformationFlowStep
226
+ # in case it has ask_before_filling set to True, we need to add the
227
+ # slot to the available_slot_names
228
+ if tracker.active_flow:
229
+ flow = flows.flow_by_id(tracker.active_flow)
230
+ step_id = tracker.current_step_id
231
+ if flow is not None:
232
+ current_step = flow.step_by_id(step_id)
233
+ if (
234
+ current_step
235
+ and isinstance(current_step, CollectInformationFlowStep)
236
+ and current_step.ask_before_filling
237
+ ):
238
+ available_slot_names.add(current_step.collect)
221
239
 
222
240
  for _, slot in tracker.slots.items():
223
241
  # if a slot is not collected in available flows,
@@ -23,7 +23,6 @@ from rasa.dialogue_understanding.generator.constants import (
23
23
  USER_INPUT_CONFIG_KEY,
24
24
  FLOW_RETRIEVAL_KEY,
25
25
  DEFAULT_LLM_CONFIG,
26
- TRAINED_MODEL_NAME_CONFIG_KEY,
27
26
  )
28
27
  from rasa.dialogue_understanding.generator.flow_retrieval import (
29
28
  FlowRetrieval,
@@ -54,7 +53,6 @@ from rasa.shared.utils.llm import (
54
53
  sanitize_message_for_prompt,
55
54
  resolve_model_client_config,
56
55
  )
57
- from rasa.shared.utils.health_check import perform_inference_time_llm_health_check
58
56
  from rasa.utils.beta import ensure_beta_feature_is_enabled, BetaNotEnabledException
59
57
  from rasa.utils.log_utils import log_llm
60
58
 
@@ -64,6 +62,7 @@ DEFAULT_COMMAND_PROMPT_TEMPLATE = importlib.resources.read_text(
64
62
  "rasa.dialogue_understanding.generator.single_step",
65
63
  "command_prompt_template.jinja2",
66
64
  )
65
+ SINGLE_STEP_LLM_COMMAND_GENERATOR_CONFIG_FILE = "config.json"
67
66
 
68
67
  structlogger = structlog.get_logger()
69
68
 
@@ -114,6 +113,7 @@ class SingleStepLLMCommandGenerator(LLMBasedCommandGenerator):
114
113
  )
115
114
 
116
115
  self.trace_prompt_tokens = self.config.get("trace_prompt_tokens", False)
116
+ self.repeat_command_enabled = self.is_repeat_command_enabled()
117
117
 
118
118
  ### Implementations of LLMBasedCommandGenerator parent
119
119
  @staticmethod
@@ -137,6 +137,16 @@ class SingleStepLLMCommandGenerator(LLMBasedCommandGenerator):
137
137
  **kwargs: Any,
138
138
  ) -> "SingleStepLLMCommandGenerator":
139
139
  """Loads trained component (see parent class for full docstring)."""
140
+
141
+ # Perform health check of the LLM API endpoint
142
+ llm_config = resolve_model_client_config(config.get(LLM_CONFIG_KEY, {}))
143
+ cls.perform_llm_health_check(
144
+ llm_config,
145
+ DEFAULT_LLM_CONFIG,
146
+ "single_step_llm_command_generator.load",
147
+ SingleStepLLMCommandGenerator.__name__,
148
+ )
149
+
140
150
  # load prompt template from the model storage.
141
151
  prompt_template = cls.load_prompt_template_from_model_storage(
142
152
  model_storage, resource, COMMAND_PROMPT_FILE_NAME
@@ -150,34 +160,28 @@ class SingleStepLLMCommandGenerator(LLMBasedCommandGenerator):
150
160
  command_generator.config, model_storage, resource
151
161
  )
152
162
 
153
- persisted_config = cls.load_config_from_model_storage(model_storage, resource)
154
- train_model_name = (
155
- persisted_config.get(TRAINED_MODEL_NAME_CONFIG_KEY, None)
156
- if persisted_config
157
- else None
158
- )
159
- perform_inference_time_llm_health_check(
160
- command_generator.config.get(LLM_CONFIG_KEY),
161
- DEFAULT_LLM_CONFIG,
162
- train_model_name,
163
- "single_step_llm_command_generator.load",
164
- SingleStepLLMCommandGenerator.__name__,
165
- )
166
-
167
163
  return command_generator
168
164
 
169
165
  def persist(self) -> None:
170
166
  """Persist this component to disk for future loading."""
171
- # persist prompt template
172
- super().persist()
167
+ self._persist_prompt_template()
168
+ self._persist_config()
169
+ if self.flow_retrieval is not None:
170
+ self.flow_retrieval.persist()
173
171
 
172
+ def _persist_prompt_template(self) -> None:
173
+ """Persist prompt template for future loading."""
174
174
  with self._model_storage.write_to(self._resource) as path:
175
175
  rasa.shared.utils.io.write_text_file(
176
176
  self.prompt_template, path / COMMAND_PROMPT_FILE_NAME
177
177
  )
178
- # persist flow retrieval
179
- if self.flow_retrieval is not None:
180
- self.flow_retrieval.persist()
178
+
179
+ def _persist_config(self) -> None:
180
+ """Persist config as a source of truth for resolved clients."""
181
+ with self._model_storage.write_to(self._resource) as path:
182
+ rasa.shared.utils.io.dump_obj_as_json_to_file(
183
+ path / SINGLE_STEP_LLM_COMMAND_GENERATOR_CONFIG_FILE, self.config
184
+ )
181
185
 
182
186
  async def predict_commands(
183
187
  self,
@@ -455,7 +459,7 @@ class SingleStepLLMCommandGenerator(LLMBasedCommandGenerator):
455
459
  "current_slot": current_slot,
456
460
  "current_slot_description": current_slot_description,
457
461
  "user_message": latest_user_message,
458
- "is_repeat_command_enabled": self.is_repeat_command_enabled(),
462
+ "is_repeat_command_enabled": self.repeat_command_enabled,
459
463
  }
460
464
 
461
465
  return self.compile_template(self.prompt_template).render(**inputs)
@@ -111,6 +111,15 @@ slots:
111
111
  type: bool
112
112
  mappings:
113
113
  - type: from_llm
114
+ silence_timeout:
115
+ type: float
116
+ initial_value: 6.0
117
+ max_value: 1000000
118
+ consecutive_silence_timeouts:
119
+ type: float
120
+ initial_value: 0.0
121
+ max_value: 1000000
122
+
114
123
 
115
124
  flows:
116
125
  pattern_cancel_flow:
@@ -8,6 +8,7 @@ from rasa.dialogue_understanding.commands import (
8
8
  Command,
9
9
  CorrectSlotsCommand,
10
10
  CorrectedSlot,
11
+ RepeatBotMessagesCommand,
11
12
  SetSlotCommand,
12
13
  StartFlowCommand,
13
14
  FreeFormAnswerCommand,
@@ -422,6 +423,9 @@ def clean_up_commands(
422
423
  elif not tracker.has_coexistence_routing_slot and len(clean_commands) > 1:
423
424
  clean_commands = filter_cannot_handle_command_for_skipped_slots(clean_commands)
424
425
 
426
+ clean_commands = ensure_max_number_of_command_type(
427
+ clean_commands, RepeatBotMessagesCommand, 1
428
+ )
425
429
  structlogger.debug(
426
430
  "command_processor.clean_up_commands.final_commands",
427
431
  command=clean_commands,
@@ -430,6 +434,22 @@ def clean_up_commands(
430
434
  return clean_commands
431
435
 
432
436
 
437
+ def ensure_max_number_of_command_type(
438
+ commands: List[Command], command_type: Type[Command], n: int
439
+ ) -> List[Command]:
440
+ """Ensures that for a given command type only the first n stay in the list."""
441
+ filtered: List[Command] = []
442
+ count = 0
443
+ for c in commands:
444
+ if isinstance(c, command_type):
445
+ if count >= n:
446
+ continue
447
+ else:
448
+ count += 1
449
+ filtered.append(c)
450
+ return filtered
451
+
452
+
433
453
  def clean_up_clarify_command(
434
454
  commands_so_far: List[Command],
435
455
  all_commands: List[Command],
@@ -452,7 +472,7 @@ def clean_up_clarify_command(
452
472
  if not (isinstance(c, SetSlotCommand) and c.name == ROUTE_TO_CALM_SLOT)
453
473
  ]
454
474
 
455
- # if there are multiple clarify commands, do add the first one
475
+ # if all commands are clarify commands, add the first one only, otherwise add none
456
476
  if all(
457
477
  isinstance(c, ClarifyCommand) for c in commands_without_route_to_calm_set_slot
458
478
  ):