rasa-pro 3.14.1__py3-none-any.whl → 3.15.0.dev20251027__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 (43) hide show
  1. rasa/builder/config.py +4 -0
  2. rasa/builder/copilot/copilot.py +28 -9
  3. rasa/builder/copilot/models.py +251 -32
  4. rasa/builder/document_retrieval/inkeep_document_retrieval.py +2 -0
  5. rasa/builder/download.py +1 -1
  6. rasa/builder/evaluator/__init__.py +0 -0
  7. rasa/builder/evaluator/constants.py +15 -0
  8. rasa/builder/evaluator/copilot_executor.py +89 -0
  9. rasa/builder/evaluator/dataset/models.py +173 -0
  10. rasa/builder/evaluator/exceptions.py +4 -0
  11. rasa/builder/evaluator/response_classification/__init__.py +0 -0
  12. rasa/builder/evaluator/response_classification/constants.py +66 -0
  13. rasa/builder/evaluator/response_classification/evaluator.py +346 -0
  14. rasa/builder/evaluator/response_classification/langfuse_runner.py +463 -0
  15. rasa/builder/evaluator/response_classification/models.py +61 -0
  16. rasa/builder/evaluator/scripts/__init__.py +0 -0
  17. rasa/builder/evaluator/scripts/run_response_classification_evaluator.py +152 -0
  18. rasa/builder/service.py +101 -24
  19. rasa/builder/telemetry/__init__.py +0 -0
  20. rasa/builder/telemetry/copilot_langfuse_telemetry.py +384 -0
  21. rasa/builder/{copilot/telemetry.py → telemetry/copilot_segment_telemetry.py} +21 -3
  22. rasa/constants.py +1 -0
  23. rasa/core/policies/flows/flow_executor.py +20 -6
  24. rasa/core/run.py +15 -4
  25. rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +15 -7
  26. rasa/dialogue_understanding/generator/single_step/search_ready_llm_command_generator.py +15 -8
  27. rasa/e2e_test/e2e_config.py +4 -3
  28. rasa/engine/recipes/default_components.py +16 -6
  29. rasa/graph_components/validators/default_recipe_validator.py +10 -4
  30. rasa/nlu/classifiers/diet_classifier.py +2 -0
  31. rasa/shared/core/slots.py +55 -24
  32. rasa/shared/utils/common.py +9 -1
  33. rasa/utils/common.py +9 -0
  34. rasa/utils/endpoints.py +2 -0
  35. rasa/utils/installation_utils.py +111 -0
  36. rasa/utils/tensorflow/callback.py +2 -0
  37. rasa/utils/train_utils.py +2 -0
  38. rasa/version.py +1 -1
  39. {rasa_pro-3.14.1.dist-info → rasa_pro-3.15.0.dev20251027.dist-info}/METADATA +4 -2
  40. {rasa_pro-3.14.1.dist-info → rasa_pro-3.15.0.dev20251027.dist-info}/RECORD +43 -28
  41. {rasa_pro-3.14.1.dist-info → rasa_pro-3.15.0.dev20251027.dist-info}/NOTICE +0 -0
  42. {rasa_pro-3.14.1.dist-info → rasa_pro-3.15.0.dev20251027.dist-info}/WHEEL +0 -0
  43. {rasa_pro-3.14.1.dist-info → rasa_pro-3.15.0.dev20251027.dist-info}/entry_points.txt +0 -0
