rasa-pro 3.13.0.dev1__py3-none-any.whl → 3.13.0.dev3__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 (54) hide show
  1. rasa/cli/run.py +10 -6
  2. rasa/cli/utils.py +7 -0
  3. rasa/core/actions/action.py +0 -6
  4. rasa/core/channels/channel.py +30 -0
  5. rasa/core/channels/voice_ready/audiocodes.py +52 -17
  6. rasa/core/channels/voice_ready/jambonz.py +25 -5
  7. rasa/core/channels/voice_ready/jambonz_protocol.py +4 -0
  8. rasa/core/channels/voice_stream/audiocodes.py +53 -9
  9. rasa/core/channels/voice_stream/genesys.py +146 -16
  10. rasa/core/information_retrieval/faiss.py +6 -62
  11. rasa/core/nlg/contextual_response_rephraser.py +3 -0
  12. rasa/core/policies/enterprise_search_policy.py +10 -1
  13. rasa/core/policies/flows/flow_executor.py +3 -38
  14. rasa/core/policies/intentless_policy.py +3 -0
  15. rasa/core/processor.py +27 -6
  16. rasa/core/utils.py +53 -0
  17. rasa/dialogue_understanding/coexistence/llm_based_router.py +8 -0
  18. rasa/dialogue_understanding/commands/cancel_flow_command.py +4 -59
  19. rasa/dialogue_understanding/commands/knowledge_answer_command.py +2 -2
  20. rasa/dialogue_understanding/commands/start_flow_command.py +0 -41
  21. rasa/dialogue_understanding/generator/command_generator.py +67 -0
  22. rasa/dialogue_understanding/generator/flow_retrieval.py +1 -4
  23. rasa/dialogue_understanding/generator/llm_based_command_generator.py +2 -12
  24. rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +13 -0
  25. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_claude_3_5_sonnet_20240620_template.jinja2 +1 -1
  26. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_gpt_4o_2024_11_20_template.jinja2 +2 -5
  27. rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +22 -10
  28. rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +27 -12
  29. rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +0 -61
  30. rasa/dialogue_understanding/processor/command_processor.py +7 -65
  31. rasa/dialogue_understanding/stack/utils.py +0 -38
  32. rasa/e2e_test/utils/validation.py +3 -3
  33. rasa/llm_fine_tuning/paraphrasing/conversation_rephraser.py +3 -0
  34. rasa/shared/core/constants.py +0 -8
  35. rasa/shared/core/domain.py +12 -3
  36. rasa/shared/core/flows/flow.py +0 -17
  37. rasa/shared/core/flows/flows_yaml_schema.json +3 -38
  38. rasa/shared/core/flows/steps/collect.py +5 -18
  39. rasa/shared/core/flows/utils.py +1 -16
  40. rasa/shared/core/slot_mappings.py +11 -5
  41. rasa/shared/nlu/constants.py +0 -1
  42. rasa/shared/utils/common.py +11 -1
  43. rasa/shared/utils/constants.py +3 -0
  44. rasa/shared/utils/llm.py +69 -23
  45. rasa/validator.py +1 -123
  46. rasa/version.py +1 -1
  47. {rasa_pro-3.13.0.dev1.dist-info → rasa_pro-3.13.0.dev3.dist-info}/METADATA +2 -2
  48. {rasa_pro-3.13.0.dev1.dist-info → rasa_pro-3.13.0.dev3.dist-info}/RECORD +51 -54
  49. rasa/core/actions/action_handle_digressions.py +0 -164
  50. rasa/dialogue_understanding/commands/handle_digressions_command.py +0 -144
  51. rasa/dialogue_understanding/patterns/handle_digressions.py +0 -81
  52. {rasa_pro-3.13.0.dev1.dist-info → rasa_pro-3.13.0.dev3.dist-info}/NOTICE +0 -0
  53. {rasa_pro-3.13.0.dev1.dist-info → rasa_pro-3.13.0.dev3.dist-info}/WHEEL +0 -0
  54. {rasa_pro-3.13.0.dev1.dist-info → rasa_pro-3.13.0.dev3.dist-info}/entry_points.txt +0 -0
@@ -31,12 +31,10 @@ class FAISS_Store(InformationRetrieval):
31
31
  index_path: str,
32
32
  docs_folder: Optional[str],
33
33
  create_index: Optional[bool] = False,
34
- use_llm: bool = False,
35
34
  ):
36
35
  """Initializes the FAISS Store."""
37
36
  self.chunk_size = 1000
38
37
  self.chunk_overlap = 20
