rasa-pro 3.13.3__py3-none-any.whl → 3.13.5__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 (69) hide show
  1. rasa/__main__.py +8 -0
  2. rasa/cli/data.py +8 -3
  3. rasa/cli/train.py +8 -0
  4. rasa/cli/utils.py +31 -15
  5. rasa/core/channels/inspector/dist/assets/{arc-371401b1.js → arc-0b11fe30.js} +1 -1
  6. rasa/core/channels/inspector/dist/assets/{blockDiagram-38ab4fdb-3f126156.js → blockDiagram-38ab4fdb-9eef30a7.js} +1 -1
  7. rasa/core/channels/inspector/dist/assets/{c4Diagram-3d4e48cf-12f22eb7.js → c4Diagram-3d4e48cf-03e94f28.js} +1 -1
  8. rasa/core/channels/inspector/dist/assets/channel-51d02e9e.js +1 -0
  9. rasa/core/channels/inspector/dist/assets/{classDiagram-70f12bd4-03b1d386.js → classDiagram-70f12bd4-95c09eba.js} +1 -1
  10. rasa/core/channels/inspector/dist/assets/{classDiagram-v2-f2320105-84f69d63.js → classDiagram-v2-f2320105-38e8446c.js} +1 -1
  11. rasa/core/channels/inspector/dist/assets/clone-cc738fa6.js +1 -0
  12. rasa/core/channels/inspector/dist/assets/{createText-2e5e7dd3-ca47fd38.js → createText-2e5e7dd3-57dc3038.js} +1 -1
  13. rasa/core/channels/inspector/dist/assets/{edges-e0da2a9e-f837ca8a.js → edges-e0da2a9e-4bac0545.js} +1 -1
  14. rasa/core/channels/inspector/dist/assets/{erDiagram-9861fffd-8717ac54.js → erDiagram-9861fffd-81795c90.js} +1 -1
  15. rasa/core/channels/inspector/dist/assets/{flowDb-956e92f1-94f38b83.js → flowDb-956e92f1-89489ae6.js} +1 -1
  16. rasa/core/channels/inspector/dist/assets/{flowDiagram-66a62f08-b616f9fb.js → flowDiagram-66a62f08-cd152627.js} +1 -1
  17. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-0c716443.js +1 -0
  18. rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-4a651766-f5d24bb8.js → flowchart-elk-definition-4a651766-3da369bc.js} +1 -1
  19. rasa/core/channels/inspector/dist/assets/{ganttDiagram-c361ad54-b43ba8d9.js → ganttDiagram-c361ad54-85ec16f8.js} +1 -1
  20. rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-72cf32ee-c3aafaa5.js → gitGraphDiagram-72cf32ee-495bc140.js} +1 -1
  21. rasa/core/channels/inspector/dist/assets/{graph-0d0a2c10.js → graph-1ec4d266.js} +1 -1
  22. rasa/core/channels/inspector/dist/assets/{index-3862675e-58ea0305.js → index-3862675e-0a0e97c9.js} +1 -1
  23. rasa/core/channels/inspector/dist/assets/{index-cce6f8a1.js → index-c804b295.js} +148 -148
  24. rasa/core/channels/inspector/dist/assets/{infoDiagram-f8f76790-b8f60461.js → infoDiagram-f8f76790-4d54bcde.js} +1 -1
  25. rasa/core/channels/inspector/dist/assets/{journeyDiagram-49397b02-95be5545.js → journeyDiagram-49397b02-dc097114.js} +1 -1
  26. rasa/core/channels/inspector/dist/assets/{layout-da885b9b.js → layout-1a08981e.js} +1 -1
  27. rasa/core/channels/inspector/dist/assets/{line-f1c817d3.js → line-95f7f1d3.js} +1 -1
  28. rasa/core/channels/inspector/dist/assets/{linear-d42801e6.js → linear-97e69543.js} +1 -1
  29. rasa/core/channels/inspector/dist/assets/{mindmap-definition-fc14e90a-a38923a6.js → mindmap-definition-fc14e90a-8c71ff03.js} +1 -1
  30. rasa/core/channels/inspector/dist/assets/{pieDiagram-8a3498a8-ca6e71e9.js → pieDiagram-8a3498a8-f14c71c7.js} +1 -1
  31. rasa/core/channels/inspector/dist/assets/{quadrantDiagram-120e2f19-b290dae9.js → quadrantDiagram-120e2f19-f1d3c9ff.js} +1 -1
  32. rasa/core/channels/inspector/dist/assets/{requirementDiagram-deff3bca-03f02ceb.js → requirementDiagram-deff3bca-bfa2412f.js} +1 -1
  33. rasa/core/channels/inspector/dist/assets/{sankeyDiagram-04a897e0-c49eee40.js → sankeyDiagram-04a897e0-53f2c97b.js} +1 -1
  34. rasa/core/channels/inspector/dist/assets/{sequenceDiagram-704730f1-b2cd6a3d.js → sequenceDiagram-704730f1-319d7c0e.js} +1 -1
  35. rasa/core/channels/inspector/dist/assets/{stateDiagram-587899a1-e53a2028.js → stateDiagram-587899a1-76a09418.js} +1 -1
  36. rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-d93cdb3a-e1982a03.js → stateDiagram-v2-d93cdb3a-a67f15d4.js} +1 -1
  37. rasa/core/channels/inspector/dist/assets/{styles-6aaf32cf-d0226ca5.js → styles-6aaf32cf-0654e7c3.js} +1 -1
  38. rasa/core/channels/inspector/dist/assets/{styles-9a916d00-0e21dc00.js → styles-9a916d00-1394bb9d.js} +1 -1
  39. rasa/core/channels/inspector/dist/assets/{styles-c10674c1-9588494e.js → styles-c10674c1-e4c5bdae.js} +1 -1
  40. rasa/core/channels/inspector/dist/assets/{svgDrawCommon-08f97a94-be478d4f.js → svgDrawCommon-08f97a94-50957104.js} +1 -1
  41. rasa/core/channels/inspector/dist/assets/{timeline-definition-85554ec2-74631749.js → timeline-definition-85554ec2-b0885a6a.js} +1 -1
  42. rasa/core/channels/inspector/dist/assets/{xychartDiagram-e933f94c-a043552f.js → xychartDiagram-e933f94c-79e6541a.js} +1 -1
  43. rasa/core/channels/inspector/dist/index.html +1 -1
  44. rasa/core/channels/inspector/package.json +4 -3
  45. rasa/core/channels/inspector/yarn.lock +12 -12
  46. rasa/core/policies/enterprise_search_policy.py +15 -18
  47. rasa/dialogue_understanding/commands/correct_slots_command.py +38 -0
  48. rasa/dialogue_understanding/generator/command_parser.py +9 -13
  49. rasa/dialogue_understanding/processor/command_processor.py +127 -54
  50. rasa/dialogue_understanding/stack/utils.py +13 -3
  51. rasa/dialogue_understanding_test/validation.py +9 -10
  52. rasa/e2e_test/e2e_config.py +18 -11
  53. rasa/e2e_test/utils/validation.py +17 -19
  54. rasa/engine/validation.py +86 -91
  55. rasa/exceptions.py +26 -1
  56. rasa/shared/providers/_configs/default_litellm_client_config.py +3 -7
  57. rasa/shared/utils/cli.py +2 -0
  58. rasa/shared/utils/health_check/health_check.py +10 -14
  59. rasa/utils/tensorflow/transformer.py +3 -3
  60. rasa/validator.py +7 -5
  61. rasa/version.py +1 -1
  62. {rasa_pro-3.13.3.dist-info → rasa_pro-3.13.5.dist-info}/METADATA +1 -1
  63. {rasa_pro-3.13.3.dist-info → rasa_pro-3.13.5.dist-info}/RECORD +66 -66
  64. rasa/core/channels/inspector/dist/assets/channel-f1efda17.js +0 -1
  65. rasa/core/channels/inspector/dist/assets/clone-fdf164e2.js +0 -1
  66. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-7d7a1629.js +0 -1
  67. {rasa_pro-3.13.3.dist-info → rasa_pro-3.13.5.dist-info}/NOTICE +0 -0
  68. {rasa_pro-3.13.3.dist-info → rasa_pro-3.13.5.dist-info}/WHEEL +0 -0
  69. {rasa_pro-3.13.3.dist-info → rasa_pro-3.13.5.dist-info}/entry_points.txt +0 -0
