rasa-pro 3.12.10.dev1__py3-none-any.whl → 3.12.12.dev1__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 (37) hide show
  1. rasa/__init__.py +0 -6
  2. rasa/api.py +4 -0
  3. rasa/cli/arguments/default_arguments.py +13 -1
  4. rasa/cli/arguments/train.py +2 -0
  5. rasa/cli/train.py +1 -0
  6. rasa/constants.py +2 -0
  7. rasa/core/actions/action.py +1 -1
  8. rasa/core/channels/voice_ready/audiocodes.py +71 -31
  9. rasa/core/persistor.py +55 -20
  10. rasa/core/policies/intentless_policy.py +1 -3
  11. rasa/dialogue_understanding/coexistence/llm_based_router.py +1 -0
  12. rasa/dialogue_understanding/generator/llm_based_command_generator.py +4 -15
  13. rasa/dialogue_understanding/generator/llm_command_generator.py +1 -3
  14. rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +4 -44
  15. rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +1 -14
  16. rasa/hooks.py +0 -55
  17. rasa/keys +1 -0
  18. rasa/model_manager/config.py +3 -1
  19. rasa/model_manager/model_api.py +1 -2
  20. rasa/model_manager/runner_service.py +8 -4
  21. rasa/model_manager/trainer_service.py +1 -0
  22. rasa/model_training.py +12 -3
  23. rasa/nlu/extractors/crf_entity_extractor.py +66 -16
  24. rasa/shared/constants.py +0 -5
  25. rasa/shared/providers/constants.py +0 -9
  26. rasa/shared/providers/llm/_base_litellm_client.py +4 -14
  27. rasa/shared/providers/llm/litellm_router_llm_client.py +7 -17
  28. rasa/shared/providers/llm/llm_client.py +15 -24
  29. rasa/shared/providers/llm/self_hosted_llm_client.py +2 -10
  30. rasa/tracing/instrumentation/attribute_extractors.py +2 -2
  31. rasa/version.py +1 -1
  32. {rasa_pro-3.12.10.dev1.dist-info → rasa_pro-3.12.12.dev1.dist-info}/METADATA +2 -3
  33. {rasa_pro-3.12.10.dev1.dist-info → rasa_pro-3.12.12.dev1.dist-info}/RECORD +36 -36
  34. rasa/monkey_patches.py +0 -91
  35. {rasa_pro-3.12.10.dev1.dist-info → rasa_pro-3.12.12.dev1.dist-info}/NOTICE +0 -0
  36. {rasa_pro-3.12.10.dev1.dist-info → rasa_pro-3.12.12.dev1.dist-info}/WHEEL +0 -0
  37. {rasa_pro-3.12.10.dev1.dist-info → rasa_pro-3.12.12.dev1.dist-info}/entry_points.txt +0 -0
rasa/__init__.py CHANGED
@@ -5,11 +5,5 @@ from rasa import version
5
5
  # define the version before the other imports since these need it
6
6
  __version__ = version.__version__
7
7
 
8
- from litellm.integrations.langfuse.langfuse import LangFuseLogger
9
-
10
- from rasa.monkey_patches import litellm_langfuse_logger_init_fixed
11
-
12
- # Monkey-patch the init method as early as possible before the class is used
13
- LangFuseLogger.__init__ = litellm_langfuse_logger_init_fixed # type: ignore
14
8
 
15
9
  logging.getLogger(__name__).addHandler(logging.NullHandler())
rasa/api.py CHANGED
@@ -81,6 +81,7 @@ def train(
81
81
  remote_storage: Optional[StorageType] = None,
82
82
  file_importer: Optional["TrainingDataImporter"] = None,
83
83
  keep_local_model_copy: bool = False,
84
+ remote_root_only: bool = False,
84
85
  ) -> "TrainingResult":
