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.

Files changed (156) hide show
  1. rasa/__main__.py +0 -3
  2. rasa/api.py +1 -1
  3. rasa/cli/dialogue_understanding_test.py +1 -1
  4. rasa/cli/e2e_test.py +1 -1
  5. rasa/cli/evaluate.py +1 -1
  6. rasa/cli/export.py +1 -1
  7. rasa/cli/llm_fine_tuning.py +12 -11
  8. rasa/cli/project_templates/defaults.py +133 -0
  9. rasa/cli/run.py +1 -1
  10. rasa/cli/studio/link.py +53 -0
  11. rasa/cli/studio/pull.py +78 -0
  12. rasa/cli/studio/push.py +78 -0
  13. rasa/cli/studio/studio.py +12 -0
  14. rasa/cli/studio/upload.py +8 -0
  15. rasa/cli/train.py +1 -1
  16. rasa/cli/utils.py +1 -1
  17. rasa/cli/x.py +1 -1
  18. rasa/constants.py +2 -0
  19. rasa/core/__init__.py +0 -16
  20. rasa/core/actions/action.py +5 -1
  21. rasa/core/actions/action_repeat_bot_messages.py +18 -22
  22. rasa/core/actions/action_run_slot_rejections.py +0 -1
  23. rasa/core/agent.py +16 -1
  24. rasa/core/available_endpoints.py +146 -0
  25. rasa/core/brokers/pika.py +1 -2
  26. rasa/core/channels/botframework.py +2 -2
  27. rasa/core/channels/channel.py +2 -2
  28. rasa/core/channels/hangouts.py +8 -5
  29. rasa/core/channels/mattermost.py +1 -1
  30. rasa/core/channels/rasa_chat.py +2 -4
  31. rasa/core/channels/rest.py +5 -4
  32. rasa/core/channels/studio_chat.py +3 -2
  33. rasa/core/channels/vier_cvg.py +1 -2
  34. rasa/core/channels/voice_ready/audiocodes.py +1 -8
  35. rasa/core/channels/voice_stream/audiocodes.py +7 -4
  36. rasa/core/channels/voice_stream/genesys.py +2 -2
  37. rasa/core/channels/voice_stream/twilio_media_streams.py +10 -5
  38. rasa/core/channels/voice_stream/voice_channel.py +33 -22
  39. rasa/core/http_interpreter.py +3 -7
  40. rasa/core/jobs.py +2 -1
  41. rasa/core/nlg/contextual_response_rephraser.py +38 -11
  42. rasa/core/nlg/generator.py +0 -1
  43. rasa/core/nlg/interpolator.py +2 -3
  44. rasa/core/nlg/summarize.py +39 -5
  45. rasa/core/policies/enterprise_search_policy.py +290 -66
  46. rasa/core/policies/enterprise_search_prompt_with_relevancy_check_and_citation_template.jinja2 +63 -0
  47. rasa/core/policies/flow_policy.py +1 -1
  48. rasa/core/policies/flows/flow_executor.py +96 -17
  49. rasa/core/policies/intentless_policy.py +24 -16
  50. rasa/core/processor.py +104 -51
  51. rasa/core/run.py +33 -11
  52. rasa/core/tracker_stores/tracker_store.py +1 -1
  53. rasa/core/training/interactive.py +1 -1
  54. rasa/core/utils.py +24 -97
  55. rasa/dialogue_understanding/coexistence/intent_based_router.py +2 -1
  56. rasa/dialogue_understanding/coexistence/llm_based_router.py +8 -3
  57. rasa/dialogue_understanding/commands/can_not_handle_command.py +2 -0
  58. rasa/dialogue_understanding/commands/cancel_flow_command.py +2 -0
  59. rasa/dialogue_understanding/commands/chit_chat_answer_command.py +2 -0
  60. rasa/dialogue_understanding/commands/clarify_command.py +5 -1
  61. rasa/dialogue_understanding/commands/command_syntax_manager.py +1 -0
  62. rasa/dialogue_understanding/commands/human_handoff_command.py +2 -0
  63. rasa/dialogue_understanding/commands/knowledge_answer_command.py +2 -0
  64. rasa/dialogue_understanding/commands/repeat_bot_messages_command.py +2 -0
  65. rasa/dialogue_understanding/commands/set_slot_command.py +11 -1
  66. rasa/dialogue_understanding/commands/skip_question_command.py +2 -0
  67. rasa/dialogue_understanding/commands/start_flow_command.py +4 -0
  68. rasa/dialogue_understanding/commands/utils.py +26 -2
  69. rasa/dialogue_understanding/generator/__init__.py +7 -1
  70. rasa/dialogue_understanding/generator/command_generator.py +4 -2
  71. rasa/dialogue_understanding/generator/command_parser.py +2 -2
  72. rasa/dialogue_understanding/generator/command_parser_validator.py +63 -0
  73. rasa/dialogue_understanding/generator/constants.py +2 -2
  74. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v3_gpt_4o_2024_11_20_template.jinja2 +78 -0
  75. rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +28 -463
  76. rasa/dialogue_understanding/generator/single_step/search_ready_llm_command_generator.py +147 -0
  77. rasa/dialogue_understanding/generator/single_step/single_step_based_llm_command_generator.py +477 -0
  78. rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +8 -58
  79. rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +37 -25
  80. rasa/dialogue_understanding/patterns/domain_for_patterns.py +190 -0
  81. rasa/dialogue_understanding/processor/command_processor.py +3 -3
  82. rasa/dialogue_understanding/processor/command_processor_component.py +3 -3
  83. rasa/dialogue_understanding/stack/frames/flow_stack_frame.py +17 -4
  84. rasa/dialogue_understanding/utils.py +68 -12
  85. rasa/dialogue_understanding_test/du_test_case.py +1 -1
  86. rasa/dialogue_understanding_test/du_test_runner.py +4 -22
  87. rasa/dialogue_understanding_test/test_case_simulation/test_case_tracker_simulator.py +2 -6
  88. rasa/e2e_test/e2e_test_runner.py +1 -1
  89. rasa/engine/constants.py +1 -1
  90. rasa/engine/recipes/default_recipe.py +26 -2
  91. rasa/engine/validation.py +3 -2
  92. rasa/hooks.py +0 -28
  93. rasa/llm_fine_tuning/annotation_module.py +39 -9
  94. rasa/llm_fine_tuning/conversations.py +3 -0
  95. rasa/llm_fine_tuning/llm_data_preparation_module.py +66 -49
  96. rasa/llm_fine_tuning/paraphrasing/conversation_rephraser.py +4 -2
  97. rasa/llm_fine_tuning/paraphrasing/rephrase_validator.py +52 -44
  98. rasa/llm_fine_tuning/paraphrasing_module.py +10 -12
  99. rasa/llm_fine_tuning/storage.py +4 -4
  100. rasa/llm_fine_tuning/utils.py +63 -1
  101. rasa/model_manager/model_api.py +88 -0
  102. rasa/model_manager/trainer_service.py +4 -4
  103. rasa/plugin.py +1 -11
  104. rasa/privacy/__init__.py +0 -0
  105. rasa/privacy/constants.py +83 -0
  106. rasa/privacy/event_broker_utils.py +77 -0
  107. rasa/privacy/privacy_config.py +281 -0
  108. rasa/privacy/privacy_config_schema.json +86 -0
  109. rasa/privacy/privacy_filter.py +340 -0
  110. rasa/privacy/privacy_manager.py +576 -0
  111. rasa/server.py +23 -2
  112. rasa/shared/constants.py +6 -0
  113. rasa/shared/core/constants.py +4 -3
  114. rasa/shared/core/domain.py +7 -0
  115. rasa/shared/core/events.py +37 -7
  116. rasa/shared/core/flows/flow.py +1 -2
  117. rasa/shared/core/flows/flows_yaml_schema.json +3 -0
  118. rasa/shared/core/flows/steps/collect.py +46 -2
  119. rasa/shared/core/slots.py +28 -0
  120. rasa/shared/exceptions.py +4 -0
  121. rasa/shared/providers/_configs/azure_openai_client_config.py +4 -0
  122. rasa/shared/providers/_configs/openai_client_config.py +4 -0
  123. rasa/shared/providers/embedding/_base_litellm_embedding_client.py +3 -0
  124. rasa/shared/providers/llm/_base_litellm_client.py +5 -2
  125. rasa/shared/utils/llm.py +161 -6
  126. rasa/shared/utils/yaml.py +32 -0
  127. rasa/studio/data_handler.py +3 -3
  128. rasa/studio/download/download.py +37 -60
  129. rasa/studio/download/flows.py +23 -31
  130. rasa/studio/link.py +200 -0
  131. rasa/studio/pull.py +94 -0
  132. rasa/studio/push.py +131 -0
  133. rasa/studio/upload.py +117 -67
  134. rasa/telemetry.py +82 -25
  135. rasa/tracing/config.py +3 -4
  136. rasa/tracing/constants.py +19 -1
  137. rasa/tracing/instrumentation/attribute_extractors.py +10 -2
  138. rasa/tracing/instrumentation/instrumentation.py +53 -2
  139. rasa/tracing/instrumentation/metrics.py +98 -15
  140. rasa/tracing/metric_instrument_provider.py +75 -3
  141. rasa/utils/common.py +1 -27
  142. rasa/utils/log_utils.py +1 -45
  143. rasa/validator.py +2 -8
  144. rasa/version.py +1 -1
  145. {rasa_pro-3.13.0.dev20250612.dist-info → rasa_pro-3.13.0.dev20250613.dist-info}/METADATA +5 -6
  146. {rasa_pro-3.13.0.dev20250612.dist-info → rasa_pro-3.13.0.dev20250613.dist-info}/RECORD +149 -135
  147. rasa/anonymization/__init__.py +0 -2
  148. rasa/anonymization/anonymisation_rule_yaml_reader.py +0 -91
  149. rasa/anonymization/anonymization_pipeline.py +0 -286
  150. rasa/anonymization/anonymization_rule_executor.py +0 -266
  151. rasa/anonymization/anonymization_rule_orchestrator.py +0 -119
  152. rasa/anonymization/schemas/config.yml +0 -47
  153. rasa/anonymization/utils.py +0 -118
  154. {rasa_pro-3.13.0.dev20250612.dist-info → rasa_pro-3.13.0.dev20250613.dist-info}/NOTICE +0 -0
  155. {rasa_pro-3.13.0.dev20250612.dist-info → rasa_pro-3.13.0.dev20250613.dist-info}/WHEEL +0 -0
  156. {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
- tracker=tracker,
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
- tracker=tracker,
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] = events_for_collect_step_execution(step, tracker)
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
- "temperature": 0.0,
115
- "max_tokens": DEFAULT_OPENAI_MAX_GENERATED_TOKENS,
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
- "model": DEFAULT_OPENAI_EMBEDDING_MODEL_NAME,
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] = resolve_model_client_config(
385
- self.config.get(LLM_CONFIG_KEY), IntentlessPolicy.__name__
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] = resolve_model_client_config(
390
- self.config.get(EMBEDDINGS_CONFIG_KEY), IntentlessPolicy.__name__
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.utils import AvailableEndpoints
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
- # resetting timeouts variables whenever something that is not a timeout occurs
823
- if (
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
- # if we are currently not routing to either CALM or dm1
884
- # we make a sticky routing to CALM if there are any commands
885
- # from the trigger intent parsing
886
- # or a sticky routing to dm1 if there are no commands
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, len(nlu_adapted_commands) > 0
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
+ )