@@ -7,10 +7,10 @@
7
7
  resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf"
8
8
  integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==
9
9
 
10
- "@adobe/css-tools@^4.3.1":
11
- version "4.3.1"
12
- resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.3.1.tgz#abfccb8ca78075a2b6187345c26243c1a0842f28"
13
- integrity sha512-/62yikz7NLScCGAAST5SHdnjaDJQBDq0M2muyRTpf2VQhw6StBg2ALiu73zSJQ4fMVLA+0uBhBHAle7Wg+2kSg==
10
+ "@adobe/css-tools@^4.3.1", "@adobe/css-tools@^4.3.2":
11
+ version "4.4.3"
12
+ resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.4.3.tgz#beebbefb0264fdeb32d3052acae0e0d94315a9a2"
13
+ integrity sha512-VQKMkwriZbaOgVCby1UDY/LDk5fIjhQicCvVPFqfe+69fWaPWydbWJ3wRt59/YzIwda1I81loas3oCoHxnqvdA==
14
14
 
15
15
  "@ampproject/remapping@^2.2.0":
16
16
  version "2.2.1"
@@ -2300,10 +2300,10 @@ available-typed-arrays@^1.0.5:
2300
2300
  resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7"
2301
2301
  integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==
2302
2302
 
2303
- axios@1.7.4:
2304
- version "1.7.4"
2305
- resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.4.tgz#4c8ded1b43683c8dd362973c393f3ede24052aa2"
2306
- integrity sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==
2303
+ axios@1.8.2:
2304
+ version "1.8.2"
2305
+ resolved "https://registry.yarnpkg.com/axios/-/axios-1.8.2.tgz#fabe06e241dfe83071d4edfbcaa7b1c3a40f7979"
2306
+ integrity sha512-ls4GYBm5aig9vWx8AWDSGLpnpDQRtWAfrjU+EuytuODrFBkqesN2RkOQCBzrA1RQNHw1SmRMSDDDSwzNAYQ6Rg==
2307
2307
  dependencies:
