rasa-pro 3.13.0.dev20250612__py3-none-any.whl → 3.13.0.dev20250613__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/__main__.py +0 -3
- rasa/api.py +1 -1
- rasa/cli/dialogue_understanding_test.py +1 -1
- rasa/cli/e2e_test.py +1 -1
- rasa/cli/evaluate.py +1 -1
- rasa/cli/export.py +1 -1
- rasa/cli/llm_fine_tuning.py +12 -11
- rasa/cli/project_templates/defaults.py +133 -0
- rasa/cli/run.py +1 -1
- rasa/cli/studio/link.py +53 -0
- rasa/cli/studio/pull.py +78 -0
- rasa/cli/studio/push.py +78 -0
- rasa/cli/studio/studio.py +12 -0
- rasa/cli/studio/upload.py +8 -0
- rasa/cli/train.py +1 -1
- rasa/cli/utils.py +1 -1
- rasa/cli/x.py +1 -1
- rasa/constants.py +2 -0
- rasa/core/__init__.py +0 -16
- rasa/core/actions/action.py +5 -1
- rasa/core/actions/action_repeat_bot_messages.py +18 -22
- rasa/core/actions/action_run_slot_rejections.py +0 -1
- rasa/core/agent.py +16 -1
- rasa/core/available_endpoints.py +146 -0
- rasa/core/brokers/pika.py +1 -2
- rasa/core/channels/botframework.py +2 -2
- rasa/core/channels/channel.py +2 -2
- rasa/core/channels/hangouts.py +8 -5
- rasa/core/channels/mattermost.py +1 -1
- rasa/core/channels/rasa_chat.py +2 -4
- rasa/core/channels/rest.py +5 -4
- rasa/core/channels/studio_chat.py +3 -2
- rasa/core/channels/vier_cvg.py +1 -2
- rasa/core/channels/voice_ready/audiocodes.py +1 -8
- rasa/core/channels/voice_stream/audiocodes.py +7 -4
- rasa/core/channels/voice_stream/genesys.py +2 -2
- rasa/core/channels/voice_stream/twilio_media_streams.py +10 -5
- rasa/core/channels/voice_stream/voice_channel.py +33 -22
- rasa/core/http_interpreter.py +3 -7
- rasa/core/jobs.py +2 -1
- rasa/core/nlg/contextual_response_rephraser.py +38 -11
- rasa/core/nlg/generator.py +0 -1
- rasa/core/nlg/interpolator.py +2 -3
- rasa/core/nlg/summarize.py +39 -5
- rasa/core/policies/enterprise_search_policy.py +290 -66
- rasa/core/policies/enterprise_search_prompt_with_relevancy_check_and_citation_template.jinja2 +63 -0
- rasa/core/policies/flow_policy.py +1 -1
- rasa/core/policies/flows/flow_executor.py +96 -17
- rasa/core/policies/intentless_policy.py +24 -16
- rasa/core/processor.py +104 -51
- rasa/core/run.py +33 -11
- rasa/core/tracker_stores/tracker_store.py +1 -1
- rasa/core/training/interactive.py +1 -1
- rasa/core/utils.py +24 -97
- rasa/dialogue_understanding/coexistence/intent_based_router.py +2 -1
- rasa/dialogue_understanding/coexistence/llm_based_router.py +8 -3
- rasa/dialogue_understanding/commands/can_not_handle_command.py +2 -0
- rasa/dialogue_understanding/commands/cancel_flow_command.py +2 -0
- rasa/dialogue_understanding/commands/chit_chat_answer_command.py +2 -0
- rasa/dialogue_understanding/commands/clarify_command.py +5 -1
- rasa/dialogue_understanding/commands/command_syntax_manager.py +1 -0
- rasa/dialogue_understanding/commands/human_handoff_command.py +2 -0
- rasa/dialogue_understanding/commands/knowledge_answer_command.py +2 -0
- rasa/dialogue_understanding/commands/repeat_bot_messages_command.py +2 -0
- rasa/dialogue_understanding/commands/set_slot_command.py +11 -1
- rasa/dialogue_understanding/commands/skip_question_command.py +2 -0
- rasa/dialogue_understanding/commands/start_flow_command.py +4 -0
- rasa/dialogue_understanding/commands/utils.py +26 -2
- rasa/dialogue_understanding/generator/__init__.py +7 -1
- rasa/dialogue_understanding/generator/command_generator.py +4 -2
- rasa/dialogue_understanding/generator/command_parser.py +2 -2
- rasa/dialogue_understanding/generator/command_parser_validator.py +63 -0
- rasa/dialogue_understanding/generator/constants.py +2 -2
- rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v3_gpt_4o_2024_11_20_template.jinja2 +78 -0
- rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +28 -463
- rasa/dialogue_understanding/generator/single_step/search_ready_llm_command_generator.py +147 -0
- rasa/dialogue_understanding/generator/single_step/single_step_based_llm_command_generator.py +477 -0
- rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +8 -58
- rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +37 -25
- rasa/dialogue_understanding/patterns/domain_for_patterns.py +190 -0
- rasa/dialogue_understanding/processor/command_processor.py +3 -3
- rasa/dialogue_understanding/processor/command_processor_component.py +3 -3
- rasa/dialogue_understanding/stack/frames/flow_stack_frame.py +17 -4
- rasa/dialogue_understanding/utils.py +68 -12
- rasa/dialogue_understanding_test/du_test_case.py +1 -1
- rasa/dialogue_understanding_test/du_test_runner.py +4 -22
- rasa/dialogue_understanding_test/test_case_simulation/test_case_tracker_simulator.py +2 -6
- rasa/e2e_test/e2e_test_runner.py +1 -1
- rasa/engine/constants.py +1 -1
- rasa/engine/recipes/default_recipe.py +26 -2
- rasa/engine/validation.py +3 -2
- rasa/hooks.py +0 -28
- rasa/llm_fine_tuning/annotation_module.py +39 -9
- rasa/llm_fine_tuning/conversations.py +3 -0
- rasa/llm_fine_tuning/llm_data_preparation_module.py +66 -49
- rasa/llm_fine_tuning/paraphrasing/conversation_rephraser.py +4 -2
- rasa/llm_fine_tuning/paraphrasing/rephrase_validator.py +52 -44
- rasa/llm_fine_tuning/paraphrasing_module.py +10 -12
- rasa/llm_fine_tuning/storage.py +4 -4
- rasa/llm_fine_tuning/utils.py +63 -1
- rasa/model_manager/model_api.py +88 -0
- rasa/model_manager/trainer_service.py +4 -4
- rasa/plugin.py +1 -11
- rasa/privacy/__init__.py +0 -0
- rasa/privacy/constants.py +83 -0
- rasa/privacy/event_broker_utils.py +77 -0
- rasa/privacy/privacy_config.py +281 -0
- rasa/privacy/privacy_config_schema.json +86 -0
- rasa/privacy/privacy_filter.py +340 -0
- rasa/privacy/privacy_manager.py +576 -0
- rasa/server.py +23 -2
- rasa/shared/constants.py +6 -0
- rasa/shared/core/constants.py +4 -3
- rasa/shared/core/domain.py +7 -0
- rasa/shared/core/events.py +37 -7
- rasa/shared/core/flows/flow.py +1 -2
- rasa/shared/core/flows/flows_yaml_schema.json +3 -0
- rasa/shared/core/flows/steps/collect.py +46 -2
- rasa/shared/core/slots.py +28 -0
- rasa/shared/exceptions.py +4 -0
- rasa/shared/providers/_configs/azure_openai_client_config.py +4 -0
- rasa/shared/providers/_configs/openai_client_config.py +4 -0
- rasa/shared/providers/embedding/_base_litellm_embedding_client.py +3 -0
- rasa/shared/providers/llm/_base_litellm_client.py +5 -2
- rasa/shared/utils/llm.py +161 -6
- rasa/shared/utils/yaml.py +32 -0
- rasa/studio/data_handler.py +3 -3
- rasa/studio/download/download.py +37 -60
- rasa/studio/download/flows.py +23 -31
- rasa/studio/link.py +200 -0
- rasa/studio/pull.py +94 -0
- rasa/studio/push.py +131 -0
- rasa/studio/upload.py +117 -67
- rasa/telemetry.py +82 -25
- rasa/tracing/config.py +3 -4
- rasa/tracing/constants.py +19 -1
- rasa/tracing/instrumentation/attribute_extractors.py +10 -2
- rasa/tracing/instrumentation/instrumentation.py +53 -2
- rasa/tracing/instrumentation/metrics.py +98 -15
- rasa/tracing/metric_instrument_provider.py +75 -3
- rasa/utils/common.py +1 -27
- rasa/utils/log_utils.py +1 -45
- rasa/validator.py +2 -8
- rasa/version.py +1 -1
- {rasa_pro-3.13.0.dev20250612.dist-info → rasa_pro-3.13.0.dev20250613.dist-info}/METADATA +5 -6
- {rasa_pro-3.13.0.dev20250612.dist-info → rasa_pro-3.13.0.dev20250613.dist-info}/RECORD +149 -135
- rasa/anonymization/__init__.py +0 -2
- rasa/anonymization/anonymisation_rule_yaml_reader.py +0 -91
- rasa/anonymization/anonymization_pipeline.py +0 -286
- rasa/anonymization/anonymization_rule_executor.py +0 -266
- rasa/anonymization/anonymization_rule_orchestrator.py +0 -119
- rasa/anonymization/schemas/config.yml +0 -47
- rasa/anonymization/utils.py +0 -118
- {rasa_pro-3.13.0.dev20250612.dist-info → rasa_pro-3.13.0.dev20250613.dist-info}/NOTICE +0 -0
- {rasa_pro-3.13.0.dev20250612.dist-info → rasa_pro-3.13.0.dev20250613.dist-info}/WHEEL +0 -0
- {rasa_pro-3.13.0.dev20250612.dist-info → rasa_pro-3.13.0.dev20250613.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
Given the following information, please provide an answer based on the provided documents and the context of the recent conversation.
|
|
2
|
+
If the answer is not known or cannot be determined from the provided documents or context, please state that you do not know to the user.
|
|
3
|
+
### Relevant Documents
|
|
4
|
+
Use the following documents to answer the question:
|
|
5
|
+
{% for doc in docs %}
|
|
6
|
+
{{ loop.cycle("*")}}. {{ doc.metadata }}
|
|
7
|
+
{{ doc.text }}
|
|
8
|
+
{% endfor %}
|
|
9
|
+
|
|
10
|
+
{% if citation_enabled %}
|
|
11
|
+
### Citing Sources
|
|
12
|
+
Find the sources from the documents that are most relevant to answering the question.
|
|
13
|
+
The sources must be extracted from the given document metadata source property and not from the conversation context.
|
|
14
|
+
If there are no relevant sources, write "No relevant sources" instead.
|
|
15
|
+
|
|
16
|
+
For each source you cite, follow a 1-based numbering system for citations.
|
|
17
|
+
Start with [1] for the first source you refer to, regardless of its index in the provided list of documents.
|
|
18
|
+
If you cite another source, use the next number in sequence, [2], and so on.
|
|
19
|
+
Ensure each source is only assigned one number, even if referenced multiple times.
|
|
20
|
+
If you refer back to a previously cited source, use its originally assigned number.
|
|
21
|
+
|
|
22
|
+
For example, if you first cite the third source in the list, refer to it as [1].
|
|
23
|
+
If you then cite the first source in the list, refer to it as [2].
|
|
24
|
+
If you mention the third source again, still refer to it as [1].
|
|
25
|
+
|
|
26
|
+
Don't say "According to Source [1]" when answering. Instead, make references to sources relevant to each section of the answer solely by adding the bracketed number at the end of the relevant sentence.
|
|
27
|
+
#### Formatting
|
|
28
|
+
First print the answer with in-text citations which follow a numbered order starting with index 1, then add the sources section.
|
|
29
|
+
The format of your overall answer must look like what's shown between the <example></example> tags.
|
|
30
|
+
Make sure to follow the formatting exactly and remove any line breaks or whitespaces between the answer and the Sources section.
|
|
31
|
+
<example>
|
|
32
|
+
You can use flows to model business logic in Rasa assistants. [1] You can use the Enterprise Search Policy to search vector stores for relevant knowledge base documents. [2]
|
|
33
|
+
Sources:
|
|
34
|
+
[1] https://rasa.com/docs/rasa-pro/concepts/flows
|
|
35
|
+
[2] https://rasa.com/docs/rasa-pro/concepts/policies/enterprise-search-policy
|
|
36
|
+
</example>
|
|
37
|
+
{% endif %}
|
|
38
|
+
|
|
39
|
+
{% if slots|length > 0 %}
|
|
40
|
+
### Slots or Variables
|
|
41
|
+
Here are the variables of the currently active conversation which may be used to answer the question:
|
|
42
|
+
{% for slot in slots -%}
|
|
43
|
+
- name: {{ slot.name }}, value: {{ slot.value }}, type: {{ slot.type }}
|
|
44
|
+
{% endfor %}
|
|
45
|
+
{% endif %}
|
|
46
|
+
### Current Conversation
|
|
47
|
+
Transcript of the current conversation, use it to determine the context of the question:
|
|
48
|
+
{{ current_conversation }}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
## Answering the Question
|
|
52
|
+
Based on the above sections, please formulate an answer to the question or request in the user's last message.
|
|
53
|
+
It is important that you ensure the answer is grounded in the provided documents and conversation context.
|
|
54
|
+
Avoid speculating or making assumptions beyond the given information and keep your answers short, 2 to 3 sentences at most.
|
|
55
|
+
|
|
56
|
+
{% if citation_enabled %}
|
|
57
|
+
If you are unable to find an answer in the given relevant documents, do not cite sources from elsewhere in the conversation context.
|
|
58
|
+
{% endif %}
|
|
59
|
+
|
|
60
|
+
{% if check_relevancy %}
|
|
61
|
+
If answer is not relevant output: "[NO_RELEVANT_ANSWER_FOUND]"
|
|
62
|
+
{% endif %}
|
|
63
|
+
Your answer:
|
|
@@ -150,12 +150,12 @@ class FlowPolicy(Policy):
|
|
|
150
150
|
except FlowCircuitBreakerTrippedException as e:
|
|
151
151
|
structlogger.error(
|
|
152
152
|
"flow.circuit_breaker",
|
|
153
|
-
dialogue_stack=e.dialogue_stack,
|
|
154
153
|
number_of_steps_taken=e.number_of_steps_taken,
|
|
155
154
|
event_info=(
|
|
156
155
|
"The flow circuit breaker tripped. "
|
|
157
156
|
"There appears to be an infinite loop in the flows."
|
|
158
157
|
),
|
|
158
|
+
error=str(e),
|
|
159
159
|
)
|
|
160
160
|
# end the current flow and start the internal error flow
|
|
161
161
|
updated_stack = tracker.stack
|
|
@@ -9,6 +9,7 @@ from structlog.contextvars import (
|
|
|
9
9
|
bound_contextvars,
|
|
10
10
|
)
|
|
11
11
|
|
|
12
|
+
from rasa.core.available_endpoints import AvailableEndpoints
|
|
12
13
|
from rasa.core.constants import ACTIVE_FLOW_METADATA_KEY, STEP_ID_METADATA_KEY
|
|
13
14
|
from rasa.core.policies.flows.flow_exceptions import (
|
|
14
15
|
FlowCircuitBreakerTrippedException,
|
|
@@ -24,6 +25,7 @@ from rasa.core.policies.flows.flow_step_result import (
|
|
|
24
25
|
from rasa.dialogue_understanding.commands import CancelFlowCommand
|
|
25
26
|
from rasa.dialogue_understanding.patterns.cancel import CancelPatternFlowStackFrame
|
|
26
27
|
from rasa.dialogue_understanding.patterns.collect_information import (
|
|
28
|
+
FLOW_PATTERN_COLLECT_INFORMATION,
|
|
27
29
|
CollectInformationPatternFlowStackFrame,
|
|
28
30
|
)
|
|
29
31
|
from rasa.dialogue_understanding.patterns.completed import (
|
|
@@ -54,6 +56,7 @@ from rasa.dialogue_understanding.stack.utils import (
|
|
|
54
56
|
from rasa.shared.constants import RASA_PATTERN_HUMAN_HANDOFF
|
|
55
57
|
from rasa.shared.core.constants import (
|
|
56
58
|
ACTION_LISTEN_NAME,
|
|
59
|
+
SILENCE_TIMEOUT_SLOT,
|
|
57
60
|
)
|
|
58
61
|
from rasa.shared.core.events import (
|
|
59
62
|
Event,
|
|
@@ -123,7 +126,6 @@ def is_condition_satisfied(
|
|
|
123
126
|
structlogger.error(
|
|
124
127
|
"flow.predicate.error",
|
|
125
128
|
predicate=predicate,
|
|
126
|
-
document=document,
|
|
127
129
|
error=str(e),
|
|
128
130
|
)
|
|
129
131
|
return False
|
|
@@ -177,7 +179,7 @@ def select_next_step_id(
|
|
|
177
179
|
"flow.link.failed_to_select_branch",
|
|
178
180
|
current=current,
|
|
179
181
|
links=next_step.links,
|
|
180
|
-
|
|
182
|
+
sender_id=tracker.sender_id,
|
|
181
183
|
)
|
|
182
184
|
return None
|
|
183
185
|
|
|
@@ -191,7 +193,7 @@ def select_next_step_id(
|
|
|
191
193
|
structlogger.error(
|
|
192
194
|
"flow.step.failed_to_select_next_step",
|
|
193
195
|
step=current,
|
|
194
|
-
|
|
196
|
+
sender_id=tracker.sender_id,
|
|
195
197
|
)
|
|
196
198
|
return None
|
|
197
199
|
|
|
@@ -226,19 +228,6 @@ def events_from_set_slots_step(step: SetSlotsFlowStep) -> List[Event]:
|
|
|
226
228
|
return [SlotSet(slot["key"], slot["value"]) for slot in step.slots]
|
|
227
229
|
|
|
228
230
|
|
|
229
|
-
def events_for_collect_step_execution(
|
|
230
|
-
step: CollectInformationFlowStep, tracker: DialogueStateTracker
|
|
231
|
-
) -> List[Event]:
|
|
232
|
-
"""Create the events needed to prepare for the execution of a collect step."""
|
|
233
|
-
# reset the slots that always need to be explicitly collected
|
|
234
|
-
slot = tracker.slots.get(step.collect, None)
|
|
235
|
-
|
|
236
|
-
if slot and step.ask_before_filling:
|
|
237
|
-
return [SlotSet(step.collect, None)]
|
|
238
|
-
else:
|
|
239
|
-
return []
|
|
240
|
-
|
|
241
|
-
|
|
242
231
|
def trigger_pattern_continue_interrupted(
|
|
243
232
|
current_frame: DialogueStackFrame,
|
|
244
233
|
stack: DialogueStack,
|
|
@@ -600,6 +589,12 @@ def run_step(
|
|
|
600
589
|
# the START_STEP meta step
|
|
601
590
|
initial_events.append(FlowStarted(flow.id, metadata=stack.current_context()))
|
|
602
591
|
|
|
592
|
+
# FLow does not start with collect step or we are not in collect information pattern
|
|
593
|
+
if _first_step_is_not_collect(
|
|
594
|
+
step, previous_step_id
|
|
595
|
+
) and not _in_collect_information_pattern(flow):
|
|
596
|
+
_append_global_silence_timeout_event(initial_events, tracker)
|
|
597
|
+
|
|
603
598
|
if isinstance(step, CollectInformationFlowStep):
|
|
604
599
|
return _run_collect_information_step(
|
|
605
600
|
available_actions,
|
|
@@ -629,12 +624,32 @@ def run_step(
|
|
|
629
624
|
return ContinueFlowWithNextStep(events=initial_events)
|
|
630
625
|
|
|
631
626
|
elif isinstance(step, EndFlowStep):
|
|
627
|
+
# If pattern collect information flow is ending,
|
|
628
|
+
# we need to reset the silence timeout slot to its global value.
|
|
629
|
+
if flow.id == FLOW_PATTERN_COLLECT_INFORMATION:
|
|
630
|
+
_append_global_silence_timeout_event(initial_events, tracker)
|
|
631
|
+
|
|
632
632
|
return _run_end_step(flow, flows, initial_events, stack, tracker)
|
|
633
633
|
|
|
634
634
|
else:
|
|
635
635
|
raise FlowException(f"Unknown flow step type {type(step)}")
|
|
636
636
|
|
|
637
637
|
|
|
638
|
+
def _first_step_is_not_collect(
|
|
639
|
+
step: FlowStep,
|
|
640
|
+
previous_step_id: str,
|
|
641
|
+
) -> bool:
|
|
642
|
+
"""Check if the first step is not a collect information step."""
|
|
643
|
+
return (previous_step_id == START_STEP) and not isinstance(
|
|
644
|
+
step, CollectInformationFlowStep
|
|
645
|
+
)
|
|
646
|
+
|
|
647
|
+
|
|
648
|
+
def _in_collect_information_pattern(flow: Flow) -> bool:
|
|
649
|
+
"""Check if the current flow is a collect information pattern."""
|
|
650
|
+
return flow.id == FLOW_PATTERN_COLLECT_INFORMATION
|
|
651
|
+
|
|
652
|
+
|
|
638
653
|
def _run_end_step(
|
|
639
654
|
flow: Flow,
|
|
640
655
|
flows: FlowsList,
|
|
@@ -745,5 +760,69 @@ def _run_collect_information_step(
|
|
|
745
760
|
step.collect, stack, step.rejections, step.utter, step.collect_action
|
|
746
761
|
)
|
|
747
762
|
|
|
748
|
-
events: List[Event] =
|
|
763
|
+
events: List[Event] = _events_for_collect_step_execution(step, tracker)
|
|
749
764
|
return ContinueFlowWithNextStep(events=initial_events + events)
|
|
765
|
+
|
|
766
|
+
|
|
767
|
+
def _events_for_collect_step_execution(
|
|
768
|
+
step: CollectInformationFlowStep, tracker: DialogueStateTracker
|
|
769
|
+
) -> List[Event]:
|
|
770
|
+
"""Create the events needed to prepare for the execution of a collect step."""
|
|
771
|
+
# reset the slots that always need to be explicitly collected
|
|
772
|
+
|
|
773
|
+
events = _silence_timeout_events_for_collect_step(step, tracker)
|
|
774
|
+
|
|
775
|
+
slot = tracker.slots.get(step.collect, None)
|
|
776
|
+
if slot and step.ask_before_filling:
|
|
777
|
+
events.append(SlotSet(step.collect, None))
|
|
778
|
+
|
|
779
|
+
return events
|
|
780
|
+
|
|
781
|
+
|
|
782
|
+
def _silence_timeout_events_for_collect_step(
|
|
783
|
+
step: CollectInformationFlowStep, tracker: DialogueStateTracker
|
|
784
|
+
) -> List[Event]:
|
|
785
|
+
events: List[Event] = []
|
|
786
|
+
|
|
787
|
+
silence_timeout = (
|
|
788
|
+
AvailableEndpoints.get_instance().interaction_handling.global_silence_timeout
|
|
789
|
+
)
|
|
790
|
+
|
|
791
|
+
if step.silence_timeout:
|
|
792
|
+
structlogger.debug(
|
|
793
|
+
"flow.step.run.adjusting_silence_timeout",
|
|
794
|
+
duration=step.silence_timeout,
|
|
795
|
+
collect=step.collect,
|
|
796
|
+
)
|
|
797
|
+
|
|
798
|
+
silence_timeout = step.silence_timeout
|
|
799
|
+
else:
|
|
800
|
+
structlogger.debug(
|
|
801
|
+
"flow.step.run.reset_silence_timeout_to_global",
|
|
802
|
+
duration=silence_timeout,
|
|
803
|
+
collect=step.collect,
|
|
804
|
+
)
|
|
805
|
+
|
|
806
|
+
current_silence_timeout = tracker.get_slot(SILENCE_TIMEOUT_SLOT)
|
|
807
|
+
|
|
808
|
+
if current_silence_timeout != silence_timeout:
|
|
809
|
+
events.append(SlotSet(SILENCE_TIMEOUT_SLOT, silence_timeout))
|
|
810
|
+
|
|
811
|
+
return events
|
|
812
|
+
|
|
813
|
+
|
|
814
|
+
def _append_global_silence_timeout_event(
|
|
815
|
+
events: List[Event], tracker: DialogueStateTracker
|
|
816
|
+
) -> None:
|
|
817
|
+
current_silence_timeout = tracker.get_slot(SILENCE_TIMEOUT_SLOT)
|
|
818
|
+
global_silence_timeout = (
|
|
819
|
+
AvailableEndpoints.get_instance().interaction_handling.global_silence_timeout
|
|
820
|
+
)
|
|
821
|
+
|
|
822
|
+
if current_silence_timeout != global_silence_timeout:
|
|
823
|
+
events.append(
|
|
824
|
+
SlotSet(
|
|
825
|
+
SILENCE_TIMEOUT_SLOT,
|
|
826
|
+
AvailableEndpoints.get_instance().interaction_handling.global_silence_timeout,
|
|
827
|
+
)
|
|
828
|
+
)
|
|
@@ -5,6 +5,7 @@ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Text, Tuple
|
|
|
5
5
|
|
|
6
6
|
import structlog
|
|
7
7
|
import tiktoken
|
|
8
|
+
from deprecated import deprecated # type: ignore[import]
|
|
8
9
|
from jinja2 import Template
|
|
9
10
|
from langchain.docstore.document import Document
|
|
10
11
|
from langchain.schema.embeddings import Embeddings
|
|
@@ -31,22 +32,19 @@ from rasa.graph_components.providers.responses_provider import Responses
|
|
|
31
32
|
from rasa.shared.constants import (
|
|
32
33
|
EMBEDDINGS_CONFIG_KEY,
|
|
33
34
|
LLM_CONFIG_KEY,
|
|
35
|
+
MAX_COMPLETION_TOKENS_CONFIG_KEY,
|
|
34
36
|
MODEL_CONFIG_KEY,
|
|
35
37
|
MODEL_GROUP_ID_CONFIG_KEY,
|
|
36
38
|
MODEL_NAME_CONFIG_KEY,
|
|
37
39
|
OPENAI_PROVIDER,
|
|
38
40
|
PROMPT_CONFIG_KEY,
|
|
39
41
|
PROVIDER_CONFIG_KEY,
|
|
42
|
+
TEMPERATURE_CONFIG_KEY,
|
|
40
43
|
TIMEOUT_CONFIG_KEY,
|
|
41
44
|
)
|
|
42
45
|
from rasa.shared.core.constants import ACTION_LISTEN_NAME
|
|
43
46
|
from rasa.shared.core.domain import KEY_RESPONSES_TEXT, Domain
|
|
44
|
-
from rasa.shared.core.events import
|
|
45
|
-
ActionExecuted,
|
|
46
|
-
BotUttered,
|
|
47
|
-
Event,
|
|
48
|
-
UserUttered,
|
|
49
|
-
)
|
|
47
|
+
from rasa.shared.core.events import ActionExecuted, BotUttered, Event, UserUttered
|
|
50
48
|
from rasa.shared.core.flows import FlowsList
|
|
51
49
|
from rasa.shared.core.generator import TrackerWithCachedStates
|
|
52
50
|
from rasa.shared.core.policies.utils import filter_responses_for_intentless_policy
|
|
@@ -63,7 +61,7 @@ from rasa.shared.utils.health_check.embeddings_health_check_mixin import (
|
|
|
63
61
|
EmbeddingsHealthCheckMixin,
|
|
64
62
|
)
|
|
65
63
|
from rasa.shared.utils.health_check.llm_health_check_mixin import LLMHealthCheckMixin
|
|
66
|
-
from rasa.shared.utils.io import deep_container_fingerprint
|
|
64
|
+
from rasa.shared.utils.io import deep_container_fingerprint, raise_deprecation_warning
|
|
67
65
|
from rasa.shared.utils.llm import (
|
|
68
66
|
AI,
|
|
69
67
|
DEFAULT_OPENAI_CHAT_MODEL_NAME,
|
|
@@ -111,14 +109,14 @@ NLU_ABSTENTION_THRESHOLD = "nlu_abstention_threshold"
|
|
|
111
109
|
DEFAULT_LLM_CONFIG = {
|
|
112
110
|
PROVIDER_CONFIG_KEY: OPENAI_PROVIDER,
|
|
113
111
|
MODEL_CONFIG_KEY: DEFAULT_OPENAI_CHAT_MODEL_NAME,
|
|
114
|
-
|
|
115
|
-
|
|
112
|
+
TEMPERATURE_CONFIG_KEY: 0.0,
|
|
113
|
+
MAX_COMPLETION_TOKENS_CONFIG_KEY: DEFAULT_OPENAI_MAX_GENERATED_TOKENS,
|
|
116
114
|
TIMEOUT_CONFIG_KEY: 5,
|
|
117
115
|
}
|
|
118
116
|
|
|
119
117
|
DEFAULT_EMBEDDINGS_CONFIG = {
|
|
120
118
|
PROVIDER_CONFIG_KEY: OPENAI_PROVIDER,
|
|
121
|
-
|
|
119
|
+
MODEL_CONFIG_KEY: DEFAULT_OPENAI_EMBEDDING_MODEL_NAME,
|
|
122
120
|
}
|
|
123
121
|
|
|
124
122
|
DEFAULT_INTENTLESS_PROMPT_TEMPLATE = importlib.resources.open_text(
|
|
@@ -323,6 +321,9 @@ def conversation_as_prompt(conversation: Conversation) -> str:
|
|
|
323
321
|
@DefaultV1Recipe.register(
|
|
324
322
|
DefaultV1Recipe.ComponentType.POLICY_WITH_END_TO_END_SUPPORT, is_trainable=True
|
|
325
323
|
)
|
|
324
|
+
@deprecated(
|
|
325
|
+
reason=("The IntentlessPolicy is deprecated and will be removed in Rasa `4.0.0`.")
|
|
326
|
+
)
|
|
326
327
|
class IntentlessPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Policy):
|
|
327
328
|
"""Policy which uses a language model to generate the next action.
|
|
328
329
|
|
|
@@ -344,8 +345,6 @@ class IntentlessPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Policy):
|
|
|
344
345
|
# ensures that the policy will not override a deterministic policy
|
|
345
346
|
# which utilizes the nlu predictions confidence (e.g. Memoization).
|
|
346
347
|
NLU_ABSTENTION_THRESHOLD: 0.9,
|
|
347
|
-
LLM_CONFIG_KEY: DEFAULT_LLM_CONFIG,
|
|
348
|
-
EMBEDDINGS_CONFIG_KEY: DEFAULT_EMBEDDINGS_CONFIG,
|
|
349
348
|
PROMPT_CONFIG_KEY: DEFAULT_INTENTLESS_PROMPT_TEMPLATE,
|
|
350
349
|
}
|
|
351
350
|
|
|
@@ -378,16 +377,25 @@ class IntentlessPolicy(LLMHealthCheckMixin, EmbeddingsHealthCheckMixin, Policy):
|
|
|
378
377
|
prompt_template: Optional[Text] = None,
|
|
379
378
|
) -> None:
|
|
380
379
|
"""Constructs a new Policy object."""
|
|
380
|
+
raise_deprecation_warning(
|
|
381
|
+
message=("Support for `IntentlessPolicy` will be removed in Rasa `4.0.0`.")
|
|
382
|
+
)
|
|
381
383
|
super().__init__(config, model_storage, resource, execution_context, featurizer)
|
|
382
384
|
|
|
383
385
|
# Resolve LLM config
|
|
384
|
-
self.config[LLM_CONFIG_KEY] =
|
|
385
|
-
|
|
386
|
+
self.config[LLM_CONFIG_KEY] = combine_custom_and_default_config(
|
|
387
|
+
resolve_model_client_config(
|
|
388
|
+
self.config.get(LLM_CONFIG_KEY), IntentlessPolicy.__name__
|
|
389
|
+
),
|
|
390
|
+
DEFAULT_LLM_CONFIG,
|
|
386
391
|
)
|
|
387
392
|
|
|
388
393
|
# Resolve embeddings config
|
|
389
|
-
self.config[EMBEDDINGS_CONFIG_KEY] =
|
|
390
|
-
|
|
394
|
+
self.config[EMBEDDINGS_CONFIG_KEY] = combine_custom_and_default_config(
|
|
395
|
+
resolve_model_client_config(
|
|
396
|
+
self.config.get(EMBEDDINGS_CONFIG_KEY), IntentlessPolicy.__name__
|
|
397
|
+
),
|
|
398
|
+
DEFAULT_EMBEDDINGS_CONFIG,
|
|
391
399
|
)
|
|
392
400
|
|
|
393
401
|
self.nlu_abstention_threshold: float = self.config[NLU_ABSTENTION_THRESHOLD]
|
rasa/core/processor.py
CHANGED
|
@@ -34,6 +34,9 @@ from rasa.dialogue_understanding.commands import (
|
|
|
34
34
|
CannotHandleCommand,
|
|
35
35
|
Command,
|
|
36
36
|
NoopCommand,
|
|
37
|
+
RestartCommand,
|
|
38
|
+
SessionEndCommand,
|
|
39
|
+
SessionStartCommand,
|
|
37
40
|
SetSlotCommand,
|
|
38
41
|
)
|
|
39
42
|
from rasa.dialogue_understanding.commands.utils import (
|
|
@@ -72,8 +75,8 @@ from rasa.shared.core.constants import (
|
|
|
72
75
|
ACTION_SESSION_START_NAME,
|
|
73
76
|
FOLLOWUP_ACTION,
|
|
74
77
|
SESSION_START_METADATA_SLOT,
|
|
78
|
+
SILENCE_TIMEOUT_SLOT,
|
|
75
79
|
SLOT_CONSECUTIVE_SILENCE_TIMEOUTS,
|
|
76
|
-
SLOT_SILENCE_TIMEOUT,
|
|
77
80
|
USER_INTENT_RESTART,
|
|
78
81
|
USER_INTENT_SILENCE_TIMEOUT,
|
|
79
82
|
SetSlotExtractor,
|
|
@@ -109,7 +112,8 @@ from rasa.utils.common import TempDirectoryPath, get_temp_dir_name
|
|
|
109
112
|
from rasa.utils.endpoints import EndpointConfig
|
|
110
113
|
|
|
111
114
|
if TYPE_CHECKING:
|
|
112
|
-
from rasa.core.
|
|
115
|
+
from rasa.core.available_endpoints import AvailableEndpoints
|
|
116
|
+
from rasa.privacy.privacy_manager import BackgroundPrivacyManager
|
|
113
117
|
|
|
114
118
|
logger = logging.getLogger(__name__)
|
|
115
119
|
structlogger = structlog.get_logger()
|
|
@@ -135,6 +139,7 @@ class MessageProcessor:
|
|
|
135
139
|
on_circuit_break: Optional[LambdaType] = None,
|
|
136
140
|
http_interpreter: Optional[RasaNLUHttpInterpreter] = None,
|
|
137
141
|
endpoints: Optional["AvailableEndpoints"] = None,
|
|
142
|
+
privacy_manager: Optional["BackgroundPrivacyManager"] = None,
|
|
138
143
|
) -> None:
|
|
139
144
|
"""Initializes a `MessageProcessor`."""
|
|
140
145
|
self.nlg = generator
|
|
@@ -164,6 +169,9 @@ class MessageProcessor:
|
|
|
164
169
|
self.model_path = Path(model_path)
|
|
165
170
|
self.domain = self.model_metadata.domain
|
|
166
171
|
self.http_interpreter = http_interpreter
|
|
172
|
+
self.privacy_manager = privacy_manager
|
|
173
|
+
if self.privacy_manager is not None:
|
|
174
|
+
self.privacy_manager.validate_sensitive_slots_in_domain(self.domain)
|
|
167
175
|
|
|
168
176
|
@staticmethod
|
|
169
177
|
def _load_model(
|
|
@@ -213,15 +221,30 @@ class MessageProcessor:
|
|
|
213
221
|
|
|
214
222
|
await self._run_prediction_loop(message.output_channel, tracker)
|
|
215
223
|
|
|
216
|
-
await self.run_anonymization_pipeline(tracker)
|
|
217
|
-
|
|
218
224
|
await self.save_tracker(tracker)
|
|
219
225
|
|
|
226
|
+
self.trigger_anonymization(tracker)
|
|
227
|
+
|
|
220
228
|
if isinstance(message.output_channel, CollectingOutputChannel):
|
|
221
229
|
return message.output_channel.messages
|
|
222
230
|
|
|
223
231
|
return None
|
|
224
232
|
|
|
233
|
+
def trigger_anonymization(self, tracker: DialogueStateTracker) -> None:
|
|
234
|
+
if self.privacy_manager is None:
|
|
235
|
+
structlogger.debug(
|
|
236
|
+
"processor.trigger_anonymization.skipping.pii_management_not_enabled",
|
|
237
|
+
)
|
|
238
|
+
return None
|
|
239
|
+
|
|
240
|
+
structlogger.info(
|
|
241
|
+
"rasa.core.processor.trigger_anonymization",
|
|
242
|
+
sender_id=tracker.sender_id,
|
|
243
|
+
event_info="Triggering anonymization for publishing anonymized "
|
|
244
|
+
"events to the event broker.",
|
|
245
|
+
)
|
|
246
|
+
return self.privacy_manager.run(tracker)
|
|
247
|
+
|
|
225
248
|
async def run_action_extract_slots(
|
|
226
249
|
self,
|
|
227
250
|
output_channel: OutputChannel,
|
|
@@ -262,26 +285,6 @@ class MessageProcessor:
|
|
|
262
285
|
|
|
263
286
|
return tracker
|
|
264
287
|
|
|
265
|
-
async def run_anonymization_pipeline(self, tracker: DialogueStateTracker) -> None:
|
|
266
|
-
"""Run the anonymization pipeline on the new tracker events.
|
|
267
|
-
|
|
268
|
-
Args:
|
|
269
|
-
tracker: A tracker representing a conversation state.
|
|
270
|
-
"""
|
|
271
|
-
anonymization_pipeline = plugin_manager().hook.get_anonymization_pipeline()
|
|
272
|
-
if anonymization_pipeline is None:
|
|
273
|
-
return None
|
|
274
|
-
|
|
275
|
-
old_tracker = await self.tracker_store.retrieve(tracker.sender_id)
|
|
276
|
-
new_events = rasa.shared.core.trackers.TrackerEventDiffEngine.event_difference(
|
|
277
|
-
old_tracker, tracker
|
|
278
|
-
)
|
|
279
|
-
|
|
280
|
-
for event in new_events:
|
|
281
|
-
body = {"sender_id": tracker.sender_id}
|
|
282
|
-
body.update(event.as_dict())
|
|
283
|
-
anonymization_pipeline.run(body)
|
|
284
|
-
|
|
285
288
|
async def predict_next_for_sender_id(
|
|
286
289
|
self, sender_id: Text
|
|
287
290
|
) -> Optional[Dict[Text, Any]]:
|
|
@@ -819,28 +822,8 @@ class MessageProcessor:
|
|
|
819
822
|
)
|
|
820
823
|
|
|
821
824
|
self._check_for_unseen_features(parse_data)
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
parse_data.get(INTENT, {}).get(INTENT_NAME_KEY)
|
|
825
|
-
!= USER_INTENT_SILENCE_TIMEOUT
|
|
826
|
-
and tracker
|
|
827
|
-
):
|
|
828
|
-
if (
|
|
829
|
-
SLOT_CONSECUTIVE_SILENCE_TIMEOUTS in tracker.slots
|
|
830
|
-
and tracker.slots[SLOT_CONSECUTIVE_SILENCE_TIMEOUTS].value != 0.0
|
|
831
|
-
):
|
|
832
|
-
tracker.update(SlotSet(SLOT_CONSECUTIVE_SILENCE_TIMEOUTS, 0.0))
|
|
833
|
-
if (
|
|
834
|
-
SLOT_SILENCE_TIMEOUT in tracker.slots
|
|
835
|
-
and tracker.slots[SLOT_SILENCE_TIMEOUT].value
|
|
836
|
-
!= tracker.slots[SLOT_SILENCE_TIMEOUT].initial_value
|
|
837
|
-
):
|
|
838
|
-
tracker.update(
|
|
839
|
-
SlotSet(
|
|
840
|
-
SLOT_SILENCE_TIMEOUT,
|
|
841
|
-
tracker.slots[SLOT_SILENCE_TIMEOUT].initial_value,
|
|
842
|
-
)
|
|
843
|
-
)
|
|
825
|
+
|
|
826
|
+
self._initialise_consecutive_silence_timeout_slots(parse_data, tracker)
|
|
844
827
|
|
|
845
828
|
return parse_data
|
|
846
829
|
|
|
@@ -880,19 +863,61 @@ class MessageProcessor:
|
|
|
880
863
|
tracker.has_coexistence_routing_slot
|
|
881
864
|
and tracker.get_slot(ROUTE_TO_CALM_SLOT) is None
|
|
882
865
|
):
|
|
883
|
-
#
|
|
884
|
-
#
|
|
885
|
-
#
|
|
886
|
-
#
|
|
866
|
+
# If we are currently not routing to either CALM or DM1:
|
|
867
|
+
# - Sticky route to CALM if there are any commands
|
|
868
|
+
# from the trigger intent parsing
|
|
869
|
+
# - Sticky route to DM1 if there are no commands present
|
|
870
|
+
route_to_calm_slot_value = self._determine_route_to_calm_slot_value(
|
|
871
|
+
nlu_adapted_commands
|
|
872
|
+
)
|
|
887
873
|
commands += [
|
|
888
874
|
SetSlotCommand(
|
|
889
|
-
ROUTE_TO_CALM_SLOT,
|
|
875
|
+
ROUTE_TO_CALM_SLOT, route_to_calm_slot_value
|
|
890
876
|
).as_dict()
|
|
891
877
|
]
|
|
892
878
|
|
|
893
879
|
parse_data[COMMANDS] = commands
|
|
894
880
|
return parse_data
|
|
895
881
|
|
|
882
|
+
def _determine_route_to_calm_slot_value(
|
|
883
|
+
self, nlu_adapted_commands: List[Dict[str, Any]]
|
|
884
|
+
) -> Optional[bool]:
|
|
885
|
+
"""Determines what value should be assigned to `ROUTE_TO_CALM_SLOT`.
|
|
886
|
+
|
|
887
|
+
Returns:
|
|
888
|
+
- True: If any command other than:
|
|
889
|
+
- SessionStartCommand
|
|
890
|
+
- SessionEndCommand
|
|
891
|
+
- RestartCommand
|
|
892
|
+
is present.
|
|
893
|
+
- None: If only ignored system commands are present.
|
|
894
|
+
- False If no commands at all.
|
|
895
|
+
"""
|
|
896
|
+
system_commands_to_ignore = [
|
|
897
|
+
SessionStartCommand.command(),
|
|
898
|
+
SessionEndCommand.command(),
|
|
899
|
+
RestartCommand.command(),
|
|
900
|
+
]
|
|
901
|
+
|
|
902
|
+
# Exclude the system commands, as it doesn't originate from the user's
|
|
903
|
+
# input intent and shouldn't influence the decision for setting
|
|
904
|
+
# ROUTE_TO_CALM_SLOT.
|
|
905
|
+
intent_triggered_commands = [
|
|
906
|
+
command
|
|
907
|
+
for command in nlu_adapted_commands
|
|
908
|
+
if command.get("command") not in system_commands_to_ignore
|
|
909
|
+
]
|
|
910
|
+
|
|
911
|
+
if len(intent_triggered_commands) > 0:
|
|
912
|
+
# There are commands other than system commands present - route to CALM
|
|
913
|
+
return True
|
|
914
|
+
elif len(nlu_adapted_commands) > 0:
|
|
915
|
+
# Only system command is present — defer routing decision
|
|
916
|
+
return None
|
|
917
|
+
else:
|
|
918
|
+
# No commands at all — route to DM1
|
|
919
|
+
return False
|
|
920
|
+
|
|
896
921
|
def _update_full_retrieval_intent(self, parse_data: Dict[Text, Any]) -> None:
|
|
897
922
|
"""Update the parse data with the full retrieval intent.
|
|
898
923
|
|
|
@@ -1577,3 +1602,31 @@ class MessageProcessor:
|
|
|
1577
1602
|
)
|
|
1578
1603
|
|
|
1579
1604
|
return tracker, validate_frames
|
|
1605
|
+
|
|
1606
|
+
@staticmethod
|
|
1607
|
+
def _initialise_consecutive_silence_timeout_slots(
|
|
1608
|
+
parse_data: Dict[str, Any],
|
|
1609
|
+
tracker: DialogueStateTracker,
|
|
1610
|
+
) -> None:
|
|
1611
|
+
# resetting timeouts variables whenever something that is not a timeout occurs
|
|
1612
|
+
if (
|
|
1613
|
+
parse_data.get(INTENT, {}).get(INTENT_NAME_KEY)
|
|
1614
|
+
!= USER_INTENT_SILENCE_TIMEOUT
|
|
1615
|
+
and tracker
|
|
1616
|
+
):
|
|
1617
|
+
if (
|
|
1618
|
+
SLOT_CONSECUTIVE_SILENCE_TIMEOUTS in tracker.slots
|
|
1619
|
+
and tracker.slots[SLOT_CONSECUTIVE_SILENCE_TIMEOUTS].value != 0.0
|
|
1620
|
+
):
|
|
1621
|
+
tracker.update(SlotSet(SLOT_CONSECUTIVE_SILENCE_TIMEOUTS, 0.0))
|
|
1622
|
+
if (
|
|
1623
|
+
SILENCE_TIMEOUT_SLOT in tracker.slots
|
|
1624
|
+
and tracker.slots[SILENCE_TIMEOUT_SLOT].value
|
|
1625
|
+
!= tracker.slots[SILENCE_TIMEOUT_SLOT].initial_value
|
|
1626
|
+
):
|
|
1627
|
+
tracker.update(
|
|
1628
|
+
SlotSet(
|
|
1629
|
+
SILENCE_TIMEOUT_SLOT,
|
|
1630
|
+
tracker.slots[SILENCE_TIMEOUT_SLOT].initial_value,
|
|
1631
|
+
)
|
|
1632
|
+
)
|