39
- self.use_llm = use_llm
40
38
 
41
39
  path = Path(index_path) / "documents_faiss"
42
40
  if create_index:
@@ -73,57 +71,6 @@ class FAISS_Store(InformationRetrieval):
73
71
 
74
72
  return loader.load()
75
73
 
76
- def _format_faqs(self, docs: List["Document"]) -> List["Document"]:
77
- """Splits each loaded file into individual FAQs.
78
-
79
- Args:
80
- docs: Documents representing whole files containing FAQs.
81
-
82
- Returns:
83
- List of Document objects, each containing a separate FAQ.
84
-
85
- Examples:
86
- An example of a file containing FAQs:
87
-
88
- Q: Who is Finley?
89
- A: Finley is your smart assistant for the FinX App. You can add him to your
90
- favorite messenger and tell him what you need help with.
91
-
92
- Q: How does Finley work?
93
- A: Finley is powered by the latest chatbot technology leveraging a unique
94
- interplay of large language models and secure logic.
95
-
96
- More details in documentation: https://rasa.com/docs/reference/config/policies/extractive-search/
97
- """
98
- structured_faqs = []
99
- from langchain.schema import Document
100
-
101
- for doc in docs:
102
- faq_chunks = doc.page_content.strip().split("\n\n")
103
-
104
- for chunk in faq_chunks:
105
- lines = chunk.strip().split("\n")
106
- if len(lines) < 2:
107
- continue # Skip if something unexpected
108
-
109
- question_line = lines[0].strip()
110
- answer_line = lines[1].strip()
111
-
112
- question = question_line.replace("Q: ", "").strip()
113
- answer = answer_line.replace("A: ", "").strip()
114
-
115
- doc_obj = Document(
116
- page_content=question,
117
- metadata={
118
- "title": question.lower().replace(" ", "_")[:-1],
119
- "type": "faq",
120
- "answer": answer,
121
- },
122
- )
123
-
124
- structured_faqs.append(doc_obj)
125
- return structured_faqs
126
-
127
74
  def _create_document_index(
128
75
  self, docs_folder: Optional[str], embedding: "Embeddings"
129
76
  ) -> FAISS:
@@ -140,15 +87,12 @@ class FAISS_Store(InformationRetrieval):
140
87
  raise ValueError("parameter `docs_folder` needs to be specified")
141
88
 
142
89
  docs = self.load_documents(docs_folder)
143
- if self.use_llm:
144
- splitter = RecursiveCharacterTextSplitter(
145
- chunk_size=self.chunk_size,
146
- chunk_overlap=self.chunk_overlap,
147
- length_function=len,
148
- )
149
- doc_chunks = splitter.split_documents(docs)
150
- else:
151
- doc_chunks = self._format_faqs(docs)
90
+ splitter = RecursiveCharacterTextSplitter(
91
+ chunk_size=self.chunk_size,
92
+ chunk_overlap=self.chunk_overlap,
93
+ length_function=len,
94
+ )
95
+ doc_chunks = splitter.split_documents(docs)
152
96
 