2308
2308
  follow-redirects "^1.15.6"
2309
2309
  form-data "^4.0.0"
@@ -6064,10 +6064,10 @@ v8-to-istanbul@^9.0.1:
6064
6064
  "@types/istanbul-lib-coverage" "^2.0.1"
6065
6065
  convert-source-map "^2.0.0"
6066
6066
 
6067
- vite@4.5.2:
6068
- version "4.5.2"
6069
- resolved "https://registry.yarnpkg.com/vite/-/vite-4.5.2.tgz#d6ea8610e099851dad8c7371599969e0f8b97e82"
6070
- integrity sha512-tBCZBNSBbHQkaGyhGCDUGqeo2ph8Fstyp6FMSvTtsXeZSPpSMGlviAOav2hxVTqFcx8Hj/twtWKsMJXNY0xI8w==
6067
+ vite@4.5.12:
6068
+ version "4.5.12"
6069
+ resolved "https://registry.yarnpkg.com/vite/-/vite-4.5.12.tgz#48f48dbcf789722765e91bc32a99cb66c628eadc"
6070
+ integrity sha512-qrMwavANtSz91nDy3zEiUHMtL09x0mniQsSMvDkNxuCBM1W5vriJ22hEmwTth6DhLSWsZnHBT0yHFAQXt6efGA==
6071
6071
  dependencies:
6072
6072
  esbuild "^0.18.10"
6073
6073
  postcss "^8.4.27"
@@ -50,6 +50,7 @@ from rasa.engine.graph import ExecutionContext
50
50
  from rasa.engine.recipes.default_recipe import DefaultV1Recipe
51
51
  from rasa.engine.storage.resource import Resource
52
52
  from rasa.engine.storage.storage import ModelStorage
53
+ from rasa.exceptions import EnterpriseSearchPolicyError
53
54
  from rasa.graph_components.providers.forms_provider import Forms
54
55
  from rasa.graph_components.providers.responses_provider import Responses
55
56
  from rasa.shared.constants import (
@@ -82,7 +83,6 @@ from rasa.shared.providers.embedding._langchain_embedding_client_adapter import
82
83
  _LangchainEmbeddingClientAdapter,
83
84
  )
84
85
  from rasa.shared.providers.llm.llm_response import LLMResponse, measure_llm_latency