85
86
  """Runs Rasa Core and NLU training in `async` loop.
86
87
 
@@ -108,6 +109,8 @@ def train(
108
109
  If it is not provided, a new instance will be created.
109
110
  keep_local_model_copy: If `True` the model will be stored locally even if
110
111
  remote storage is configured.
112
+ remote_root_only: If `True`, the model will be stored in the root of the
113
+ remote model storage.
111
114
 
112
115
  Returns:
113
116
  An instance of `TrainingResult`.
@@ -131,6 +134,7 @@ def train(
131
134
  remote_storage=remote_storage,
132
135
  file_importer=file_importer,
133
136
  keep_local_model_copy=keep_local_model_copy,
137
+ remote_root_only=remote_root_only,
134
138
  )
135
139
  )
136
140
 
@@ -172,7 +172,7 @@ def add_remote_storage_param(
172
172
  ) -> None:
173
173
  parser.add_argument(
174
174
  "--remote-storage",
175
- help="Remote storage which should be used to store/load the model."
175
+ help="Remote storage which should be used to store/load the model. "
176
176
  f"Supported storages are: {RemoteStorageType.list()}. "
177
177
  "You can also provide your own implementation of the `Persistor` interface.",
178
178
  required=required,
@@ -180,6 +180,18 @@ def add_remote_storage_param(
180
180
  )
181
181
 
182
182
 
183
+ def add_remote_root_only_param(
184
+ parser: argparse.ArgumentParser, required: bool = False
185
+ ) -> None:
186
+ parser.add_argument(
187
+ "--remote-root-only",
188
+ action="store_true",
189
+ help="If set, models will be stored only at the root directory "
190
+ "of the remote storage.",
191
+ required=required,
192
+ )
193
+
194
+
183
195
  def parse_remote_storage_arg(value: str) -> StorageType:
184
196
  try:
185
197
  return parse_remote_storage(value)
@@ -7,6 +7,7 @@ from rasa.cli.arguments.default_arguments import (
7
7
  add_endpoint_param,
8
8
  add_nlu_data_param,
9
9
  add_out_param,
10
+ add_remote_root_only_param,
10
11
  add_remote_storage_param,
11
12
  add_stories_param,
12
13
  )
@@ -41,6 +42,7 @@ def set_train_arguments(parser: argparse.ArgumentParser) -> None:
41
42
  parser, help_text="Configuration file for the connectors as a yml file."
42
43
  )
43
44
  add_remote_storage_param(parser)
45
+ add_remote_root_only_param(parser)
44
46
 
45
47
 
46
48
  def set_train_core_arguments(parser: argparse.ArgumentParser) -> None:
rasa/cli/train.py CHANGED
@@ -155,6 +155,7 @@ def run_training(args: argparse.Namespace, can_exit: bool = False) -> Optional[T
155
155
  remote_storage=args.remote_storage,
156
156
  file_importer=training_data_importer,
157
157
  keep_local_model_copy=args.keep_local_model_copy,
158
+ remote_root_only=args.remote_root_only,
158
159
  )
159
160
  if training_result.code != 0 and can_exit:
160
161
  display_research_study_prompt()
rasa/constants.py CHANGED
@@ -42,3 +42,5 @@ DEFAULT_BUCKET_NAME = "rasa-models"
42
42
 
43
43
  HTTP_STATUS_FORBIDDEN = 403
44
44
  HTTP_STATUS_NOT_FOUND = 404
45
+
46
+ RASA_REMOTE_STORAGE_ENV_VAR_NAME = "RASA_REMOTE_STORAGE"
@@ -898,7 +898,7 @@ class RemoteAction(Action):
898
898
  draft["buttons"].extend(buttons)
899
899
 
900
900
  # Avoid overwriting `draft` values with empty values
901
- response = {k: v for k, v in response.items() if v}
901
+ response = {k: v for k, v in response.items() if v is not None}
902
902
  draft.update(response)
903
903
  bot_messages.append(create_bot_utterance(draft))
904
904
 
@@ -6,7 +6,18 @@ import uuid
6
6
  from collections import defaultdict
7
7
  from dataclasses import asdict
8
8
  from datetime import datetime, timedelta, timezone
9
- from typing import Any, Awaitable, Callable, Dict, List, Optional, Set, Text, Union
9
+ from typing import (
10
+ Any,
11
+ Awaitable,
12
+ Callable,
13
+ Dict,
14
+ List,
15
+ Optional,
16
+ Set,
17
+ Text,
18
+ Tuple,
19
+ Union,
20
+ )
10
21
 
11
22
  import structlog
12
23
  from jsonschema import ValidationError, validate
@@ -76,35 +87,45 @@ class Conversation:
76
87
 
77
88
  @staticmethod
78
89
  def get_metadata(activity: Dict[Text, Any]) -> Optional[Dict[Text, Any]]:
79
- """Get metadata from the activity."""
80
- return asdict(map_call_params(activity["parameters"]))
90
+ """Get metadata from the activity.
91
+
92
+ ONLY used for activities NOT for events (see _handle_event)."""
93
+ return activity.get("parameters")
81
94
 
82
95
  @staticmethod
83
- def _handle_event(event: Dict[Text, Any]) -> Text:
84
- """Handle start and DTMF event and return the corresponding text."""
96
+ def _handle_event(event: Dict[Text, Any]) -> Tuple[Text, Dict[Text, Any]]:
97
+ """Handle events and return a tuple of text and metadata.
98
+
99
+ Args:
100
+ event: The event to handle.
101
+
102
+ Returns:
103
+ Tuple of text and metadata.
104
+ text is either /session_start or /vaig_event_<event_name>
105
+ metadata is a dictionary with the event parameters.
106
+ """
85
107
  structlogger.debug("audiocodes.handle.event", event_payload=event)
86
108
  if "name" not in event:
87
109
  structlogger.warning(
88
110
  "audiocodes.handle.event.no_name_key", event_payload=event
89
111
  )
90
- return ""
112
+ return "", {}
91
113
 
92
114
  if event["name"] == EVENT_START:
93
115
  text = f"{INTENT_MESSAGE_PREFIX}{USER_INTENT_SESSION_START}"
116
+ metadata = asdict(map_call_params(event.get("parameters", {})))
94
117
  elif event["name"] == EVENT_DTMF:
95
118
  text = f"{INTENT_MESSAGE_PREFIX}vaig_event_DTMF"
96
- event_params = {"value": event["value"]}
97
- text += json.dumps(event_params)
119
+ metadata = {"value": event["value"]}
98
120
  else:
99
121
  # handle other events described by Audiocodes
100
122
  # https://techdocs.audiocodes.com/voice-ai-connect/#VAIG_Combined/inactivity-detection.htm?TocPath=Bot%2520integration%257CReceiving%2520notifications%257C_____3
101
123
  text = f"{INTENT_MESSAGE_PREFIX}vaig_event_{event['name']}"
102
- event_params = {**event.get("parameters", {})}
124
+ metadata = {**event.get("parameters", {})}
103
125
  if "value" in event:
104
- event_params["value"] = event["value"]
105
- text += json.dumps(event_params)
126
+ metadata["value"] = event["value"]
106
127
 
107
- return text
128
+ return text, metadata
108
129
 
109
130
  def is_active_conversation(self, now: datetime, delta: timedelta) -> bool:
110
131
  """Check if the conversation is active."""
@@ -139,21 +160,29 @@ class Conversation:
139
160
  structlogger.warning(
140
161
  "audiocodes.handle.activities.duplicate_activity",
141
162
  activity_id=activity[ACTIVITY_ID_KEY],
163
+ event_info=(
164
+ "Audiocodes might send duplicate activities if the bot has not "
165
+ "responded to the previous one or responded too late. Please "
166
+ "consider enabling the `use_websocket` option to use"
167
+ " Audiocodes Asynchronous API."
168
+ ),
142
169
  )
143
170
  continue
144
171
  self.activity_ids.append(activity[ACTIVITY_ID_KEY])
145
172
  if activity["type"] == ACTIVITY_MESSAGE:
146
173
  text = activity["text"]
174
+ metadata = self.get_metadata(activity)
147
175
  elif activity["type"] == ACTIVITY_EVENT:
148
- text = self._handle_event(activity)
176
+ text, metadata = self._handle_event(activity)
149
177
  else:
150
178
  structlogger.warning(
151
179
  "audiocodes.handle.activities.unknown_activity_type",
152
180
  activity=activity,
153
181
  )
182
+ continue
183
+
154
184
  if not text:
155
185
  continue
156
- metadata = self.get_metadata(activity)
157
186
  user_msg = UserMessage(
158
187
  text=text,
159
188
  input_channel=input_channel_name,
@@ -392,30 +421,41 @@ class AudiocodesInput(InputChannel):
392
421
  "audiocodes.on_activities.no_conversation", request=request.json
393
422
  )
394
423
  return response.json({})
395
- elif conversation.ws:
424
+
425
+ if self.use_websocket:
426
+ # send an empty response for this request
427
+ # activities are processed in the background
428
+ # chat response is sent via the websocket
396
429
  ac_output: Union[WebsocketOutput, AudiocodesOutput] = WebsocketOutput(
397
430
  conversation.ws, conversation_id
398
431
  )
399
- response_json = {}
400
- else:
401
- # handle non websocket case where messages get returned in json
402
- ac_output = AudiocodesOutput()
403
- response_json = {
432
+ self._create_task(
433
+ conversation_id,
434
+ conversation.handle_activities(
435
+ request.json,
436
+ input_channel_name=self.name(),
437
+ output_channel=ac_output,
438
+ on_new_message=on_new_message,
439
+ ),
440
+ )
441
+ return response.json({})
442
+
443
+ # without websockets, this becomes a blocking call
444
+ # and the response is sent back to the Audiocodes server
445
+ # after the activities are processed
446
+ ac_output = AudiocodesOutput()
447
+ await conversation.handle_activities(
448
+ request.json,
449
+ input_channel_name=self.name(),
450
+ output_channel=ac_output,
451
+ on_new_message=on_new_message,
452
+ )
453
+ return response.json(
454
+ {
404
455
  "conversation": conversation_id,
405
456
  "activities": ac_output.messages,
406
457
  }
407
-
408
- # start a background task to handle activities
409
- self._create_task(
410
- conversation_id,
411
- conversation.handle_activities(
412
- request.json,
413
- input_channel_name=self.name(),
414
- output_channel=ac_output,
415
- on_new_message=on_new_message,
416
- ),
417
458
  )
418
- return response.json(response_json)
419
459
 
420
460
  @ac_webhook.route(
421
461
  "/conversation/<conversation_id>/disconnect", methods=["POST"]
rasa/core/persistor.py CHANGED
@@ -121,10 +121,12 @@ def get_persistor(storage: StorageType) -> Optional[Persistor]:
121
121
  class Persistor(abc.ABC):
122
122
  """Store models in cloud and fetch them when needed."""
123
123
 
124
- def persist(self, trained_model: str) -> None:
124
+ def persist(self, trained_model: str, remote_root_only: bool = False) -> None:
125
125
  """Uploads a trained model persisted in the `target_dir` to cloud storage."""
126
126
  absolute_file_key = self._create_file_key(trained_model)
127
- file_key = Path(absolute_file_key).name
127
+ file_key = (
128
+ Path(absolute_file_key).name if remote_root_only else absolute_file_key
129
+ )
128
130
  self._persist_tar(file_key, trained_model)
129
131
 
130
132
  def retrieve(self, model_name: Text, target_path: Text) -> Text:
@@ -143,30 +145,32 @@ class Persistor(abc.ABC):
143
145
  # ensure backward compatibility
144
146
  tar_name = self._tar_name(model_name)
145
147
  tar_name = self._create_file_key(tar_name)
146
- target_filename = os.path.basename(tar_name)
147
- self._retrieve_tar(target_filename)
148
- self._copy(os.path.basename(tar_name), target_path)
148
+ self._retrieve_tar(tar_name, target_path)
149
149
 
150
150
  if os.path.isdir(target_path):
151
151
  return os.path.join(target_path, model_name)
152
152
 
153
153
  return target_path
154
154
 
155
- def size_of_persisted_model(self, model_name: Text) -> int:
155
+ def size_of_persisted_model(
156
+ self, model_name: Text, target_path: Optional[str] = None
157
+ ) -> int:
156
158
  """Returns the size of the model that has been persisted to cloud storage.