@@ -357,6 +357,10 @@ def reset_scoped_slots(
357
357
  flow_persistable_slots = current_flow.persisted_slots
358
358
 
359
359
  for step in current_flow.steps_with_calls_resolved:
360
+ # take persisted slots from called flows into consideration
361
+ # before resetting slots
362
+ if isinstance(step, CallFlowStep) and step.called_flow_reference:
363
+ flow_persistable_slots.extend(step.called_flow_reference.persisted_slots)
360
364
  if isinstance(step, CollectInformationFlowStep):
361
365
  # reset all slots scoped to the flow
362
366
  slot_name = step.collect
@@ -368,7 +372,22 @@ def reset_scoped_slots(
368
372
  # slots set by the set slots step should be reset after the flow ends
369
373
  # unless they are also used in a collect step where `reset_after_flow_ends`
370
374
  # is set to `False` or set in the `persisted_slots` list.
371
- resettable_set_slots = [
375
+ resettable_set_slots = _get_resettable_set_slots(
376
+ current_flow, not_resettable_slot_names, flow_persistable_slots
377
+ )
378
+ for name in resettable_set_slots:
379
+ _reset_slot(name, tracker)
380
+
381
+ return events
382
+
383
+
384
+ def _get_resettable_set_slots(
385
+ current_flow: Flow,
386
+ not_resettable_slot_names: set[Text],
387
+ flow_persistable_slots: List[Text],
388
+ ) -> List[Text]:
389
+ """Get list of slot names from SetSlotsFlowStep that should be reset."""
390
+ return [
372
391
  slot["key"]
373
392
  for step in current_flow.steps_with_calls_resolved
374
393
  if isinstance(step, SetSlotsFlowStep)
@@ -377,11 +396,6 @@ def reset_scoped_slots(
377
396
  and slot["key"] not in flow_persistable_slots
378
397
  ]
379
398
 
380
- for name in resettable_set_slots:
381
- _reset_slot(name, tracker)
382
-
383
- return events
384
-
385
399
 
386
400
  async def advance_flows(
387
401
  tracker: DialogueStateTracker,
rasa/core/run.py CHANGED
@@ -328,10 +328,21 @@ def serve_application(
328
328
 
329
329
  logger.info(f"Starting Rasa server on {protocol}://{interface}:{port}")
330
330
 
331
- app.register_listener(
332
- partial(load_agent_on_start, model_path, endpoints, remote_storage, sub_agents),
333
- "before_server_start",
334
- )
331
+ async def load_agent_and_check_failure(app: Sanic, loop: AbstractEventLoop) -> None:
332
+ """Load agent and exit if it fails in non-debug mode."""
333
+ try:
334
+ await load_agent_on_start(
335
+ model_path, endpoints, remote_storage, sub_agents, app, loop
336
+ )
337
+ except Exception as e:
338
+ is_debug = logger.isEnabledFor(logging.DEBUG)
339
+ if is_debug:
340
+ raise e # show traceback in debug
341
+ # non-debug: log and exit without starting server
342
+ logger.error(f"Failed to load agent: {e}")
343
+ os._exit(1) # Any other exit method would show a traceback.
344
+
345
+ app.register_listener(load_agent_and_check_failure, "before_server_start")
335
346
 
336
347
  app.register_listener(
337
348
  licensing.validate_limited_server_license, "after_server_start"
@@ -168,6 +168,20 @@ class CompactLLMCommandGenerator(SingleStepBasedLLMCommandGenerator):
168
168
  if prompt_template is not None:
169
169
  return prompt_template
170
170
 
171
+ # Try to load the template from the given path or fallback to the default for
172
+ # the component.
173
+ custom_prompt_template_path = config.get(PROMPT_TEMPLATE_CONFIG_KEY)
174
+ if custom_prompt_template_path is not None:
175
+ custom_prompt_template = get_prompt_template(
176
+ custom_prompt_template_path,
177
+ None, # Default will be based on the model
178
+ log_source_component=log_source_component,
179
+ log_source_method=log_context,
180
+ )
181
+ if custom_prompt_template is not None:
182
+ return custom_prompt_template
183
+
184
+ # Fallback to the default prompt template based on the model.
171
185
  default_command_prompt_template = get_default_prompt_template_based_on_model(
172
186
  llm_config=config.get(LLM_CONFIG_KEY, {}) or {},
173
187
  model_prompt_mapping=cls.get_model_prompt_mapper(),
@@ -177,10 +191,4 @@ class CompactLLMCommandGenerator(SingleStepBasedLLMCommandGenerator):
177
191
  log_source_method=log_context,
178
192
  )
179
193
 
180
- # Return the prompt template either from the config or the default prompt.
181
- return get_prompt_template(
182
- config.get(PROMPT_TEMPLATE_CONFIG_KEY),
183
- default_command_prompt_template,
184
- log_source_component=log_source_component,
185
- log_source_method=log_context,
186
- )
194
+ return default_command_prompt_template
@@ -165,7 +165,20 @@ class SearchReadyLLMCommandGenerator(SingleStepBasedLLMCommandGenerator):
165
165
  if prompt_template is not None:
166
166
  return prompt_template
167
167
 
168
- # Get the default prompt template based on the model name.
168
+ # Try to load the template from the given path or fallback to the default for
169
+ # the component.
170
+ custom_prompt_template_path = config.get(PROMPT_TEMPLATE_CONFIG_KEY)
171
+ if custom_prompt_template_path is not None:
172
+ custom_prompt_template = get_prompt_template(
173
+ custom_prompt_template_path,
174
+ None, # Default will be based on the model
175
+ log_source_component=log_source_component,
176
+ log_source_method=log_context,
177
+ )
178
+ if custom_prompt_template is not None:
179
+ return custom_prompt_template
180
+
181
+ # Fallback to the default prompt template based on the model.
169
182
  default_command_prompt_template = get_default_prompt_template_based_on_model(
170
183
  llm_config=config.get(LLM_CONFIG_KEY, {}) or {},
171
184
  model_prompt_mapping=cls.get_model_prompt_mapper(),
@@ -175,10 +188,4 @@ class SearchReadyLLMCommandGenerator(SingleStepBasedLLMCommandGenerator):
175
188
  log_source_method=log_context,
176
189
  )
177
190
 
178
- # Return the prompt template either from the config or the default prompt.
179
- return get_prompt_template(
180
- config.get(PROMPT_TEMPLATE_CONFIG_KEY),
181
- default_command_prompt_template,
182
- log_source_component=log_source_component,
183
- log_source_method=log_context,
184
- )
191
+ return default_command_prompt_template
@@ -72,9 +72,10 @@ class LLMJudgeConfig(BaseModel):
72
72
 
73
73
  llm_config = resolve_model_client_config(llm_config)
74
74
  llm_config, llm_extra_parameters = cls.extract_attributes(llm_config)
75
- llm_config = combine_custom_and_default_config(
76
- llm_config, cls.get_default_llm_config()
77
- )
75
+ if not llm_config:
76
+ llm_config = combine_custom_and_default_config(
77
+ llm_config, cls.get_default_llm_config()
78
+ )
78
79
  embeddings_config = resolve_model_client_config(embeddings)
79
80
  embeddings_config, embeddings_extra_parameters = cls.extract_attributes(
80
81
  embeddings_config
@@ -27,22 +27,32 @@ from rasa.shared.utils.common import conditional_import
27
27
 
28
28
  # components dependent on tensorflow
29
29
  TEDPolicy, TED_POLICY_AVAILABLE = conditional_import(
30
- "rasa.core.policies.ted_policy", "TEDPolicy"
30
+ "rasa.core.policies.ted_policy", "TEDPolicy", check_installation_setup=True
31
31
  )
32
32
  UnexpecTEDIntentPolicy, UNEXPECTED_INTENT_POLICY_AVAILABLE = conditional_import(
33
- "rasa.core.policies.unexpected_intent_policy", "UnexpecTEDIntentPolicy"
33
+ "rasa.core.policies.unexpected_intent_policy",
34
+ "UnexpecTEDIntentPolicy",
35
+ check_installation_setup=True,
34
36
  )
35
37
  DIETClassifier, DIET_CLASSIFIER_AVAILABLE = conditional_import(
36
- "rasa.nlu.classifiers.diet_classifier", "DIETClassifier"
38
+ "rasa.nlu.classifiers.diet_classifier",
39
+ "DIETClassifier",
40
+ check_installation_setup=True,
37
41
  )
38
42
  ConveRTFeaturizer, CONVERT_FEATURIZER_AVAILABLE = conditional_import(
39
- "rasa.nlu.featurizers.dense_featurizer.convert_featurizer", "ConveRTFeaturizer"
43
+ "rasa.nlu.featurizers.dense_featurizer.convert_featurizer",
44
+ "ConveRTFeaturizer",
45
+ check_installation_setup=True,
40
46
  )
41
47
  LanguageModelFeaturizer, LANGUAGE_MODEL_FEATURIZER_AVAILABLE = conditional_import(
42
- "rasa.nlu.featurizers.dense_featurizer.lm_featurizer", "LanguageModelFeaturizer"
48
+ "rasa.nlu.featurizers.dense_featurizer.lm_featurizer",
49
+ "LanguageModelFeaturizer",
50
+ check_installation_setup=True,
43
51
  )
44
52
  ResponseSelector, RESPONSE_SELECTOR_AVAILABLE = conditional_import(
45
- "rasa.nlu.selectors.response_selector", "ResponseSelector"
53
+ "rasa.nlu.selectors.response_selector",
54
+ "ResponseSelector",
55
+ check_installation_setup=True,
46
56
  )
47
57
 
48
58
  # components dependent on skops
@@ -40,16 +40,22 @@ from rasa.shared.utils.common import conditional_import
40
40
 
41
41
  # Conditional imports for TensorFlow-dependent components
42
42
  TEDPolicy, TED_POLICY_AVAILABLE = conditional_import(
43
- "rasa.core.policies.ted_policy", "TEDPolicy"
43
+ "rasa.core.policies.ted_policy", "TEDPolicy", check_installation_setup=True
44
44
  )
45
45
  UnexpecTEDIntentPolicy, UNEXPECTED_INTENT_POLICY_AVAILABLE = conditional_import(
46
- "rasa.core.policies.unexpected_intent_policy", "UnexpecTEDIntentPolicy"
46
+ "rasa.core.policies.unexpected_intent_policy",
47
+ "UnexpecTEDIntentPolicy",
48
+ check_installation_setup=True,
47
49
  )
48
50
  DIETClassifier, DIET_CLASSIFIER_AVAILABLE = conditional_import(
49
- "rasa.nlu.classifiers.diet_classifier", "DIETClassifier"
51
+ "rasa.nlu.classifiers.diet_classifier",
52
+ "DIETClassifier",
53
+ check_installation_setup=True,
50
54
  )
51
55
  ResponseSelector, RESPONSE_SELECTOR_AVAILABLE = conditional_import(
52
- "rasa.nlu.selectors.response_selector", "ResponseSelector"
56
+ "rasa.nlu.selectors.response_selector",
57
+ "ResponseSelector",
58
+ check_installation_setup=True,
53
59
  )
54
60
 
55
61
  # Conditional imports for nlu components requiring other dependencies than tensorflow
@@ -9,9 +9,11 @@ from typing import Any, Dict, List, Optional, Text, Tuple, Type, TypeVar, Union
9
9
  import numpy as np
10
10
  import scipy.sparse
11
11
 
12
+ from rasa.utils.installation_utils import check_for_installation_issues
12
13
  from rasa.utils.tensorflow import TENSORFLOW_AVAILABLE
13
14
 
14
15
  if TENSORFLOW_AVAILABLE:
16
+ check_for_installation_issues()
15
17
  import tensorflow as tf
16
18
  else:
17
19
  tf = None
rasa/shared/core/slots.py CHANGED
@@ -355,8 +355,8 @@ class FloatSlot(Slot):
355
355
  mappings: List[Dict[Text, Any]],
356
356
  initial_value: Optional[float] = None,
357
357
  value_reset_delay: Optional[int] = None,
358
- max_value: float = 1.0,
359
- min_value: float = 0.0,
358
+ max_value: Optional[float] = None,
359
+ min_value: Optional[float] = None,
360
360
  influence_conversation: bool = True,
361
361
  is_builtin: bool = False,
362
362
  shared_for_coexistence: bool = False,
@@ -380,32 +380,24 @@ class FloatSlot(Slot):
380
380
  filled_by=filled_by,
381
381
  validation=validation,
382
382
  )
383
+ self.validate_min_max_range(min_value, max_value)
384
+
383
385
  self.max_value = max_value
384
386
  self.min_value = min_value
385
387
 
386
- if min_value >= max_value:
387
- raise InvalidSlotConfigError(
388
- "Float slot ('{}') created with an invalid range "
389
- "using min ({}) and max ({}) values. Make sure "
390
- "min is smaller than max."
391
- "".format(self.name, self.min_value, self.max_value)
392
- )
393
-
394
- if initial_value is not None and not (min_value <= initial_value <= max_value):
395
- rasa.shared.utils.io.raise_warning(
396
- f"Float slot ('{self.name}') created with an initial value "
397
- f"{self.value}. This value is outside of the configured min "
398
- f"({self.min_value}) and max ({self.max_value}) values."
399
- )
400
-
401
388
  def _as_feature(self) -> List[float]:
389
+ # set default min and max values used in prior releases
390
+ # to prevent regressions for existing models
391
+ min_value = self.min_value or 0.0
392
+ max_value = self.max_value or 1.0
393
+
402
394
  try:
403
- capped_value = max(self.min_value, min(self.max_value, float(self.value)))
404
- if abs(self.max_value - self.min_value) > 0:
405
- covered_range = abs(self.max_value - self.min_value)
395
+ capped_value = max(min_value, min(max_value, float(self.value)))
396
+ if abs(max_value - min_value) > 0:
397
+ covered_range = abs(max_value - min_value)
406
398
  else:
407
399
  covered_range = 1
408
- return [1.0, (capped_value - self.min_value) / covered_range]
400
+ return [1.0, (capped_value - min_value) / covered_range]
409
401
  except (TypeError, ValueError):
410
402
  return [0.0, 0.0]
411
403
 
@@ -424,13 +416,52 @@ class FloatSlot(Slot):
424
416
  return value
425
417
 
426
418
  def is_valid_value(self, value: Any) -> bool:
427
- """Checks if the slot contains the value."""
428
- # check that coerced type is float
429
- return value is None or isinstance(self.coerce_value(value), float)
419
+ """Checks if the slot value is valid."""
420
+ if value is None:
421
+ return True
422
+
423
+ if not isinstance(self.coerce_value(value), float):
424
+ return False
425
+
426
+ if (
427
+ self.min_value is not None
428
+ and self.max_value is not None
429
+ and not (self.min_value <= value <= self.max_value)
430
+ ):
431
+ return False
432
+
433
+ return True
430
434
 
431
435
  def _feature_dimensionality(self) -> int:
432
436
  return len(self.as_feature())
433
437
 
438
+ def validate_min_max_range(
439
+ self, min_value: Optional[float], max_value: Optional[float]
440
+ ) -> None:
441
+ """Validates the min-max range for the slot.
442
+
443
+ Raises:
444
+ InvalidSlotConfigError, if the min-max range is invalid.
445
+ """
446
+ if min_value is not None and max_value is not None and min_value >= max_value:
447
+ raise InvalidSlotConfigError(
448
+ f"Float slot ('{self.name}') created with an invalid range "
449
+ f"using min ({min_value}) and max ({max_value}) values. Make sure "
450
+ f"min is smaller than max."
451
+ )
452
+
453
+ if (
454
+ self.initial_value is not None
455
+ and min_value is not None
456
+ and max_value is not None
457
+ and not (min_value <= self.initial_value <= max_value)
458
+ ):
459
+ raise InvalidSlotConfigError(
460
+ f"Float slot ('{self.name}') created with an initial value "
461
+ f"{self.initial_value}. This value is outside of the configured min "
462
+ f"({min_value}) and max ({max_value}) values."
463
+ )
464
+
434
465
 
435
466
  class BooleanSlot(Slot):
436
467
  """A slot storing a truth value."""
@@ -26,6 +26,7 @@ from rasa.exceptions import MissingDependencyException
26
26
  from rasa.shared.constants import DOCS_URL_MIGRATION_GUIDE
27
27
  from rasa.shared.exceptions import ProviderClientValidationError, RasaException
28
28
  from rasa.shared.utils.cli import print_success
29
+ from rasa.utils.installation_utils import check_for_installation_issues
29
30
 
30
31
  logger = logging.getLogger(__name__)
31
32
 
@@ -396,7 +397,11 @@ Sign up at: https://feedback.rasa.com
396
397
  print_success(message)
397
398
 
398
399
 
399
- def conditional_import(module_name: str, class_name: str) -> Tuple[Any, bool]:
400
+ def conditional_import(
401
+ module_name: str,
402
+ class_name: str,
403
+ check_installation_setup: bool = False,
404
+ ) -> Tuple[Any, bool]:
400
405
  """Conditionally import a class, returning (class, is_available) tuple.
401
406
 
402
407
  Args:
@@ -408,6 +413,9 @@ def conditional_import(module_name: str, class_name: str) -> Tuple[Any, bool]:
408
413
  or None if import failed, and is_available is a boolean indicating
409
414
  whether the import was successful.
410
415
  """
416
+ if check_installation_setup:
417
+ check_for_installation_issues()
418
+
411
419
  try:
412
420
  module = __import__(module_name, fromlist=[class_name])
413
421
  return getattr(module, class_name), True
rasa/utils/common.py CHANGED
@@ -36,6 +36,7 @@ from rasa.constants import (
36
36
  ENV_LOG_LEVEL_LIBRARIES,
37
37
  ENV_LOG_LEVEL_MATPLOTLIB,
38
38
  ENV_LOG_LEVEL_MCP,
39
+ ENV_LOG_LEVEL_PYMONGO,
39
40
  ENV_LOG_LEVEL_RABBITMQ,
40
41
  ENV_MCP_LOGGING_ENABLED,
41
42
  )
@@ -297,6 +298,7 @@ def configure_library_logging() -> None:
297
298
  update_rabbitmq_log_level(library_log_level)
298
299
  update_websockets_log_level(library_log_level)
299
300
  update_mcp_log_level()
301
+ update_pymongo_log_level(library_log_level)
300
302
 
301
303
 
302
304
  def update_apscheduler_log_level() -> None:
@@ -481,6 +483,13 @@ def update_mcp_log_level() -> None:
481
483
  logging.getLogger(logger_name).propagate = False
482
484
 
483
485
 
486
+ def update_pymongo_log_level(library_log_level: str) -> None:
487
+ """Set the log level of pymongo."""
488
+ log_level = os.environ.get(ENV_LOG_LEVEL_PYMONGO, library_log_level)
489
+ logging.getLogger("pymongo").setLevel(log_level)
490
+ logging.getLogger("pymongo").propagate = False
491
+
492
+
484
493
  def sort_list_of_dicts_by_first_key(dicts: List[Dict]) -> List[Dict]:
485
494
  """Sorts a list of dictionaries by their first key."""
486
495
  return sorted(dicts, key=lambda d: next(iter(d.keys())))
rasa/utils/endpoints.py CHANGED
@@ -1,5 +1,6 @@
1
1
  import os
2
2
  import ssl
3
+ from functools import lru_cache
3
4
  from pathlib import Path
4
5
  from types import ModuleType
5
6
  from typing import Any, Dict, List, Optional, Text, Union
@@ -16,6 +17,7 @@ from rasa.shared.utils.yaml import read_config_file
16
17
  structlogger = structlog.get_logger()
17
18
 
18
19
 
20
+ @lru_cache(maxsize=10)
19
21
  def read_endpoint_config(
20
22
  filename: Union[str, Path], endpoint_type: Text
21
23
  ) -> Optional["EndpointConfig"]:
@@ -0,0 +1,111 @@
1
+ import importlib.util
2
+ import sys
3
+
4
+ import structlog
5
+
6
+ structlogger = structlog.get_logger()
7
+
8
+
9
+ def check_tensorflow_installation() -> None:
10
+ """Check if TensorFlow is installed without proper Rasa extras."""
11
+ # Check if tensorflow is available in the environment
12
+ tensorflow_available = importlib.util.find_spec("tensorflow") is not None
13
+
14
+ if not tensorflow_available:
15
+ return
16
+
17
+ # Check if any TensorFlow-related extras were installed
18
+ # We do this by checking for packages that are only installed with nlu/full extras
19
+ tensorflow_extras_indicators = [
20
+ "tensorflow_text", # Only in nlu/full extras
21
+ "tensorflow_hub", # Only in nlu/full extras
22
+ "tf_keras", # Only in nlu/full extras
23
+ ]
24
+
25
+ extras_installed = any(
26
+ importlib.util.find_spec(pkg) is not None
27
+ for pkg in tensorflow_extras_indicators
28
+ )
29
+
30
+ if tensorflow_available and not extras_installed:
31
+ structlogger.warning(
32
+ "installation_utils.tensorflow_installation",
33
+ warning=(
34
+ "TensorFlow is installed but Rasa was not installed with TensorFlow "
35
+ "support, i.e. additional packages required to use NLU components "
36
+ "have not been installed. For the most reliable setup, delete your "
37
+ "current virtual environment, create a new one, and install Rasa "
38
+ "again. Please follow the instructions at "
39
+ "https://rasa.com/docs/pro/installation/python"
40
+ ),
41
+ )
42
+
43
+
44
+ def check_tensorflow_integrity() -> None:
45
+ """Check if TensorFlow installation is corrupted or incomplete."""
46
+ # Only check if tensorflow is available
47
+ if importlib.util.find_spec("tensorflow") is None:
48
+ return
49
+
50
+ try:
51
+ # Try to import tensorflow - this will fail if installation is corrupted
52
+ import tensorflow as tf
53
+
54
+ # Try to access a basic TensorFlow function
55
+ _ = tf.constant([1, 2, 3])
56
+ except Exception:
57
+ # Simplified error message for all TensorFlow corruption issues
58
+ structlogger.error(
59
+ "installation_utils.tensorflow_integrity",
60
+ issue=(
61
+ "TensorFlow is installed but appears to be corrupted or incomplete. "
62
+ "For the most reliable setup, delete your current virtual "
63
+ "environment, create a new one, and install Rasa again. "
64
+ "Please follow the instructions at "
65
+ "https://rasa.com/docs/pro/installation/python"
66
+ ),
67
+ )
68
+ sys.exit(1)
69
+
70
+
71
+ def check_rasa_availability() -> None:
72
+ """Check if Rasa is installed and importable."""
73
+ if importlib.util.find_spec("rasa") is None:
74
+ structlogger.error(
75
+ "installation_utils.rasa_availability",
76
+ issue=(
77
+ "Rasa is not installed in this environment. "
78
+ "Please follow the instructions at "
79
+ "https://rasa.com/docs/pro/installation/python"
80
+ ),
81
+ )
82
+ sys.exit(1)
83
+
84
+ try:
85
+ _ = importlib.import_module("rasa")
86
+ except Exception as e:
87
+ structlogger.error(
88
+ "installation_utils.rasa_availability",
89
+ issue=(
90
+ f"Rasa is installed but cannot be imported: {e!s}."
91
+ f"Please follow the instructions at "
92
+ f"https://rasa.com/docs/pro/installation/python"
93
+ ),
94
+ )
95
+ sys.exit(1)
96
+
97
+
98
+ def check_for_installation_issues() -> None:
99
+ """Check for all potential installation issues.
100
+
101
+ Returns:
102
+ List of warning messages for detected issues.
103
+ """
104
+ # Check if Rasa is available first
105
+ check_rasa_availability()
106
+
107
+ # Check TensorFlow integrity first (more critical)
108
+ check_tensorflow_integrity()
109
+
110
+ # Check for orphaned TensorFlow
111
+ check_tensorflow_installation()
@@ -2,9 +2,11 @@ import logging
2
2
  from pathlib import Path
3
3
  from typing import Any, Dict, Optional, Text
4
4
 
5
+ from rasa.utils.installation_utils import check_for_installation_issues
5
6
  from rasa.utils.tensorflow import TENSORFLOW_AVAILABLE
6
7
 
7
8
  if TENSORFLOW_AVAILABLE:
9
+ check_for_installation_issues()
8
10
  import tensorflow as tf
9
11
  from tqdm import tqdm
10
12
  else:
rasa/utils/train_utils.py CHANGED
@@ -11,10 +11,12 @@ from rasa.nlu.constants import NUMBER_OF_SUB_TOKENS
11
11
  from rasa.shared.constants import NEXT_MAJOR_VERSION_FOR_DEPRECATIONS
12
12
  from rasa.shared.exceptions import InvalidConfigException
13
13
  from rasa.shared.nlu.constants import SPLIT_ENTITIES_BY_COMMA
14
+ from rasa.utils.installation_utils import check_for_installation_issues
14
15
  from rasa.utils.tensorflow import TENSORFLOW_AVAILABLE
15
16
 
16
17
  # Conditional imports for TensorFlow-dependent modules
17
18
  if TENSORFLOW_AVAILABLE:
19
+ check_for_installation_issues()
18
20
  from rasa.utils.tensorflow.callback import RasaModelCheckpoint, RasaTrainingLogger
19
21
  from rasa.utils.tensorflow.constants import (
20
22
  AUTO,
rasa/version.py CHANGED
@@ -1,3 +1,3 @@
1
1
  # this file will automatically be changed,
2
2
  # do not add anything but the version number here!
3
- __version__ = "3.14.1"
3
+ __version__ = "3.15.0.dev20251027"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: rasa-pro
3
- Version: 3.14.1
3
+ Version: 3.15.0.dev20251027
4
4
  Summary: State-of-the-art open-core Conversational AI framework for Enterprises that natively leverages generative AI for effortless assistant development.
5
5
  Keywords: nlp,machine-learning,machine-learning-library,bot,bots,botkit,rasa conversational-agents,conversational-ai,chatbot,chatbot-framework,bot-framework
6
6
  Author: Rasa Technologies GmbH
@@ -21,6 +21,7 @@ Provides-Extra: full
21
21
  Provides-Extra: gh-release-notes
22
22
  Provides-Extra: jieba
23
23
  Provides-Extra: metal
24
+ Provides-Extra: monitoring
24
25
  Provides-Extra: nlu
25
26
  Provides-Extra: pii
26
27
  Provides-Extra: spacy
@@ -72,6 +73,7 @@ Requires-Dist: keras (>=3.11.0)
72
73
  Requires-Dist: langchain (>=0.3.27,<0.4.0)
73
74
  Requires-Dist: langchain-community (>=0.3.29,<0.4.0)
74
75
  Requires-Dist: langcodes (>=3.5.0,<4.0.0)
76
+ Requires-Dist: langfuse (>=3.6.0,<3.7.0) ; extra == "full" or extra == "monitoring"
75
77
  Requires-Dist: litellm (>=1.69.0,<1.70.0)
76
78
  Requires-Dist: matplotlib (>=3.9.4,<3.10.0)
77
79
  Requires-Dist: mattermostwrapper (>=2.2,<2.3) ; extra == "full" or extra == "channels"
@@ -108,7 +110,7 @@ Requires-Dist: pyyaml (>=6.0.2,<6.1.0)
108
110
  Requires-Dist: qdrant-client (>=1.9.1,<1.10.0)
109
111
  Requires-Dist: questionary (>=2.1.1,<2.2.0)
110
112
  Requires-Dist: randomname (>=0.2.1,<0.3.0)
111
- Requires-Dist: rasa-sdk (==3.14.0)
113
+ Requires-Dist: rasa-sdk (==3.15.0.dev1)
112
114
  Requires-Dist: redis (>=4.6.0,<6.0)
113
115
  Requires-Dist: regex (>=2024.7.24,<2024.8.0)
114
116
  Requires-Dist: requests (>=2.32.5,<2.33.0)