85
- from rasa.shared.utils.cli import print_error_and_exit
86
86
  from rasa.shared.utils.constants import (
87
87
  LOG_COMPONENT_SOURCE_METHOD_FINGERPRINT_ADDON,
88
88
  LOG_COMPONENT_SOURCE_METHOD_INIT,
@@ -343,14 +343,11 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
343
343
  try:
344
344
  embeddings = self._create_plain_embedder(self.embeddings_config)
345
345
  except (ValidationError, Exception) as e:
346
- structlogger.error(
347
- "enterprise_search_policy.train.embedder_instantiation_failed",
348
- message="Unable to instantiate the embedding client.",
349
- error=e,
350
- )
351
- print_error_and_exit(
352
- "Unable to create embedder. Please make sure you specified the "
353
- f"required environment variables. Error: {e}"
346
+ raise EnterpriseSearchPolicyError(
347
+ code="core.policies.enterprise_search_policy.train"
348
+ ".embedder_instantiation_failed",
349
+ event_info=f"Unable to create embedder. Please make sure you "
350
+ f"specified the required environment variables. Error: {e}",
354
351
  )
355
352
 
356
353
  if self.vector_store_type == DEFAULT_VECTOR_STORE_TYPE:
@@ -788,23 +785,23 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
788
785
  "Please specify a valid path to the documents source directory in the "
789
786
  "vector_store configuration."
790
787
  )
791
- structlogger.error(
792
- "enterprise_search_policy.train.faiss.invalid_source_directory",
793
- message=error_message,
788
+ error_code = (
789
+ "core.policies.enterprise_search_policy.train"
790
+ ".faiss.invalid_source_directory"
794
791
  )
795
- print_error_and_exit(error_message)
792
+ raise EnterpriseSearchPolicyError(code=error_code, event_info=error_message)
796
793
 
797
- docs = glob.glob(os.path.join(docs_folder, "*.txt"), recursive=True)
794
+ docs = glob.glob(os.path.join(docs_folder, "**", "*.txt"), recursive=True)
798
795
  if not docs or len(docs) < 1:
799
796
  error_message = (
800
797
  f"Document source directory is empty: '{docs_folder}'. "
801
798
  "Please add documents to this directory or specify a different one."
802
799
  )
803
- structlogger.error(
804
- "enterprise_search_policy.train.faiss.source_directory_empty",
805
- message=error_message,
800
+ error_code = (
801
+ "core.policies.enterprise_search_policy.train"
802
+ ".faiss.source_directory_empty"
806
803
  )
807
- print_error_and_exit(error_message)
804
+ raise EnterpriseSearchPolicyError(code=error_code, event_info=error_message)
808
805
 
809
806
  @classmethod
810
807
  def load(
@@ -269,6 +269,20 @@ class CorrectSlotsCommand(Command):
269
269
  return []
270
270
 
271
271
  structlogger.debug("correct_slots_command", command=self)
272
+
273
+ # check if the correct slot is referring to a slot of a flow on the stack
274
+ # the slot also needs to be part of a collect step in any of those flows
275
+ # if this is not the case, we don't want to correct the slot
276
+ for slot in self.corrected_slots:
277
+ if not self.should_correct_slot(slot, tracker, all_flows):
278
+ structlogger.warning(
279
+ "correct_slots_command.skip_correct_slot",
280
+ correct_slot=slot,
281
+ reason="The slot is not part of a collect step in any of the flows "
282
+ "on the stack. Skipping correction.",
283
+ )
284
+ return []
285
+
272
286
  proposed_slots = self.corrected_slots_dict(tracker)
273
287
 
274
288
  correction_frame = self.create_correction_frame(
@@ -293,3 +307,27 @@ class CorrectSlotsCommand(Command):
293
307
  return False
294
308
 
295
309
  return True
310
+
311
+ def should_correct_slot(
312
+ self, slot: CorrectedSlot, tracker: DialogueStateTracker, all_flows: FlowsList
313
+ ) -> bool:
314
+ """Checks if the slot should be corrected.
315
+
316
+ Args:
317
+ slot: The slot to check.
318
+ tracker: The tracker.
319
+ all_flows: All flows in the assistant.
320
+ """
321
+ # get all flows on the stack
322
+ flows_on_stack = utils.user_flows_on_the_stack(tracker.stack)
323
+
324
+ # check if the slot is part of a collect step in any of the flows on the stack
325
+ for flow_id in flows_on_stack:
326
+ flow = all_flows.flow_by_id(flow_id)
327
+ if flow is None:
328
+ continue
329
+ for collect_step in flow.get_collect_steps():
330
+ if collect_step.collect == slot.name:
331
+ return True
332
+
333
+ return False
@@ -1,5 +1,4 @@
1
1
  import re
2
- import sys
3
2
  from functools import lru_cache
4
3
  from typing import Any, Callable, Dict, List, Optional, Type, Union
5
4
 
@@ -19,6 +18,7 @@ from rasa.dialogue_understanding.commands import (
19
18
  )
20
19
  from rasa.dialogue_understanding.commands.prompt_command import PromptCommand
21
20
  from rasa.dialogue_understanding.commands.utils import start_flow_by_name
21
+ from rasa.exceptions import ValidationError
22
22
  from rasa.shared.core.flows import FlowsList
23
23
 
24
24
  structlogger = structlog.get_logger()
@@ -87,16 +87,13 @@ def validate_custom_commands(command_classes: List[Type[PromptCommand]]) -> None
87
87
  ]
88
88
 
89
89
  if clz_not_inheriting_from_command_clz:
90
- structlogger.error(
91
- "command_parser.validate_custom_commands.invalid_command",
90
+ raise ValidationError(
91
+ code="command_parser.validate_custom_commands.invalid_command",
92
+ event_info="The additional command classes must be a subclass of the "
93
+ "'Command' class. Please refer to the class in "
94
+ "`rasa.dialogue_understanding.commands.command.Command`",
92
95
  invalid_commands=clz_not_inheriting_from_command_clz,
93
- event_info=(
94
- "The additional command classes must be a subclass of the 'Command' "
95
- "class. Please refer to the class in "
96
- "`rasa.dialogue_understanding.commands.command.Command`"
97
- ),
98
96
  )
99
- sys.exit(1)
100
97
 
101
98
  clz_not_adhering_to_prompt_command_protocol = [
102
99
  command_clz.__name__
@@ -105,16 +102,15 @@ def validate_custom_commands(command_classes: List[Type[PromptCommand]]) -> None
105
102
  ]
106
103
 
107
104
  if clz_not_adhering_to_prompt_command_protocol:
108
- structlogger.error(
109
- "command_parser.validate_custom_commands.invalid_command",
110
- invalid_commands=clz_not_adhering_to_prompt_command_protocol,
105
+ raise ValidationError(
106
+ code="command_parser.validate_custom_commands.invalid_command",
111
107
  event_info=(
112
108
  "The additional command classes must adhere to the 'PromptCommand' "
113
109
  "protocol. Please refer to the protocol in "
114
110
  "`rasa.dialogue_understanding.commands.prompt_command.PromptCommand`"
115
111
  ),
112
+ invalid_commands=clz_not_adhering_to_prompt_command_protocol,
116
113
  )
117
- sys.exit(1)
118
114
 
119
115
 
120
116
  def parse_commands(
@@ -37,7 +37,6 @@ from rasa.dialogue_understanding.stack.frames import (
37
37
  BaseFlowStackFrame,
38
38
  )
39
39
  from rasa.dialogue_understanding.stack.utils import (
40
- filled_slots_for_active_flow,
41
40
  top_flow_frame,
42
41
  top_user_flow_frame,
43
42
  )
@@ -125,7 +124,8 @@ def validate_state_of_commands(commands: List[Command]) -> None:
125
124
  # check that there is only at max one cancel flow command
126
125
  if sum(isinstance(c, CancelFlowCommand) for c in commands) > 1:
127
126
  structlogger.error(
128
- "command_processor.validate_state_of_commands.multiple_cancel_flow_commands",
127
+ "command_processor.validate_state_of_commands."
128
+ "multiple_cancel_flow_commands",
129
129
  commands=[command.__class__.__name__ for command in commands],
130
130
  )
131
131
  raise ValueError("There can only be one cancel flow command.")
@@ -136,7 +136,8 @@ def validate_state_of_commands(commands: List[Command]) -> None:
136
136
  ]
137
137
  if free_form_answer_commands != commands[: len(free_form_answer_commands)]:
138
138
  structlogger.error(
139
- "command_processor.validate_state_of_commands.free_form_answer_commands_not_at_beginning",
139
+ "command_processor.validate_state_of_commands."
140
+ "free_form_answer_commands_not_at_beginning",
140
141
  commands=[command.__class__.__name__ for command in commands],
141
142
  )
142
143
  raise ValueError(
@@ -146,7 +147,8 @@ def validate_state_of_commands(commands: List[Command]) -> None:
146
147
  # check that there is at max only one correctslots command
147
148
  if sum(isinstance(c, CorrectSlotsCommand) for c in commands) > 1:
148
149
  structlogger.error(
149
- "command_processor.validate_state_of_commands.multiple_correct_slots_commands",
150
+ "command_processor.validate_state_of_commands."
151
+ "multiple_correct_slots_commands",
150
152
  commands=[command.__class__.__name__ for command in commands],
151
153
  )
152
154
  raise ValueError("There can only be one correct slots command.")
@@ -230,11 +232,9 @@ def execute_commands(
230
232
 
231
233
  events: List[Event] = flow_hash_events
232
234
 
233
- # commands need to be reversed to make sure they end up in the right order
234
- # on the stack. e.g. if there multiple start flow commands, the first one
235
- # should be on top of the stack. this is achieved by reversing the list
236
- # and then pushing the commands onto the stack in the reversed order.
237
- reversed_commands = list(reversed(commands))
235
+ # reorder commands: in case there is no active flow, we want to make sure to
236
+ # run the start flow commands first.
237
+ final_commands = reorder_commands(commands, tracker)
238
238
 
239
239
  # we need to keep track of the ValidateSlotPatternFlowStackFrame that
240
240
  # should be pushed onto the stack before executing the StartFlowCommands.
@@ -245,7 +245,7 @@ def execute_commands(
245
245
 
246
246
  validate_state_of_commands(commands)
247
247
 
248
- for command in reversed_commands:
248
+ for command in final_commands:
249
249
  new_events = command.run_command_on_tracker(
250
250
  tracker, all_flows, original_tracker
251
251
  )
@@ -398,11 +398,11 @@ def clean_up_commands(
398
398
  """
399
399
  domain = domain if domain else Domain.empty()
400
400
 
401
- slots_so_far, _ = filled_slots_for_active_flow(tracker, all_flows)
402
-
403
- # update the slots so far with the slots that were set in the tracker
404
- slots_so_far.update(
405
- {event.key for event in tracker.events if isinstance(event, SlotSet)}
401
+ # we consider all slots that were set in the tracker for potential corrections
402
+ # in the correct_slot_command we will check if a slot should actually be
403
+ # corrected
404
+ slots_so_far = set(
405
+ [event.key for event in tracker.events if isinstance(event, SlotSet)]
406
406
  )
407
407
 
408
408
  clean_commands: List[Command] = []
@@ -444,7 +444,8 @@ def clean_up_commands(
444
444
  # drop a start flow command if the starting flow is equal
445
445
  # to the currently active flow
446
446
  structlogger.debug(
447
- "command_processor.clean_up_commands.skip_command_flow_already_active",
447
+ "command_processor.clean_up_commands."
448
+ "skip_command_flow_already_active",
448
449
  command=command,
449
450
  )
450
451
  continue
@@ -475,7 +476,8 @@ def clean_up_commands(
475
476
  clean_commands = clean_up_clarify_command(clean_commands, commands, command)
476
477
  if command not in clean_commands:
477
478
  structlogger.debug(
478
- "command_processor.clean_up_commands.drop_clarify_given_other_commands",
479
+ "command_processor.clean_up_commands."
480
+ "drop_clarify_given_other_commands",
479
481
  command=command,
480
482
  )
481
483
  else:
@@ -577,10 +579,10 @@ def clean_up_slot_command(
577
579
  The cleaned up commands.
578
580
  """
579
581
  stack = tracker.stack
580
-
581
582
  resulting_commands = commands_so_far[:]
582
-
583
583
  slot = tracker.slots.get(command.name)
584
+
585
+ # if the slot is not in the domain, we cannot set it
584
586
  if slot is None:
585
587
  structlogger.debug(
586
588
  "command_processor.clean_up_slot_command.skip_command_slot_not_in_domain",
@@ -593,6 +595,7 @@ def clean_up_slot_command(
593
595
  )
594
596
  return resulting_commands
595
597
 
598
+ # check if the slot should be set by the command
596
599
  if not should_slot_be_set(slot, command, resulting_commands):
597
600
  structlogger.debug(
598
601
  "command_processor.clean_up_slot_command.skip_command.extractor_"
@@ -618,6 +621,7 @@ def clean_up_slot_command(
618
621
 
619
622
  return resulting_commands
620
623
 
624
+ # check if the slot can be corrected by the LLM
621
625
  if (
622
626
  slot.filled_by == SetSlotExtractor.NLU.value
623
627
  and command.extractor == SetSlotExtractor.LLM.value
@@ -646,49 +650,71 @@ def clean_up_slot_command(
646
650
  resulting_commands.append(command)
647
651
  return resulting_commands
648
652
 
649
- if (slot := tracker.slots.get(command.name)) is not None and str(
650
- slot.value
651
- ) == str(command.value):
652
- # the slot is already set, we don't need to set it again
653
- structlogger.debug(
654
- "command_processor.clean_up_slot_command.skip_command_slot_already_set",
655
- command=command,
656
- )
653
+ if should_slot_be_corrected(command, tracker, stack, all_flows):
654
+ # if the slot was already set before, we need to convert it into
655
+ # a correction
656
+ return convert_set_slot_to_correction(command, resulting_commands)
657
+ else:
657
658
  return resulting_commands
658
659
 
659
- top = top_flow_frame(stack)
660
- if isinstance(top, CorrectionPatternFlowStackFrame):
661
- already_corrected_slots = top.corrected_slots
662
- else:
663
- already_corrected_slots = {}
660
+ resulting_commands.append(command)
661
+ return resulting_commands
664
662
 
665
- if command.name in already_corrected_slots and str(
666
- already_corrected_slots[command.name]
667
- ) == str(command.value):
668
- structlogger.debug(
669
- "command_processor.clean_up_slot_command"
670
- ".skip_command_slot_already_corrected",
671
- command=command,
672
- )
673
- return resulting_commands
674
663
 
664
+ def should_slot_be_corrected(
665
+ command: SetSlotCommand,
666
+ tracker: DialogueStateTracker,
667
+ stack: DialogueStack,
668
+ all_flows: FlowsList,
669
+ ) -> bool:
670
+ """Check if a slot should be corrected."""
671
+ if (slot := tracker.slots.get(command.name)) is not None and str(slot.value) == str(
672
+ command.value
673
+ ):
674
+ # the slot is already set to the same value, we don't need to set it again
675
675
  structlogger.debug(
676
- "command_processor.clean_up_slot_command.convert_command_to_correction",
676
+ "command_processor.clean_up_slot_command.skip_command_slot_already_set",
677
677
  command=command,
678
678
  )
679
+ return False
679
680
 
680
- # Group all corrections into one command
681
- corrected_slot = CorrectedSlot(command.name, command.value, command.extractor)
682
- for c in resulting_commands:
683
- if isinstance(c, CorrectSlotsCommand):
684
- c.corrected_slots.append(corrected_slot)
685
- break
686
- else:
687
- resulting_commands.append(
688
- CorrectSlotsCommand(corrected_slots=[corrected_slot])
689
- )
681
+ top = top_flow_frame(stack)
682
+ if isinstance(top, CorrectionPatternFlowStackFrame):
683
+ already_corrected_slots = top.corrected_slots
690
684
  else:
691
- resulting_commands.append(command)
685
+ already_corrected_slots = {}
686
+
687
+ if command.name in already_corrected_slots and str(
688
+ already_corrected_slots[command.name]
689
+ ) == str(command.value):
690
+ structlogger.debug(
691
+ "command_processor.clean_up_slot_command"
692
+ ".skip_command_slot_already_corrected",
693
+ command=command,
694
+ )
695
+ return False
696
+
697
+ return True
698
+
699
+
700
+ def convert_set_slot_to_correction(
701
+ command: SetSlotCommand,
702
+ resulting_commands: List[Command],
703
+ ) -> List[Command]:
704
+ """Convert a set slot command to a correction command."""
705
+ structlogger.debug(
706
+ "command_processor.convert_set_slot_to_correction",
707
+ command=command,
708
+ )
709
+
710
+ # Group all corrections into one command
711
+ corrected_slot = CorrectedSlot(command.name, command.value, command.extractor)
712
+ for c in resulting_commands:
713
+ if isinstance(c, CorrectSlotsCommand):
714
+ c.corrected_slots.append(corrected_slot)
715
+ break
716
+ else:
717
+ resulting_commands.append(CorrectSlotsCommand(corrected_slots=[corrected_slot]))
692
718
 
693
719
  return resulting_commands
694
720
 
@@ -747,7 +773,8 @@ def clean_up_chitchat_command(
747
773
  0, CannotHandleCommand(RASA_PATTERN_CANNOT_HANDLE_CHITCHAT)
748
774
  )
749
775
  structlogger.warn(
750
- "command_processor.clean_up_chitchat_command.replace_chitchat_answer_with_cannot_handle",
776
+ "command_processor.clean_up_chitchat_command."
777
+ "replace_chitchat_answer_with_cannot_handle",
751
778
  command=resulting_commands[0], # no PII
752
779
  pattern_chitchat_uses_action_trigger_chitchat=has_action_trigger_chitchat,
753
780
  defined_intentless_policy_in_config=defines_intentless_policy,
@@ -850,3 +877,49 @@ def filter_cannot_handle_command(
850
877
  for command in clean_commands
851
878
  if not isinstance(command, CannotHandleCommand)
852
879
  ]
880
+
881
+
882
+ def reorder_commands(
883
+ commands: List[Command], tracker: DialogueStateTracker
884
+ ) -> List[Command]:
885
+ """Reorder commands.
886
+
887
+ In case there is no active flow, we want to make sure to run the start flow
888
+ commands first.
889
+ """
890
+ reordered_commands = commands
891
+
892
+ top_flow_frame = top_user_flow_frame(tracker.stack)
893
+
894
+ if top_flow_frame is None:
895
+ # no active flow, we want to make sure to run the start flow commands first
896
+ start_flow_commands: List[Command] = [
897
+ command for command in commands if isinstance(command, StartFlowCommand)
898
+ ]
899
+
900
+ # if there are no start flow commands, we can return the commands as they are
901
+ if not start_flow_commands:
902
+ reordered_commands = commands
903
+
904
+ # if there is just one start flow command, we want to run it first
905
+ # as the order of commands is reserved later,
906
+ # we need to add it to the end of the list
907
+ elif len(start_flow_commands) == 1:
908
+ reordered_commands = [
909
+ command for command in commands if command not in start_flow_commands
910
+ ] + start_flow_commands
911
+
912
+ # if there are multiple start flow commands,
913
+ # we just make sure to move the first start flow command to the end of the list
914
+ # (due to the reverse execution order of commands) and keep the other commands
915
+ # as they are.
916
+ else:
917
+ reordered_commands = [
918
+ command for command in commands if command != start_flow_commands[-1]
919
+ ] + [start_flow_commands[-1]]
920
+
921
+ # commands need to be reversed to make sure they end up in the right order
922
+ # on the stack. e.g. if there multiple start flow commands, the first one
923
+ # should be on top of the stack. this is achieved by reversing the list
924
+ # and then pushing the commands onto the stack in the reversed order.
925
+ return list(reversed(reordered_commands))
@@ -209,14 +209,24 @@ def get_collect_steps_excluding_ask_before_filling_for_active_flow(
209
209
  All collect steps that are part of the current active flow,
210
210
  excluding the collect steps that have to be asked before filling.
211
211
  """
212
- active_frame = top_user_flow_frame(
212
+ active_primary_frame = top_user_flow_frame(dialogue_stack)
213
+ any_active_frame = top_user_flow_frame(
213
214
  dialogue_stack, ignore_call_and_link_frames=False
214
215
  )
215
- if active_frame is None:
216
+
217
+ active_flows = []
218
+ if any_active_frame:
219
+ active_flows.append(any_active_frame.flow(all_flows))
220
+
221
+ if active_primary_frame and active_primary_frame != any_active_frame:
222
+ active_flows.append(active_primary_frame.flow(all_flows))
223
+
224
+ if not active_flows:
216
225
  return set()
217
- active_flow = active_frame.flow(all_flows)
226
+
218
227
  return set(
219
228
  step.collect
229
+ for active_flow in active_flows
220
230
  for step in active_flow.get_collect_steps()
221
231
  if not step.ask_before_filling
222
232
  )
@@ -1,5 +1,4 @@
1
1
  import argparse
2
- import sys
3
2
  from typing import List, Optional
4
3
 
5
4
  import structlog
@@ -9,6 +8,7 @@ from rasa.dialogue_understanding_test.constants import (
9
8
  PLACEHOLDER_GENERATED_ANSWER_TEMPLATE,
10
9
  )
11
10
  from rasa.dialogue_understanding_test.du_test_case import DialogueUnderstandingTestCase
11
+ from rasa.exceptions import ValidationError
12
12
  from rasa.shared.core.domain import Domain
13
13
 
14
14
  structlogger = structlog.get_logger()
@@ -28,14 +28,14 @@ def validate_cli_arguments(args: argparse.Namespace) -> None:
28
28
  args.remote_storage
29
29
  and args.remote_storage.lower() not in supported_remote_storages
30
30
  ):
31
- structlogger.error(
32
- "dialogue_understanding_test.validate_cli_arguments.invalid_remote_storage",
31
+ raise ValidationError(
32
+ code="dialogue_understanding_test.validate_cli_arguments"
33
+ ".invalid_remote_storage",
33
34
  event_info=(
34
35
  f"Invalid remote storage option - '{args.remote_storage}'. Supported "
35
36
  f"options are: {supported_remote_storages}"
36
37
  ),
37
38
  )
38
- sys.exit(1)
39
39
 
40
40
 
41
41
  def validate_test_cases(
@@ -48,11 +48,10 @@ def validate_test_cases(
48
48
  domain: Domain of the assistant.
49
49
  """
50
50
  if not domain:
51
- structlogger.error(
52
- "dialogue_understanding_test.validate_test_cases.no_domain",
51
+ raise ValidationError(
52
+ code="dialogue_understanding_test.validate_test_cases.no_domain",
53
53
  event_info="No domain found. Retrain the model with a valid domain.",
54
54
  )
55
- sys.exit(1)
56
55
 
57
56
  # Retrieve all valid templates from the domain
58
57
  valid_templates = domain.utterances_for_response
@@ -64,8 +63,9 @@ def validate_test_cases(
64
63
  for step in test_case.steps:
65
64
  if step.actor == ACTOR_BOT and step.template:
66
65
  if step.template not in valid_templates:
67
- structlogger.error(
68
- "dialogue_understanding_test.validate_test_cases.invalid_template",
66
+ raise ValidationError(
67
+ code="dialogue_understanding_test.validate_test_cases"
68
+ ".invalid_template",
69
69
  event_info=(
70
70
  f"Invalid bot utterance template '{step.template}' in test "
71
71
  f"case '{test_case.name}' at line {step.line}. Please "
@@ -74,4 +74,3 @@ def validate_test_cases(
74
74
  test_case=test_case.name,
75
75
  template=step.template,
76
76
  )
77
- sys.exit(1)
@@ -67,26 +67,33 @@ class LLMJudgeConfig(BaseModel):
67
67
  @classmethod
68
68
  def from_dict(cls, config_data: Dict[str, Any]) -> LLMJudgeConfig:
69
69
  """Loads the configuration from a dictionary."""
70
- embeddings = config_data.pop(EMBEDDINGS_CONFIG_KEY, None)
70
+ embeddings = config_data.pop(EMBEDDINGS_CONFIG_KEY, {})
71
71
  llm_config = config_data.pop("llm", {})
72
72
 
73
73
  llm_config = resolve_model_client_config(llm_config)
74
- llm_config, extra_parameters = cls.extract_attributes(llm_config)
74
+ llm_config, llm_extra_parameters = cls.extract_attributes(llm_config)
75
75
  llm_config = combine_custom_and_default_config(
76
76
  llm_config, cls.get_default_llm_config()
77
77
  )
78
78
  embeddings_config = resolve_model_client_config(embeddings)
79
+ embeddings_config, embeddings_extra_parameters = cls.extract_attributes(
80
+ embeddings_config
81
+ )
79
82
 
80
83
  return LLMJudgeConfig(
81
- llm_config=BaseModelConfig(extra_parameters=extra_parameters, **llm_config),
82
- embeddings=BaseModelConfig(**embeddings_config)
84
+ llm_config=BaseModelConfig(
85
+ extra_parameters=llm_extra_parameters, **llm_config
86
+ ),
87
+ embeddings=BaseModelConfig(
88
+ extra_parameters=embeddings_extra_parameters, **embeddings_config
89
+ )
83
90
  if embeddings_config
84
91
  else None,
85
92
  )
86
93
 
87
94
  @classmethod
88
95
  def extract_attributes(
89
- cls, llm_config: Dict[str, Any]
96
+ cls, config: Dict[str, Any]
90
97
  ) -> Tuple[Dict[str, Any], Dict[str, Any]]:
91
98
  """Extract the expected fields from the configuration."""
92
99
  required_config = {}
@@ -96,22 +103,22 @@ class LLMJudgeConfig(BaseModel):
96
103
  MODEL_CONFIG_KEY,
97
104
  ]
98
105
 
99
- if PROVIDER_CONFIG_KEY in llm_config:
106
+ if PROVIDER_CONFIG_KEY in config:
100
107
  required_config = {
101
- expected_field: llm_config.pop(expected_field, None)
108
+ expected_field: config.pop(expected_field, None)
102
109
  for expected_field in expected_fields
103
110
  }
104
111
 
105
- elif MODELS_CONFIG_KEY in llm_config:
106
- llm_config = llm_config.pop(MODELS_CONFIG_KEY)[0]
112
+ elif MODELS_CONFIG_KEY in config:
113
+ config = config.pop(MODELS_CONFIG_KEY)[0]
107
114
 
108
115
  required_config = {
109
- expected_field: llm_config.pop(expected_field, None)
116
+ expected_field: config.pop(expected_field, None)
110
117
  for expected_field in expected_fields
111
118
  }
112
119
 
113
120
  clean_config = clean_up_config(required_config)
114
- return clean_config, llm_config
121
+ return clean_config, config
115
122
 
116
123
  @property
117
124
  def llm_config_as_dict(self) -> Dict[str, Any]: