rasa-pro 3.12.0.dev12__py3-none-any.whl → 3.12.0rc1__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/anonymization/anonymization_rule_executor.py +16 -10
- rasa/cli/data.py +16 -0
- rasa/cli/inspect.py +20 -1
- rasa/cli/project_templates/calm/config.yml +2 -2
- rasa/cli/project_templates/calm/endpoints.yml +2 -2
- rasa/cli/shell.py +3 -3
- rasa/cli/utils.py +12 -0
- rasa/core/actions/action.py +99 -193
- rasa/core/actions/action_handle_digressions.py +142 -0
- rasa/core/actions/action_run_slot_rejections.py +16 -4
- rasa/core/actions/forms.py +10 -5
- rasa/core/channels/__init__.py +4 -0
- rasa/core/channels/studio_chat.py +19 -0
- rasa/core/channels/telegram.py +42 -24
- rasa/core/channels/voice_ready/audiocodes.py +42 -23
- rasa/core/channels/voice_ready/utils.py +1 -1
- rasa/core/channels/voice_stream/asr/asr_engine.py +10 -4
- rasa/core/channels/voice_stream/asr/azure.py +14 -1
- rasa/core/channels/voice_stream/asr/deepgram.py +20 -4
- rasa/core/channels/voice_stream/audiocodes.py +264 -0
- rasa/core/channels/voice_stream/browser_audio.py +5 -1
- rasa/core/channels/voice_stream/call_state.py +10 -1
- rasa/core/channels/voice_stream/genesys.py +335 -0
- rasa/core/channels/voice_stream/tts/azure.py +11 -2
- rasa/core/channels/voice_stream/tts/cartesia.py +29 -10
- rasa/core/channels/voice_stream/twilio_media_streams.py +2 -1
- rasa/core/channels/voice_stream/voice_channel.py +25 -3
- rasa/core/constants.py +2 -0
- rasa/core/migrate.py +2 -2
- rasa/core/nlg/contextual_response_rephraser.py +18 -1
- rasa/core/nlg/generator.py +83 -15
- rasa/core/nlg/response.py +6 -3
- rasa/core/nlg/translate.py +55 -0
- rasa/core/policies/enterprise_search_prompt_with_citation_template.jinja2 +1 -1
- rasa/core/policies/flows/flow_executor.py +47 -46
- rasa/core/processor.py +72 -9
- rasa/core/run.py +4 -3
- rasa/dialogue_understanding/commands/can_not_handle_command.py +20 -2
- rasa/dialogue_understanding/commands/cancel_flow_command.py +80 -4
- rasa/dialogue_understanding/commands/change_flow_command.py +20 -2
- rasa/dialogue_understanding/commands/chit_chat_answer_command.py +20 -2
- rasa/dialogue_understanding/commands/clarify_command.py +29 -3
- rasa/dialogue_understanding/commands/command.py +1 -16
- rasa/dialogue_understanding/commands/command_syntax_manager.py +55 -0
- rasa/dialogue_understanding/commands/correct_slots_command.py +11 -2
- rasa/dialogue_understanding/commands/handle_digressions_command.py +150 -0
- rasa/dialogue_understanding/commands/human_handoff_command.py +20 -2
- rasa/dialogue_understanding/commands/knowledge_answer_command.py +20 -2
- rasa/dialogue_understanding/commands/prompt_command.py +94 -0
- rasa/dialogue_understanding/commands/repeat_bot_messages_command.py +20 -2
- rasa/dialogue_understanding/commands/set_slot_command.py +29 -15
- rasa/dialogue_understanding/commands/skip_question_command.py +20 -2
- rasa/dialogue_understanding/commands/start_flow_command.py +61 -2
- rasa/dialogue_understanding/commands/utils.py +98 -4
- rasa/dialogue_understanding/constants.py +1 -0
- rasa/dialogue_understanding/generator/__init__.py +2 -0
- rasa/dialogue_understanding/generator/command_generator.py +110 -73
- rasa/dialogue_understanding/generator/command_parser.py +16 -13
- rasa/dialogue_understanding/generator/constants.py +3 -0
- rasa/dialogue_understanding/generator/llm_based_command_generator.py +170 -5
- rasa/dialogue_understanding/generator/llm_command_generator.py +5 -3
- rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +26 -4
- rasa/dialogue_understanding/generator/nlu_command_adapter.py +44 -3
- rasa/dialogue_understanding/generator/prompt_templates/__init__.py +0 -0
- rasa/dialogue_understanding/generator/prompt_templates/command_prompt_template.jinja2 +60 -0
- rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_claude_3_5_sonnet_20240620_template.jinja2 +77 -0
- rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_default.jinja2 +68 -0
- rasa/dialogue_understanding/generator/{single_step/command_prompt_template.jinja2 → prompt_templates/command_prompt_v2_gpt_4o_2024_11_20_template.jinja2} +1 -1
- rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +460 -0
- rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +12 -318
- rasa/dialogue_understanding/generator/utils.py +32 -1
- rasa/dialogue_understanding/patterns/collect_information.py +1 -1
- rasa/dialogue_understanding/patterns/correction.py +13 -1
- rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +78 -2
- rasa/dialogue_understanding/patterns/handle_digressions.py +81 -0
- rasa/dialogue_understanding/patterns/validate_slot.py +65 -0
- rasa/dialogue_understanding/processor/command_processor.py +154 -28
- rasa/dialogue_understanding/utils.py +31 -0
- rasa/dialogue_understanding_test/README.md +50 -0
- rasa/dialogue_understanding_test/du_test_case.py +28 -8
- rasa/dialogue_understanding_test/du_test_result.py +13 -9
- rasa/dialogue_understanding_test/io.py +14 -0
- rasa/dialogue_understanding_test/test_case_simulation/test_case_tracker_simulator.py +3 -3
- rasa/e2e_test/utils/io.py +0 -37
- rasa/engine/graph.py +1 -0
- rasa/engine/language.py +140 -0
- rasa/engine/recipes/config_files/default_config.yml +4 -0
- rasa/engine/recipes/default_recipe.py +2 -0
- rasa/engine/recipes/graph_recipe.py +2 -0
- rasa/engine/storage/local_model_storage.py +1 -0
- rasa/engine/storage/storage.py +4 -1
- rasa/model_manager/runner_service.py +7 -4
- rasa/model_manager/socket_bridge.py +7 -6
- rasa/model_manager/warm_rasa_process.py +0 -1
- rasa/model_training.py +24 -27
- rasa/shared/constants.py +15 -13
- rasa/shared/core/constants.py +30 -3
- rasa/shared/core/domain.py +13 -20
- rasa/shared/core/events.py +13 -2
- rasa/shared/core/flows/constants.py +11 -0
- rasa/shared/core/flows/flow.py +100 -19
- rasa/shared/core/flows/flows_yaml_schema.json +69 -3
- rasa/shared/core/flows/steps/collect.py +19 -37
- rasa/shared/core/flows/utils.py +43 -4
- rasa/shared/core/flows/validation.py +1 -1
- rasa/shared/core/slot_mappings.py +350 -111
- rasa/shared/core/slots.py +154 -3
- rasa/shared/core/trackers.py +77 -2
- rasa/shared/importers/importer.py +50 -2
- rasa/shared/nlu/constants.py +1 -0
- rasa/shared/nlu/training_data/schemas/responses.yml +19 -12
- rasa/shared/providers/_configs/azure_entra_id_config.py +541 -0
- rasa/shared/providers/_configs/azure_openai_client_config.py +138 -3
- rasa/shared/providers/_configs/client_config.py +3 -1
- rasa/shared/providers/_configs/default_litellm_client_config.py +3 -1
- rasa/shared/providers/_configs/huggingface_local_embedding_client_config.py +3 -1
- rasa/shared/providers/_configs/litellm_router_client_config.py +3 -1
- rasa/shared/providers/_configs/model_group_config.py +4 -2
- rasa/shared/providers/_configs/oauth_config.py +33 -0
- rasa/shared/providers/_configs/openai_client_config.py +3 -1
- rasa/shared/providers/_configs/rasa_llm_client_config.py +3 -1
- rasa/shared/providers/_configs/self_hosted_llm_client_config.py +3 -1
- rasa/shared/providers/constants.py +6 -0
- rasa/shared/providers/embedding/azure_openai_embedding_client.py +28 -3
- rasa/shared/providers/embedding/litellm_router_embedding_client.py +3 -1
- rasa/shared/providers/llm/_base_litellm_client.py +42 -17
- rasa/shared/providers/llm/azure_openai_llm_client.py +81 -25
- rasa/shared/providers/llm/default_litellm_llm_client.py +3 -1
- rasa/shared/providers/llm/litellm_router_llm_client.py +29 -8
- rasa/shared/providers/llm/llm_client.py +23 -7
- rasa/shared/providers/llm/openai_llm_client.py +9 -3
- rasa/shared/providers/llm/rasa_llm_client.py +11 -2
- rasa/shared/providers/llm/self_hosted_llm_client.py +30 -11
- rasa/shared/providers/router/_base_litellm_router_client.py +3 -1
- rasa/shared/providers/router/router_client.py +3 -1
- rasa/shared/utils/constants.py +3 -0
- rasa/shared/utils/llm.py +31 -8
- rasa/shared/utils/pykwalify_extensions.py +24 -0
- rasa/shared/utils/schemas/domain.yml +26 -1
- rasa/telemetry.py +45 -14
- rasa/tracing/config.py +2 -0
- rasa/tracing/constants.py +12 -0
- rasa/tracing/instrumentation/instrumentation.py +36 -0
- rasa/tracing/instrumentation/metrics.py +41 -0
- rasa/tracing/metric_instrument_provider.py +40 -0
- rasa/utils/common.py +0 -1
- rasa/validator.py +561 -89
- rasa/version.py +1 -1
- {rasa_pro-3.12.0.dev12.dist-info → rasa_pro-3.12.0rc1.dist-info}/METADATA +2 -1
- {rasa_pro-3.12.0.dev12.dist-info → rasa_pro-3.12.0rc1.dist-info}/RECORD +153 -134
- {rasa_pro-3.12.0.dev12.dist-info → rasa_pro-3.12.0rc1.dist-info}/NOTICE +0 -0
- {rasa_pro-3.12.0.dev12.dist-info → rasa_pro-3.12.0rc1.dist-info}/WHEEL +0 -0
- {rasa_pro-3.12.0.dev12.dist-info → rasa_pro-3.12.0rc1.dist-info}/entry_points.txt +0 -0
rasa/shared/core/domain.py
CHANGED
|
@@ -46,10 +46,8 @@ from rasa.shared.constants import (
|
|
|
46
46
|
)
|
|
47
47
|
from rasa.shared.core.constants import (
|
|
48
48
|
ACTION_SHOULD_SEND_DOMAIN,
|
|
49
|
-
|
|
49
|
+
KEY_MAPPING_TYPE,
|
|
50
50
|
KNOWLEDGE_BASE_SLOT_NAMES,
|
|
51
|
-
MAPPING_CONDITIONS,
|
|
52
|
-
MAPPING_TYPE,
|
|
53
51
|
SLOT_MAPPINGS,
|
|
54
52
|
SlotMappingType,
|
|
55
53
|
)
|
|
@@ -290,8 +288,6 @@ class Domain:
|
|
|
290
288
|
responses = data.get(KEY_RESPONSES, {})
|
|
291
289
|
|
|
292
290
|
domain_slots = data.get(KEY_SLOTS, {})
|
|
293
|
-
if domain_slots:
|
|
294
|
-
rasa.shared.core.slot_mappings.validate_slot_mappings(domain_slots)
|
|
295
291
|
slots = cls.collect_slots(domain_slots)
|
|
296
292
|
domain_actions = data.get(KEY_ACTIONS, [])
|
|
297
293
|
actions = cls._collect_action_names(domain_actions)
|
|
@@ -596,7 +592,7 @@ class Domain:
|
|
|
596
592
|
),
|
|
597
593
|
)
|
|
598
594
|
slot_dict[slot_name][SLOT_MAPPINGS] = [
|
|
599
|
-
{
|
|
595
|
+
{KEY_MAPPING_TYPE: SlotMappingType.FROM_LLM.value}
|
|
600
596
|
]
|
|
601
597
|
|
|
602
598
|
slot = slot_class(slot_name, **slot_dict[slot_name])
|
|
@@ -1569,21 +1565,18 @@ class Domain:
|
|
|
1569
1565
|
matching_entities = []
|
|
1570
1566
|
|
|
1571
1567
|
for mapping in slot.mappings:
|
|
1572
|
-
mapping_conditions = mapping.
|
|
1573
|
-
if mapping
|
|
1568
|
+
mapping_conditions = mapping.conditions
|
|
1569
|
+
if mapping.type != SlotMappingType.FROM_ENTITY or (
|
|
1574
1570
|
mapping_conditions
|
|
1575
|
-
and mapping_conditions[0].
|
|
1571
|
+
and mapping_conditions[0].active_loop is not None
|
|
1576
1572
|
):
|
|
1577
1573
|
continue
|
|
1578
1574
|
|
|
1579
1575
|
for entity in entities:
|
|
1580
1576
|
if (
|
|
1581
|
-
entity.get(ENTITY_ATTRIBUTE_TYPE)
|
|
1582
|
-
|
|
1583
|
-
and entity.get(
|
|
1584
|
-
== mapping.get(ENTITY_ATTRIBUTE_ROLE)
|
|
1585
|
-
and entity.get(ENTITY_ATTRIBUTE_GROUP)
|
|
1586
|
-
== mapping.get(ENTITY_ATTRIBUTE_GROUP)
|
|
1577
|
+
entity.get(ENTITY_ATTRIBUTE_TYPE) == mapping.entity
|
|
1578
|
+
and entity.get(ENTITY_ATTRIBUTE_ROLE) == mapping.role
|
|
1579
|
+
and entity.get(ENTITY_ATTRIBUTE_GROUP) == mapping.group
|
|
1587
1580
|
):
|
|
1588
1581
|
matching_entities.append(entity.get("value"))
|
|
1589
1582
|
|
|
@@ -2015,19 +2008,19 @@ class Domain:
|
|
|
2015
2008
|
is the total number of mappings which have conditions attached.
|
|
2016
2009
|
"""
|
|
2017
2010
|
total_mappings = 0
|
|
2018
|
-
|
|
2011
|
+
controlled_mappings = 0
|
|
2019
2012
|
conditional_mappings = 0
|
|
2020
2013
|
|
|
2021
2014
|
for slot in self.slots:
|
|
2022
2015
|
total_mappings += len(slot.mappings)
|
|
2023
2016
|
for mapping in slot.mappings:
|
|
2024
|
-
if mapping
|
|
2025
|
-
|
|
2017
|
+
if mapping.type == SlotMappingType.CONTROLLED:
|
|
2018
|
+
controlled_mappings += 1
|
|
2026
2019
|
|
|
2027
|
-
if
|
|
2020
|
+
if mapping.conditions:
|
|
2028
2021
|
conditional_mappings += 1
|
|
2029
2022
|
|
|
2030
|
-
return
|
|
2023
|
+
return total_mappings, controlled_mappings, conditional_mappings
|
|
2031
2024
|
|
|
2032
2025
|
def does_custom_action_explicitly_need_domain(self, action_name: Text) -> bool:
|
|
2033
2026
|
"""Assert if action has explicitly stated that it needs domain.
|
rasa/shared/core/events.py
CHANGED
|
@@ -1032,6 +1032,7 @@ class SlotSet(Event):
|
|
|
1032
1032
|
value: Optional[Any] = None,
|
|
1033
1033
|
timestamp: Optional[float] = None,
|
|
1034
1034
|
metadata: Optional[Dict[Text, Any]] = None,
|
|
1035
|
+
filled_by: Optional[str] = None,
|
|
1035
1036
|
) -> None:
|
|
1036
1037
|
"""Creates event to set slot.
|
|
1037
1038
|
|
|
@@ -1043,6 +1044,7 @@ class SlotSet(Event):
|
|
|
1043
1044
|
"""
|
|
1044
1045
|
self.key = key
|
|
1045
1046
|
self.value = value
|
|
1047
|
+
self._filled_by = filled_by
|
|
1046
1048
|
super().__init__(timestamp, metadata)
|
|
1047
1049
|
|
|
1048
1050
|
def __repr__(self) -> Text:
|
|
@@ -1060,6 +1062,14 @@ class SlotSet(Event):
|
|
|
1060
1062
|
|
|
1061
1063
|
return (self.key, self.value) == (other.key, other.value)
|
|
1062
1064
|
|
|
1065
|
+
@property
|
|
1066
|
+
def filled_by(self) -> Optional[str]:
|
|
1067
|
+
return self._filled_by
|
|
1068
|
+
|
|
1069
|
+
@filled_by.setter
|
|
1070
|
+
def filled_by(self, value: str) -> None:
|
|
1071
|
+
self._filled_by = value
|
|
1072
|
+
|
|
1063
1073
|
def as_story_string(self) -> Text:
|
|
1064
1074
|
"""Returns text representation of event."""
|
|
1065
1075
|
props = json.dumps({self.key: self.value}, ensure_ascii=False)
|
|
@@ -1081,7 +1091,7 @@ class SlotSet(Event):
|
|
|
1081
1091
|
def as_dict(self) -> Dict[Text, Any]:
|
|
1082
1092
|
"""Returns serialized event."""
|
|
1083
1093
|
d = super().as_dict()
|
|
1084
|
-
d.update({"name": self.key, "value": self.value})
|
|
1094
|
+
d.update({"name": self.key, "value": self.value, "filled_by": self.filled_by})
|
|
1085
1095
|
return d
|
|
1086
1096
|
|
|
1087
1097
|
@classmethod
|
|
@@ -1092,13 +1102,14 @@ class SlotSet(Event):
|
|
|
1092
1102
|
parameters.get("value"),
|
|
1093
1103
|
parameters.get("timestamp"),
|
|
1094
1104
|
parameters.get("metadata"),
|
|
1105
|
+
filled_by=parameters.get("filled_by"),
|
|
1095
1106
|
)
|
|
1096
1107
|
except KeyError as e:
|
|
1097
1108
|
raise ValueError(f"Failed to parse set slot event. {e}")
|
|
1098
1109
|
|
|
1099
1110
|
def apply_to(self, tracker: "DialogueStateTracker") -> None:
|
|
1100
1111
|
"""Applies event to current conversation state."""
|
|
1101
|
-
tracker._set_slot(self.key, self.value)
|
|
1112
|
+
tracker._set_slot(self.key, self.value, self.filled_by)
|
|
1102
1113
|
|
|
1103
1114
|
|
|
1104
1115
|
class Restarted(AlwaysEqualEventMixin):
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
KEY_ID = "id"
|
|
2
|
+
KEY_STEPS = "steps"
|
|
3
|
+
KEY_NAME = "name"
|
|
4
|
+
KEY_DESCRIPTION = "description"
|
|
5
|
+
KEY_IF = "if"
|
|
6
|
+
KEY_ALWAYS_INCLUDE_IN_PROMPT = "always_include_in_prompt"
|
|
7
|
+
KEY_NLU_TRIGGER = "nlu_trigger"
|
|
8
|
+
KEY_FILE_PATH = "file_path"
|
|
9
|
+
KEY_PERSISTED_SLOTS = "persisted_slots"
|
|
10
|
+
KEY_RUN_PATTERN_COMPLETED = "run_pattern_completed"
|
|
11
|
+
KEY_TRANSLATION = "translation"
|
rasa/shared/core/flows/flow.py
CHANGED
|
@@ -7,10 +7,29 @@ from pathlib import Path
|
|
|
7
7
|
from typing import Any, Dict, List, Optional, Set, Text, Union
|
|
8
8
|
|
|
9
9
|
import structlog
|
|
10
|
+
from pydantic import BaseModel
|
|
10
11
|
from pypred import Predicate
|
|
11
12
|
|
|
12
13
|
import rasa.shared.utils.io
|
|
14
|
+
from rasa.engine.language import Language
|
|
13
15
|
from rasa.shared.constants import RASA_DEFAULT_FLOW_PATTERN_PREFIX
|
|
16
|
+
from rasa.shared.core.constants import (
|
|
17
|
+
KEY_ASK_CONFIRM_DIGRESSIONS,
|
|
18
|
+
KEY_BLOCK_DIGRESSIONS,
|
|
19
|
+
)
|
|
20
|
+
from rasa.shared.core.flows.constants import (
|
|
21
|
+
KEY_ALWAYS_INCLUDE_IN_PROMPT,
|
|
22
|
+
KEY_DESCRIPTION,
|
|
23
|
+
KEY_FILE_PATH,
|
|
24
|
+
KEY_ID,
|
|
25
|
+
KEY_IF,
|
|
26
|
+
KEY_NAME,
|
|
27
|
+
KEY_NLU_TRIGGER,
|
|
28
|
+
KEY_PERSISTED_SLOTS,
|
|
29
|
+
KEY_RUN_PATTERN_COMPLETED,
|
|
30
|
+
KEY_STEPS,
|
|
31
|
+
KEY_TRANSLATION,
|
|
32
|
+
)
|
|
14
33
|
from rasa.shared.core.flows.flow_path import FlowPath, FlowPathsList, PathNode
|
|
15
34
|
from rasa.shared.core.flows.flow_step import FlowStep
|
|
16
35
|
from rasa.shared.core.flows.flow_step_links import (
|
|
@@ -33,11 +52,22 @@ from rasa.shared.core.flows.steps.constants import (
|
|
|
33
52
|
START_STEP,
|
|
34
53
|
)
|
|
35
54
|
from rasa.shared.core.flows.steps.continuation import ContinueFlowStep
|
|
55
|
+
from rasa.shared.core.flows.utils import extract_digression_prop
|
|
36
56
|
from rasa.shared.core.slots import Slot
|
|
37
57
|
|
|
38
58
|
structlogger = structlog.get_logger()
|
|
39
59
|
|
|
40
60
|
|
|
61
|
+
class FlowLanguageTranslation(BaseModel):
|
|
62
|
+
"""Represents the translation of the flow properties in a specific language."""
|
|
63
|
+
|
|
64
|
+
name: str
|
|
65
|
+
"""The human-readable name of the flow."""
|
|
66
|
+
|
|
67
|
+
class Config:
|
|
68
|
+
extra = "ignore"
|
|
69
|
+
|
|
70
|
+
|
|
41
71
|
@dataclass
|
|
42
72
|
class Flow:
|
|
43
73
|
"""Represents the configuration of a flow."""
|
|
@@ -48,6 +78,8 @@ class Flow:
|
|
|
48
78
|
"""The human-readable name of the flow."""
|
|
49
79
|
description: Optional[Text] = None
|
|
50
80
|
"""The description of the flow."""
|
|
81
|
+
translation: Dict[Text, FlowLanguageTranslation] = field(default_factory=dict)
|
|
82
|
+
"""The translation of the flow properties in different languages."""
|
|
51
83
|
guard_condition: Optional[Text] = None
|
|
52
84
|
"""The condition that needs to be fulfilled for the flow to be startable."""
|
|
53
85
|
step_sequence: FlowStepSequence = field(default_factory=FlowStepSequence.empty)
|
|
@@ -62,6 +94,12 @@ class Flow:
|
|
|
62
94
|
"""The path to the file where the flow is stored."""
|
|
63
95
|
persisted_slots: List[str] = field(default_factory=list)
|
|
64
96
|
"""The list of slots that should be persisted after the flow ends."""
|
|
97
|
+
ask_confirm_digressions: List[str] = field(default_factory=list)
|
|
98
|
+
"""The flow ids for which the assistant should ask for confirmation."""
|
|
99
|
+
block_digressions: List[str] = field(default_factory=list)
|
|
100
|
+
"""The flow ids that the assistant should block from digressing to."""
|
|
101
|
+
run_pattern_completed: bool = True
|
|
102
|
+
"""Whether the pattern_completed flow should be run after the flow ends."""
|
|
65
103
|
|
|
66
104
|
@staticmethod
|
|
67
105
|
def from_json(
|
|
@@ -79,6 +117,8 @@ class Flow:
|
|
|
79
117
|
Returns:
|
|
80
118
|
A Flow object.
|
|
81
119
|
"""
|
|
120
|
+
from rasa.shared.core.flows.utils import extract_translations
|
|
121
|
+
|
|
82
122
|
step_sequence = FlowStepSequence.from_json(flow_id, data.get("steps"))
|
|
83
123
|
nlu_triggers = NLUTriggers.from_json(data.get("nlu_trigger"))
|
|
84
124
|
|
|
@@ -87,17 +127,25 @@ class Flow:
|
|
|
87
127
|
|
|
88
128
|
return Flow(
|
|
89
129
|
id=flow_id,
|
|
90
|
-
custom_name=data.get(
|
|
91
|
-
description=data.get(
|
|
92
|
-
always_include_in_prompt=data.get(
|
|
93
|
-
# str or bool are permitted in the flow schema but internally we want a str
|
|
94
|
-
guard_condition=str(data[
|
|
130
|
+
custom_name=data.get(KEY_NAME),
|
|
131
|
+
description=data.get(KEY_DESCRIPTION),
|
|
132
|
+
always_include_in_prompt=data.get(KEY_ALWAYS_INCLUDE_IN_PROMPT),
|
|
133
|
+
# str or bool are permitted in the flow schema, but internally we want a str
|
|
134
|
+
guard_condition=str(data[KEY_IF]) if KEY_IF in data else None,
|
|
95
135
|
step_sequence=Flow.resolve_default_ids(step_sequence),
|
|
96
136
|
nlu_triggers=nlu_triggers,
|
|
97
137
|
# If we are reading the flows in after training the file_path is part of
|
|
98
138
|
# data. When the model is trained, take the provided file_path.
|
|
99
|
-
file_path=data.get(
|
|
100
|
-
persisted_slots=data.get(
|
|
139
|
+
file_path=data.get(KEY_FILE_PATH) if KEY_FILE_PATH in data else file_path,
|
|
140
|
+
persisted_slots=data.get(KEY_PERSISTED_SLOTS, []),
|
|
141
|
+
ask_confirm_digressions=extract_digression_prop(
|
|
142
|
+
KEY_ASK_CONFIRM_DIGRESSIONS, data
|
|
143
|
+
),
|
|
144
|
+
block_digressions=extract_digression_prop(KEY_BLOCK_DIGRESSIONS, data),
|
|
145
|
+
run_pattern_completed=data.get(KEY_RUN_PATTERN_COMPLETED, True),
|
|
146
|
+
translation=extract_translations(
|
|
147
|
+
translation_data=data.get(KEY_TRANSLATION, {})
|
|
148
|
+
),
|
|
101
149
|
)
|
|
102
150
|
|
|
103
151
|
def get_full_name(self) -> str:
|
|
@@ -155,29 +203,62 @@ class Flow:
|
|
|
155
203
|
The Flow object as serialized data.
|
|
156
204
|
"""
|
|
157
205
|
data: Dict[Text, Any] = {
|
|
158
|
-
|
|
159
|
-
|
|
206
|
+
KEY_ID: self.id,
|
|
207
|
+
KEY_STEPS: self.step_sequence.as_json(),
|
|
160
208
|
}
|
|
161
209
|
if self.custom_name is not None:
|
|
162
|
-
data[
|
|
210
|
+
data[KEY_NAME] = self.custom_name
|
|
163
211
|
if self.description is not None:
|
|
164
|
-
data[
|
|
212
|
+
data[KEY_DESCRIPTION] = self.description
|
|
165
213
|
if self.guard_condition is not None:
|
|
166
|
-
data[
|
|
214
|
+
data[KEY_IF] = self.guard_condition
|
|
167
215
|
if self.always_include_in_prompt is not None:
|
|
168
|
-
data[
|
|
216
|
+
data[KEY_ALWAYS_INCLUDE_IN_PROMPT] = self.always_include_in_prompt
|
|
169
217
|
if self.nlu_triggers:
|
|
170
|
-
data[
|
|
218
|
+
data[KEY_NLU_TRIGGER] = self.nlu_triggers.as_json()
|
|
171
219
|
if self.file_path:
|
|
172
|
-
data[
|
|
220
|
+
data[KEY_FILE_PATH] = self.file_path
|
|
173
221
|
if self.persisted_slots:
|
|
174
|
-
data[
|
|
222
|
+
data[KEY_PERSISTED_SLOTS] = self.persisted_slots
|
|
223
|
+
if self.ask_confirm_digressions:
|
|
224
|
+
data[KEY_ASK_CONFIRM_DIGRESSIONS] = self.ask_confirm_digressions
|
|
225
|
+
if self.block_digressions:
|
|
226
|
+
data[KEY_BLOCK_DIGRESSIONS] = self.block_digressions
|
|
227
|
+
if self.run_pattern_completed is not None:
|
|
228
|
+
data["run_pattern_completed"] = self.run_pattern_completed
|
|
229
|
+
if self.translation:
|
|
230
|
+
data[KEY_TRANSLATION] = {
|
|
231
|
+
language_code: translation.dict()
|
|
232
|
+
for language_code, translation in self.translation.items()
|
|
233
|
+
}
|
|
175
234
|
|
|
176
235
|
return data
|
|
177
236
|
|
|
178
|
-
def
|
|
179
|
-
"""Returns the
|
|
180
|
-
|
|
237
|
+
def localized_name(self, language: Optional[Language] = None) -> Optional[Text]:
|
|
238
|
+
"""Returns the language specific flow name or None.
|
|
239
|
+
|
|
240
|
+
Args:
|
|
241
|
+
language: Preferred language code.
|
|
242
|
+
|
|
243
|
+
Returns:
|
|
244
|
+
Flow name in the specified language or None.
|
|
245
|
+
"""
|
|
246
|
+
language_code = language.code if language else None
|
|
247
|
+
translation = self.translation.get(language_code)
|
|
248
|
+
return translation.name if translation else None
|
|
249
|
+
|
|
250
|
+
def readable_name(self, language: Optional[Language] = None) -> str:
|
|
251
|
+
"""
|
|
252
|
+
Returns the flow's name in the specified language if available; otherwise
|
|
253
|
+
falls back to the flow's name, and finally the flow's ID.
|
|
254
|
+
|
|
255
|
+
Args:
|
|
256
|
+
language: Preferred language code.
|
|
257
|
+
|
|
258
|
+
Returns:
|
|
259
|
+
string: the localized name, the default name, or the flow's ID.
|
|
260
|
+
"""
|
|
261
|
+
return self.localized_name(language) or self.name or self.id
|
|
181
262
|
|
|
182
263
|
def step_by_id(self, step_id: Optional[Text]) -> Optional[FlowStep]:
|
|
183
264
|
"""Returns the step with the given id."""
|
|
@@ -217,6 +217,12 @@
|
|
|
217
217
|
"reset_after_flow_ends": {
|
|
218
218
|
"type": "boolean"
|
|
219
219
|
},
|
|
220
|
+
"ask_confirm_digressions": {
|
|
221
|
+
"$ref": "#/$defs/ask_confirm_digressions"
|
|
222
|
+
},
|
|
223
|
+
"block_digressions": {
|
|
224
|
+
"$ref": "#/$defs/block_digressions"
|
|
225
|
+
},
|
|
220
226
|
"utter": {
|
|
221
227
|
"type": "string"
|
|
222
228
|
},
|
|
@@ -247,6 +253,32 @@
|
|
|
247
253
|
}
|
|
248
254
|
}
|
|
249
255
|
},
|
|
256
|
+
"ask_confirm_digressions": {
|
|
257
|
+
"oneOf": [
|
|
258
|
+
{
|
|
259
|
+
"type": "boolean"
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
"type": "array",
|
|
263
|
+
"items": {
|
|
264
|
+
"type": "string"
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
]
|
|
268
|
+
},
|
|
269
|
+
"block_digressions": {
|
|
270
|
+
"oneOf": [
|
|
271
|
+
{
|
|
272
|
+
"type": "boolean"
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
"type": "array",
|
|
276
|
+
"items": {
|
|
277
|
+
"type": "string"
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
]
|
|
281
|
+
},
|
|
250
282
|
"flow": {
|
|
251
283
|
"required": [
|
|
252
284
|
"steps",
|
|
@@ -256,9 +288,37 @@
|
|
|
256
288
|
"additionalProperties": false,
|
|
257
289
|
"schema_name": "dictionary with flow properties",
|
|
258
290
|
"properties": {
|
|
291
|
+
"name": {
|
|
292
|
+
"type": "string"
|
|
293
|
+
},
|
|
259
294
|
"description": {
|
|
260
295
|
"type": "string"
|
|
261
296
|
},
|
|
297
|
+
"translation": {
|
|
298
|
+
"type": "object",
|
|
299
|
+
"schema_name": "flow translation mapping",
|
|
300
|
+
"properties": {
|
|
301
|
+
"metadata": {
|
|
302
|
+
"type": "object"
|
|
303
|
+
}
|
|
304
|
+
},
|
|
305
|
+
"patternProperties": {
|
|
306
|
+
"^(?!metadata$).+": {
|
|
307
|
+
"type": "object",
|
|
308
|
+
"additionalProperties": false,
|
|
309
|
+
"required": [
|
|
310
|
+
"name"
|
|
311
|
+
],
|
|
312
|
+
"properties": {
|
|
313
|
+
"name": {
|
|
314
|
+
"type": "string"
|
|
315
|
+
},
|
|
316
|
+
"metadata": {}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
},
|
|
320
|
+
"additionalProperties": false
|
|
321
|
+
},
|
|
262
322
|
"if": {
|
|
263
323
|
"type": [
|
|
264
324
|
"string",
|
|
@@ -268,9 +328,6 @@
|
|
|
268
328
|
"always_include_in_prompt": {
|
|
269
329
|
"type": "boolean"
|
|
270
330
|
},
|
|
271
|
-
"name": {
|
|
272
|
-
"type": "string"
|
|
273
|
-
},
|
|
274
331
|
"nlu_trigger": {
|
|
275
332
|
"$ref": "#/$defs/nlu_trigger"
|
|
276
333
|
},
|
|
@@ -282,6 +339,15 @@
|
|
|
282
339
|
},
|
|
283
340
|
"persisted_slots": {
|
|
284
341
|
"$ref": "#/$defs/persisted_slots"
|
|
342
|
+
},
|
|
343
|
+
"ask_confirm_digressions": {
|
|
344
|
+
"$ref": "#/$defs/ask_confirm_digressions"
|
|
345
|
+
},
|
|
346
|
+
"block_digressions": {
|
|
347
|
+
"$ref": "#/$defs/block_digressions"
|
|
348
|
+
},
|
|
349
|
+
"run_pattern_completed": {
|
|
350
|
+
"type": "boolean"
|
|
285
351
|
}
|
|
286
352
|
}
|
|
287
353
|
},
|
|
@@ -1,46 +1,16 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from dataclasses import dataclass
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
4
|
from typing import Any, Dict, List, Set, Text
|
|
5
5
|
|
|
6
6
|
from rasa.shared.constants import ACTION_ASK_PREFIX, UTTER_ASK_PREFIX
|
|
7
|
+
from rasa.shared.core.constants import (
|
|
8
|
+
KEY_ASK_CONFIRM_DIGRESSIONS,
|
|
9
|
+
KEY_BLOCK_DIGRESSIONS,
|
|
10
|
+
)
|
|
7
11
|
from rasa.shared.core.flows.flow_step import FlowStep
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
@dataclass
|
|
11
|
-
class SlotRejection:
|
|
12
|
-
"""A pair of validation condition and an utterance for the case of failure."""
|
|
13
|
-
|
|
14
|
-
if_: str
|
|
15
|
-
"""The condition that should be checked."""
|
|
16
|
-
utter: str
|
|
17
|
-
"""The utterance that should be executed if the condition is met."""
|
|
18
|
-
|
|
19
|
-
@staticmethod
|
|
20
|
-
def from_dict(data: Dict[str, Any]) -> SlotRejection:
|
|
21
|
-
"""Create a SlotRejection object from serialized data.
|
|
22
|
-
|
|
23
|
-
Args:
|
|
24
|
-
data: data for a SlotRejection object in a serialized format
|
|
25
|
-
|
|
26
|
-
Returns:
|
|
27
|
-
A SlotRejection object
|
|
28
|
-
"""
|
|
29
|
-
return SlotRejection(
|
|
30
|
-
if_=data["if"],
|
|
31
|
-
utter=data["utter"],
|
|
32
|
-
)
|
|
33
|
-
|
|
34
|
-
def as_dict(self) -> Dict[str, Any]:
|
|
35
|
-
"""Serialize the SlotRejection object.
|
|
36
|
-
|
|
37
|
-
Returns:
|
|
38
|
-
the SlotRejection object as serialized data
|
|
39
|
-
"""
|
|
40
|
-
return {
|
|
41
|
-
"if": self.if_,
|
|
42
|
-
"utter": self.utter,
|
|
43
|
-
}
|
|
12
|
+
from rasa.shared.core.flows.utils import extract_digression_prop
|
|
13
|
+
from rasa.shared.core.slots import SlotRejection
|
|
44
14
|
|
|
45
15
|
|
|
46
16
|
@dataclass
|
|
@@ -59,6 +29,10 @@ class CollectInformationFlowStep(FlowStep):
|
|
|
59
29
|
"""Whether to always ask the question even if the slot is already filled."""
|
|
60
30
|
reset_after_flow_ends: bool = True
|
|
61
31
|
"""Whether to reset the slot value at the end of the flow."""
|
|
32
|
+
ask_confirm_digressions: List[str] = field(default_factory=list)
|
|
33
|
+
"""The flow id digressions for which the assistant should ask for confirmation."""
|
|
34
|
+
block_digressions: List[str] = field(default_factory=list)
|
|
35
|
+
"""The flow id digressions that should be blocked during the flow step."""
|
|
62
36
|
|
|
63
37
|
@classmethod
|
|
64
38
|
def from_json(
|
|
@@ -86,6 +60,10 @@ class CollectInformationFlowStep(FlowStep):
|
|
|
86
60
|
SlotRejection.from_dict(rejection)
|
|
87
61
|
for rejection in data.get("rejections", [])
|
|
88
62
|
],
|
|
63
|
+
ask_confirm_digressions=extract_digression_prop(
|
|
64
|
+
KEY_ASK_CONFIRM_DIGRESSIONS, data
|
|
65
|
+
),
|
|
66
|
+
block_digressions=extract_digression_prop(KEY_BLOCK_DIGRESSIONS, data),
|
|
89
67
|
**base.__dict__,
|
|
90
68
|
)
|
|
91
69
|
|
|
@@ -101,6 +79,10 @@ class CollectInformationFlowStep(FlowStep):
|
|
|
101
79
|
data["ask_before_filling"] = self.ask_before_filling
|
|
102
80
|
data["reset_after_flow_ends"] = self.reset_after_flow_ends
|
|
103
81
|
data["rejections"] = [rejection.as_dict() for rejection in self.rejections]
|
|
82
|
+
data["ask_confirm_digressions"] = self.ask_confirm_digressions
|
|
83
|
+
data["block_digressions"] = (
|
|
84
|
+
self.block_digressions if self.block_digressions else False
|
|
85
|
+
)
|
|
104
86
|
|
|
105
87
|
return data
|
|
106
88
|
|
rasa/shared/core/flows/utils.py
CHANGED
|
@@ -1,16 +1,21 @@
|
|
|
1
|
-
from typing import Set
|
|
1
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Set, Text
|
|
2
2
|
|
|
3
3
|
from rasa.shared.utils.io import raise_deprecation_warning
|
|
4
4
|
|
|
5
|
+
if TYPE_CHECKING:
|
|
6
|
+
from rasa.shared.core.flows.flow import FlowLanguageTranslation
|
|
7
|
+
|
|
8
|
+
|
|
5
9
|
RESET_PROPERTY_NAME = "reset_after_flow_ends"
|
|
6
10
|
PERSIST_PROPERTY_NAME = "persisted_slots"
|
|
11
|
+
ALL_LABEL = "ALL"
|
|
7
12
|
|
|
8
13
|
|
|
9
|
-
def warn_deprecated_collect_step_config(
|
|
14
|
+
def warn_deprecated_collect_step_config() -> None:
|
|
10
15
|
"""Warns about deprecated reset_after_flow_ends usage in collect steps."""
|
|
11
16
|
raise_deprecation_warning(
|
|
12
|
-
f"Configuring '{RESET_PROPERTY_NAME}' in collect
|
|
13
|
-
f"deprecated and will be removed in Rasa Pro 4.0.0. In
|
|
17
|
+
f"Configuring '{RESET_PROPERTY_NAME}' in collect steps is "
|
|
18
|
+
f"deprecated and will be removed in Rasa Pro 4.0.0. In the parent flow, "
|
|
14
19
|
f"please use the '{PERSIST_PROPERTY_NAME}' "
|
|
15
20
|
"property at the flow level instead."
|
|
16
21
|
)
|
|
@@ -38,3 +43,37 @@ def get_invalid_slot_persistence_config_error_message(
|
|
|
38
43
|
f"are neither used in a collect step nor a set_slot step of the flow. "
|
|
39
44
|
f"Please remove such slots from the '{PERSIST_PROPERTY_NAME}' property."
|
|
40
45
|
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def extract_digression_prop(prop: str, data: Dict[str, Any]) -> List[str]:
|
|
49
|
+
"""Extracts the digression property from the data.
|
|
50
|
+
|
|
51
|
+
There can be two types of properties: ask_confirm_digressions and
|
|
52
|
+
block_digressions.
|
|
53
|
+
"""
|
|
54
|
+
digression_property = data.get(prop, [])
|
|
55
|
+
|
|
56
|
+
if isinstance(digression_property, bool):
|
|
57
|
+
digression_property = [ALL_LABEL] if digression_property else []
|
|
58
|
+
|
|
59
|
+
return digression_property
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def extract_translations(
|
|
63
|
+
translation_data: Dict[Text, Any],
|
|
64
|
+
) -> Dict[Text, "FlowLanguageTranslation"]:
|
|
65
|
+
"""Extracts translations from a dictionary.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
translation_data: The dictionary containing the translations.
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
A dictionary containing the extracted translations.
|
|
72
|
+
"""
|
|
73
|
+
from rasa.shared.core.flows.flow import FlowLanguageTranslation
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
language_code: FlowLanguageTranslation.parse_obj({**data})
|
|
77
|
+
for language_code, data in translation_data.items()
|
|
78
|
+
if language_code != "metadata"
|
|
79
|
+
}
|
|
@@ -723,7 +723,7 @@ def validate_slot_persistence_configuration(flow: Flow) -> None:
|
|
|
723
723
|
flow_slots.add(step.collect)
|
|
724
724
|
if not step.reset_after_flow_ends:
|
|
725
725
|
collect_step = step.collect
|
|
726
|
-
warn_deprecated_collect_step_config(
|
|
726
|
+
warn_deprecated_collect_step_config()
|
|
727
727
|
if has_flow_level_persistence:
|
|
728
728
|
raise DuplicateSlotPersistConfigException(flow_id, collect_step)
|
|
729
729
|
|