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
@@ -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,11 +66,13 @@ from rasa.shared.constants import (
67
66
  MODEL_GROUP_ID_CONFIG_KEY,
68
67
  ROUTER_CONFIG_KEY,
69
68
  MODELS_CONFIG_KEY,
70
- ROUTER_STRATEGY_CONFIG_KEY,
71
- VALID_ROUTER_STRATEGIES,
72
- ROUTER_STRATEGIES_REQUIRING_REDIS_CACHE,
73
- ROUTER_STRATEGIES_NOT_REQUIRING_CACHE,
69
+ MODEL_GROUP_CONFIG_KEY,
70
+ ROUTING_STRATEGY_CONFIG_KEY,
71
+ VALID_ROUTING_STRATEGIES,
72
+ ROUTING_STRATEGIES_REQUIRING_REDIS_CACHE,
73
+ ROUTING_STRATEGIES_NOT_REQUIRING_CACHE,
74
74
  REDIS_HOST_CONFIG_KEY,
75
+ USE_CHAT_COMPLETIONS_ENDPOINT_CONFIG_KEY,
75
76
  )
76
77
  from rasa.shared.core.constants import ACTION_RESET_ROUTING, ACTION_TRIGGER_CHITCHAT
77
78
  from rasa.shared.core.domain import Domain
@@ -890,13 +891,13 @@ def _validate_component_model_client_config(
890
891
  # no llm configuration present
891
892
  return
892
893
 
893
- if MODELS_CONFIG_KEY in component_config[key]:
894
+ if MODEL_GROUP_CONFIG_KEY in component_config[key]:
894
895
  model_group_syntax_used.append(True)
895
- model_group_ids.append(component_config[key][MODELS_CONFIG_KEY])
896
+ model_group_ids.append(component_config[key][MODEL_GROUP_CONFIG_KEY])
896
897
 
897
898
  if len(component_config[key]) > 1:
898
899
  print_error_and_exit(
899
- f"You specified a '{MODELS_CONFIG_KEY}' for the '{key}' "
900
+ f"You specified a '{MODEL_GROUP_CONFIG_KEY}' for the '{key}' "
900
901
  f"config key for the component "
901
902
  f"'{component_name or component_config['name']}'. "
902
903
  "No other parameters are allowed under the "
@@ -908,10 +909,9 @@ def _validate_component_model_client_config(
908
909
  # check that api_key is not set in config
909
910
  if API_KEY in component_config[key]:
910
911
  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."
912
+ f"You specified '{API_KEY}' in the config for "
913
+ f"'{component_name or component_config['name']}', which is not allowed."
914
+ " Set API keys through environment variables."
915
915
  )
916
916
 
917
917
 
@@ -936,34 +936,46 @@ def validate_model_client_configuration_setup(config: Dict[str, Any]) -> None:
936
936
  model_group_syntax_used: List[bool] = []
937
937
  model_group_ids: List[str] = []
938
938
 
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
- )
939
+ for outer_key in ["pipeline", "policies"]:
940
+ if outer_key not in config or config[outer_key] is None:
941
+ continue
947
942
 
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]:
943
+ for component in config[outer_key]:
944
+ for key in [LLM_CONFIG_KEY, EMBEDDINGS_CONFIG_KEY]:
952
945
  _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,
946
+ component, key, model_group_syntax_used, model_group_ids
958
947
  )
959
948
 
949
+ # as flow retrieval is not a component itself, we need to
950
+ # check it separately
951
+ if FLOW_RETRIEVAL_KEY in component:
952
+ if EMBEDDINGS_CONFIG_KEY in component[FLOW_RETRIEVAL_KEY]:
953
+ _validate_component_model_client_config(
954
+ component[FLOW_RETRIEVAL_KEY],
955
+ EMBEDDINGS_CONFIG_KEY,
956
+ model_group_syntax_used,
957
+ model_group_ids,
958
+ component["name"] + "." + FLOW_RETRIEVAL_KEY,
959
+ )
960
+
961
+ # also include the ContextualResponseRephraser component
962
+ endpoints = AvailableEndpoints.get_instance()
963
+ if endpoints.nlg is not None:
964
+ _validate_component_model_client_config(
965
+ endpoints.nlg.kwargs,
966
+ LLM_CONFIG_KEY,
967
+ model_group_syntax_used,
968
+ model_group_ids,
969
+ ContextualResponseRephraser.__name__,
970
+ )
971
+
960
972
  if not is_uniform_bool_list(model_group_syntax_used):
