rasa-pro 3.11.0rc1__py3-none-any.whl → 3.11.0rc2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of rasa-pro might be problematic. Click here for more details.

Files changed (52) 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/voice_ready/audiocodes.py +12 -0
  6. rasa/core/channels/voice_ready/jambonz.py +13 -2
  7. rasa/core/channels/voice_ready/twilio_voice.py +6 -21
  8. rasa/core/channels/voice_stream/voice_channel.py +13 -1
  9. rasa/core/nlg/contextual_response_rephraser.py +18 -10
  10. rasa/core/policies/enterprise_search_policy.py +27 -67
  11. rasa/core/policies/intentless_policy.py +25 -67
  12. rasa/dialogue_understanding/coexistence/llm_based_router.py +18 -33
  13. rasa/dialogue_understanding/generator/constants.py +0 -2
  14. rasa/dialogue_understanding/generator/flow_retrieval.py +33 -50
  15. rasa/dialogue_understanding/generator/llm_based_command_generator.py +12 -40
  16. rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +18 -20
  17. rasa/dialogue_understanding/generator/nlu_command_adapter.py +19 -1
  18. rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +24 -21
  19. rasa/dialogue_understanding/processor/command_processor.py +21 -1
  20. rasa/e2e_test/e2e_test_case.py +85 -6
  21. rasa/engine/validation.py +57 -41
  22. rasa/model_service.py +3 -0
  23. rasa/nlu/tokenizers/whitespace_tokenizer.py +3 -14
  24. rasa/server.py +3 -1
  25. rasa/shared/core/flows/flows_list.py +5 -1
  26. rasa/shared/providers/embedding/_base_litellm_embedding_client.py +6 -14
  27. rasa/shared/providers/llm/_base_litellm_client.py +6 -1
  28. rasa/shared/utils/health_check/__init__.py +0 -0
  29. rasa/shared/utils/health_check/embeddings_health_check_mixin.py +31 -0
  30. rasa/shared/utils/health_check/health_check.py +256 -0
  31. rasa/shared/utils/health_check/llm_health_check_mixin.py +31 -0
  32. rasa/shared/utils/llm.py +5 -2
  33. rasa/shared/utils/yaml.py +102 -62
  34. rasa/studio/auth.py +3 -5
  35. rasa/studio/config.py +13 -4
  36. rasa/studio/constants.py +1 -0
  37. rasa/studio/data_handler.py +10 -3
  38. rasa/studio/upload.py +21 -10
  39. rasa/telemetry.py +12 -0
  40. rasa/tracing/config.py +2 -0
  41. rasa/tracing/instrumentation/attribute_extractors.py +20 -0
  42. rasa/tracing/instrumentation/instrumentation.py +121 -0
  43. rasa/utils/common.py +5 -0
  44. rasa/utils/io.py +8 -16
  45. rasa/utils/sanic_error_handler.py +32 -0
  46. rasa/version.py +1 -1
  47. {rasa_pro-3.11.0rc1.dist-info → rasa_pro-3.11.0rc2.dist-info}/METADATA +3 -2
  48. {rasa_pro-3.11.0rc1.dist-info → rasa_pro-3.11.0rc2.dist-info}/RECORD +51 -47
  49. rasa/shared/utils/health_check.py +0 -533
  50. {rasa_pro-3.11.0rc1.dist-info → rasa_pro-3.11.0rc2.dist-info}/NOTICE +0 -0
  51. {rasa_pro-3.11.0rc1.dist-info → rasa_pro-3.11.0rc2.dist-info}/WHEEL +0 -0
  52. {rasa_pro-3.11.0rc1.dist-info → rasa_pro-3.11.0rc2.dist-info}/entry_points.txt +0 -0
@@ -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
 
@@ -137,6 +136,16 @@ class SingleStepLLMCommandGenerator(LLMBasedCommandGenerator):
137
136
  **kwargs: Any,
138
137
  ) -> "SingleStepLLMCommandGenerator":
139
138
  """Loads trained component (see parent class for full docstring)."""