153
97
  logger.info(
154
98
  "information_retrieval.faiss_store._create_document_index",
@@ -27,6 +27,7 @@ from rasa.shared.nlu.constants import (
27
27
  PROMPTS,
28
28
  )
29
29
  from rasa.shared.providers.llm.llm_response import LLMResponse, measure_llm_latency
30
+ from rasa.shared.utils.constants import LOG_COMPONENT_SOURCE_METHOD_INIT
30
31
  from rasa.shared.utils.health_check.llm_health_check_mixin import LLMHealthCheckMixin
31
32
  from rasa.shared.utils.llm import (
32
33
  DEFAULT_OPENAI_GENERATE_MODEL_NAME,
@@ -105,6 +106,8 @@ class ContextualResponseRephraser(
105
106
  self.prompt_template = get_prompt_template(
106
107
  self.nlg_endpoint.kwargs.get(PROMPT_CONFIG_KEY),
107
108
  DEFAULT_RESPONSE_VARIATION_PROMPT_TEMPLATE,
109
+ log_source_component=ContextualResponseRephraser.__name__,
110
+ log_source_method=LOG_COMPONENT_SOURCE_METHOD_INIT,
108
111
  )
109
112
  self.rephrase_all = self.nlg_endpoint.kwargs.get(
110
113
  "rephrase_all", DEFAULT_REPHRASE_ALL
@@ -78,6 +78,10 @@ from rasa.shared.providers.embedding._langchain_embedding_client_adapter import
78
78
  from rasa.shared.providers.llm.llm_client import LLMClient
79
79
  from rasa.shared.providers.llm.llm_response import LLMResponse, measure_llm_latency
80
80
  from rasa.shared.utils.cli import print_error_and_exit
81
+ from rasa.shared.utils.constants import (
82
+ LOG_COMPONENT_SOURCE_METHOD_FINGERPRINT_ADDON,
83
+ LOG_COMPONENT_SOURCE_METHOD_INIT,
84
+ )
81
85
  from rasa.shared.utils.health_check.embeddings_health_check_mixin import (
82
86
  EmbeddingsHealthCheckMixin,
83
87
  )
@@ -254,10 +258,14 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
254
258
  self.prompt_template = prompt_template or get_prompt_template(
255
259
  self.config.get(PROMPT_CONFIG_KEY),
256
260
  DEFAULT_ENTERPRISE_SEARCH_PROMPT_TEMPLATE,
261
+ log_source_component=EnterpriseSearchPolicy.__name__,
262
+ log_source_method=LOG_COMPONENT_SOURCE_METHOD_INIT,
257
263
  )
258
264
  self.citation_prompt_template = get_prompt_template(
259
265
  self.config.get(PROMPT_CONFIG_KEY),
260
266
  DEFAULT_ENTERPRISE_SEARCH_PROMPT_WITH_CITATION_TEMPLATE,
267
+ log_source_component=EnterpriseSearchPolicy.__name__,
268
+ log_source_method=LOG_COMPONENT_SOURCE_METHOD_INIT,
261
269
  )
262
270
  # If citation is enabled, use the citation prompt template
263
271
  if self.citation_enabled:
@@ -373,7 +381,6 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
373
381
  embeddings=embeddings,
374
382
  index_path=path,
375
383
  create_index=True,
376
- use_llm=self.use_llm,
377
384
  )
378
385
  else:
379
386
  logger.info("enterprise_search_policy.train.custom", store_type=store_type)
@@ -844,6 +851,8 @@ class EnterpriseSearchPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Po
844
851
  prompt_template = get_prompt_template(
845
852
  config.get(PROMPT_CONFIG_KEY),
846
853
  DEFAULT_ENTERPRISE_SEARCH_PROMPT_TEMPLATE,
854
+ log_source_component=EnterpriseSearchPolicy.__name__,
855
+ log_source_method=LOG_COMPONENT_SOURCE_METHOD_FINGERPRINT_ADDON,
847
856
  )
848
857
 
849
858
  llm_config = resolve_model_client_config(
@@ -23,7 +23,6 @@ from rasa.core.policies.flows.flow_step_result import (
23
23
  )
24
24
  from rasa.dialogue_understanding.commands import CancelFlowCommand
25
25
  from rasa.dialogue_understanding.patterns.cancel import CancelPatternFlowStackFrame
26
- from rasa.dialogue_understanding.patterns.clarify import ClarifyPatternFlowStackFrame
27
26
  from rasa.dialogue_understanding.patterns.collect_information import (
28
27
  CollectInformationPatternFlowStackFrame,
29
28
  )
@@ -51,7 +50,6 @@ from rasa.dialogue_understanding.stack.frames.flow_stack_frame import (
51
50
  )
52
51
  from rasa.dialogue_understanding.stack.utils import (
53
52
  top_user_flow_frame,
54
- user_flows_on_the_stack,
55
53
  )
56
54
  from rasa.shared.constants import RASA_PATTERN_HUMAN_HANDOFF
57
55
  from rasa.shared.core.constants import (
@@ -280,33 +278,6 @@ def trigger_pattern_continue_interrupted(
280
278
  return events
281
279
 
282
280
 
283
- def trigger_pattern_clarification(
284
- current_frame: DialogueStackFrame, stack: DialogueStack, flows: FlowsList
285
- ) -> None:
286
- """Trigger the pattern to clarify which topic to continue if needed."""
287
- if not isinstance(current_frame, UserFlowStackFrame):
288
- return None
289
-
290
- if current_frame.frame_type in [
291
- FlowStackFrameType.CALL,
292
- FlowStackFrameType.INTERRUPT,
293
- ]:
294
- # we want to return to the flow that called
295
- # the current flow or the flow that was interrupted
296
- # by the current flow
297
- return None
298
-
299
- pending_flows = [
300
- flows.flow_by_id(frame.flow_id)
301
- for frame in stack.frames
302
- if isinstance(frame, UserFlowStackFrame)
303
- and frame.flow_id != current_frame.flow_id
304
- ]
305
-
306
- flow_names = [flow.readable_name() for flow in pending_flows if flow is not None]
307
- stack.push(ClarifyPatternFlowStackFrame(names=flow_names))
308
-
309
-
310
281
  def trigger_pattern_completed(
311
282
  current_frame: DialogueStackFrame, stack: DialogueStack, flows: FlowsList
312
283
  ) -> None:
@@ -675,15 +646,9 @@ def _run_end_step(
675
646
  structlogger.debug("flow.step.run.flow_end")
676
647
  current_frame = stack.pop()
677
648
  trigger_pattern_completed(current_frame, stack, flows)
678
- resumed_events = []
679
- if len(user_flows_on_the_stack(stack)) > 1:
680
- # if there are more user flows on the stack,
681
- # we need to trigger the pattern clarify
682
- trigger_pattern_clarification(current_frame, stack, flows)
683
- else:
684
- resumed_events = trigger_pattern_continue_interrupted(
685
- current_frame, stack, flows, tracker
686
- )
649
+ resumed_events = trigger_pattern_continue_interrupted(
650
+ current_frame, stack, flows, tracker
651
+ )
687
652
  reset_events: List[Event] = reset_scoped_slots(current_frame, flow, tracker)
688
653
  return ContinueFlowWithNextStep(
689
654
  events=initial_events + reset_events + resumed_events, has_flow_ended=True
@@ -58,6 +58,7 @@ from rasa.shared.providers.embedding._langchain_embedding_client_adapter import
58
58
  _LangchainEmbeddingClientAdapter,
59
59
  )
60
60
  from rasa.shared.providers.llm.llm_client import LLMClient
61
+ from rasa.shared.utils.constants import LOG_COMPONENT_SOURCE_METHOD_FINGERPRINT_ADDON
61
62
  from rasa.shared.utils.health_check.embeddings_health_check_mixin import (
62
63
  EmbeddingsHealthCheckMixin,
63
64
  )
@@ -939,6 +940,8 @@ class IntentlessPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Policy):
939
940
  prompt_template = get_prompt_template(
940
941
  config.get(PROMPT_CONFIG_KEY),
941
942
  DEFAULT_INTENTLESS_PROMPT_TEMPLATE,
943
+ log_source_component=IntentlessPolicy.__name__,
944
+ log_source_method=LOG_COMPONENT_SOURCE_METHOD_FINGERPRINT_ADDON,
942
945
  )
943
946
 
944
947
  llm_config = resolve_model_client_config(
rasa/core/processor.py CHANGED
@@ -76,6 +76,7 @@ from rasa.shared.core.constants import (
76
76
  SLOT_SILENCE_TIMEOUT,
77
77
  USER_INTENT_RESTART,
78
78
  USER_INTENT_SILENCE_TIMEOUT,
79
+ SetSlotExtractor,
79
80
  )
80
81
  from rasa.shared.core.events import (
81
82
  ActionExecuted,
@@ -766,13 +767,26 @@ class MessageProcessor:
766
767
  if self.http_interpreter:
767
768
  parse_data = await self.http_interpreter.parse(message)
768
769
  else:
769
- regex_reader = create_regex_pattern_reader(message, self.domain)
770
-
771
770
  processed_message = Message({TEXT: message.text})
772
- if regex_reader:
773
- processed_message = regex_reader.unpack_regex_message(
774
- message=processed_message, domain=self.domain
771
+
772
+ all_flows = await self.get_flows()
773
+ should_force_slot_command, slot_name = (
774
+ rasa.core.utils.should_force_slot_filling(tracker, all_flows)
775
+ )
776
+
777
+ if should_force_slot_command:
778
+ command = SetSlotCommand(
779
+ name=slot_name,
780
+ value=message.text,
781
+ extractor=SetSlotExtractor.COMMAND_PAYLOAD_READER.value,
775
782
  )
783
+ processed_message.set(COMMANDS, [command.as_dict()], add_to_output=True)
784
+ else:
785
+ regex_reader = create_regex_pattern_reader(message, self.domain)
786
+ if regex_reader:
787
+ processed_message = regex_reader.unpack_regex_message(
788
+ message=processed_message, domain=self.domain
789
+ )
776
790
 
777
791
  # Invalid use of slash syntax, sanitize the message before passing
778
792
  # it to the graph
@@ -1009,7 +1023,14 @@ class MessageProcessor:
1009
1023
 
1010
1024
  @staticmethod
1011
1025
  def _should_handle_message(tracker: DialogueStateTracker) -> bool:
1012
- return not tracker.is_paused() or (
1026
+ return not tracker.is_paused() or MessageProcessor._last_user_intent_is_restart(
1027
+ tracker
1028
+ )
1029
+
1030
+ @staticmethod
1031
+ def _last_user_intent_is_restart(tracker: DialogueStateTracker) -> bool:
1032
+ """Check if the last user intent is a restart intent."""
1033
+ return (
1013
1034
  tracker.latest_message is not None
1014
1035
  and tracker.latest_message.intent.get(INTENT_NAME_KEY)
1015
1036
  == USER_INTENT_RESTART
rasa/core/utils.py CHANGED
@@ -19,6 +19,7 @@ from rasa.core.constants import (
19
19
  )
20
20
  from rasa.core.lock_store import InMemoryLockStore, LockStore, RedisLockStore
21
21
  from rasa.shared.constants import DEFAULT_ENDPOINTS_PATH, TCP_PROTOCOL
22
+ from rasa.shared.core.constants import SlotMappingType
22
23
  from rasa.shared.core.trackers import DialogueStateTracker
23
24
  from rasa.utils.endpoints import (
24
25
  EndpointConfig,
@@ -30,6 +31,7 @@ from rasa.utils.io import write_yaml
30
31
  if TYPE_CHECKING:
31
32
  from rasa.core.nlg import NaturalLanguageGenerator
32
33
  from rasa.shared.core.domain import Domain
34
+ from rasa.shared.core.flows.flows_list import FlowsList
33
35
 
34
36
  structlogger = structlog.get_logger()
35
37
 
@@ -364,3 +366,54 @@ def add_bot_utterance_metadata(
364
366
  ]
365
367
 
366
368
  return message
369
+
370
+
371
+ def should_force_slot_filling(
372
+ tracker: Optional[DialogueStateTracker], flows: "FlowsList"
373
+ ) -> Tuple[bool, Optional[str]]:
374
+ """Check if the flow should force slot filling.
375
+
376
+ This is only valid when the flow is at a collect information step which
377
+ has set `force_slot_filling` to true and the slot has a valid `from_text` mapping.
378
+
379
+ Args:
380
+ tracker: The dialogue state tracker.
381
+ flows: The list of flows.
382
+
383
+ Returns:
384
+ A tuple of a boolean indicating if the flow should force slot filling
385
+ and the name of the slot if applicable.
386
+ """
387
+ from rasa.dialogue_understanding.processor.command_processor import (
388
+ get_current_collect_step,
389
+ )
390
+
391
+ if tracker is None:
392
+ structlogger.error(
393
+ "slot.force_slot_filling.error",
394
+ event_info="Tracker is None. Cannot force slot filling.",
395
+ )
396
+ return False, None
397
+
398
+ stack = tracker.stack
399
+ step = get_current_collect_step(stack, flows)
400
+ if step is None or not step.force_slot_filling:
401
+ return False, None
402
+
403
+ slot_name = step.collect
404
+ slot = tracker.slots.get(slot_name)
405
+
406
+ if not slot:
407
+ structlogger.debug(
408
+ "slot.force_slot_filling.error",
409
+ event_info=f"Slot '{slot_name}' not found in tracker. "
410
+ f"Cannot force slot filling. "
411
+ f"Please check if the slot is defined in the domain.",
412
+ )
413
+ return False, None
414
+
415
+ for slot_mapping in slot.mappings:
416
+ if slot_mapping.type == SlotMappingType.FROM_TEXT:
417
+ return True, slot_name
418
+
419
+ return False, None
@@ -35,6 +35,10 @@ from rasa.shared.exceptions import FileIOException, InvalidConfigException
35
35
  from rasa.shared.nlu.constants import COMMANDS, TEXT
36
36
  from rasa.shared.nlu.training_data.message import Message
37
37
  from rasa.shared.nlu.training_data.training_data import TrainingData
38
+ from rasa.shared.utils.constants import (
39
+ LOG_COMPONENT_SOURCE_METHOD_FINGERPRINT_ADDON,
40
+ LOG_COMPONENT_SOURCE_METHOD_INIT,
41
+ )
38
42
  from rasa.shared.utils.health_check.llm_health_check_mixin import LLMHealthCheckMixin
39
43
  from rasa.shared.utils.io import deep_container_fingerprint
40
44
  from rasa.shared.utils.llm import (
@@ -107,6 +111,8 @@ class LLMBasedRouter(LLMHealthCheckMixin, GraphComponent):
107
111
  or get_prompt_template(
108
112
  config.get(PROMPT_CONFIG_KEY),
109
113
  DEFAULT_COMMAND_PROMPT_TEMPLATE,
114
+ log_source_component=LLMBasedRouter.__name__,
115
+ log_source_method=LOG_COMPONENT_SOURCE_METHOD_INIT,
110
116
  ).strip()
111
117
  )
112
118
 
@@ -318,6 +324,8 @@ class LLMBasedRouter(LLMHealthCheckMixin, GraphComponent):
318
324
  prompt_template = get_prompt_template(
319
325
  config.get(PROMPT_CONFIG_KEY),
320
326
  DEFAULT_COMMAND_PROMPT_TEMPLATE,
327
+ log_source_component=LLMBasedRouter.__name__,
328
+ log_source_method=LOG_COMPONENT_SOURCE_METHOD_FINGERPRINT_ADDON,
321
329
  )
322
330
 
323
331
  llm_config = resolve_model_client_config(
@@ -1,6 +1,5 @@
1
1
  from __future__ import annotations
2
2
 
3
- import copy
4
3
  import re
5
4
  from dataclasses import dataclass
6
5
  from typing import Any, Dict, List
@@ -13,10 +12,11 @@ from rasa.dialogue_understanding.commands.command_syntax_manager import (
13
12
  CommandSyntaxVersion,
14
13
  )
15
14
  from rasa.dialogue_understanding.patterns.cancel import CancelPatternFlowStackFrame
16
- from rasa.dialogue_understanding.patterns.clarify import ClarifyPatternFlowStackFrame
17
15
  from rasa.dialogue_understanding.stack.dialogue_stack import DialogueStack
18
- from rasa.dialogue_understanding.stack.frames import UserFlowStackFrame
19
- from rasa.dialogue_understanding.stack.frames.flow_stack_frame import FlowStackFrameType
16
+ from rasa.dialogue_understanding.stack.frames.flow_stack_frame import (
17
+ FlowStackFrameType,
18
+ UserFlowStackFrame,
19
+ )
20
20
  from rasa.dialogue_understanding.stack.utils import top_user_flow_frame
21
21
  from rasa.shared.core.events import Event, FlowCancelled
22
22
  from rasa.shared.core.flows import FlowsList
@@ -95,8 +95,6 @@ class CancelFlowCommand(Command):
95
95
  original_stack = original_tracker.stack
96
96
 
97
97
  applied_events: List[Event] = []
98
- # capture the top frame before we push new frames onto the stack
99
- initial_top_frame = stack.top()
100
98
  user_frame = top_user_flow_frame(original_stack)
101
99
  current_flow = user_frame.flow(all_flows) if user_frame else None
102
100
 
@@ -123,21 +121,6 @@ class CancelFlowCommand(Command):
123
121
  if user_frame:
124
122
  applied_events.append(FlowCancelled(user_frame.flow_id, user_frame.step_id))
125
123
 
126
- if initial_top_frame and isinstance(
127
- initial_top_frame, ClarifyPatternFlowStackFrame
128
- ):
129
- structlogger.debug(
130
- "command_executor.cancel_flow.cancel_clarification_options",
131
- clarification_options=initial_top_frame.clarification_options,
132
- )
133
- applied_events += cancel_all_pending_clarification_options(
134
- initial_top_frame,
135
- original_stack,
136
- canceled_frames,
137
- all_flows,
138
- stack,
139
- )
140
-
141
124
  return applied_events + tracker.create_stack_updated_events(stack)
142
125
 
143
126
  def __hash__(self) -> int:
@@ -172,41 +155,3 @@ class CancelFlowCommand(Command):
172
155
  CommandSyntaxManager.get_syntax_version(),
173
156
  mapper[CommandSyntaxManager.get_default_syntax_version()],
174
157
  )
175
-
176
-
177
- def cancel_all_pending_clarification_options(
178
- initial_top_frame: ClarifyPatternFlowStackFrame,
179
- original_stack: DialogueStack,
180
- canceled_frames: List[str],
181
- all_flows: FlowsList,
182
- stack: DialogueStack,
183
- ) -> List[FlowCancelled]:
184
- """Cancel all pending clarification options.
185
-
186
- This is a special case when the assistant asks the user to clarify
187
- which pending digression flow to start after the completion of an active flow.
188
- If the user chooses to cancel all options, this function takes care of
189
- updating the stack by removing all pending flow stack frames
190
- listed as clarification options.
191
- """
192
- clarification_names = set(initial_top_frame.names)
193
- to_be_canceled_frames = []
194
- applied_events = []
195
- for frame in reversed(original_stack.frames):
196
- if frame.frame_id in canceled_frames:
197
- continue
198
-
199
- to_be_canceled_frames.append(frame.frame_id)
200
- if isinstance(frame, UserFlowStackFrame):
201
- readable_flow_name = frame.flow(all_flows).readable_name()
202
- if readable_flow_name in clarification_names:
203
- stack.push(
204
- CancelPatternFlowStackFrame(
205
- canceled_name=readable_flow_name,
206
- canceled_frames=copy.deepcopy(to_be_canceled_frames),
207
- )
208
- )
209
- applied_events.append(FlowCancelled(frame.flow_id, frame.step_id))
210
- to_be_canceled_frames.clear()
211
-
212
- return applied_events
@@ -65,7 +65,7 @@ class KnowledgeAnswerCommand(FreeFormAnswerCommand):
65
65
  """Converts the command to a DSL string."""
66
66
  mapper = {
67
67
  CommandSyntaxVersion.v1: "SearchAndReply()",
68
- CommandSyntaxVersion.v2: "search and reply",
68
+ CommandSyntaxVersion.v2: "provide info",
69
69
  }
70
70
  return mapper.get(
71
71
  CommandSyntaxManager.get_syntax_version(),
@@ -81,7 +81,7 @@ class KnowledgeAnswerCommand(FreeFormAnswerCommand):
81
81
  def regex_pattern() -> str:
82
82
  mapper = {
83
83
  CommandSyntaxVersion.v1: r"SearchAndReply\(\)",
84
- CommandSyntaxVersion.v2: r"""^[\s\W\d]*search and reply['"`]*$""",
84
+ CommandSyntaxVersion.v2: r"""^[\s\W\d]*provide info['"`]*$""",
85
85
  }
86
86
  return mapper.get(
87
87
  CommandSyntaxManager.get_syntax_version(),
@@ -11,11 +11,6 @@ from rasa.dialogue_understanding.commands.command_syntax_manager import (
11
11
  CommandSyntaxManager,
12
12
  CommandSyntaxVersion,
13
13
  )
14
- from rasa.dialogue_understanding.patterns.clarify import FLOW_PATTERN_CLARIFICATION
15
- from rasa.dialogue_understanding.patterns.continue_interrupted import (
16
- ContinueInterruptedPatternFlowStackFrame,
17
- )
18
- from rasa.dialogue_understanding.stack.dialogue_stack import DialogueStack
19
14
  from rasa.dialogue_understanding.stack.frames.flow_stack_frame import (
20
15
  FlowStackFrameType,
21
16
  UserFlowStackFrame,
@@ -77,10 +72,6 @@ class StartFlowCommand(Command):
77
72
  applied_events: List[Event] = []
78
73
 
79
74
  if self.flow in user_flows_on_the_stack(stack):
80
- top_frame = stack.top()
81
- if top_frame is not None and top_frame.type() == FLOW_PATTERN_CLARIFICATION:
82
- return self.change_flow_frame_position_in_the_stack(stack, tracker)
83
-
84
75
  structlogger.debug(
85
76
  "command_executor.skip_command.already_started_flow", command=self
86
77
  )
@@ -149,35 +140,3 @@ class StartFlowCommand(Command):
149
140
  CommandSyntaxManager.get_syntax_version(),
150
141
  mapper[CommandSyntaxManager.get_default_syntax_version()],
151
142
  )
152
-
153
- def change_flow_frame_position_in_the_stack(
154
- self, stack: DialogueStack, tracker: DialogueStateTracker
155
- ) -> List[Event]:
156
- """Changes the position of the flow frame in the stack.
157
-
158
- This is a special case when pattern clarification is the active flow and
159
- the same flow is selected to start. In this case, the existing flow frame
160
- should be moved up in the stack.
161
- """
162
- frames = stack.frames[:]
163
-
164
- for idx, frame in enumerate(frames):
165
- if isinstance(frame, UserFlowStackFrame) and frame.flow_id == self.flow:
166
- structlogger.debug(
167
- "command_executor.change_flow_position_during_clarification",
168
- command=self,
169
- index=idx,
170
- )
171
- # pop the continue interrupted flow frame if it exists
172
- next_frame = frames[idx + 1] if idx + 1 < len(frames) else None
173
- if (
174
- isinstance(next_frame, ContinueInterruptedPatternFlowStackFrame)
175
- and next_frame.previous_flow_name == self.flow
176
- ):
177
- stack.frames.pop(idx + 1)
178
- # move up the existing flow from the stack
179
- stack.frames.pop(idx)
180
- stack.push(frame)
181
- return tracker.create_stack_updated_events(stack)
182
-
183
- return []
@@ -4,6 +4,7 @@ from typing import Any, Dict, List, Optional, Set, Text, Tuple
4
4
  import structlog
5
5
 
6
6
  from rasa.dialogue_understanding.commands import (
7
+ CannotHandleCommand,
7
8
  Command,
8
9
  CorrectSlotsCommand,
9
10
  ErrorCommand,
@@ -107,6 +108,14 @@ class CommandGenerator:
107
108
  commands = self._check_commands_against_startable_flows(
108
109
  commands, startable_flows
109
110
  )
111
+
112
+ # During force slot filling, keep only the command that sets the
113
+ # slot asked by the active collect step.
114
+ # Or return a CannotHandleCommand if no matching command is found.
115
+ commands = self._filter_commands_during_force_slot_filling(
116
+ commands, available_flows, tracker
117
+ )
118
+
110
119
  commands_dicts = [command.as_dict() for command in commands]
111
120
  message.set(COMMANDS, commands_dicts, add_to_output=True)
112
121
 
@@ -370,6 +379,64 @@ class CommandGenerator:
370
379
  Command.command_from_json(command) for command in message.get(COMMANDS, [])
371
380
  ]
372
381
 
382
+ @staticmethod
383
+ def _filter_commands_during_force_slot_filling(
384
+ commands: List[Command],
385
+ available_flows: FlowsList,
386
+ tracker: Optional[DialogueStateTracker] = None,
387
+ ) -> List[Command]:
388
+ """Filter commands during a collect step that has set `force_slot_filling`.
389
+
390
+ Args:
391
+ commands: The commands to filter.
392
+ available_flows: The available flows.
393
+ tracker: The tracker.
394
+
395
+ Returns:
396
+ The filtered commands.
397
+ """
398
+ from rasa.dialogue_understanding.processor.command_processor import (
399
+ get_current_collect_step,
400
+ )
401
+
402
+ if tracker is None:
403
+ structlogger.error(
404
+ "command_generator.filter_commands_during_force_slot_filling.tracker_not_found",
405
+ )
406
+ return commands
407
+
408
+ stack = tracker.stack
409
+ step = get_current_collect_step(stack, available_flows)
410
+
411
+ if step is None or not step.force_slot_filling:
412
+ return commands
413
+
414
+ # Retain only the command that sets the slot asked by
415
+ # the active collect step
416
+ filtered_commands: List[Command] = [
417
+ command
418
+ for command in commands
419
+ if (isinstance(command, SetSlotCommand) and command.name == step.collect)
420
+ ]
421
+
422
+ if not filtered_commands:
423
+ # If no commands were predicted, we need to return a CannotHandleCommand
424
+ structlogger.debug(
425
+ "command_generator.filter_commands_during_force_slot_filling.no_commands",
426
+ event_info=f"The command generator did not find any SetSlot "
427
+ f"command at the collect step for the slot '{step.collect}'. "
428
+ f"Returning a CannotHandleCommand instead.",
429
+ )
430
+ return [CannotHandleCommand()]
431
+
432
+ structlogger.debug(
433
+ "command_generator.filter_commands_during_force_slot_filling.filtered_commands",
434
+ slot_name=step.collect,
435
+ filtered_commands=filtered_commands,
436
+ )
437
+
438
+ return filtered_commands
439
+
373
440
 
374
441
  def gather_slot_names(commands: List[Command]) -> Set[str]:
375
442
  """Gather all slot names from the commands."""
@@ -52,7 +52,6 @@ from rasa.shared.utils.llm import (
52
52
  USER,
53
53
  allowed_values_for_slot,
54
54
  embedder_factory,
55
- get_prompt_template,
56
55
  resolve_model_client_config,
57
56
  tracker_as_readable_transcript,
58
57
  )
@@ -103,9 +102,7 @@ class FlowRetrieval(EmbeddingsHealthCheckMixin):
103
102
  self.config.get(EMBEDDINGS_CONFIG_KEY), FlowRetrieval.__name__
104
103
  )
105
104
  self.vector_store: Optional[FAISS] = None
106
- self.flow_document_template = get_prompt_template(
107
- None, DEFAULT_FLOW_DOCUMENT_TEMPLATE
108
- )
105
+ self.flow_document_template = DEFAULT_FLOW_DOCUMENT_TEMPLATE
109
106
  self._model_storage = model_storage
110
107
  self._resource = resource
111
108