157
159
 
158
160
  Args:
159
161
  model_name: The name of the model to retrieve.
162
+ target_path: The path to which the model should be saved.
160
163
  """
161
164
  tar_name = model_name
162
165
  if not model_name.endswith(MODEL_ARCHIVE_EXTENSION):
163
166
  # ensure backward compatibility
164
167
  tar_name = self._tar_name(model_name)
165
168
  tar_name = self._create_file_key(tar_name)
166
- target_filename = os.path.basename(tar_name)
167
- return self._retrieve_tar_size(target_filename)
169
+ return self._retrieve_tar_size(tar_name, target_path)
168
170
 
169
- def _retrieve_tar_size(self, filename: Text) -> int:
171
+ def _retrieve_tar_size(
172
+ self, filename: Text, target_path: Optional[str] = None
173
+ ) -> int:
170
174
  """Returns the size of the model that has been persisted to cloud storage."""
171
175
  structlogger.warning(
172
176
  "persistor.retrieve_tar_size.not_implemented",
@@ -179,11 +183,11 @@ class Persistor(abc.ABC):
179
183
  "size directly from the cloud storage."
180
184
  ),
181
185
  )
182
- self._retrieve_tar(filename)
186
+ self._retrieve_tar(filename, target_path)
183
187
  return os.path.getsize(os.path.basename(filename))
184
188
 
185
189
  @abc.abstractmethod
186
- def _retrieve_tar(self, filename: Text) -> None:
190
+ def _retrieve_tar(self, filename: str, target_path: Optional[str] = None) -> None:
187
191
  """Downloads a model previously persisted to cloud storage."""
188
192
  raise NotImplementedError
189
193
 
@@ -302,7 +306,9 @@ class AWSPersistor(Persistor):
302
306
  with open(tar_path, "rb") as f:
303
307
  self.s3.Object(self.bucket_name, file_key).put(Body=f)
304
308
 
305
- def _retrieve_tar_size(self, model_path: Text) -> int:
309
+ def _retrieve_tar_size(
310
+ self, model_path: Text, target_path: Optional[str] = None
311
+ ) -> int:
306
312
  """Returns the size of the model that has been persisted to s3."""
307
313
  try:
308
314
  obj = self.s3.Object(self.bucket_name, model_path)
@@ -310,7 +316,9 @@ class AWSPersistor(Persistor):
310
316
  except Exception:
311
317
  raise ModelNotFound()
312
318
 
313
- def _retrieve_tar(self, target_filename: str) -> None:
319
+ def _retrieve_tar(
320
+ self, target_filename: str, target_path: Optional[str] = None
321
+ ) -> None:
314
322
  """Downloads a model that has previously been persisted to s3."""
315
323
  from botocore import exceptions
316
324
 
@@ -320,8 +328,14 @@ class AWSPersistor(Persistor):
320
328
  f"in the bucket."
321
329
  )
322
330
 
331
+ tar_name = (
332
+ os.path.join(target_path, os.path.basename(target_filename))
333
+ if target_path
334
+ else os.path.basename(target_filename)
335
+ )
336
+
323
337
  try:
324
- with open(target_filename, "wb") as f:
338
+ with open(tar_name, "wb") as f:
325
339
  self.bucket.download_fileobj(target_filename, f)
326
340
 
327
341
  structlogger.debug(
@@ -425,7 +439,9 @@ class GCSPersistor(Persistor):
425
439
  blob = self.bucket.blob(file_key)
426
440
  blob.upload_from_filename(tar_path)
427
441
 
428
- def _retrieve_tar_size(self, target_filename: Text) -> int:
442
+ def _retrieve_tar_size(
443
+ self, target_filename: Text, target_path: Optional[str] = None
444
+ ) -> int:
429
445
  """Returns the size of the model that has been persisted to GCS."""
430
446
  try:
431
447
  blob = self.bucket.blob(target_filename)
@@ -433,13 +449,22 @@ class GCSPersistor(Persistor):
433
449
  except Exception:
434
450
  raise ModelNotFound()
435
451
 
436
- def _retrieve_tar(self, target_filename: Text) -> None:
452
+ def _retrieve_tar(
453
+ self, target_filename: str, target_path: Optional[str] = None
454
+ ) -> None:
437
455
  """Downloads a model that has previously been persisted to GCS."""
438
456
  from google.api_core import exceptions
439
457
 
440
458
  blob = self.bucket.blob(target_filename)
459
+
460
+ destination = (
461
+ os.path.join(target_path, os.path.basename(target_filename))
462
+ if target_path
463
+ else target_filename
464
+ )
465
+
441
466
  try:
442
- blob.download_to_filename(target_filename)
467
+ blob.download_to_filename(destination)
443
468
 
444
469
  structlogger.debug(
445
470
  "gcs_persistor.retrieve_tar.object_found", object_key=target_filename
@@ -500,7 +525,9 @@ class AzurePersistor(Persistor):
500
525
  with open(tar_path, "rb") as data:
501
526
  self._container_client().upload_blob(name=file_key, data=data)
502
527
 
503
- def _retrieve_tar_size(self, target_filename: Text) -> int:
528
+ def _retrieve_tar_size(
529
+ self, target_filename: Text, target_path: Optional[str] = None
530
+ ) -> int:
504
531
  """Returns the size of the model that has been persisted to Azure."""
505
532
  try:
506
533
  blob_client = self._container_client().get_blob_client(target_filename)
@@ -509,12 +536,20 @@ class AzurePersistor(Persistor):
509
536
  except Exception:
510
537
  raise ModelNotFound()
511
538
 
512
- def _retrieve_tar(self, target_filename: Text) -> None:
539
+ def _retrieve_tar(
540
+ self, target_filename: Text, target_path: Optional[str] = None
541
+ ) -> None:
513
542
  """Downloads a model that has previously been persisted to Azure."""
514
543
  from azure.core.exceptions import AzureError
515
544
 
545
+ destination = (
546
+ os.path.join(target_path, os.path.basename(target_filename))
547
+ if target_path
548
+ else target_filename
549
+ )
550
+
516
551
  try:
517
- with open(target_filename, "wb") as model_file:
552
+ with open(destination, "wb") as model_file:
518
553
  blob_client = self._container_client().get_blob_client(target_filename)
519
554
  download_stream = blob_client.download_blob()
520
555
  model_file.write(download_stream.readall())
@@ -715,9 +715,7 @@ class IntentlessPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Policy):
715
715
  final_response_examples.append(resp)
716
716
 
717
717
  llm_response = await self.generate_answer(
718
- final_response_examples,
719
- conversation_samples,
720
- history,
718
+ final_response_examples, conversation_samples, history
721
719
  )
722
720
  if not llm_response:
723
721
  structlogger.debug("intentless_policy.prediction.skip_llm_fail")
@@ -166,6 +166,7 @@ class LLMBasedRouter(LLMHealthCheckMixin, GraphComponent):
166
166
  **kwargs: Any,
167
167
  ) -> "LLMBasedRouter":
168
168
  """Loads trained component (see parent class for full docstring)."""
169
+
169
170
  # Perform health check on the resolved LLM client config
170
171
  llm_config = resolve_model_client_config(config.get(LLM_CONFIG_KEY, {}))
171
172
  cls.perform_llm_health_check(
@@ -1,8 +1,6 @@
1
1
  from abc import ABC, abstractmethod
2
- from asyncio import Lock
3
2
  from functools import lru_cache
4
3
  from typing import Any, Dict, List, Optional, Set, Text, Tuple, Union
5
- from uuid import UUID, uuid4
6
4
 
7
5
  import structlog
8
6
  from jinja2 import Environment, Template, select_autoescape
@@ -91,9 +89,6 @@ class LLMBasedCommandGenerator(
91
89
  else:
92
90
  self.flow_retrieval = None
93
91
 
94
- self.sender_id_to_session_id_mapping: Dict[str, UUID] = {}
95
- self._lock = Lock()
96
-
97
92
  ### Abstract methods
98
93
  @staticmethod
99
94
  @abstractmethod
@@ -230,7 +225,8 @@ class LLMBasedCommandGenerator(
230
225
 
231
226
  @lru_cache
232
227
  def compile_template(self, template: str) -> Template:
233
- """Compile the prompt template and register custom filters.
228
+ """
229
+ Compile the prompt template and register custom filters.
234
230
  Compiling the template is an expensive operation,
235
231
  so we cache the result.
236
232
  """
@@ -332,9 +328,7 @@ class LLMBasedCommandGenerator(
332
328
 
333
329
  @measure_llm_latency
334
330
  async def invoke_llm(
335
- self,
336
- prompt: Union[List[dict], List[str], str],
337
- metadata: Optional[Dict[str, Any]] = None,
331
+ self, prompt: Union[List[dict], List[str], str]
338
332
  ) -> Optional[LLMResponse]:
339
333
  """Use LLM to generate a response.
340
334
 
@@ -347,7 +341,6 @@ class LLMBasedCommandGenerator(
347
341
  - a list of messages. Each message is a string and will be formatted
348
342
  as a user message.
349
343
  - a single message as a string which will be formatted as user message.
350
- metadata: Optional metadata to be passed to the LLM call.
351
344
 
352
345
  Returns:
353
346
  An LLMResponse object.
@@ -359,7 +352,7 @@ class LLMBasedCommandGenerator(
359
352
  self.config.get(LLM_CONFIG_KEY), self.get_default_llm_config()
360
353
  )
361
354
  try:
362
- return await llm.acompletion(prompt, metadata)
355
+ return await llm.acompletion(prompt)
363
356
  except Exception as e:
364
357
  # unfortunately, langchain does not wrap LLM exceptions which means
365
358
  # we have to catch all exceptions here
@@ -662,7 +655,3 @@ class LLMBasedCommandGenerator(
662
655
  def get_default_llm_config() -> Dict[str, Any]:
663
656
  """Get the default LLM config for the command generator."""
664
657
  return DEFAULT_LLM_CONFIG
665
-
666
- async def _get_or_create_session_id(self, sender_id: str) -> UUID:
667
- async with self._lock:
668
- return self.sender_id_to_session_id_mapping.setdefault(sender_id, uuid4())
@@ -55,9 +55,7 @@ class LLMCommandGenerator(SingleStepLLMCommandGenerator):
55
55
  )
56
56
 
57
57
  async def invoke_llm(
58
- self,
59
- prompt: Union[List[dict], List[str], str],
60
- metadata: Optional[Dict[str, Any]] = None,
58
+ self, prompt: Union[List[dict], List[str], str]
61
59
  ) -> Optional[LLMResponse]:
62
60
  try:
63
61
  return await super().invoke_llm(prompt)
@@ -42,9 +42,6 @@ from rasa.engine.storage.resource import Resource
42
42
  from rasa.engine.storage.storage import ModelStorage
43
43
  from rasa.shared.constants import (
44
44
  EMBEDDINGS_CONFIG_KEY,
45
- LANGFUSE_CUSTOM_METADATA_DICT,
46
- LANGFUSE_METADATA_SESSION_ID,
47
- LANGFUSE_TAGS,
48
45
  RASA_PATTERN_CANNOT_HANDLE_NOT_SUPPORTED,
49
46
  ROUTE_TO_CALM_SLOT,
50
47
  )
@@ -110,7 +107,7 @@ structlogger = structlog.get_logger()
110
107
  )
111
108
  @deprecated(
112
109
  reason=(
113
- "The MultiStepLLMCommandGenerator is deprecated and will be removed in "
110
+ "The MultiStepLLMCommandGenerator is deprecated and will be removed in "
114
111
  "Rasa `4.0.0`."
115
112
  )
116
113
  )
@@ -495,20 +492,7 @@ class MultiStepLLMCommandGenerator(LLMBasedCommandGenerator):
495
492
  prompt=prompt,
496
493
  )
497
494
 
498
- if tracker:
499
- session_id = str(await self._get_or_create_session_id(tracker.sender_id))
500
- else:
501
- session_id = "unknown"
502
- metadata = {
503
- LANGFUSE_METADATA_SESSION_ID: session_id,
504
- LANGFUSE_CUSTOM_METADATA_DICT: {
505
- "component": self.__class__.__name__,
506
- "function": "_predict_commands_for_active_flow",
507
- },
508
- LANGFUSE_TAGS: [self.__class__.__name__],
509
- }
510
-
511
- response = await self.invoke_llm(prompt, metadata)
495
+ response = await self.invoke_llm(prompt)
512
496
  llm_response = LLMResponse.ensure_llm_response(response)
513
497
  actions = None
514
498
  if llm_response and llm_response.choices:
@@ -562,20 +546,8 @@ class MultiStepLLMCommandGenerator(LLMBasedCommandGenerator):
562
546
  ".prompt_rendered",
563
547
  prompt=prompt,
564
548
  )
565
- if tracker:
566
- session_id = str(await self._get_or_create_session_id(tracker.sender_id))
567
- else:
568
- session_id = "unknown"
569
- metadata = {
570
- LANGFUSE_METADATA_SESSION_ID: session_id,
571
- LANGFUSE_CUSTOM_METADATA_DICT: {
572
- "component": self.__class__.__name__,
573
- "function": "_predict_commands_for_handling_flows",
574
- },
575
- LANGFUSE_TAGS: [self.__class__.__name__],
576
- }
577
549
 
578
- response = await self.invoke_llm(prompt, metadata)
550
+ response = await self.invoke_llm(prompt)
579
551
  llm_response = LLMResponse.ensure_llm_response(response)
580
552
  actions = None
581
553
  if llm_response and llm_response.choices:
@@ -664,20 +636,8 @@ class MultiStepLLMCommandGenerator(LLMBasedCommandGenerator):
664
636
  flow=newly_started_flow.id,
665
637
  prompt=prompt,
666
638
  )
667
- if tracker:
668
- session_id = str(await self._get_or_create_session_id(tracker.sender_id))
669
- else:
670
- session_id = "unknown"
671
- metadata = {
672
- LANGFUSE_METADATA_SESSION_ID: session_id,
673
- LANGFUSE_CUSTOM_METADATA_DICT: {
674
- "component": self.__class__.__name__,
675
- "function": "_predict_commands_for_newly_started_flow",
676
- },
677
- LANGFUSE_TAGS: [self.__class__.__name__],
678
- }
679
639
 
680
- response = await self.invoke_llm(prompt, metadata)
640
+ response = await self.invoke_llm(prompt)
681
641
  llm_response = LLMResponse.ensure_llm_response(response)
682
642
  actions = None
683
643
  if llm_response and llm_response.choices:
@@ -47,9 +47,6 @@ from rasa.shared.constants import (
47
47
  AWS_BEDROCK_PROVIDER,
48
48
  AZURE_OPENAI_PROVIDER,
49
49
  EMBEDDINGS_CONFIG_KEY,
50
- LANGFUSE_CUSTOM_METADATA_DICT,
51
- LANGFUSE_METADATA_SESSION_ID,
52
- LANGFUSE_TAGS,
53
50
  MAX_TOKENS_CONFIG_KEY,
54
51
  PROMPT_TEMPLATE_CONFIG_KEY,
55
52
  ROUTE_TO_CALM_SLOT,
@@ -369,17 +366,7 @@ class CompactLLMCommandGenerator(LLMBasedCommandGenerator):
369
366
  prompt=flow_prompt,
370
367
  )
371
368
 
372
- if tracker:
373
- session_id = str(await self._get_or_create_session_id(tracker.sender_id))
374
- else:
375
- session_id = "unknown"
376
- metadata = {
377
- LANGFUSE_METADATA_SESSION_ID: session_id,
378
- LANGFUSE_CUSTOM_METADATA_DICT: {"component": self.__class__.__name__},
379
- LANGFUSE_TAGS: [self.__class__.__name__],
380
- }
381
-
382
- response = await self.invoke_llm(flow_prompt, metadata)
369
+ response = await self.invoke_llm(flow_prompt)
383
370
  llm_response = LLMResponse.ensure_llm_response(response)
384
371
  # The check for 'None' maintains compatibility with older versions
385
372
  # of LLMCommandGenerator. In previous implementations, 'invoke_llm'