139
+
140
+ # Perform health check of the LLM API endpoint
141
+ llm_config = resolve_model_client_config(config.get(LLM_CONFIG_KEY, {}))
142
+ cls.perform_llm_health_check(
143
+ llm_config,
144
+ DEFAULT_LLM_CONFIG,
145
+ "single_step_llm_command_generator.load",
146
+ SingleStepLLMCommandGenerator.__name__,
147
+ )
148
+
140
149
  # load prompt template from the model storage.
141
150
  prompt_template = cls.load_prompt_template_from_model_storage(
142
151
  model_storage, resource, COMMAND_PROMPT_FILE_NAME
@@ -150,34 +159,28 @@ class SingleStepLLMCommandGenerator(LLMBasedCommandGenerator):
150
159
  command_generator.config, model_storage, resource
151
160
  )
152
161
 
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
162
  return command_generator
168
163
 
169
164
  def persist(self) -> None:
170
165
  """Persist this component to disk for future loading."""
171
- # persist prompt template
172
- super().persist()
166
+ self._persist_prompt_template()
167
+ self._persist_config()
168
+ if self.flow_retrieval is not None:
169
+ self.flow_retrieval.persist()
173
170
 
171
+ def _persist_prompt_template(self) -> None:
172
+ """Persist prompt template for future loading."""
174
173
  with self._model_storage.write_to(self._resource) as path:
175
174
  rasa.shared.utils.io.write_text_file(
176
175
  self.prompt_template, path / COMMAND_PROMPT_FILE_NAME
177
176
  )
178
- # persist flow retrieval
179
- if self.flow_retrieval is not None:
180
- self.flow_retrieval.persist()
177
+
178
+ def _persist_config(self) -> None:
179
+ """Persist config as a source of truth for resolved clients."""
180
+ with self._model_storage.write_to(self._resource) as path:
181
+ rasa.shared.utils.io.dump_obj_as_json_to_file(
182
+ path / SINGLE_STEP_LLM_COMMAND_GENERATOR_CONFIG_FILE, self.config
183
+ )
181
184
 
182
185
  async def predict_commands(
183
186
  self,
@@ -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
  ):
@@ -1,8 +1,10 @@
1
- import logging
2
1
  from collections import OrderedDict
2
+ from collections import defaultdict
3
3
  from dataclasses import dataclass
4
4
  from typing import Any, Dict, List, Optional, Text, Union
5
5
 
6
+ import structlog
7
+
6
8
  from rasa.e2e_test.assertions import Assertion