961
973
  print_error_and_exit(
962
974
  "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 "
975
+ f"'{MODEL_GROUP_CONFIG_KEY}' parameter, other components directly"
976
+ f" define the LLM under the '{LLM_CONFIG_KEY}' or the "
965
977
  f"'{EMBEDDINGS_CONFIG_KEY}' key. You cannot use"
966
- "a both types of definition. Please chose one syntax "
978
+ " both types of definitions. Please chose one syntax "
967
979
  "and update your config."
968
980
  )
969
981
 
@@ -1042,31 +1054,42 @@ def _validate_model_group_router_setting(
1042
1054
  if ROUTER_CONFIG_KEY not in model_group:
1043
1055
  continue
1044
1056
 
1057
+ for model_config in model_group.get(MODELS_CONFIG_KEY, []):
1058
+ if USE_CHAT_COMPLETIONS_ENDPOINT_CONFIG_KEY in model_config:
1059
+ print_error_and_exit(
1060
+ f"You defined the '{USE_CHAT_COMPLETIONS_ENDPOINT_CONFIG_KEY}' in "
1061
+ f"the model group '{model_group[MODEL_GROUP_ID_CONFIG_KEY]}'. This "
1062
+ f"key is not allowed in the model configuration as the router is "
1063
+ f"defined. Please remove this key from your model configuration "
1064
+ f"and update it in the '{ROUTER_CONFIG_KEY} configuration, as it "
1065
+ f"is a router level setting."
1066
+ )
1067
+
1045
1068
  router_config = model_group[ROUTER_CONFIG_KEY]
1046
- if ROUTER_STRATEGY_CONFIG_KEY in router_config:
1047
- router_strategy = router_config.get(ROUTER_STRATEGY_CONFIG_KEY)
1048
- if router_strategy and router_strategy not in VALID_ROUTER_STRATEGIES:
1069
+ if ROUTING_STRATEGY_CONFIG_KEY in router_config:
1070
+ routing_strategy = router_config.get(ROUTING_STRATEGY_CONFIG_KEY)
1071
+ if routing_strategy and routing_strategy not in VALID_ROUTING_STRATEGIES:
1049
1072
  print_error_and_exit(
1050
- f"The router strategy you defined for the model group "
1051
- f"'{model_group[MODEL_GROUP_ID_CONFIG_KEY]}' is not valid. "
1052
- f"Valid router strategies are categorized as follows:\n"
1073
+ f"The routing strategy '{routing_strategy}' you defined for the "
1074
+ f"model group '{model_group[MODEL_GROUP_ID_CONFIG_KEY]}' is not "
1075
+ f"valid. Valid routing strategies are categorized as follows:\n"
1053
1076
  f"- Strategies requiring Redis caching: "
1054
- f"{', '.join(ROUTER_STRATEGIES_REQUIRING_REDIS_CACHE)}\n"
1077
+ f"{', '.join(ROUTING_STRATEGIES_REQUIRING_REDIS_CACHE)}\n"
1055
1078
  f"- Strategies not requiring caching: "
1056
- f"{', '.join(ROUTER_STRATEGIES_NOT_REQUIRING_CACHE)}"
1079
+ f"{', '.join(ROUTING_STRATEGIES_NOT_REQUIRING_CACHE)}"
1057
1080
  )
1058
1081
  if (
1059
- router_strategy in ROUTER_STRATEGIES_REQUIRING_REDIS_CACHE
1082
+ routing_strategy in ROUTING_STRATEGIES_REQUIRING_REDIS_CACHE
1060
1083
  and REDIS_HOST_CONFIG_KEY not in router_config
1061
1084
  ):
1062
1085
  structlogger.warning(
1063
- "validation.router_strategy.redis_host_not_defined",
1086
+ "validation.routing_strategy.redis_host_not_defined",
1064
1087
  event_info=(
1065
- f"The router strategy '{router_strategy}' requires a Redis host"
1066
- f" to be defined. Without a Redis host, the system defaults to "
1067
- f"'in-memory' caching. Please add the '{REDIS_HOST_CONFIG_KEY}'"
1068
- f" to the router configuration for the model group "
1069
- f"'{model_group[MODEL_GROUP_ID_CONFIG_KEY]}'."
1088
+ f"The routing strategy '{routing_strategy}' requires a Redis "
1089
+ f"host to be defined. Without a Redis host, the system "
1090
+ f"defaults to 'in-memory' caching. Please add the "
1091
+ f"'{REDIS_HOST_CONFIG_KEY}' to the router configuration for "
1092
+ f"the model group '{model_group[MODEL_GROUP_ID_CONFIG_KEY]}'."
1070
1093
  ),
1071
1094
  )
1072
1095
 
@@ -1106,17 +1129,22 @@ def _validate_api_key_is_an_environment_variable(
1106
1129
  for model_group in model_groups:
1107
1130
  for model_config in model_group[MODELS_CONFIG_KEY]:
1108
1131
  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
- )
1132
+ if key == API_KEY:
1133
+ if isinstance(value, str):
1134
+ if not re.match(r"\${(\w+)}", value):
1135
+ print_error_and_exit(
1136
+ f"You defined the '{API_KEY}' in model group "
1137
+ f"'{model_group[MODEL_GROUP_ID_CONFIG_KEY]}' as a "
1138
+ f"string. The '{API_KEY}' must be set as an environment"
1139
+ f" variable. Please update your config."
1140
+ )
1141
+ else:
1142
+ print_error_and_exit(
1143
+ f"You should define the '{API_KEY}' in model group "
1144
+ f"'{model_group[MODEL_GROUP_ID_CONFIG_KEY]}' using the "
1145
+ f"environment variable syntax - ${{ENV_VARIABLE_NAME}}. "
1146
+ f"Please update your config."
1147
+ )
1120
1148
 
1121
1149
 
1122
1150
  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.*")
rasa/shared/constants.py CHANGED
@@ -184,19 +184,19 @@ N_REPHRASES_CONFIG_KEY = "n"
184
184
  USE_CHAT_COMPLETIONS_ENDPOINT_CONFIG_KEY = "use_chat_completions_endpoint"
185
185
 
186
186
  ROUTER_CONFIG_KEY = "router"
187
- ROUTER_STRATEGY_CONFIG_KEY = "router_strategy"
187
+ ROUTING_STRATEGY_CONFIG_KEY = "routing_strategy"
188
188
  REDIS_HOST_CONFIG_KEY = "redis_host"
189
- ROUTER_STRATEGIES_REQUIRING_REDIS_CACHE = [
189
+ ROUTING_STRATEGIES_REQUIRING_REDIS_CACHE = [
190
190
  "cost-based-routing",
191
191
  "usage-based-routing",
192
192
  ]
193
- ROUTER_STRATEGIES_NOT_REQUIRING_CACHE = [
193
+ ROUTING_STRATEGIES_NOT_REQUIRING_CACHE = [
194
194
  "latency-based-routing",
195
195
  "least-busy",
196
196
  "simple-shuffle",
197
197
  ]
198
- VALID_ROUTER_STRATEGIES = (
199
- ROUTER_STRATEGIES_REQUIRING_REDIS_CACHE + ROUTER_STRATEGIES_NOT_REQUIRING_CACHE
198
+ VALID_ROUTING_STRATEGIES = (
199
+ ROUTING_STRATEGIES_REQUIRING_REDIS_CACHE + ROUTING_STRATEGIES_NOT_REQUIRING_CACHE
200
200
  )
201
201
 
202
202
  MODELS_CONFIG_KEY = "models"
@@ -110,8 +110,8 @@ FLOW_SLOT_NAMES = [FLOW_HASHES_SLOT]
110
110
 
111
111
  # slots for audio timeout
112
112
  SLOT_SILENCE_TIMEOUT = "silence_timeout"
113
- SILENCE_TIMEOUT_DEFAULT_VALUE = 6.0
114
113
  SLOT_CONSECUTIVE_SILENCE_TIMEOUTS = "consecutive_silence_timeouts"
114
+ SILENCE_TIMEOUT_DEFAULT_VALUE = 6.0
115
115
  SILENCE_SLOTS = [SLOT_SILENCE_TIMEOUT, SLOT_CONSECUTIVE_SILENCE_TIMEOUTS]
116
116
  # slots for knowledge base
117
117
  SLOT_LISTED_ITEMS = "knowledge_base_listed_objects"
@@ -3,7 +3,6 @@ from __future__ import annotations
3
3
  import collections
4
4
  import copy
5
5
  import json
6
- import math
7
6
  import os
8
7
  from dataclasses import dataclass
9
8
  from functools import cached_property
@@ -58,7 +57,6 @@ from rasa.shared.core.events import SlotSet, UserUttered
58
57
  from rasa.shared.core.slots import (
59
58
  AnySlot,
60
59
  CategoricalSlot,
61
- FloatSlot,
62
60
  ListSlot,
63
61
  Slot,
64
62
  TextSlot,
@@ -1084,7 +1082,6 @@ class Domain:
1084
1082
  self._add_knowledge_base_slots()
1085
1083
  self._add_categorical_slot_default_value()
1086
1084
  self._add_session_metadata_slot()
1087
- self._add_audio_slots()
1088
1085
 
1089
1086
  def _add_categorical_slot_default_value(self) -> None:
1090
1087
  """Add a default value to all categorical slots.
@@ -1139,29 +1136,6 @@ class Domain:
1139
1136
  )
1140
1137
  )
1141
1138
 
1142
- def _add_audio_slots(self) -> None:
1143
- """Add slots relevant for audio channels."""
1144
- self.slots.append(
1145
- FloatSlot(
1146
- rasa.shared.core.constants.SLOT_SILENCE_TIMEOUT,
1147
- mappings=[],
1148
- influence_conversation=False,
1149
- is_builtin=True,
1150
- initial_value=rasa.shared.core.constants.SILENCE_TIMEOUT_DEFAULT_VALUE,
1151
- max_value=math.inf,
1152
- )
1153
- )
1154
- self.slots.append(
1155
- FloatSlot(
1156
- rasa.shared.core.constants.SLOT_CONSECUTIVE_SILENCE_TIMEOUTS,
1157
- mappings=[],
1158
- influence_conversation=False,
1159
- is_builtin=True,
1160
- initial_value=0.0,
1161
- max_value=math.inf,
1162
- )
1163
- )
1164
-
1165
1139
  def _add_knowledge_base_slots(self) -> None:
1166
1140
  """Add slots for the knowledge base action to slots.
1167
1141
 
@@ -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]:
@@ -14,6 +14,7 @@ from rasa.shared.constants import (
14
14
  API_TYPE_CONFIG_KEY,
15
15
  MODEL_CONFIG_KEY,
16
16
  MODEL_LIST_KEY,
17
+ USE_CHAT_COMPLETIONS_ENDPOINT_CONFIG_KEY,
17
18
  )
18
19
  from rasa.shared.providers._configs.model_group_config import (
19
20
  ModelGroupConfig,
@@ -29,6 +30,7 @@ _LITELLM_UNSUPPORTED_KEYS = [
29
30
  PROVIDER_CONFIG_KEY,
30
31
  DEPLOYMENT_CONFIG_KEY,
31
32
  API_TYPE_CONFIG_KEY,
33
+ USE_CHAT_COMPLETIONS_ENDPOINT_CONFIG_KEY,
32
34
  ]
33
35
 
34
36
 
@@ -84,6 +86,7 @@ class LiteLLMRouterClientConfig:
84
86
 
85
87
  _model_group_config: ModelGroupConfig
86
88
  router: Dict[str, Any]
89
+ _use_chat_completions_endpoint: bool = True
87
90
  extra_parameters: dict = field(default_factory=dict)
88
91
 
89
92
  @property
@@ -98,6 +101,14 @@ class LiteLLMRouterClientConfig:
98
101
  def litellm_model_list(self) -> List[Dict[str, Any]]:
99
102
  return self._convert_models_to_litellm_model_list()
100
103
 
104
+ @property
105
+ def litellm_router_settings(self) -> Dict[str, Any]:
106
+ return self._convert_router_to_litellm_router_settings()
107
+
108
+ @property
109
+ def use_chat_completions_endpoint(self) -> bool:
110
+ return self._use_chat_completions_endpoint
111
+
101
112
  def __post_init__(self) -> None:
102
113
  if not self.router:
103
114
  message = "Router cannot be empty."
@@ -121,7 +132,6 @@ class LiteLLMRouterClientConfig:
121
132
  Returns:
122
133
  LiteLLMRouterClientConfig
123
134
  """
124
-
125
135
  model_group_config = ModelGroupConfig.from_dict(config)
126
136
 
127
137
  # Copy config to avoid mutating the original
@@ -130,13 +140,18 @@ class LiteLLMRouterClientConfig:
130
140
  config_copy.pop(MODEL_GROUP_ID_CONFIG_KEY, None)
131
141
  config_copy.pop(MODELS_CONFIG_KEY, None)
132
142
  # Get the router settings
133
- router_settings = config_copy.pop(ROUTER_CONFIG_KEY, None)
143
+ router_settings = config_copy.pop(ROUTER_CONFIG_KEY, {})
144
+ # Get the use_chat_completions_endpoint setting
145
+ use_chat_completions_endpoint = router_settings.get(
146
+ USE_CHAT_COMPLETIONS_ENDPOINT_CONFIG_KEY, True
147
+ )
134
148
  # The rest is considered as extra parameters
135
149
  extra_parameters = config_copy
136
150
 
137
151
  this = LiteLLMRouterClientConfig(
138
152
  _model_group_config=model_group_config,
139
153
  router=router_settings,
154
+ _use_chat_completions_endpoint=use_chat_completions_endpoint,
140
155
  extra_parameters=extra_parameters,
141
156
  )
142
157
  return this
@@ -150,14 +165,17 @@ class LiteLLMRouterClientConfig:
150
165
  return d
151
166
 
152
167
  def to_litellm_dict(self) -> dict:
153
- litellm_model_list = self._convert_models_to_litellm_model_list()
154
- d = {
168
+ return {
155
169
  **self.extra_parameters,
156
170
  MODEL_GROUP_ID_CONFIG_KEY: self.model_group_id,
157
- MODEL_LIST_KEY: litellm_model_list,
158
- ROUTER_CONFIG_KEY: self.router,
171
+ MODEL_LIST_KEY: self._convert_models_to_litellm_model_list(),
172
+ ROUTER_CONFIG_KEY: self._convert_router_to_litellm_router_settings(),
159
173
  }
160
- return d
174
+
175
+ def _convert_router_to_litellm_router_settings(self) -> Dict[str, Any]:
176
+ _router_settings_copy = copy.deepcopy(self.router)
177
+ _router_settings_copy.pop(USE_CHAT_COMPLETIONS_ENDPOINT_CONFIG_KEY, None)
178
+ return _router_settings_copy
161
179
 
162
180
  def _convert_models_to_litellm_model_list(self) -> List[Dict[str, Any]]:
163
181
  litellm_model_list = []
@@ -172,7 +190,7 @@ class LiteLLMRouterClientConfig:
172
190
  prefix = get_prefix_from_provider(provider)
173
191
 
174
192
  # Determine whether to use model or deployment key based on the provider.
175
- litellm_model_name_without_prefix = (
193
+ litellm_model_name = (
176
194
  litellm_model_config[DEPLOYMENT_CONFIG_KEY]
177
195
  if provider in DEPLOYMENT_CENTRIC_PROVIDERS
178
196
  else litellm_model_config[MODEL_CONFIG_KEY]
@@ -180,7 +198,9 @@ class LiteLLMRouterClientConfig:
180
198
 
181
199
  # Set 'model' to a provider prefixed model name e.g. openai/gpt-4
182
200
  litellm_model_config[MODEL_CONFIG_KEY] = (
183
- f"{prefix}/{litellm_model_name_without_prefix}"
201
+ litellm_model_name
202
+ if f"{prefix}/" in litellm_model_name
203
+ else f"{prefix}/{litellm_model_name}"
184
204
  )
185
205
 
186
206
  # Remove parameters that are None and not supported by LiteLLM.