rasa-pro 3.13.0.dev9__py3-none-any.whl → 3.13.0.dev10__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of rasa-pro might be problematic. Click here for more details.
- rasa/cli/export.py +2 -0
- rasa/core/exporter.py +36 -0
- rasa/core/policies/enterprise_search_policy.py +150 -236
- rasa/core/policies/enterprise_search_policy_config.py +242 -0
- rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v3_claude_3_5_sonnet_20240620_template.jinja2 +78 -0
- rasa/dialogue_understanding/generator/single_step/search_ready_llm_command_generator.py +2 -2
- rasa/shared/core/flows/validation.py +9 -2
- rasa/shared/providers/_configs/azure_openai_client_config.py +2 -2
- rasa/shared/providers/_configs/default_litellm_client_config.py +1 -1
- rasa/shared/providers/_configs/huggingface_local_embedding_client_config.py +1 -1
- rasa/shared/providers/_configs/openai_client_config.py +1 -1
- rasa/shared/providers/_configs/rasa_llm_client_config.py +1 -1
- rasa/shared/providers/_configs/self_hosted_llm_client_config.py +1 -1
- rasa/shared/providers/_configs/utils.py +0 -99
- rasa/shared/utils/configs.py +110 -0
- rasa/tracing/instrumentation/attribute_extractors.py +10 -5
- rasa/version.py +1 -1
- {rasa_pro-3.13.0.dev9.dist-info → rasa_pro-3.13.0.dev10.dist-info}/METADATA +1 -1
- {rasa_pro-3.13.0.dev9.dist-info → rasa_pro-3.13.0.dev10.dist-info}/RECORD +22 -19
- {rasa_pro-3.13.0.dev9.dist-info → rasa_pro-3.13.0.dev10.dist-info}/NOTICE +0 -0
- {rasa_pro-3.13.0.dev9.dist-info → rasa_pro-3.13.0.dev10.dist-info}/WHEEL +0 -0
- {rasa_pro-3.13.0.dev9.dist-info → rasa_pro-3.13.0.dev10.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import asdict, dataclass
|
|
4
|
+
from typing import Any, Dict, List, Optional
|
|
5
|
+
|
|
6
|
+
import structlog
|
|
7
|
+
|
|
8
|
+
from rasa.core.constants import (
|
|
9
|
+
POLICY_MAX_HISTORY,
|
|
10
|
+
POLICY_PRIORITY,
|
|
11
|
+
SEARCH_POLICY_PRIORITY,
|
|
12
|
+
)
|
|
13
|
+
from rasa.shared.constants import (
|
|
14
|
+
EMBEDDINGS_CONFIG_KEY,
|
|
15
|
+
LLM_CONFIG_KEY,
|
|
16
|
+
MAX_COMPLETION_TOKENS_CONFIG_KEY,
|
|
17
|
+
MAX_RETRIES_CONFIG_KEY,
|
|
18
|
+
MODEL_CONFIG_KEY,
|
|
19
|
+
OPENAI_PROVIDER,
|
|
20
|
+
PROMPT_CONFIG_KEY,
|
|
21
|
+
PROMPT_TEMPLATE_CONFIG_KEY,
|
|
22
|
+
PROVIDER_CONFIG_KEY,
|
|
23
|
+
TEMPERATURE_CONFIG_KEY,
|
|
24
|
+
TIMEOUT_CONFIG_KEY,
|
|
25
|
+
)
|
|
26
|
+
from rasa.shared.utils.configs import (
|
|
27
|
+
raise_deprecation_warnings,
|
|
28
|
+
resolve_aliases,
|
|
29
|
+
validate_forbidden_keys,
|
|
30
|
+
validate_required_keys,
|
|
31
|
+
)
|
|
32
|
+
from rasa.shared.utils.llm import (
|
|
33
|
+
DEFAULT_OPENAI_CHAT_MODEL_NAME,
|
|
34
|
+
DEFAULT_OPENAI_EMBEDDING_MODEL_NAME,
|
|
35
|
+
resolve_model_client_config,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
structlogger = structlog.get_logger()
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
SOURCE_PROPERTY = "source"
|
|
42
|
+
VECTOR_STORE_TYPE_PROPERTY = "type"
|
|
43
|
+
VECTOR_STORE_PROPERTY = "vector_store"
|
|
44
|
+
VECTOR_STORE_THRESHOLD_PROPERTY = "threshold"
|
|
45
|
+
TRACE_TOKENS_PROPERTY = "trace_prompt_tokens"
|
|
46
|
+
CITATION_ENABLED_PROPERTY = "citation_enabled"
|
|
47
|
+
USE_LLM_PROPERTY = "use_generative_llm"
|
|
48
|
+
CHECK_RELEVANCY_PROPERTY = "check_relevancy"
|
|
49
|
+
MAX_MESSAGES_IN_QUERY_KEY = "max_messages_in_query"
|
|
50
|
+
|
|
51
|
+
DEFAULT_VECTOR_STORE_TYPE = "faiss"
|
|
52
|
+
DEFAULT_VECTOR_STORE_THRESHOLD = 0.0
|
|
53
|
+
DEFAULT_VECTOR_STORE = {
|
|
54
|
+
VECTOR_STORE_TYPE_PROPERTY: DEFAULT_VECTOR_STORE_TYPE,
|
|
55
|
+
SOURCE_PROPERTY: "./docs",
|
|
56
|
+
VECTOR_STORE_THRESHOLD_PROPERTY: DEFAULT_VECTOR_STORE_THRESHOLD,
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
DEFAULT_CHECK_RELEVANCY_PROPERTY = False
|
|
60
|
+
DEFAULT_USE_LLM_PROPERTY = True
|
|
61
|
+
DEFAULT_CITATION_ENABLED_PROPERTY = False
|
|
62
|
+
DEFAULT_TRACE_PROMPT_TOKEN_PROPERTY = False
|
|
63
|
+
|
|
64
|
+
DEFAULT_MAX_MESSAGES_IN_QUERY = 2
|
|
65
|
+
|
|
66
|
+
DEFAULT_LLM_CONFIG = {
|
|
67
|
+
PROVIDER_CONFIG_KEY: OPENAI_PROVIDER,
|
|
68
|
+
MODEL_CONFIG_KEY: DEFAULT_OPENAI_CHAT_MODEL_NAME,
|
|
69
|
+
TIMEOUT_CONFIG_KEY: 10,
|
|
70
|
+
TEMPERATURE_CONFIG_KEY: 0.0,
|
|
71
|
+
MAX_COMPLETION_TOKENS_CONFIG_KEY: 256,
|
|
72
|
+
MAX_RETRIES_CONFIG_KEY: 1,
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
DEFAULT_EMBEDDINGS_CONFIG = {
|
|
76
|
+
PROVIDER_CONFIG_KEY: OPENAI_PROVIDER,
|
|
77
|
+
MODEL_CONFIG_KEY: DEFAULT_OPENAI_EMBEDDING_MODEL_NAME,
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
DEFAULT_ENTERPRISE_SEARCH_CONFIG = {
|
|
81
|
+
POLICY_PRIORITY: SEARCH_POLICY_PRIORITY,
|
|
82
|
+
VECTOR_STORE_PROPERTY: DEFAULT_VECTOR_STORE,
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
REQUIRED_KEYS: List[str] = []
|
|
86
|
+
|
|
87
|
+
FORBIDDEN_KEYS: List[str] = []
|
|
88
|
+
|
|
89
|
+
DEPRECATED_ALIASES_TO_STANDARD_KEY_MAPPING = {
|
|
90
|
+
PROMPT_CONFIG_KEY: PROMPT_TEMPLATE_CONFIG_KEY
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@dataclass
|
|
95
|
+
class EnterpriseSearchPolicyConfig:
|
|
96
|
+
"""Parses configuration for Enterprise Search Policy."""
|
|
97
|
+
|
|
98
|
+
# TODO: llm_config, embeddings_config, and vector_store_config should also be parsed
|
|
99
|
+
# as "Config" objects. Likely part of a broader Rasa 4.0 rewrite where all
|
|
100
|
+
# components rely on configuration parser. So, for example, llm_config and
|
|
101
|
+
# embeddings_config should be parsed as ClientConfig objects, and
|
|
102
|
+
# vector_store_config parsed as VectorStoreConfig object.
|
|
103
|
+
llm_config: dict
|
|
104
|
+
embeddings_config: dict
|
|
105
|
+
vector_store_config: dict
|
|
106
|
+
|
|
107
|
+
prompt_template: str
|
|
108
|
+
|
|
109
|
+
use_generative_llm: bool = DEFAULT_USE_LLM_PROPERTY
|
|
110
|
+
enable_citation: bool = DEFAULT_CITATION_ENABLED_PROPERTY
|
|
111
|
+
check_relevancy: bool = DEFAULT_CHECK_RELEVANCY_PROPERTY
|
|
112
|
+
|
|
113
|
+
max_history: Optional[int] = None
|
|
114
|
+
max_messages_in_query: int = DEFAULT_MAX_MESSAGES_IN_QUERY
|
|
115
|
+
trace_prompt_tokens: bool = DEFAULT_TRACE_PROMPT_TOKEN_PROPERTY
|
|
116
|
+
|
|
117
|
+
@property
|
|
118
|
+
def vector_store_type(self) -> str:
|
|
119
|
+
# TODO: In the future this should ideally be part of the Vector config
|
|
120
|
+
# and not the property of the EnterpriseSearch config
|
|
121
|
+
return (
|
|
122
|
+
self.vector_store_config.get(VECTOR_STORE_TYPE_PROPERTY)
|
|
123
|
+
or DEFAULT_VECTOR_STORE_TYPE
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
@property
|
|
127
|
+
def vector_store_threshold(self) -> float:
|
|
128
|
+
# TODO: In the future this should ideally be part of the Vector config
|
|
129
|
+
# and not the property of the EnterpriseSearch config
|
|
130
|
+
return (
|
|
131
|
+
self.vector_store_config.get(VECTOR_STORE_THRESHOLD_PROPERTY)
|
|
132
|
+
or DEFAULT_VECTOR_STORE_THRESHOLD
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
@property
|
|
136
|
+
def vector_store_source(self) -> Optional[str]:
|
|
137
|
+
# TODO: In the future this should ideally be part of the Vector config
|
|
138
|
+
# and not the property of the EnterpriseSearch config
|
|
139
|
+
return self.vector_store_config.get(SOURCE_PROPERTY)
|
|
140
|
+
|
|
141
|
+
def __post_init__(self) -> None:
|
|
142
|
+
if self.check_relevancy and not self.use_generative_llm:
|
|
143
|
+
structlogger.warning(
|
|
144
|
+
"enterprise_search_policy"
|
|
145
|
+
".relevancy_check_enabled_with_disabled_generative_search",
|
|
146
|
+
event_info=(
|
|
147
|
+
f"The config parameter '{CHECK_RELEVANCY_PROPERTY}' is set to"
|
|
148
|
+
f"'True', but the generative search is disabled (config"
|
|
149
|
+
f"parameter '{USE_LLM_PROPERTY}' is set to 'False'). As a result, "
|
|
150
|
+
"the relevancy check for the generative search will be disabled. "
|
|
151
|
+
f"To use this check, set the config parameter '{USE_LLM_PROPERTY}' "
|
|
152
|
+
f"to `True`."
|
|
153
|
+
),
|
|
154
|
+
)
|
|
155
|
+
if self.enable_citation and not self.use_generative_llm:
|
|
156
|
+
structlogger.warning(
|
|
157
|
+
"enterprise_search_policy"
|
|
158
|
+
".citation_enabled_with_disabled_generative_search",
|
|
159
|
+
event_info=(
|
|
160
|
+
f"The config parameter '{CITATION_ENABLED_PROPERTY}' is set to"
|
|
161
|
+
f"'True', but the generative search is disabled (config"
|
|
162
|
+
f"parameter '{USE_LLM_PROPERTY}' is set to 'False'). As a result, "
|
|
163
|
+
"the citation for the generative search will be disabled. "
|
|
164
|
+
f"To use this check, set the config parameter '{USE_LLM_PROPERTY}' "
|
|
165
|
+
f"to `True`."
|
|
166
|
+
),
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
@classmethod
|
|
170
|
+
def from_dict(cls, config: dict) -> EnterpriseSearchPolicyConfig:
|
|
171
|
+
"""
|
|
172
|
+
Initializes a dataclass from the passed config.
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
config: (dict) The config from which to initialize.
|
|
176
|
+
|
|
177
|
+
Raises:
|
|
178
|
+
ValueError: Config is missing required keys.
|
|
179
|
+
|
|
180
|
+
Returns:
|
|
181
|
+
AzureOpenAIClientConfig
|
|
182
|
+
"""
|
|
183
|
+
# Resolve LLM config
|
|
184
|
+
llm_config = (
|
|
185
|
+
resolve_model_client_config(
|
|
186
|
+
config.get(LLM_CONFIG_KEY), EnterpriseSearchPolicyConfig.__name__
|
|
187
|
+
)
|
|
188
|
+
or DEFAULT_LLM_CONFIG
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
# Resolve embeddings config
|
|
192
|
+
embeddings_config = (
|
|
193
|
+
resolve_model_client_config(
|
|
194
|
+
config.get(EMBEDDINGS_CONFIG_KEY), EnterpriseSearchPolicyConfig.__name__
|
|
195
|
+
)
|
|
196
|
+
or DEFAULT_EMBEDDINGS_CONFIG
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
# Vector store config
|
|
200
|
+
vector_store_config = config.get(VECTOR_STORE_PROPERTY, DEFAULT_VECTOR_STORE)
|
|
201
|
+
|
|
202
|
+
# Check for deprecated keys
|
|
203
|
+
raise_deprecation_warnings(
|
|
204
|
+
config, DEPRECATED_ALIASES_TO_STANDARD_KEY_MAPPING, "EnterpriseSearchPolicy"
|
|
205
|
+
)
|
|
206
|
+
# Resolve any potential aliases (e.g. 'prompt_template' vs 'prompt')
|
|
207
|
+
config = cls.resolve_config_aliases(config)
|
|
208
|
+
|
|
209
|
+
# Validate that the required keys are present
|
|
210
|
+
validate_required_keys(config, REQUIRED_KEYS)
|
|
211
|
+
# Validate that the forbidden keys are not present
|
|
212
|
+
validate_forbidden_keys(config, FORBIDDEN_KEYS)
|
|
213
|
+
|
|
214
|
+
this = EnterpriseSearchPolicyConfig(
|
|
215
|
+
llm_config=llm_config,
|
|
216
|
+
embeddings_config=embeddings_config,
|
|
217
|
+
vector_store_config=vector_store_config,
|
|
218
|
+
prompt_template=config.get(PROMPT_TEMPLATE_CONFIG_KEY),
|
|
219
|
+
use_generative_llm=config.get(USE_LLM_PROPERTY, DEFAULT_USE_LLM_PROPERTY),
|
|
220
|
+
enable_citation=config.get(
|
|
221
|
+
CITATION_ENABLED_PROPERTY, DEFAULT_CITATION_ENABLED_PROPERTY
|
|
222
|
+
),
|
|
223
|
+
check_relevancy=config.get(
|
|
224
|
+
CHECK_RELEVANCY_PROPERTY, DEFAULT_CHECK_RELEVANCY_PROPERTY
|
|
225
|
+
),
|
|
226
|
+
max_history=config.get(POLICY_MAX_HISTORY),
|
|
227
|
+
max_messages_in_query=config.get(
|
|
228
|
+
MAX_MESSAGES_IN_QUERY_KEY, DEFAULT_MAX_MESSAGES_IN_QUERY
|
|
229
|
+
),
|
|
230
|
+
trace_prompt_tokens=config.get(
|
|
231
|
+
TRACE_TOKENS_PROPERTY, DEFAULT_TRACE_PROMPT_TOKEN_PROPERTY
|
|
232
|
+
),
|
|
233
|
+
)
|
|
234
|
+
return this
|
|
235
|
+
|
|
236
|
+
def to_dict(self) -> dict:
|
|
237
|
+
"""Converts the config instance into a dictionary."""
|
|
238
|
+
return asdict(self)
|
|
239
|
+
|
|
240
|
+
@staticmethod
|
|
241
|
+
def resolve_config_aliases(config: Dict[str, Any]) -> Dict[str, Any]:
|
|
242
|
+
return resolve_aliases(config, DEPRECATED_ALIASES_TO_STANDARD_KEY_MAPPING)
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
## Task Description
|
|
2
|
+
Your task is to analyze the current conversation context and generate a list of actions to start new business processes that we call flows, to extract slots, or respond to off-topic and knowledge requests.
|
|
3
|
+
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## Available Actions:
|
|
7
|
+
* `start flow flow_name`: Start a flow. For example, `start flow transfer_money` or `start flow list_contacts`.
|
|
8
|
+
* `set slot slot_name slot_value`: Set a slot for the active flow. For example, `set slot transfer_money_recipient Freddy`. Can be used to correct and change previously set values.
|
|
9
|
+
* `disambiguate flows flow_name1 flow_name2 ... flow_name_n`: When a message could refer to multiple flows, list the possible flows as options to clarify. Example: `disambiguate flows list_contacts add_contact remove_contact`.
|
|
10
|
+
* `search and reply`: Provide a response from the knowledge base to address the user's inquiry when no flows fit, including domain knowledge, FAQs, and all off-topic or social messages.
|
|
11
|
+
* `cancel flow`: Cancel the current flow if the user requests it.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## General Instructions
|
|
16
|
+
### Start Flow
|
|
17
|
+
* Only start a flow if the user's message is clear and fully addressed by that flow's description and purpose.
|
|
18
|
+
* Pay close attention to exact wording and scope in the flow description — do not assume or “stretch” the intended use of a flow.
|
|
19
|
+
### Set Slot
|
|
20
|
+
* Do not fill slots with abstract values or placeholders.
|
|
21
|
+
* For categorical slots, try to match the user message with allowed slot values. Use "other" if you cannot match it.
|
|
22
|
+
* Set the boolean slots based on the user's response. Map positive responses to `True`, and negative to `False`.
|
|
23
|
+
* Extract text slot values exactly as provided by the user. Avoid assumptions, format changes, or partial extractions.
|
|
24
|
+
### Disambiguate Flows
|
|
25
|
+
* Use `disambiguate flows` when the user's message matches multiple flows and you cannot decide which flow is most appropriate.
|
|
26
|
+
* If the user message is short and not precise enough to start a flow or `search and reply`, disambiguate.
|
|
27
|
+
* If a single flow is a strong/plausible fit, prefer starting that flow directly.
|
|
28
|
+
* If a user's message unambiguously and distinctly matches multiple flows, start all relevant flows at once (rather than disambiguating).
|
|
29
|
+
### Search and Reply
|
|
30
|
+
* Only start `search and reply` if the user intent is clear.
|
|
31
|
+
* Flow Priority: If you are unsure between starting a flow or `search and reply`, always prioritize starting a flow.
|
|
32
|
+
### Cancel Flow
|
|
33
|
+
* Do not cancel any flow unless the user explicitly requests it.
|
|
34
|
+
* Multiple flows can be started without cancelling the previous, if the user wants to pursue multiple processes.
|
|
35
|
+
### General Tips
|
|
36
|
+
* Only use information provided by the user.
|
|
37
|
+
* Strictly adhere to the provided action format.
|
|
38
|
+
* Focus on the last message and take it one step at a time.
|
|
39
|
+
* Use the previous conversation steps only to aid understanding.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Decision Rule Table
|
|
44
|
+
| Condition | Action |
|
|
45
|
+
|-------------------------------------------------------|--------------------|
|
|
46
|
+
| Flow perfectly matches user's message | start flow |
|
|
47
|
+
| Multiple flows are equally strong, relevant matches | disambiguate flows |
|
|
48
|
+
| User's message is unclear or imprecise | disambiguate flows |
|
|
49
|
+
| No flow fits at all, but knowledge base may help | search and reply |
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Available Flows and Slots
|
|
54
|
+
Use the following structured data:
|
|
55
|
+
```json
|
|
56
|
+
{"flows":[{% for flow in available_flows %}{"name":"{{ flow.name }}","description":{{ flow.description | to_json_escaped_string }}{% if flow.slots %},"slots":[{% for slot in flow.slots %}{"name":"{{ slot.name }}"{% if slot.description %},"description":{{ slot.description | to_json_escaped_string }}{% endif %}{% if slot.allowed_values %},"allowed_values":{{ slot.allowed_values }}{% endif %}}{% if not loop.last %},{% endif %}{% endfor %}]{% endif %}}{% if not loop.last %},{% endif %}{% endfor %}]}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Current State
|
|
62
|
+
{% if current_flow != None %}Use the following structured data:
|
|
63
|
+
```json
|
|
64
|
+
{"active_flow":"{{ current_flow }}","current_step":{"requested_slot":"{{ current_slot }}","requested_slot_description":{{ current_slot_description | to_json_escaped_string }}},"slots":[{% for slot in flow_slots %}{"name":"{{ slot.name }}","value":"{{ slot.value }}","type":"{{ slot.type }}"{% if slot.description %},"description":{{ slot.description | to_json_escaped_string }}{% endif %}{% if slot.allowed_values %},"allowed_values":"{{ slot.allowed_values }}"{% endif %}}{% if not loop.last %},{% endif %}{% endfor %}]}
|
|
65
|
+
```{% else %}
|
|
66
|
+
You are currently not inside any flow.{% endif %}
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Conversation History
|
|
71
|
+
{{ current_conversation }}
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Task
|
|
76
|
+
Create an action list with one action per line in response to the user's last message: """{{ user_message }}""".
|
|
77
|
+
|
|
78
|
+
Your action list:
|
|
@@ -59,10 +59,10 @@ MODEL_PROMPT_MAPPER = {
|
|
|
59
59
|
),
|
|
60
60
|
f"{AWS_BEDROCK_PROVIDER}/anthropic."
|
|
61
61
|
f"{MODEL_NAME_CLAUDE_3_5_SONNET_20240620}-v1:0": (
|
|
62
|
-
"
|
|
62
|
+
"command_prompt_v3_claude_3_5_sonnet_20240620_template.jinja2"
|
|
63
63
|
),
|
|
64
64
|
f"{ANTHROPIC_PROVIDER}/{MODEL_NAME_CLAUDE_3_5_SONNET_20240620}": (
|
|
65
|
-
"
|
|
65
|
+
"command_prompt_v3_claude_3_5_sonnet_20240620_template.jinja2"
|
|
66
66
|
),
|
|
67
67
|
}
|
|
68
68
|
|
|
@@ -581,11 +581,18 @@ def validate_linked_flows_exists(flows: "FlowsList") -> None:
|
|
|
581
581
|
continue
|
|
582
582
|
|
|
583
583
|
# It might be that the flows do not contain the default rasa patterns, but
|
|
584
|
-
# only the user flows. Manually check for `pattern_human_handoff`
|
|
585
|
-
#
|
|
584
|
+
# only the user flows. Manually check for `pattern_human_handoff` and
|
|
585
|
+
# 'pattern_chitchat' as these patterns can be linked to and are part of the
|
|
586
|
+
# default patterns of rasa.
|
|
586
587
|
if (
|
|
587
588
|
flows.flow_by_id(step.link) is None
|
|
589
|
+
# Allow linking to human-handoff from both patterns
|
|
590
|
+
# and user-defined flows
|
|
588
591
|
and step.link != RASA_PATTERN_HUMAN_HANDOFF
|
|
592
|
+
# Allow linking to 'pattern_chitchat' only from other patterns
|
|
593
|
+
and not (
|
|
594
|
+
flow.is_rasa_default_flow and step.link == RASA_PATTERN_CHITCHAT
|
|
595
|
+
)
|
|
589
596
|
):
|
|
590
597
|
raise UnresolvedFlowException(step.link, flow.id, step.id)
|
|
591
598
|
|
|
@@ -46,13 +46,13 @@ from rasa.shared.providers._configs.oauth_config import (
|
|
|
46
46
|
OAUTH_TYPE_FIELD,
|
|
47
47
|
OAuth,
|
|
48
48
|
)
|
|
49
|
-
from rasa.shared.
|
|
49
|
+
from rasa.shared.utils.common import class_from_module_path
|
|
50
|
+
from rasa.shared.utils.configs import (
|
|
50
51
|
raise_deprecation_warnings,
|
|
51
52
|
resolve_aliases,
|
|
52
53
|
validate_forbidden_keys,
|
|
53
54
|
validate_required_keys,
|
|
54
55
|
)
|
|
55
|
-
from rasa.shared.utils.common import class_from_module_path
|
|
56
56
|
|
|
57
57
|
structlogger = structlog.get_logger()
|
|
58
58
|
|
|
@@ -15,7 +15,7 @@ from rasa.shared.constants import (
|
|
|
15
15
|
STREAM_CONFIG_KEY,
|
|
16
16
|
TIMEOUT_CONFIG_KEY,
|
|
17
17
|
)
|
|
18
|
-
from rasa.shared.
|
|
18
|
+
from rasa.shared.utils.configs import (
|
|
19
19
|
raise_deprecation_warnings,
|
|
20
20
|
resolve_aliases,
|
|
21
21
|
validate_forbidden_keys,
|
|
@@ -21,7 +21,7 @@ from rasa.shared.constants import (
|
|
|
21
21
|
REQUEST_TIMEOUT_CONFIG_KEY,
|
|
22
22
|
TIMEOUT_CONFIG_KEY,
|
|
23
23
|
)
|
|
24
|
-
from rasa.shared.
|
|
24
|
+
from rasa.shared.utils.configs import (
|
|
25
25
|
raise_deprecation_warnings,
|
|
26
26
|
resolve_aliases,
|
|
27
27
|
validate_required_keys,
|
|
@@ -26,7 +26,7 @@ from rasa.shared.constants import (
|
|
|
26
26
|
STREAM_CONFIG_KEY,
|
|
27
27
|
TIMEOUT_CONFIG_KEY,
|
|
28
28
|
)
|
|
29
|
-
from rasa.shared.
|
|
29
|
+
from rasa.shared.utils.configs import (
|
|
30
30
|
raise_deprecation_warnings,
|
|
31
31
|
resolve_aliases,
|
|
32
32
|
validate_forbidden_keys,
|
|
@@ -25,7 +25,7 @@ from rasa.shared.constants import (
|
|
|
25
25
|
TIMEOUT_CONFIG_KEY,
|
|
26
26
|
USE_CHAT_COMPLETIONS_ENDPOINT_CONFIG_KEY,
|
|
27
27
|
)
|
|
28
|
-
from rasa.shared.
|
|
28
|
+
from rasa.shared.utils.configs import (
|
|
29
29
|
raise_deprecation_warnings,
|
|
30
30
|
resolve_aliases,
|
|
31
31
|
validate_forbidden_keys,
|
|
@@ -1,107 +1,8 @@
|
|
|
1
1
|
import structlog
|
|
2
2
|
|
|
3
|
-
from rasa.shared.utils.io import raise_deprecation_warning
|
|
4
|
-
|
|
5
3
|
structlogger = structlog.get_logger()
|
|
6
4
|
|
|
7
5
|
|
|
8
|
-
def resolve_aliases(config: dict, deprecated_alias_mapping: dict) -> dict:
|
|
9
|
-
"""
|
|
10
|
-
Resolve aliases in the configuration to standard keys.
|
|
11
|
-
|
|
12
|
-
Args:
|
|
13
|
-
config: Dictionary containing the configuration.
|
|
14
|
-
deprecated_alias_mapping: Dictionary mapping aliases to
|
|
15
|
-
their standard keys.
|
|
16
|
-
|
|
17
|
-
Returns:
|
|
18
|
-
New dictionary containing the processed configuration.
|
|
19
|
-
"""
|
|
20
|
-
config = config.copy()
|
|
21
|
-
|
|
22
|
-
for alias, standard_key in deprecated_alias_mapping.items():
|
|
23
|
-
# We check for the alias instead of the standard key because our goal is to
|
|
24
|
-
# update the standard key when the alias is found. Since the standard key is
|
|
25
|
-
# always included in the default component configurations, we overwrite it
|
|
26
|
-
# with the alias value if the alias exists.
|
|
27
|
-
if alias in config:
|
|
28
|
-
config[standard_key] = config.pop(alias)
|
|
29
|
-
|
|
30
|
-
return config
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def raise_deprecation_warnings(config: dict, deprecated_alias_mapping: dict) -> None:
|
|
34
|
-
"""
|
|
35
|
-
Raises warnings for deprecated keys in the configuration.
|
|
36
|
-
|
|
37
|
-
Args:
|
|
38
|
-
config: Dictionary containing the configuration.
|
|
39
|
-
deprecated_alias_mapping: Dictionary mapping deprecated keys to
|
|
40
|
-
their standard keys.
|
|
41
|
-
|
|
42
|
-
Raises:
|
|
43
|
-
DeprecationWarning: If any deprecated key is found in the config.
|
|
44
|
-
"""
|
|
45
|
-
for alias, standard_key in deprecated_alias_mapping.items():
|
|
46
|
-
if alias in config:
|
|
47
|
-
raise_deprecation_warning(
|
|
48
|
-
message=(
|
|
49
|
-
f"'{alias}' is deprecated and will be removed in "
|
|
50
|
-
f"4.0.0. Use '{standard_key}' instead."
|
|
51
|
-
)
|
|
52
|
-
)
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
def validate_required_keys(config: dict, required_keys: list) -> None:
|
|
56
|
-
"""
|
|
57
|
-
Validates that the passed config contains all the required keys.
|
|
58
|
-
|
|
59
|
-
Args:
|
|
60
|
-
config: Dictionary containing the configuration.
|
|
61
|
-
required_keys: List of keys that must be present in the config.
|
|
62
|
-
|
|
63
|
-
Raises:
|
|
64
|
-
ValueError: If any required key is missing.
|
|
65
|
-
"""
|
|
66
|
-
missing_keys = [key for key in required_keys if key not in config]
|
|
67
|
-
if missing_keys:
|
|
68
|
-
message = f"Missing required keys '{missing_keys}' for configuration."
|
|
69
|
-
structlogger.error(
|
|
70
|
-
"validate_required_keys",
|
|
71
|
-
message=message,
|
|
72
|
-
missing_keys=missing_keys,
|
|
73
|
-
config=config,
|
|
74
|
-
)
|
|
75
|
-
raise ValueError(message)
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
def validate_forbidden_keys(config: dict, forbidden_keys: list) -> None:
|
|
79
|
-
"""
|
|
80
|
-
Validates that the passed config doesn't contain any forbidden keys.
|
|
81
|
-
|
|
82
|
-
Args:
|
|
83
|
-
config: Dictionary containing the configuration.
|
|
84
|
-
forbidden_keys: List of keys that are forbidden in the config.
|
|
85
|
-
|
|
86
|
-
Raises:
|
|
87
|
-
ValueError: If any forbidden key is present.
|
|
88
|
-
"""
|
|
89
|
-
forbidden_keys_in_config = set(config.keys()).intersection(set(forbidden_keys))
|
|
90
|
-
|
|
91
|
-
if forbidden_keys_in_config:
|
|
92
|
-
message = (
|
|
93
|
-
f"Forbidden keys '{forbidden_keys_in_config}' present "
|
|
94
|
-
f"in the configuration."
|
|
95
|
-
)
|
|
96
|
-
structlogger.error(
|
|
97
|
-
"validate_forbidden_keys",
|
|
98
|
-
message=message,
|
|
99
|
-
forbidden_keys=forbidden_keys_in_config,
|
|
100
|
-
config=config,
|
|
101
|
-
)
|
|
102
|
-
raise ValueError(message)
|
|
103
|
-
|
|
104
|
-
|
|
105
6
|
def get_provider_prefixed_model_name(provider: str, model: str) -> str:
|
|
106
7
|
"""
|
|
107
8
|
Returns the model name with the provider prefixed.
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
import structlog
|
|
4
|
+
|
|
5
|
+
from rasa.shared.utils.io import raise_deprecation_warning
|
|
6
|
+
|
|
7
|
+
structlogger = structlog.get_logger()
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def resolve_aliases(config: dict, deprecated_alias_mapping: dict) -> dict:
|
|
11
|
+
"""
|
|
12
|
+
Resolve aliases in the configuration to standard keys.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
config: Dictionary containing the configuration.
|
|
16
|
+
deprecated_alias_mapping: Dictionary mapping aliases to
|
|
17
|
+
their standard keys.
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
New dictionary containing the processed configuration.
|
|
21
|
+
"""
|
|
22
|
+
config = config.copy()
|
|
23
|
+
|
|
24
|
+
for alias, standard_key in deprecated_alias_mapping.items():
|
|
25
|
+
# We check for the alias instead of the standard key because our goal is to
|
|
26
|
+
# update the standard key when the alias is found. Since the standard key is
|
|
27
|
+
# always included in the default component configurations, we overwrite it
|
|
28
|
+
# with the alias value if the alias exists.
|
|
29
|
+
if alias in config:
|
|
30
|
+
config[standard_key] = config.pop(alias)
|
|
31
|
+
|
|
32
|
+
return config
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def raise_deprecation_warnings(
|
|
36
|
+
config: dict,
|
|
37
|
+
deprecated_alias_mapping: dict,
|
|
38
|
+
source: Optional[str] = None,
|
|
39
|
+
) -> None:
|
|
40
|
+
"""
|
|
41
|
+
Raises warnings for deprecated keys in the configuration.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
config: Dictionary containing the configuration.
|
|
45
|
+
deprecated_alias_mapping: Dictionary mapping deprecated keys to
|
|
46
|
+
their standard keys.
|
|
47
|
+
|
|
48
|
+
Raises:
|
|
49
|
+
DeprecationWarning: If any deprecated key is found in the config.
|
|
50
|
+
"""
|
|
51
|
+
for alias, standard_key in deprecated_alias_mapping.items():
|
|
52
|
+
if alias in config:
|
|
53
|
+
source = f"{source}: " or ""
|
|
54
|
+
raise_deprecation_warning(
|
|
55
|
+
message=(
|
|
56
|
+
f"{source}"
|
|
57
|
+
f"'{alias}' is deprecated and will be removed in "
|
|
58
|
+
f"4.0.0. Use '{standard_key}' instead."
|
|
59
|
+
)
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def validate_required_keys(config: dict, required_keys: list) -> None:
|
|
64
|
+
"""
|
|
65
|
+
Validates that the passed config contains all the required keys.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
config: Dictionary containing the configuration.
|
|
69
|
+
required_keys: List of keys that must be present in the config.
|
|
70
|
+
|
|
71
|
+
Raises:
|
|
72
|
+
ValueError: If any required key is missing.
|
|
73
|
+
"""
|
|
74
|
+
missing_keys = [key for key in required_keys if key not in config]
|
|
75
|
+
if missing_keys:
|
|
76
|
+
message = f"Missing required keys '{missing_keys}' for configuration."
|
|
77
|
+
structlogger.error(
|
|
78
|
+
"validate_required_keys",
|
|
79
|
+
message=message,
|
|
80
|
+
missing_keys=missing_keys,
|
|
81
|
+
config=config,
|
|
82
|
+
)
|
|
83
|
+
raise ValueError(message)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def validate_forbidden_keys(config: dict, forbidden_keys: list) -> None:
|
|
87
|
+
"""
|
|
88
|
+
Validates that the passed config doesn't contain any forbidden keys.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
config: Dictionary containing the configuration.
|
|
92
|
+
forbidden_keys: List of keys that are forbidden in the config.
|
|
93
|
+
|
|
94
|
+
Raises:
|
|
95
|
+
ValueError: If any forbidden key is present.
|
|
96
|
+
"""
|
|
97
|
+
forbidden_keys_in_config = set(config.keys()).intersection(set(forbidden_keys))
|
|
98
|
+
|
|
99
|
+
if forbidden_keys_in_config:
|
|
100
|
+
message = (
|
|
101
|
+
f"Forbidden keys '{forbidden_keys_in_config}' present "
|
|
102
|
+
f"in the configuration."
|
|
103
|
+
)
|
|
104
|
+
structlogger.error(
|
|
105
|
+
"validate_forbidden_keys",
|
|
106
|
+
message=message,
|
|
107
|
+
forbidden_keys=forbidden_keys_in_config,
|
|
108
|
+
config=config,
|
|
109
|
+
)
|
|
110
|
+
raise ValueError(message)
|
|
@@ -64,7 +64,10 @@ from rasa.shared.core.trackers import DialogueStateTracker
|
|
|
64
64
|
from rasa.shared.core.training_data.structures import StoryGraph
|
|
65
65
|
from rasa.shared.importers.importer import TrainingDataImporter
|
|
66
66
|
from rasa.shared.nlu.constants import INTENT_NAME_KEY, SET_SLOT_COMMAND
|
|
67
|
-
from rasa.shared.utils.llm import
|
|
67
|
+
from rasa.shared.utils.llm import (
|
|
68
|
+
combine_custom_and_default_config,
|
|
69
|
+
resolve_model_client_config,
|
|
70
|
+
)
|
|
68
71
|
from rasa.tracing.constants import (
|
|
69
72
|
PROMPT_TOKEN_LENGTH_ATTRIBUTE_NAME,
|
|
70
73
|
REQUEST_BODY_SIZE_IN_BYTES_ATTRIBUTE_NAME,
|
|
@@ -335,9 +338,8 @@ def extract_llm_config(
|
|
|
335
338
|
else:
|
|
336
339
|
config = self.config
|
|
337
340
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
)
|
|
341
|
+
llm_config = resolve_model_client_config(config.get(LLM_CONFIG_KEY))
|
|
342
|
+
llm_property = combine_custom_and_default_config(llm_config, default_llm_config)
|
|
341
343
|
|
|
342
344
|
if isinstance(self, LLMBasedCommandGenerator):
|
|
343
345
|
flow_retrieval_config = config.get(FLOW_RETRIEVAL_KEY, {}) or {}
|
|
@@ -346,8 +348,11 @@ def extract_llm_config(
|
|
|
346
348
|
default_embeddings_config,
|
|
347
349
|
)
|
|
348
350
|
else:
|
|
351
|
+
embeddings_config = resolve_model_client_config(
|
|
352
|
+
config.get(EMBEDDINGS_CONFIG_KEY)
|
|
353
|
+
)
|
|
349
354
|
embeddings_property = combine_custom_and_default_config(
|
|
350
|
-
|
|
355
|
+
embeddings_config, default_embeddings_config
|
|
351
356
|
)
|
|
352
357
|
|
|
353
358
|
attributes = {
|