7
9
  from rasa.e2e_test.constants import (
8
10
  KEY_ASSERTIONS,
@@ -20,10 +22,11 @@ from rasa.e2e_test.constants import (
20
22
  KEY_USER_INPUT,
21
23
  )
22
24
  from rasa.e2e_test.stub_custom_action import StubCustomAction
25
+ from rasa.shared.constants import DOCS_BASE_URL
23
26
  from rasa.shared.core.events import BotUttered, SlotSet, UserUttered
24
27
  from rasa.shared.exceptions import RasaException
25
28
 
26
- logger = logging.getLogger(__name__)
29
+ structlogger = structlog.get_logger(__name__)
27
30
 
28
31
 
29
32
  @dataclass(frozen=True)
@@ -343,9 +346,10 @@ class ActualStepOutput:
343
346
  try:
344
347
  return self.user_uttered_events[0]
345
348
  except IndexError:
346
- logger.debug(
347
- f"Could not find `UserUttered` event in the ActualStepOutput: "
348
- f"{self}"
349
+ structlogger.debug(
350
+ "e2e_test_case.get_user_uttered_event.no_user_uttered_event",
351
+ event_info=f"Could not find `UserUttered` event in the "
352
+ f"ActualStepOutput: {self}",
349
353
  )
350
354
  return None
351
355
  return None
@@ -395,7 +399,7 @@ class TestCase:
395
399
  else:
396
400
  steps.append(TestStep.from_dict(step))
397
401
 
398
- return TestCase(
402
+ test_case = TestCase(
399
403
  name=input_test_case.get(KEY_TEST_CASE, "default"),
400
404
  steps=steps,
401
405
  file=file,
@@ -405,6 +409,81 @@ class TestCase:
405
409
  fixture_names=input_test_case.get(KEY_FIXTURES),
406
410
  metadata_name=input_test_case.get(KEY_METADATA),
407
411
  )
412
+ test_case.validate()
413
+ return test_case
414
+
415
+ def validate(self) -> None:
416
+ """Validates the test case.
417
+
418
+ This method calls all validation methods required for the test case.
419
+ """
420
+ if self.uses_assertions():
421
+ self.validate_duplicate_user_messages_metadata()
422
+
423
+ def validate_duplicate_user_messages_metadata(self) -> None:
424
+ """Validates that duplicate user messages use metadata correctly.
425
+
426
+ Ensures that each duplicate user message uses unique metadata.
427
+
428
+ Raises warnings if any issues are found.
429
+ """
430
+ docs_link = (
431
+ f"{DOCS_BASE_URL}/testing/e2e-testing-assertions/assertions-how-to-guide/"
432
+ "#how-to-handle-duplicate-user-text-messages-in-the-same-test-case"
433
+ )
434
+ no_metadata_event_info = (
435
+ "Test case '{name}' has duplicate user steps with text '{text}', "
436
+ "and user step at line {line} lacks metadata. When using "
437
+ "duplicate user messages, metadata should be set on each step to ensure "
438
+ f"correct processing. Please refer to the documentation: {docs_link}"
439
+ )
440
+ non_unique_metadata_event_info = (
441
+ "Test case '{name}' has duplicate user steps with text '{text}', "
442
+ "and user step at line {line} has duplicate metadata "
443
+ "name '{metadata_name}'. Metadata names should be unique for each user "
444
+ "step among duplicates. This may cause issues in processing "
445
+ f"user messages. Please refer to the documentation: {docs_link}"
446
+ )
447
+
448
+ # Use dict[str, list] structure to group steps by user message text to easily
449
+ # identify and validate instances with duplicate messages and their metadata.
450
+ message_steps = defaultdict(list)
451
+
452
+ # Collect user steps by text
453
+ for step in self.steps:
454
+ if step.actor == KEY_USER_INPUT and step.text:
455
+ message_steps[step.text].append(step)
456
+
457
+ # Check for duplicate messages
458
+ for text, steps in message_steps.items():
459
+ if len(steps) <= 1:
460
+ continue
461
+
462
+ metadata_names_used = set()
463
+ for step in steps:
464
+ if not step.metadata_name:
465
+ structlogger.warning(
466
+ "e2e_test_case.validate_duplicate_user_messages_metadata.no_metadata",
467
+ event_info=no_metadata_event_info.format(
468
+ name=self.name,
469
+ text=text,
470
+ line=step.line,
471
+ ),
472
+ )
473
+ break
474
+ elif step.metadata_name in metadata_names_used:
475
+ structlogger.warning(
476
+ "e2e_test_case.validate_duplicate_user_messages_metadata.non_unique_metadata",
477
+ event_info=non_unique_metadata_event_info.format(
478
+ name=self.name,
479
+ text=text,
480
+ line=step.line,
481
+ metadata_name=step.metadata_name,
482
+ ),
483
+ )
484
+ break
485
+ else:
486
+ metadata_names_used.add(step.metadata_name)
408
487
 
409
488
  def as_dict(self) -> Dict[Text, Any]:
410
489
  """Returns the test case as a dictionary."""
rasa/engine/validation.py CHANGED
@@ -18,11 +18,10 @@ from typing import (
18
18
  List,
19
19
  )
20
20
 
21
+ import rasa.utils.common
21
22
  import structlog
22
23
  import typing_utils
23
-
24
- import rasa.utils.common
25
- from rasa.core import IntentlessPolicy
24
+ from rasa.core import IntentlessPolicy, ContextualResponseRephraser
26
25
  from rasa.core.policies.policy import PolicyPrediction
27
26
  from rasa.core.utils import AvailableEndpoints
28
27
  from rasa.dialogue_understanding.coexistence.constants import (
@@ -67,6 +66,7 @@ from rasa.shared.constants import (
67
66
  MODEL_GROUP_ID_CONFIG_KEY,
68
67
  ROUTER_CONFIG_KEY,
69
68
  MODELS_CONFIG_KEY,
69
+ MODEL_GROUP_CONFIG_KEY,
70
70
  ROUTER_STRATEGY_CONFIG_KEY,
71
71
  VALID_ROUTER_STRATEGIES,
72
72
  ROUTER_STRATEGIES_REQUIRING_REDIS_CACHE,
@@ -890,13 +890,13 @@ def _validate_component_model_client_config(
890
890
  # no llm configuration present
891
891
  return
892
892
 
893
- if MODELS_CONFIG_KEY in component_config[key]:
893
+ if MODEL_GROUP_CONFIG_KEY in component_config[key]:
894
894
  model_group_syntax_used.append(True)
895
- model_group_ids.append(component_config[key][MODELS_CONFIG_KEY])
895
+ model_group_ids.append(component_config[key][MODEL_GROUP_CONFIG_KEY])
896
896
 
897
897
  if len(component_config[key]) > 1:
898
898
  print_error_and_exit(
899
- f"You specified a '{MODELS_CONFIG_KEY}' for the '{key}' "
899
+ f"You specified a '{MODEL_GROUP_CONFIG_KEY}' for the '{key}' "
900
900
  f"config key for the component "
901
901
  f"'{component_name or component_config['name']}'. "
902
902
  "No other parameters are allowed under the "
@@ -908,10 +908,9 @@ def _validate_component_model_client_config(
908
908
  # check that api_key is not set in config
909
909
  if API_KEY in component_config[key]:
910
910
  print_error_and_exit(
911
- f"You specified '{API_KEY}' in the config for"
912
- f"{component_name or component_config['name']}, which "
913
- "is not allowed. Set API keys through "
914
- "environment variables."
911
+ f"You specified '{API_KEY}' in the config for "
912
+ f"'{component_name or component_config['name']}', which is not allowed."
913
+ " Set API keys through environment variables."
915
914
  )
916
915
 
917
916
 
@@ -936,34 +935,46 @@ def validate_model_client_configuration_setup(config: Dict[str, Any]) -> None:
936
935
  model_group_syntax_used: List[bool] = []
937
936
  model_group_ids: List[str] = []
938
937
 
939
- if "pipeline" not in config:
940
- return
941
-
942
- for component in config["pipeline"]:
943
- for key in [LLM_CONFIG_KEY, EMBEDDINGS_CONFIG_KEY]:
944
- _validate_component_model_client_config(
945
- component, key, model_group_syntax_used, model_group_ids
946
- )
938
+ for outer_key in ["pipeline", "policies"]:
939
+ if outer_key not in config or config[outer_key] is None:
940
+ continue
947
941
 
948
- # as flow retrieval is not a component itself, we need to
949
- # check it separately
950
- if FLOW_RETRIEVAL_KEY in component:
951
- if EMBEDDINGS_CONFIG_KEY in component[FLOW_RETRIEVAL_KEY]:
942
+ for component in config[outer_key]:
943
+ for key in [LLM_CONFIG_KEY, EMBEDDINGS_CONFIG_KEY]:
952
944
  _validate_component_model_client_config(
953
- component[FLOW_RETRIEVAL_KEY],
954
- EMBEDDINGS_CONFIG_KEY,
955
- model_group_syntax_used,
956
- model_group_ids,
957
- component["name"] + "." + FLOW_RETRIEVAL_KEY,
945
+ component, key, model_group_syntax_used, model_group_ids
958
946
  )
959
947
 
948
+ # as flow retrieval is not a component itself, we need to
949
+ # check it separately
950
+ if FLOW_RETRIEVAL_KEY in component:
951
+ if EMBEDDINGS_CONFIG_KEY in component[FLOW_RETRIEVAL_KEY]:
952
+ _validate_component_model_client_config(
953
+ component[FLOW_RETRIEVAL_KEY],
954
+ EMBEDDINGS_CONFIG_KEY,
955
+ model_group_syntax_used,
956
+ model_group_ids,
957
+ component["name"] + "." + FLOW_RETRIEVAL_KEY,
958
+ )
959
+
960
+ # also include the ContextualResponseRephraser component
961
+ endpoints = AvailableEndpoints.get_instance()
962
+ if endpoints.nlg is not None:
963
+ _validate_component_model_client_config(
964
+ endpoints.nlg.kwargs,
965
+ LLM_CONFIG_KEY,
966
+ model_group_syntax_used,
967
+ model_group_ids,
968
+ ContextualResponseRephraser.__name__,
969
+ )
970
+
960
971
  if not is_uniform_bool_list(model_group_syntax_used):
961
972
  print_error_and_exit(
962
973
  "Some of your components refer to an LLM using the "
963
- f"'{MODELS_CONFIG_KEY}' parameter, other components directly"
964
- f"define the LLM under the '{LLM_CONFIG_KEY}' or the "
974
+ f"'{MODEL_GROUP_CONFIG_KEY}' parameter, other components directly"
975
+ f" define the LLM under the '{LLM_CONFIG_KEY}' or the "
965
976
  f"'{EMBEDDINGS_CONFIG_KEY}' key. You cannot use"
966
- "a both types of definition. Please chose one syntax "
977
+ " both types of definitions. Please chose one syntax "
967
978
  "and update your config."
968
979
  )
969
980
 
@@ -1106,17 +1117,22 @@ def _validate_api_key_is_an_environment_variable(
1106
1117
  for model_group in model_groups:
1107
1118
  for model_config in model_group[MODELS_CONFIG_KEY]:
1108
1119
  for key, value in model_config.items():
1109
- if (
1110
- key == API_KEY
1111
- and isinstance(value, str)
1112
- and not re.match(r"\${(\w+)}", value)
1113
- ):
1114
- print_error_and_exit(
1115
- f"You defined the '{API_KEY}' in model group "
1116
- f"'{model_group[MODEL_GROUP_ID_CONFIG_KEY]}' as a string. "
1117
- f"The '{API_KEY}' must be set as an environment variable. "
1118
- f"Please update your config."
1119
- )
1120
+ if key == API_KEY:
1121
+ if isinstance(value, str):
1122
+ if not re.match(r"\${(\w+)}", value):
1123
+ print_error_and_exit(
1124
+ f"You defined the '{API_KEY}' in model group "
1125
+ f"'{model_group[MODEL_GROUP_ID_CONFIG_KEY]}' as a "
1126
+ f"string. The '{API_KEY}' must be set as an environment"
1127
+ f" variable. Please update your config."
1128
+ )
1129
+ else:
1130
+ print_error_and_exit(
1131
+ f"You should define the '{API_KEY}' in model group "
1132
+ f"'{model_group[MODEL_GROUP_ID_CONFIG_KEY]}' using the "
1133
+ f"environment variable syntax - ${{ENV_VARIABLE_NAME}}. "
1134
+ f"Please update your config."
1135
+ )
1120
1136
 
1121
1137
 
1122
1138
  def validate_model_group_configuration_setup() -> None:
rasa/model_service.py CHANGED
@@ -14,6 +14,7 @@ import rasa.utils.licensing
14
14
  from urllib.parse import urlparse
15
15
 
16
16
  from rasa.utils.log_utils import configure_structlog
17
+ from rasa.utils.sanic_error_handler import register_custom_sanic_error_handler
17
18
 
18
19
  structlogger = structlog.get_logger()
19
20
 
@@ -104,6 +105,8 @@ def main() -> None:
104
105
  # list all routes
105
106
  list_routes(app)
106
107
 
108
+ register_custom_sanic_error_handler(app)
109
+
107
110
  app.run(host="0.0.0.0", port=MODEL_SERVICE_PORT, legacy=True, motd=False)
108
111
 
109
112
 
@@ -43,8 +43,6 @@ class WhitespaceTokenizer(Tokenizer):
43
43
  def __init__(self, config: Dict[Text, Any]) -> None:
44
44
  """Initialize the tokenizer."""
45
45
  super().__init__(config)
46
- self.emoji_pattern = rasa.utils.io.get_emoji_regex()
47
-
48
46
  if "case_sensitive" in self._config:
49
47
  rasa.shared.utils.io.raise_warning(
50
48
  "The option 'case_sensitive' was moved from the tokenizers to the "
@@ -64,18 +62,9 @@ class WhitespaceTokenizer(Tokenizer):
64
62
  # Path to the dictionaries on the local filesystem.
65
63
  return cls(config)
66
64
 
67
- def remove_emoji(self, text: Text) -> Text:
68
- """Remove emoji if the full text, aka token, matches the emoji regex."""
69
- match = self.emoji_pattern.fullmatch(text)
70
-
71
- if match is not None:
72
- return ""
73
-
74
- return text
75
-
76
65
  def tokenize(self, message: Message, attribute: Text) -> List[Token]:
77
- text = message.get(attribute)
78
-
66
+ original_text = message.get(attribute)
67
+ text = rasa.utils.io.remove_emojis(original_text)
79
68
  # we need to use regex instead of re, because of
80
69
  # https://stackoverflow.com/questions/12746458/python-unicode-regular-expression-matching-failing-with-some-unicode-characters
81
70
 
@@ -94,11 +83,11 @@ class WhitespaceTokenizer(Tokenizer):
94
83
  text,
95
84
  ).split()
96
85
 
97
- words = [self.remove_emoji(w) for w in words]
98
86
  words = [w for w in words if w]
99
87
 
100
88
  # if we removed everything like smiles `:)`, use the whole text as 1 token
101
89
  if not words:
90
+ text = original_text
102
91
  words = [text]
103
92
 
104
93
  tokens = self._convert_words_to_tokens(words, text)
rasa/server.py CHANGED
@@ -78,6 +78,7 @@ from rasa.shared.utils.schemas.events import EVENTS_SCHEMA
78
78
  from rasa.shared.utils.yaml import validate_training_data
79
79
  from rasa.utils.common import TempDirectoryPath, get_temp_dir_name
80
80
  from rasa.utils.endpoints import EndpointConfig
81
+ from rasa.utils.sanic_error_handler import register_custom_sanic_error_handler
81
82
 
82
83
  if TYPE_CHECKING:
83
84
  from ssl import SSLContext
@@ -528,7 +529,7 @@ def add_root_route(app: Sanic) -> None:
528
529
  <p>Hello from Rasa: {rasa.__version__}</p>
529
530
  <a href="./webhooks/inspector/inspect.html">Go to the inspector</a>
530
531
  <script>
531
- window.location.replace("./webhooks/inspector/inspect.html");
532
+ window.location.replace("./webhooks/socketio/inspect.html");
532
533
  </script>
533
534
  </body>
534
535
  </html>
@@ -687,6 +688,7 @@ def create_app(
687
688
  app = Sanic("rasa_server")
688
689
  app.config.RESPONSE_TIMEOUT = response_timeout
689
690
  configure_cors(app, cors_origins)
691
+ register_custom_sanic_error_handler(app)
690
692
 
691
693
  # Reset Sanic warnings filter that allows the triggering of Sanic warnings
692
694
  warnings.filterwarnings("ignore", category=DeprecationWarning, module=r"sanic.*")
@@ -234,12 +234,16 @@ class FlowsList:
234
234
  [f for f in self.underlying_flows if not f.is_startable_only_via_link()]
235
235
  )
236
236
 
237
- def available_slot_names(self) -> Set[str]:
237
+ def available_slot_names(
238
+ self, ask_before_filling: Optional[bool] = None
239
+ ) -> Set[str]:
238
240
  """Get all slot names collected by flows."""
239
241
  return {
240
242
  step.collect
241
243
  for flow in self.underlying_flows
242
244
  for step in flow.get_collect_steps()
245
+ if ask_before_filling is None
246
+ or step.ask_before_filling == ask_before_filling
243
247
  }
244
248
 
245
249
  def available_custom_actions(self) -> Set[str]:
@@ -6,6 +6,7 @@ import litellm
6
6
  import structlog
7
7
  from litellm import aembedding, embedding, validate_environment
8
8
 
9
+ from rasa.shared.constants import API_BASE_CONFIG_KEY, API_KEY
9
10
  from rasa.shared.exceptions import (
10
11
  ProviderClientAPIException,
11
12
  ProviderClientValidationError,
@@ -81,11 +82,14 @@ class _BaseLiteLLMEmbeddingClient:
81
82
  ProviderClientValidationError if validation fails.
82
83
  """
83
84
  self._validate_environment_variables()
84
- self._validate_api_key_not_in_config()
85
85
 
86
86
  def _validate_environment_variables(self) -> None:
87
87
  """Validate that the required environment variables are set."""
88
- validation_info = validate_environment(self._litellm_model_name)
88
+ validation_info = validate_environment(
89
+ self._litellm_model_name,
90
+ api_key=self._litellm_extra_parameters.get(API_KEY),
91
+ api_base=self._litellm_extra_parameters.get(API_BASE_CONFIG_KEY),
92
+ )
89
93
  if missing_environment_variables := validation_info.get(
90
94
  _VALIDATE_ENVIRONMENT_MISSING_KEYS_KEY
91
95
  ):
@@ -100,18 +104,6 @@ class _BaseLiteLLMEmbeddingClient:
100
104
  )
101
105
  raise ProviderClientValidationError(event_info)
102
106
 
103
- def _validate_api_key_not_in_config(self) -> None:
104
- if "api_key" in self._litellm_extra_parameters:
105
- event_info = (
106
- "API Key is set through `api_key` extra parameter."
107
- "Set API keys through environment variables."
108
- )
109
- structlogger.error(
110
- "base_litellm_client.validate_api_key_not_in_config",
111
- event_info=event_info,
112
- )
113
- raise ProviderClientValidationError(event_info)
114
-
115
107
  def validate_documents(self, documents: List[str]) -> None:
116
108
  """Validates a list of documents to ensure they are suitable for embedding.
117
109
 
@@ -9,6 +9,7 @@ from litellm import (
9
9
  validate_environment,
10
10
  )
11
11
 
12
+ from rasa.shared.constants import API_BASE_CONFIG_KEY, API_KEY
12
13
  from rasa.shared.exceptions import (
13
14
  ProviderClientAPIException,
14
15
  ProviderClientValidationError,
@@ -101,7 +102,11 @@ class _BaseLiteLLMClient:
101
102
 
102
103
  def _validate_environment_variables(self) -> None:
103
104
  """Validate that the required environment variables are set."""
104
- validation_info = validate_environment(self._litellm_model_name)
105
+ validation_info = validate_environment(
106
+ self._litellm_model_name,
107
+ api_key=self._litellm_extra_parameters.get(API_KEY),
108
+ api_base=self._litellm_extra_parameters.get(API_BASE_CONFIG_KEY),
109
+ )
105
110
  if missing_environment_variables := validation_info.get(
106
111
  _VALIDATE_ENVIRONMENT_MISSING_KEYS_KEY
107
112
  ):
File without changes
@@ -0,0 +1,31 @@
1
+ from typing import Optional, Dict, Any
2
+
3
+
4
+ class EmbeddingsHealthCheckMixin:
5
+ """Mixin class that provides methods for performing embeddings health checks during
6
+ training and inference within components.
7
+
8
+ This mixin offers static methods that wrap the following health check functions:
9
+ - `perform_embeddings_health_check`
10
+ """
11
+
12
+ @staticmethod
13
+ def perform_embeddings_health_check(
14
+ custom_embeddings_config: Optional[Dict[str, Any]],
15
+ default_embeddings_config: Dict[str, Any],
16
+ log_source_method: str,
17
+ log_source_component: str,
18
+ ) -> None:
19
+ """Wraps the `perform_embeddings_health_check` function to enable
20
+ tracing and instrumentation.
21
+ """
22
+ from rasa.shared.utils.health_check.health_check import (
23
+ perform_embeddings_health_check,
24
+ )
25
+
26
+ perform_embeddings_health_check(
27
+ custom_embeddings_config,
28
+ default_embeddings_config,
29
+ log_source_method,
30
+ log_source_component,
31
+ )