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.
- rasa/cli/run.py +10 -6
- rasa/cli/utils.py +7 -0
- rasa/core/actions/action.py +0 -6
- rasa/core/channels/channel.py +30 -0
- rasa/core/channels/voice_ready/audiocodes.py +52 -17
- rasa/core/channels/voice_ready/jambonz.py +25 -5
- rasa/core/channels/voice_ready/jambonz_protocol.py +4 -0
- rasa/core/channels/voice_stream/audiocodes.py +53 -9
- rasa/core/channels/voice_stream/genesys.py +146 -16
- rasa/core/information_retrieval/faiss.py +6 -62
- rasa/core/nlg/contextual_response_rephraser.py +3 -0
- rasa/core/policies/enterprise_search_policy.py +10 -1
- rasa/core/policies/flows/flow_executor.py +3 -38
- rasa/core/policies/intentless_policy.py +3 -0
- rasa/core/processor.py +27 -6
- rasa/core/utils.py +53 -0
- rasa/dialogue_understanding/coexistence/llm_based_router.py +8 -0
- rasa/dialogue_understanding/commands/cancel_flow_command.py +4 -59
- rasa/dialogue_understanding/commands/knowledge_answer_command.py +2 -2
- rasa/dialogue_understanding/commands/start_flow_command.py +0 -41
- rasa/dialogue_understanding/generator/command_generator.py +67 -0
- rasa/dialogue_understanding/generator/flow_retrieval.py +1 -4
- rasa/dialogue_understanding/generator/llm_based_command_generator.py +2 -12
- rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +13 -0
- rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_claude_3_5_sonnet_20240620_template.jinja2 +1 -1
- rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_gpt_4o_2024_11_20_template.jinja2 +2 -5
- rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +22 -10
- rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +27 -12
- rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +0 -61
- rasa/dialogue_understanding/processor/command_processor.py +7 -65
- rasa/dialogue_understanding/stack/utils.py +0 -38
- rasa/e2e_test/utils/validation.py +3 -3
- rasa/llm_fine_tuning/paraphrasing/conversation_rephraser.py +3 -0
- rasa/shared/core/constants.py +0 -8
- rasa/shared/core/domain.py +12 -3
- rasa/shared/core/flows/flow.py +0 -17
- rasa/shared/core/flows/flows_yaml_schema.json +3 -38
- rasa/shared/core/flows/steps/collect.py +5 -18
- rasa/shared/core/flows/utils.py +1 -16
- rasa/shared/core/slot_mappings.py +11 -5
- rasa/shared/nlu/constants.py +0 -1
- rasa/shared/utils/common.py +11 -1
- rasa/shared/utils/constants.py +3 -0
- rasa/shared/utils/llm.py +69 -23
- rasa/validator.py +1 -123
- rasa/version.py +1 -1
- {rasa_pro-3.13.0.dev1.dist-info → rasa_pro-3.13.0.dev3.dist-info}/METADATA +2 -2
- {rasa_pro-3.13.0.dev1.dist-info → rasa_pro-3.13.0.dev3.dist-info}/RECORD +51 -54
- rasa/core/actions/action_handle_digressions.py +0 -164
- rasa/dialogue_understanding/commands/handle_digressions_command.py +0 -144
- rasa/dialogue_understanding/patterns/handle_digressions.py +0 -81
- {rasa_pro-3.13.0.dev1.dist-info → rasa_pro-3.13.0.dev3.dist-info}/NOTICE +0 -0
- {rasa_pro-3.13.0.dev1.dist-info → rasa_pro-3.13.0.dev3.dist-info}/WHEEL +0 -0
- {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
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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
|
-
|
|
680
|
-
|
|
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
|
-
|
|
773
|
-
|
|
774
|
-
|
|
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
|
|
19
|
-
|
|
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: "
|
|
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]*
|
|
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 =
|
|
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
|
|