rasa-pro 3.14.0rc2__py3-none-any.whl → 3.14.0rc4__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/agents/protocol/a2a/a2a_agent.py +50 -42
- rasa/agents/protocol/mcp/mcp_base_agent.py +15 -1
- rasa/agents/utils.py +27 -5
- rasa/agents/validation.py +64 -13
- rasa/api.py +1 -2
- rasa/builder/copilot/constants.py +4 -0
- rasa/builder/copilot/copilot.py +37 -1
- rasa/builder/copilot/copilot_templated_message_provider.py +23 -0
- rasa/builder/copilot/models.py +43 -49
- rasa/builder/copilot/prompts/copilot_system_prompt.jinja2 +33 -12
- rasa/builder/copilot/prompts/latest_user_message_context_prompt.jinja2 +59 -29
- rasa/builder/copilot/telemetry.py +8 -0
- rasa/builder/copilot/templated_messages/copilot_templated_responses.yml +3 -0
- rasa/builder/copilot/templated_messages/copilot_welcome_messages.yml +56 -0
- rasa/builder/jobs.py +162 -5
- rasa/builder/models.py +3 -0
- rasa/builder/service.py +1 -0
- rasa/cli/dialogue_understanding_test.py +1 -0
- rasa/cli/e2e_test.py +1 -0
- rasa/cli/inspect.py +1 -0
- rasa/cli/project_templates/basic/tests/e2e_test_cases/without_stub/general/feedback.yml +46 -0
- rasa/cli/project_templates/basic/tests/e2e_test_cases/without_stub/general/goodbye.yml +9 -0
- rasa/cli/project_templates/basic/tests/e2e_test_cases/without_stub/general/help.yml +8 -0
- rasa/cli/project_templates/basic/tests/e2e_test_cases/without_stub/general/human_handoff.yml +41 -0
- rasa/cli/project_templates/basic/tests/e2e_test_cases/without_stub/general/patterns.yml +32 -0
- rasa/cli/project_templates/basic/tests/e2e_test_cases/without_stub/general/show_faqs.yml +8 -0
- rasa/cli/project_templates/telco/data/network/flow_solve_internet_issue.yml +2 -2
- rasa/cli/project_templates/telco/domain/network/solve_internet_issue.yml +1 -2
- rasa/cli/project_templates/telco/tests/e2e_test_cases/with_stub/network/solve_internet_not_slow.yml +33 -0
- rasa/cli/project_templates/telco/tests/e2e_test_cases/with_stub/network/solve_internet_slow.yml +47 -0
- rasa/cli/project_templates/telco/tests/e2e_test_cases/without_stub/general/hello.yml +8 -0
- rasa/cli/run.py +1 -5
- rasa/cli/shell.py +1 -0
- rasa/cli/train.py +1 -0
- rasa/cli/validation/bot_config.py +9 -2
- rasa/core/available_agents.py +65 -55
- rasa/core/config/available_endpoints.py +6 -3
- rasa/core/config/configuration.py +36 -1
- rasa/core/policies/flows/agent_executor.py +16 -8
- rasa/dialogue_understanding/commands/start_flow_command.py +10 -3
- rasa/dialogue_understanding/commands/utils.py +15 -4
- rasa/dialogue_understanding/generator/llm_based_command_generator.py +4 -2
- rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +4 -4
- rasa/dialogue_understanding/generator/single_step/search_ready_llm_command_generator.py +4 -4
- rasa/dialogue_understanding/generator/single_step/single_step_based_llm_command_generator.py +2 -2
- rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +22 -20
- rasa/dialogue_understanding_test/du_test_runner.py +2 -2
- rasa/e2e_test/e2e_test_runner.py +2 -2
- rasa/engine/validation.py +6 -2
- rasa/shared/agents/auth/auth_strategy/oauth2_auth_strategy.py +10 -4
- rasa/shared/agents/auth/constants.py +1 -0
- rasa/shared/agents/auth/utils.py +85 -0
- rasa/shared/constants.py +3 -0
- rasa/shared/core/flows/steps/call.py +2 -2
- rasa/shared/utils/llm.py +33 -0
- rasa/telemetry.py +3 -3
- rasa/utils/pypred.py +7 -0
- rasa/validator.py +127 -2
- rasa/version.py +1 -1
- {rasa_pro-3.14.0rc2.dist-info → rasa_pro-3.14.0rc4.dist-info}/METADATA +19 -7
- {rasa_pro-3.14.0rc2.dist-info → rasa_pro-3.14.0rc4.dist-info}/RECORD +81 -71
- rasa/cli/project_templates/telco/tests/e2e_test_cases/network/solve_internet_issue.yml +0 -57
- /rasa/cli/project_templates/{finance/tests/e2e_test_cases → basic/tests/e2e_test_cases/without_stub}/general/hello.yml +0 -0
- /rasa/cli/project_templates/finance/tests/e2e_test_cases/{accounts → without_stub/accounts}/check_balance.yml +0 -0
- /rasa/cli/project_templates/finance/tests/e2e_test_cases/{accounts → without_stub/accounts}/download_statements.yml +0 -0
- /rasa/cli/project_templates/finance/tests/e2e_test_cases/{cards → without_stub/cards}/block_card.yml +0 -0
- /rasa/cli/project_templates/finance/tests/e2e_test_cases/{general → without_stub/general}/bot_challenge.yml +0 -0
- /rasa/cli/project_templates/finance/tests/e2e_test_cases/{general → without_stub/general}/feedback.yml +0 -0
- /rasa/cli/project_templates/finance/tests/e2e_test_cases/{general → without_stub/general}/goodbye.yml +0 -0
- /rasa/cli/project_templates/{telco/tests/e2e_test_cases → finance/tests/e2e_test_cases/without_stub}/general/hello.yml +0 -0
- /rasa/cli/project_templates/finance/tests/e2e_test_cases/{general → without_stub/general}/human_handoff.yml +0 -0
- /rasa/cli/project_templates/finance/tests/e2e_test_cases/{general → without_stub/general}/patterns.yml +0 -0
- /rasa/cli/project_templates/finance/tests/e2e_test_cases/{transfers → without_stub/transfers}/transfer_money.yml +0 -0
- /rasa/cli/project_templates/telco/tests/e2e_test_cases/{billing → without_stub/billing}/understand_bill.yml +0 -0
- /rasa/cli/project_templates/telco/tests/e2e_test_cases/{general → without_stub/general}/bot_challenge.yml +0 -0
- /rasa/cli/project_templates/telco/tests/e2e_test_cases/{general → without_stub/general}/feedback.yml +0 -0
- /rasa/cli/project_templates/telco/tests/e2e_test_cases/{general → without_stub/general}/goodbye.yml +0 -0
- /rasa/cli/project_templates/telco/tests/e2e_test_cases/{general → without_stub/general}/human_handoff.yml +0 -0
- /rasa/cli/project_templates/telco/tests/e2e_test_cases/{general → without_stub/general}/patterns.yml +0 -0
- {rasa_pro-3.14.0rc2.dist-info → rasa_pro-3.14.0rc4.dist-info}/NOTICE +0 -0
- {rasa_pro-3.14.0rc2.dist-info → rasa_pro-3.14.0rc4.dist-info}/WHEEL +0 -0
- {rasa_pro-3.14.0rc2.dist-info → rasa_pro-3.14.0rc4.dist-info}/entry_points.txt +0 -0
|
@@ -104,7 +104,10 @@ class StartFlowCommand(Command):
|
|
|
104
104
|
# predicted a start flow command for the flow which is on top of the stack,
|
|
105
105
|
# we just need to remove the pattern_continue_interrupted frame(s) from the
|
|
106
106
|
# stack
|
|
107
|
-
stack = remove_pattern_continue_interrupted_frames(
|
|
107
|
+
stack, flow_completed_events = remove_pattern_continue_interrupted_frames(
|
|
108
|
+
stack
|
|
109
|
+
)
|
|
110
|
+
applied_events.extend(flow_completed_events)
|
|
108
111
|
return applied_events + tracker.create_stack_updated_events(stack)
|
|
109
112
|
|
|
110
113
|
# if the flow is already on the stack, resume it
|
|
@@ -114,7 +117,10 @@ class StartFlowCommand(Command):
|
|
|
114
117
|
):
|
|
115
118
|
# if pattern_continue_interrupted is active, we need to remove it
|
|
116
119
|
# from the stack before resuming the flow
|
|
117
|
-
stack = remove_pattern_continue_interrupted_frames(
|
|
120
|
+
stack, flow_completed_events = remove_pattern_continue_interrupted_frames(
|
|
121
|
+
stack
|
|
122
|
+
)
|
|
123
|
+
applied_events.extend(flow_completed_events)
|
|
118
124
|
applied_events.extend(resume_flow(self.flow, tracker, stack))
|
|
119
125
|
# the current active flow is interrupted
|
|
120
126
|
applied_events.append(
|
|
@@ -128,7 +134,8 @@ class StartFlowCommand(Command):
|
|
|
128
134
|
|
|
129
135
|
# remove the pattern_continue_interrupted frames from the stack
|
|
130
136
|
# if it is currently active but the user digressed from the pattern
|
|
131
|
-
stack = remove_pattern_continue_interrupted_frames(stack)
|
|
137
|
+
stack, flow_completed_events = remove_pattern_continue_interrupted_frames(stack)
|
|
138
|
+
applied_events.extend(flow_completed_events)
|
|
132
139
|
|
|
133
140
|
if original_top_flow:
|
|
134
141
|
# if the original top flow is not the same as the flow to start,
|
|
@@ -19,6 +19,7 @@ from rasa.shared.constants import ACTION_ASK_PREFIX, UTTER_ASK_PREFIX
|
|
|
19
19
|
from rasa.shared.core.events import (
|
|
20
20
|
AgentResumed,
|
|
21
21
|
Event,
|
|
22
|
+
FlowCompleted,
|
|
22
23
|
FlowResumed,
|
|
23
24
|
SlotSet,
|
|
24
25
|
)
|
|
@@ -250,18 +251,28 @@ def collect_frames_to_resume(
|
|
|
250
251
|
return list(frames_to_resume), frame_to_resume
|
|
251
252
|
|
|
252
253
|
|
|
253
|
-
def remove_pattern_continue_interrupted_frames(
|
|
254
|
-
|
|
254
|
+
def remove_pattern_continue_interrupted_frames(
|
|
255
|
+
stack: DialogueStack,
|
|
256
|
+
) -> Tuple[DialogueStack, List[FlowCompleted]]:
|
|
257
|
+
"""Remove pattern_continue_interrupted frames from the stack and return events.
|
|
258
|
+
|
|
259
|
+
Returns:
|
|
260
|
+
A tuple containing (updated_stack, flow_completed_events)
|
|
261
|
+
"""
|
|
255
262
|
from rasa.dialogue_understanding.stack.utils import (
|
|
256
263
|
is_continue_interrupted_flow_active,
|
|
257
264
|
)
|
|
258
265
|
|
|
259
266
|
if not is_continue_interrupted_flow_active(stack):
|
|
260
|
-
return stack
|
|
267
|
+
return stack, []
|
|
261
268
|
|
|
269
|
+
events = []
|
|
262
270
|
# remove pattern_continue_interrupted from the stack
|
|
263
271
|
top_frame = stack.top()
|
|
264
272
|
while isinstance(top_frame, PatternFlowStackFrame):
|
|
273
|
+
# Create FlowCompleted event for the pattern frame being removed
|
|
274
|
+
events.append(FlowCompleted(top_frame.flow_id, top_frame.step_id))
|
|
275
|
+
|
|
265
276
|
# If the top frame is a pattern frame, we need to remove it
|
|
266
277
|
# before continuing with the active user flow frame.
|
|
267
278
|
# This prevents the pattern frame
|
|
@@ -271,4 +282,4 @@ def remove_pattern_continue_interrupted_frames(stack: DialogueStack) -> Dialogue
|
|
|
271
282
|
stack.pop()
|
|
272
283
|
top_frame = stack.top()
|
|
273
284
|
|
|
274
|
-
return stack
|
|
285
|
+
return stack, events
|
|
@@ -7,7 +7,7 @@ from jinja2 import Environment, Template, select_autoescape
|
|
|
7
7
|
|
|
8
8
|
import rasa.dialogue_understanding.generator.utils
|
|
9
9
|
import rasa.shared.utils.io
|
|
10
|
-
from rasa.core.
|
|
10
|
+
from rasa.core.config.configuration import Configuration
|
|
11
11
|
from rasa.dialogue_understanding.commands import (
|
|
12
12
|
Command,
|
|
13
13
|
SetSlotCommand,
|
|
@@ -398,7 +398,9 @@ class LLMBasedCommandGenerator(
|
|
|
398
398
|
if isinstance(event, AgentStarted) and event.flow_id == flow.id
|
|
399
399
|
]
|
|
400
400
|
available_agents = [
|
|
401
|
-
|
|
401
|
+
Configuration.get_instance().available_agents.get_agent_config(
|
|
402
|
+
event.agent_id
|
|
403
|
+
)
|
|
402
404
|
for event in agent_events
|
|
403
405
|
]
|
|
404
406
|
if available_agents:
|
|
@@ -2,7 +2,7 @@ from typing import Any, Dict, Literal, Optional, Text
|
|
|
2
2
|
|
|
3
3
|
import structlog
|
|
4
4
|
|
|
5
|
-
from rasa.core.
|
|
5
|
+
from rasa.core.config.configuration import Configuration
|
|
6
6
|
from rasa.dialogue_understanding.commands.command_syntax_manager import (
|
|
7
7
|
CommandSyntaxVersion,
|
|
8
8
|
)
|
|
@@ -125,7 +125,7 @@ class CompactLLMCommandGenerator(SingleStepBasedLLMCommandGenerator):
|
|
|
125
125
|
"""Get the default prompt template file name for the command generator."""
|
|
126
126
|
return (
|
|
127
127
|
AGENT_DEFAULT_COMMAND_PROMPT_TEMPLATE_FILE_NAME
|
|
128
|
-
if
|
|
128
|
+
if Configuration.get_instance().available_agents.has_agents()
|
|
129
129
|
else DEFAULT_COMMAND_PROMPT_TEMPLATE_FILE_NAME
|
|
130
130
|
)
|
|
131
131
|
|
|
@@ -134,7 +134,7 @@ class CompactLLMCommandGenerator(SingleStepBasedLLMCommandGenerator):
|
|
|
134
134
|
"""Get the fallback prompt template file name for the command generator."""
|
|
135
135
|
return (
|
|
136
136
|
AGENT_FALLBACK_COMMAND_PROMPT_TEMPLATE_FILE_NAME
|
|
137
|
-
if
|
|
137
|
+
if Configuration.get_instance().available_agents.has_agents()
|
|
138
138
|
else FALLBACK_COMMAND_PROMPT_TEMPLATE_FILE_NAME
|
|
139
139
|
)
|
|
140
140
|
|
|
@@ -143,7 +143,7 @@ class CompactLLMCommandGenerator(SingleStepBasedLLMCommandGenerator):
|
|
|
143
143
|
"""Get the model prompt mapper for the command generator."""
|
|
144
144
|
return (
|
|
145
145
|
AGENT_MODEL_PROMPT_MAPPER
|
|
146
|
-
if
|
|
146
|
+
if Configuration.get_instance().available_agents.has_agents()
|
|
147
147
|
else MODEL_PROMPT_MAPPER
|
|
148
148
|
)
|
|
149
149
|
|
|
@@ -2,7 +2,7 @@ from typing import Any, Dict, Literal, Optional, Text
|
|
|
2
2
|
|
|
3
3
|
import structlog
|
|
4
4
|
|
|
5
|
-
from rasa.core.
|
|
5
|
+
from rasa.core.config.configuration import Configuration
|
|
6
6
|
from rasa.dialogue_understanding.commands.command_syntax_manager import (
|
|
7
7
|
CommandSyntaxVersion,
|
|
8
8
|
)
|
|
@@ -127,7 +127,7 @@ class SearchReadyLLMCommandGenerator(SingleStepBasedLLMCommandGenerator):
|
|
|
127
127
|
"""Get the default prompt template file name for the command generator."""
|
|
128
128
|
return (
|
|
129
129
|
AGENT_DEFAULT_COMMAND_PROMPT_TEMPLATE_FILE_NAME
|
|
130
|
-
if
|
|
130
|
+
if Configuration.get_instance().available_agents.has_agents()
|
|
131
131
|
else DEFAULT_COMMAND_PROMPT_TEMPLATE_FILE_NAME
|
|
132
132
|
)
|
|
133
133
|
|
|
@@ -136,7 +136,7 @@ class SearchReadyLLMCommandGenerator(SingleStepBasedLLMCommandGenerator):
|
|
|
136
136
|
"""Get the fallback prompt template file name for the command generator."""
|
|
137
137
|
return (
|
|
138
138
|
AGENT_FALLBACK_COMMAND_PROMPT_TEMPLATE_FILE_NAME
|
|
139
|
-
if
|
|
139
|
+
if Configuration.get_instance().available_agents.has_agents()
|
|
140
140
|
else FALLBACK_COMMAND_PROMPT_TEMPLATE_FILE_NAME
|
|
141
141
|
)
|
|
142
142
|
|
|
@@ -145,7 +145,7 @@ class SearchReadyLLMCommandGenerator(SingleStepBasedLLMCommandGenerator):
|
|
|
145
145
|
"""Get the model prompt mapper for the command generator."""
|
|
146
146
|
return (
|
|
147
147
|
AGENT_MODEL_PROMPT_MAPPER
|
|
148
|
-
if
|
|
148
|
+
if Configuration.get_instance().available_agents.has_agents()
|
|
149
149
|
else MODEL_PROMPT_MAPPER
|
|
150
150
|
)
|
|
151
151
|
|
rasa/dialogue_understanding/generator/single_step/single_step_based_llm_command_generator.py
CHANGED
|
@@ -9,7 +9,7 @@ from rasa.agents.utils import (
|
|
|
9
9
|
get_active_agent_info,
|
|
10
10
|
get_completed_agents_info,
|
|
11
11
|
)
|
|
12
|
-
from rasa.core.
|
|
12
|
+
from rasa.core.config.configuration import Configuration
|
|
13
13
|
from rasa.dialogue_understanding.commands import (
|
|
14
14
|
CannotHandleCommand,
|
|
15
15
|
Command,
|
|
@@ -403,7 +403,7 @@ class SingleStepBasedLLMCommandGenerator(LLMBasedCommandGenerator, ABC):
|
|
|
403
403
|
current_slot_allowed_values = allowed_values_for_slot(
|
|
404
404
|
tracker.slots.get(current_slot)
|
|
405
405
|
)
|
|
406
|
-
has_agents =
|
|
406
|
+
has_agents = Configuration.get_instance().available_agents.has_agents()
|
|
407
407
|
current_conversation = tracker_as_readable_transcript(
|
|
408
408
|
tracker, highlight_agent_turns=has_agents
|
|
409
409
|
)
|
|
@@ -227,28 +227,30 @@ flows:
|
|
|
227
227
|
- noop: true
|
|
228
228
|
next:
|
|
229
229
|
- if: context.multiple_flows_interrupted
|
|
230
|
+
then: collect_interrupted_flow_to_continue
|
|
231
|
+
- else: collect_continue_interrupted_flow_confirmation
|
|
232
|
+
- id: collect_interrupted_flow_to_continue
|
|
233
|
+
collect: interrupted_flow_to_continue
|
|
234
|
+
description: "Fill this slot with the name of the flow the user wants to continue. If the user does not want to continue any of the interrupted flows, fill this slot with 'none'."
|
|
235
|
+
next:
|
|
236
|
+
- if: slots.interrupted_flow_to_continue != "none"
|
|
237
|
+
then:
|
|
238
|
+
- action: action_continue_interrupted_flow
|
|
239
|
+
next: END
|
|
240
|
+
- else:
|
|
241
|
+
- action: action_cancel_interrupted_flows
|
|
242
|
+
next: END
|
|
243
|
+
- id: collect_continue_interrupted_flow_confirmation
|
|
244
|
+
collect: continue_interrupted_flow_confirmation
|
|
245
|
+
description: "If the user wants to continue the interrupted flow, fill this slot with true. If the user does not want to continue the interrupted flow, fill this slot with false."
|
|
246
|
+
next:
|
|
247
|
+
- if: slots.continue_interrupted_flow_confirmation
|
|
230
248
|
then:
|
|
231
|
-
-
|
|
232
|
-
|
|
233
|
-
next:
|
|
234
|
-
- if: slots.interrupted_flow_to_continue != "none"
|
|
235
|
-
then:
|
|
236
|
-
- action: action_continue_interrupted_flow
|
|
237
|
-
next: END
|
|
238
|
-
- else:
|
|
239
|
-
- action: action_cancel_interrupted_flows
|
|
240
|
-
next: END
|
|
249
|
+
- action: action_continue_interrupted_flow
|
|
250
|
+
next: END
|
|
241
251
|
- else:
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
next:
|
|
245
|
-
- if: slots.continue_interrupted_flow_confirmation
|
|
246
|
-
then:
|
|
247
|
-
- action: action_continue_interrupted_flow
|
|
248
|
-
next: END
|
|
249
|
-
- else:
|
|
250
|
-
- action: action_cancel_interrupted_flows
|
|
251
|
-
next: END
|
|
252
|
+
- action: action_cancel_interrupted_flows
|
|
253
|
+
next: END
|
|
252
254
|
|
|
253
255
|
pattern_correction:
|
|
254
256
|
description: Conversation repair flow for managing user input changes or error corrections
|
|
@@ -6,9 +6,9 @@ from typing import Any, Dict, List, Optional, Union
|
|
|
6
6
|
import structlog
|
|
7
7
|
from tqdm import tqdm
|
|
8
8
|
|
|
9
|
-
from rasa.core.available_agents import AvailableAgents
|
|
10
9
|
from rasa.core.channels import CollectingOutputChannel, UserMessage
|
|
11
10
|
from rasa.core.config.available_endpoints import AvailableEndpoints
|
|
11
|
+
from rasa.core.config.configuration import Configuration
|
|
12
12
|
from rasa.core.exceptions import AgentNotReady
|
|
13
13
|
from rasa.core.persistor import StorageType
|
|
14
14
|
from rasa.dialogue_understanding.commands import Command
|
|
@@ -71,7 +71,7 @@ class DialogueUnderstandingTestRunner:
|
|
|
71
71
|
import rasa.core.agent
|
|
72
72
|
|
|
73
73
|
self._check_action_server(endpoints)
|
|
74
|
-
sub_agents =
|
|
74
|
+
sub_agents = Configuration.get_instance().available_agents
|
|
75
75
|
|
|
76
76
|
self.agent = asyncio.run(
|
|
77
77
|
rasa.core.agent.load_agent(
|
rasa/e2e_test/e2e_test_runner.py
CHANGED
|
@@ -13,9 +13,9 @@ import structlog
|
|
|
13
13
|
from tqdm import tqdm
|
|
14
14
|
|
|
15
15
|
import rasa.shared.utils.io
|
|
16
|
-
from rasa.core.available_agents import AvailableAgents
|
|
17
16
|
from rasa.core.channels import CollectingOutputChannel, UserMessage
|
|
18
17
|
from rasa.core.config.available_endpoints import AvailableEndpoints
|
|
18
|
+
from rasa.core.config.configuration import Configuration
|
|
19
19
|
from rasa.core.constants import ACTIVE_FLOW_METADATA_KEY, STEP_ID_METADATA_KEY
|
|
20
20
|
from rasa.core.exceptions import AgentNotReady
|
|
21
21
|
from rasa.core.persistor import StorageType
|
|
@@ -96,7 +96,7 @@ class E2ETestRunner:
|
|
|
96
96
|
if endpoints and not are_custom_actions_stubbed:
|
|
97
97
|
self._action_server_is_reachable(endpoints)
|
|
98
98
|
|
|
99
|
-
sub_agents =
|
|
99
|
+
sub_agents = Configuration.get_instance().available_agents
|
|
100
100
|
|
|
101
101
|
self.agent = asyncio.run(
|
|
102
102
|
rasa.core.agent.load_agent(
|
rasa/engine/validation.py
CHANGED
|
@@ -81,6 +81,7 @@ from rasa.shared.constants import (
|
|
|
81
81
|
ROUTING_STRATEGIES_NOT_REQUIRING_CACHE,
|
|
82
82
|
ROUTING_STRATEGIES_REQUIRING_REDIS_CACHE,
|
|
83
83
|
ROUTING_STRATEGY_CONFIG_KEY,
|
|
84
|
+
SECRET_DATA_FORMAT_PATTERN,
|
|
84
85
|
SENSITIVE_DATA,
|
|
85
86
|
USE_CHAT_COMPLETIONS_ENDPOINT_CONFIG_KEY,
|
|
86
87
|
VALID_PROVIDERS_FOR_API_TYPE_CONFIG_KEY,
|
|
@@ -1359,7 +1360,10 @@ def _validate_usage_of_environment_variables_in_model_group_config(
|
|
|
1359
1360
|
for model_config in model_group[MODELS_CONFIG_KEY]:
|
|
1360
1361
|
for key, value in model_config.items():
|
|
1361
1362
|
if isinstance(value, str):
|
|
1362
|
-
if
|
|
1363
|
+
if (
|
|
1364
|
+
re.match(SECRET_DATA_FORMAT_PATTERN, value)
|
|
1365
|
+
and key not in allowed_env_vars
|
|
1366
|
+
):
|
|
1363
1367
|
raise ValidationError(
|
|
1364
1368
|
code="engine.validation.validate_model_group_configuration_setup"
|
|
1365
1369
|
".invalid_use_of_environment_variables",
|
|
@@ -1386,7 +1390,7 @@ def _validate_sensitive_keys_are_an_environment_variables_for_model_groups(
|
|
|
1386
1390
|
for key, value in model_config.items():
|
|
1387
1391
|
if key in SENSITIVE_DATA:
|
|
1388
1392
|
if isinstance(value, str):
|
|
1389
|
-
if not re.match(
|
|
1393
|
+
if not re.match(SECRET_DATA_FORMAT_PATTERN, value):
|
|
1390
1394
|
raise ValidationError(
|
|
1391
1395
|
code="engine.validation.validate_model_group_configuration_setup"
|
|
1392
1396
|
".sensitive_key_string_value_must_be_set_as_env_var",
|
|
@@ -8,6 +8,7 @@ import httpx
|
|
|
8
8
|
|
|
9
9
|
from rasa.shared.agents.auth.auth_strategy import AgentAuthStrategy
|
|
10
10
|
from rasa.shared.agents.auth.constants import (
|
|
11
|
+
CONFIG_AUDIENCE_KEY,
|
|
11
12
|
CONFIG_CLIENT_ID_KEY,
|
|
12
13
|
CONFIG_CLIENT_SECRET_KEY,
|
|
13
14
|
CONFIG_OAUTH_KEY,
|
|
@@ -35,12 +36,14 @@ class OAuth2AuthStrategy(AgentAuthStrategy):
|
|
|
35
36
|
token_url: str,
|
|
36
37
|
client_id: str,
|
|
37
38
|
client_secret: str,
|
|
38
|
-
|
|
39
|
+
audience: Optional[str] = None,
|
|
40
|
+
scope: Optional[str] = None,
|
|
39
41
|
timeout: Optional[int] = None,
|
|
40
42
|
):
|
|
41
43
|
self.token_url = token_url
|
|
42
44
|
self.client_id = client_id
|
|
43
45
|
self.client_secret = client_secret
|
|
46
|
+
self.audience = audience
|
|
44
47
|
self.scope = scope
|
|
45
48
|
self.timeout = timeout or self.DEFAULT_ACCESS_TOKEN_TIMEOUT
|
|
46
49
|
|
|
@@ -65,6 +68,7 @@ class OAuth2AuthStrategy(AgentAuthStrategy):
|
|
|
65
68
|
token_url = oauth_config.get(CONFIG_TOKEN_URL_KEY)
|
|
66
69
|
client_id = oauth_config.get(CONFIG_CLIENT_ID_KEY)
|
|
67
70
|
client_secret = oauth_config.get(CONFIG_CLIENT_SECRET_KEY)
|
|
71
|
+
audience = oauth_config.get(CONFIG_AUDIENCE_KEY)
|
|
68
72
|
scope = oauth_config.get(CONFIG_SCOPE_KEY)
|
|
69
73
|
timeout = (
|
|
70
74
|
oauth_config.get(CONFIG_TIMEOUT_KEY) or cls.DEFAULT_ACCESS_TOKEN_TIMEOUT
|
|
@@ -76,13 +80,12 @@ class OAuth2AuthStrategy(AgentAuthStrategy):
|
|
|
76
80
|
raise ValueError("Client ID is required for OAuth2 authentication")
|
|
77
81
|
if not client_secret:
|
|
78
82
|
raise ValueError("Client secret is required for OAuth2 authentication")
|
|
79
|
-
if not scope:
|
|
80
|
-
raise ValueError("Scope is required for OAuth2 authentication")
|
|
81
83
|
|
|
82
84
|
return cls(
|
|
83
85
|
token_url=token_url,
|
|
84
86
|
client_id=client_id,
|
|
85
87
|
client_secret=client_secret,
|
|
88
|
+
audience=audience,
|
|
86
89
|
scope=scope,
|
|
87
90
|
timeout=timeout,
|
|
88
91
|
)
|
|
@@ -118,8 +121,11 @@ class OAuth2AuthStrategy(AgentAuthStrategy):
|
|
|
118
121
|
"grant_type": self._grant_type,
|
|
119
122
|
"client_id": self.client_id,
|
|
120
123
|
"client_secret": self.client_secret,
|
|
121
|
-
"scope": self.scope,
|
|
122
124
|
}
|
|
125
|
+
if self.scope:
|
|
126
|
+
data["scope"] = self.scope
|
|
127
|
+
if self.audience:
|
|
128
|
+
data["audience"] = self.audience
|
|
123
129
|
|
|
124
130
|
# Resolve environment variables in data.
|
|
125
131
|
resolved_data = resolve_environment_variables(data)
|
|
@@ -7,6 +7,7 @@ CONFIG_CLIENT_ID_KEY = "client_id"
|
|
|
7
7
|
CONFIG_CLIENT_SECRET_KEY = "client_secret"
|
|
8
8
|
CONFIG_TOKEN_URL_KEY = "token_url"
|
|
9
9
|
CONFIG_SCOPE_KEY = "scope"
|
|
10
|
+
CONFIG_AUDIENCE_KEY = "audience"
|
|
10
11
|
CONFIG_GRANT_TYPE_KEY = "grant_type"
|
|
11
12
|
CONFIG_TIMEOUT_KEY = "timeout"
|
|
12
13
|
CONFIG_MODULE_KEY = "module"
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""Utilities for agent authentication and secret validation."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from typing import Any, Dict
|
|
5
|
+
|
|
6
|
+
from rasa.exceptions import ValidationError
|
|
7
|
+
from rasa.shared.agents.auth.constants import (
|
|
8
|
+
CONFIG_API_KEY_KEY,
|
|
9
|
+
CONFIG_CLIENT_SECRET_KEY,
|
|
10
|
+
CONFIG_OAUTH_KEY,
|
|
11
|
+
CONFIG_TOKEN_KEY,
|
|
12
|
+
)
|
|
13
|
+
from rasa.shared.constants import SECRET_DATA_FORMAT_PATTERN
|
|
14
|
+
|
|
15
|
+
AUTH_SECRETS = [CONFIG_API_KEY_KEY, CONFIG_TOKEN_KEY, CONFIG_CLIENT_SECRET_KEY]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _is_valid_secret_data_format(value: str) -> bool:
|
|
19
|
+
"""Check if a value is in the correct environment variable format.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
value: The value to check
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
True if the value is in format "${env_var}", False otherwise
|
|
26
|
+
"""
|
|
27
|
+
if not isinstance(value, str):
|
|
28
|
+
return False
|
|
29
|
+
|
|
30
|
+
# Use the common regex pattern for environment variable validation
|
|
31
|
+
return bool(re.match(SECRET_DATA_FORMAT_PATTERN, value))
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _validate_secret_value(value: Any, key: str, context: str) -> None:
|
|
35
|
+
"""Generic function to validate a single secret value.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
value: The value to validate
|
|
39
|
+
key: The key name for error messages
|
|
40
|
+
context: Context for error messages
|
|
41
|
+
(e.g., "agent 'my_agent'", "MCP server 'my_server'")
|
|
42
|
+
"""
|
|
43
|
+
if isinstance(value, str):
|
|
44
|
+
if not _is_valid_secret_data_format(value):
|
|
45
|
+
raise ValidationError(
|
|
46
|
+
code="validation.sensitive_key_string_value_must_be_set_as_env_var",
|
|
47
|
+
event_info=(
|
|
48
|
+
f"You defined the '{key}' in {context} as a string. The '{key}' "
|
|
49
|
+
f"must be set as an environment variable. Please update your "
|
|
50
|
+
f"config."
|
|
51
|
+
),
|
|
52
|
+
key=key,
|
|
53
|
+
)
|
|
54
|
+
else:
|
|
55
|
+
raise ValidationError(
|
|
56
|
+
code="validation.sensitive_key_must_be_set_as_env_var",
|
|
57
|
+
event_info=(
|
|
58
|
+
f"You should define the '{key}' in {context} using the environment "
|
|
59
|
+
f"variable syntax - ${{ENV_VARIABLE_NAME}}. Please update your config."
|
|
60
|
+
),
|
|
61
|
+
key=key,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def validate_secrets_in_params(
|
|
66
|
+
params: Dict[str, Any], context_name: str = "configuration"
|
|
67
|
+
) -> None:
|
|
68
|
+
"""Validate that secrets in params are in environment variable format.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
params: The parameters dictionary to validate
|
|
72
|
+
context_name: Name of the context for error messages
|
|
73
|
+
(e.g., "agent", "MCP server")
|
|
74
|
+
"""
|
|
75
|
+
for key, value in params.items():
|
|
76
|
+
if key in AUTH_SECRETS:
|
|
77
|
+
_validate_secret_value(value, key, context_name)
|
|
78
|
+
elif key == CONFIG_OAUTH_KEY and isinstance(value, dict):
|
|
79
|
+
# Handle oauth object specifically - we know it contains client_secret
|
|
80
|
+
if CONFIG_CLIENT_SECRET_KEY in value:
|
|
81
|
+
_validate_secret_value(
|
|
82
|
+
value[CONFIG_CLIENT_SECRET_KEY],
|
|
83
|
+
CONFIG_CLIENT_SECRET_KEY,
|
|
84
|
+
context_name,
|
|
85
|
+
)
|
rasa/shared/constants.py
CHANGED
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
from dataclasses import dataclass
|
|
4
4
|
from typing import TYPE_CHECKING, Any, Dict, Generator, List, Optional, Text
|
|
5
5
|
|
|
6
|
-
from rasa.core.
|
|
6
|
+
from rasa.core.config.configuration import Configuration
|
|
7
7
|
from rasa.shared.core.flows.flow_step import FlowStep
|
|
8
8
|
|
|
9
9
|
if TYPE_CHECKING:
|
|
@@ -102,7 +102,7 @@ class CallFlowStep(FlowStep):
|
|
|
102
102
|
|
|
103
103
|
def is_calling_agent(self) -> bool:
|
|
104
104
|
"""Returns True if the call references an agent."""
|
|
105
|
-
return self.call in
|
|
105
|
+
return self.call in Configuration.get_instance().available_agents.agents
|
|
106
106
|
|
|
107
107
|
@property
|
|
108
108
|
def default_id_postfix(self) -> str:
|
rasa/shared/utils/llm.py
CHANGED
|
@@ -23,6 +23,7 @@ from typing import (
|
|
|
23
23
|
)
|
|
24
24
|
|
|
25
25
|
import structlog
|
|
26
|
+
from jinja2 import Environment, select_autoescape
|
|
26
27
|
from pydantic import BaseModel, Field
|
|
27
28
|
|
|
28
29
|
import rasa.cli.telemetry
|
|
@@ -718,6 +719,38 @@ def embedder_client_factory(
|
|
|
718
719
|
return client
|
|
719
720
|
|
|
720
721
|
|
|
722
|
+
def validate_jinja2_template(template_content: Text) -> None:
|
|
723
|
+
"""Validate that a template string has valid Jinja2 syntax.
|
|
724
|
+
|
|
725
|
+
Args:
|
|
726
|
+
template_content: The template content to validate
|
|
727
|
+
|
|
728
|
+
Raises:
|
|
729
|
+
jinja2.exceptions.TemplateSyntaxError: If the template has invalid syntax
|
|
730
|
+
Exception: If there's an error during validation
|
|
731
|
+
"""
|
|
732
|
+
# Create environment with custom filters
|
|
733
|
+
env = Environment(
|
|
734
|
+
autoescape=select_autoescape(
|
|
735
|
+
disabled_extensions=["jinja2"],
|
|
736
|
+
default_for_string=False,
|
|
737
|
+
default=True,
|
|
738
|
+
)
|
|
739
|
+
)
|
|
740
|
+
# Register filters - lazy import to avoid circular dependencies
|
|
741
|
+
from rasa.dialogue_understanding.generator._jinja_filters import (
|
|
742
|
+
to_json_escaped_string,
|
|
743
|
+
)
|
|
744
|
+
from rasa.dialogue_understanding.generator.constants import (
|
|
745
|
+
TO_JSON_ESCAPED_STRING_JINJA_FILTER,
|
|
746
|
+
)
|
|
747
|
+
|
|
748
|
+
env.filters[TO_JSON_ESCAPED_STRING_JINJA_FILTER] = to_json_escaped_string
|
|
749
|
+
|
|
750
|
+
# Validate Jinja2 syntax
|
|
751
|
+
env.from_string(template_content)
|
|
752
|
+
|
|
753
|
+
|
|
721
754
|
def get_prompt_template(
|
|
722
755
|
jinja_file_path: Optional[Text],
|
|
723
756
|
default_prompt_template: Text,
|
rasa/telemetry.py
CHANGED
|
@@ -1129,11 +1129,11 @@ def track_model_training(
|
|
|
1129
1129
|
def _collect_agent_configuration(flows: FlowsList) -> Dict[str, Any]:
|
|
1130
1130
|
agent_data: Dict[str, Any] = {}
|
|
1131
1131
|
|
|
1132
|
-
from rasa.core.available_agents import AvailableAgents
|
|
1133
1132
|
from rasa.core.config.configuration import Configuration
|
|
1134
1133
|
|
|
1135
|
-
|
|
1136
|
-
|
|
1134
|
+
configuration = Configuration.get_instance()
|
|
1135
|
+
agents = configuration.available_agents.agents
|
|
1136
|
+
mcp_servers = configuration.endpoints.mcp_servers
|
|
1137
1137
|
|
|
1138
1138
|
if not agents and not mcp_servers:
|
|
1139
1139
|
return agent_data
|
rasa/utils/pypred.py
CHANGED
|
@@ -10,6 +10,7 @@ https://rasahq.atlassian.net/browse/ATO-1925
|
|
|
10
10
|
The solution is based on https://github.com/FreeCAD/FreeCAD/issues/6315
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
|
+
import logging
|
|
13
14
|
from typing import Any
|
|
14
15
|
|
|
15
16
|
import ply.yacc
|
|
@@ -19,12 +20,18 @@ from pypred import Predicate as OriginalPredicate # noqa: TID251
|
|
|
19
20
|
# Store the original yacc function
|
|
20
21
|
_original_yacc = ply.yacc.yacc
|
|
21
22
|
|
|
23
|
+
# Create a logger that suppresses warnings to avoid yacc table file version warnings
|
|
24
|
+
_yacc_logger = logging.getLogger("ply.yacc")
|
|
25
|
+
_yacc_logger.setLevel(logging.ERROR)
|
|
26
|
+
|
|
22
27
|
|
|
23
28
|
def patched_yacc(*args: Any, **kwargs: Any) -> Any:
|
|
24
29
|
# Disable generation of debug ('parser.out') and table
|
|
25
30
|
# cache ('parsetab.py'), as it requires a writable location.
|
|
26
31
|
kwargs["write_tables"] = False
|
|
27
32
|
kwargs["module"] = pypred.parser
|
|
33
|
+
# Suppress yacc warnings by using a logger that only shows errors
|
|
34
|
+
kwargs["errorlog"] = _yacc_logger
|
|
28
35
|
return _original_yacc(*args, **kwargs)
|
|
29
36
|
|
|
30
37
|
|