rasa-pro 3.14.0rc4__py3-none-any.whl → 3.14.2__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/agent_manager.py +7 -5
- rasa/agents/protocol/a2a/a2a_agent.py +13 -11
- rasa/agents/protocol/mcp/mcp_base_agent.py +49 -11
- rasa/agents/validation.py +4 -2
- rasa/builder/copilot/copilot_templated_message_provider.py +1 -1
- rasa/builder/validation_service.py +4 -0
- rasa/cli/arguments/data.py +9 -0
- rasa/cli/data.py +72 -6
- rasa/cli/interactive.py +3 -0
- rasa/cli/llm_fine_tuning.py +1 -0
- rasa/cli/project_templates/defaults.py +1 -0
- rasa/cli/validation/bot_config.py +2 -0
- rasa/constants.py +2 -1
- rasa/core/actions/action_clean_stack.py +32 -0
- rasa/core/actions/action_exceptions.py +1 -1
- rasa/core/actions/constants.py +4 -0
- rasa/core/actions/custom_action_executor.py +70 -12
- rasa/core/actions/grpc_custom_action_executor.py +41 -2
- rasa/core/actions/http_custom_action_executor.py +49 -25
- rasa/core/agent.py +4 -1
- rasa/core/available_agents.py +1 -1
- rasa/core/channels/voice_stream/browser_audio.py +3 -3
- rasa/core/channels/voice_stream/voice_channel.py +27 -17
- rasa/core/config/credentials.py +3 -3
- rasa/core/exceptions.py +1 -1
- rasa/core/featurizers/tracker_featurizers.py +3 -2
- rasa/core/persistor.py +7 -7
- rasa/core/policies/flows/agent_executor.py +84 -4
- rasa/core/policies/flows/flow_exceptions.py +5 -2
- rasa/core/policies/flows/flow_executor.py +52 -31
- rasa/core/policies/flows/mcp_tool_executor.py +7 -1
- rasa/core/policies/rule_policy.py +1 -1
- rasa/core/run.py +21 -5
- rasa/dialogue_understanding/commands/cancel_flow_command.py +1 -1
- rasa/dialogue_understanding/generator/llm_based_command_generator.py +6 -3
- rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +15 -7
- rasa/dialogue_understanding/generator/single_step/search_ready_llm_command_generator.py +15 -8
- rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +1 -1
- rasa/dialogue_understanding/processor/command_processor.py +13 -7
- rasa/e2e_test/e2e_config.py +4 -3
- rasa/engine/recipes/default_components.py +16 -6
- rasa/graph_components/validators/default_recipe_validator.py +10 -4
- rasa/model_manager/runner_service.py +1 -1
- rasa/nlu/classifiers/diet_classifier.py +2 -0
- rasa/privacy/privacy_config.py +1 -1
- rasa/shared/agents/auth/auth_strategy/oauth2_auth_strategy.py +4 -7
- rasa/shared/core/flows/flow.py +8 -2
- rasa/shared/core/slots.py +55 -24
- rasa/shared/core/training_data/story_reader/story_reader.py +1 -1
- rasa/shared/exceptions.py +23 -2
- rasa/shared/providers/_configs/azure_openai_client_config.py +4 -5
- rasa/shared/providers/_configs/default_litellm_client_config.py +4 -4
- rasa/shared/providers/_configs/litellm_router_client_config.py +3 -2
- rasa/shared/providers/_configs/openai_client_config.py +5 -7
- rasa/shared/providers/_configs/rasa_llm_client_config.py +4 -4
- rasa/shared/providers/_configs/self_hosted_llm_client_config.py +4 -4
- rasa/shared/providers/llm/_base_litellm_client.py +42 -14
- rasa/shared/providers/llm/litellm_router_llm_client.py +40 -17
- rasa/shared/providers/llm/self_hosted_llm_client.py +34 -32
- rasa/shared/utils/common.py +9 -1
- rasa/shared/utils/configs.py +5 -8
- rasa/shared/utils/llm.py +21 -4
- rasa/shared/utils/mcp/server_connection.py +7 -4
- rasa/studio/download.py +3 -0
- rasa/studio/prompts.py +1 -0
- rasa/studio/upload.py +4 -0
- rasa/utils/common.py +9 -0
- rasa/utils/endpoints.py +6 -0
- rasa/utils/installation_utils.py +111 -0
- rasa/utils/log_utils.py +20 -1
- rasa/utils/tensorflow/callback.py +2 -0
- rasa/utils/tensorflow/models.py +3 -0
- rasa/utils/train_utils.py +2 -0
- rasa/version.py +1 -1
- {rasa_pro-3.14.0rc4.dist-info → rasa_pro-3.14.2.dist-info}/METADATA +3 -3
- {rasa_pro-3.14.0rc4.dist-info → rasa_pro-3.14.2.dist-info}/RECORD +79 -78
- {rasa_pro-3.14.0rc4.dist-info → rasa_pro-3.14.2.dist-info}/NOTICE +0 -0
- {rasa_pro-3.14.0rc4.dist-info → rasa_pro-3.14.2.dist-info}/WHEEL +0 -0
- {rasa_pro-3.14.0rc4.dist-info → rasa_pro-3.14.2.dist-info}/entry_points.txt +0 -0
rasa/agents/agent_manager.py
CHANGED
|
@@ -15,7 +15,7 @@ structlogger = structlog.get_logger()
|
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
class AgentManager(metaclass=Singleton):
|
|
18
|
-
"""High-level agent management with protocol abstraction"""
|
|
18
|
+
"""High-level agent management with protocol abstraction."""
|
|
19
19
|
|
|
20
20
|
agents: ClassVar[Dict[AgentIdentifier, AgentProtocol]] = {}
|
|
21
21
|
|
|
@@ -66,9 +66,11 @@ class AgentManager(metaclass=Singleton):
|
|
|
66
66
|
async def connect_agent(
|
|
67
67
|
self, agent_name: str, protocol_type: ProtocolType, config: AgentConfig
|
|
68
68
|
) -> None:
|
|
69
|
-
"""Connect to agent using specified protocol
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
"""Connect to agent using specified protocol.
|
|
70
|
+
|
|
71
|
+
Also, load the default resources and persist the agent to the manager
|
|
72
|
+
in a ready-to-use state so that it can be used immediately
|
|
73
|
+
to send messages to the agent.
|
|
72
74
|
|
|
73
75
|
Args:
|
|
74
76
|
agent_name: The name of the agent.
|
|
@@ -109,7 +111,7 @@ class AgentManager(metaclass=Singleton):
|
|
|
109
111
|
agent_id=str(agent_identifier),
|
|
110
112
|
event_info=event_info,
|
|
111
113
|
)
|
|
112
|
-
raise AgentInitializationException(e) from e
|
|
114
|
+
raise AgentInitializationException(e, suppress_stack_trace=True) from e
|
|
113
115
|
|
|
114
116
|
async def run_agent(
|
|
115
117
|
self, agent_name: str, protocol_type: ProtocolType, context: AgentInput
|
|
@@ -65,7 +65,7 @@ structlogger = structlog.get_logger()
|
|
|
65
65
|
|
|
66
66
|
|
|
67
67
|
class A2AAgent(AgentProtocol):
|
|
68
|
-
"""A2A client implementation"""
|
|
68
|
+
"""A2A client implementation."""
|
|
69
69
|
|
|
70
70
|
__SUPPORTED_OUTPUT_MODES: ClassVar[list[str]] = [
|
|
71
71
|
"text",
|
|
@@ -169,7 +169,8 @@ class A2AAgent(AgentProtocol):
|
|
|
169
169
|
error=str(exception),
|
|
170
170
|
)
|
|
171
171
|
raise AgentInitializationException(
|
|
172
|
-
f"Failed to initialize A2A client
|
|
172
|
+
f"Failed to initialize A2A client "
|
|
173
|
+
f"for agent '{self._name}': {exception}",
|
|
173
174
|
) from exception
|
|
174
175
|
|
|
175
176
|
await self._perform_health_check()
|
|
@@ -180,7 +181,7 @@ class A2AAgent(AgentProtocol):
|
|
|
180
181
|
)
|
|
181
182
|
|
|
182
183
|
async def disconnect(self) -> None:
|
|
183
|
-
"""We don't need to explicitly disconnect the A2A client"""
|
|
184
|
+
"""We don't need to explicitly disconnect the A2A client."""
|
|
184
185
|
return
|
|
185
186
|
|
|
186
187
|
# ============================================================================
|
|
@@ -297,7 +298,7 @@ class A2AAgent(AgentProtocol):
|
|
|
297
298
|
def _handle_send_message_response(
|
|
298
299
|
self, agent_input: AgentInput, response: ClientEvent | Message
|
|
299
300
|
) -> Optional[AgentOutput]:
|
|
300
|
-
"""Handle possible response types from the A2A client
|
|
301
|
+
"""Handle possible response types from the A2A client.
|
|
301
302
|
|
|
302
303
|
In case of streaming, the response can be either exactly *one* Message,
|
|
303
304
|
or a *series* of tuples of (Task, Optional[TaskUpdateEvent]).
|
|
@@ -410,8 +411,8 @@ class A2AAgent(AgentProtocol):
|
|
|
410
411
|
agent_input: AgentInput,
|
|
411
412
|
task: Task,
|
|
412
413
|
) -> Optional[AgentOutput]:
|
|
413
|
-
"""If
|
|
414
|
-
|
|
414
|
+
"""If task status is terminal (e.g. completed, failed) return AgentOutput.
|
|
415
|
+
|
|
415
416
|
If the task is still in progress (i.e., submitted, working), return None,
|
|
416
417
|
so that the streaming or pooling agent can continue to wait for updates.
|
|
417
418
|
"""
|
|
@@ -655,6 +656,7 @@ class A2AAgent(AgentProtocol):
|
|
|
655
656
|
@staticmethod
|
|
656
657
|
def _generate_completed_response_message(task: Task) -> str:
|
|
657
658
|
"""Generate a response message for a completed task.
|
|
659
|
+
|
|
658
660
|
In case of completed tasks, the final message might be in
|
|
659
661
|
the task status message or in the artifacts (or both).
|
|
660
662
|
"""
|
|
@@ -728,19 +730,19 @@ class A2AAgent(AgentProtocol):
|
|
|
728
730
|
|
|
729
731
|
except FileNotFoundError as e:
|
|
730
732
|
raise AgentInitializationException(
|
|
731
|
-
f"Agent card file not found: {agent_card_path}"
|
|
733
|
+
f"Agent card file not found: {agent_card_path}",
|
|
732
734
|
) from e
|
|
733
735
|
except (IOError, PermissionError) as e:
|
|
734
736
|
raise AgentInitializationException(
|
|
735
|
-
f"Error reading agent card file {agent_card_path}: {e}"
|
|
737
|
+
f"Error reading agent card file {agent_card_path}: {e}",
|
|
736
738
|
) from e
|
|
737
739
|
except json.JSONDecodeError as e:
|
|
738
740
|
raise AgentInitializationException(
|
|
739
|
-
f"Invalid JSON in agent card file {agent_card_path}: {e}"
|
|
741
|
+
f"Invalid JSON in agent card file {agent_card_path}: {e}",
|
|
740
742
|
) from e
|
|
741
743
|
except ValidationError as e:
|
|
742
744
|
raise AgentInitializationException(
|
|
743
|
-
f"Failed to load agent card from {agent_card_path}: {e}"
|
|
745
|
+
f"Failed to load agent card from {agent_card_path}: {e}",
|
|
744
746
|
) from e
|
|
745
747
|
|
|
746
748
|
@staticmethod
|
|
@@ -798,7 +800,7 @@ class A2AAgent(AgentProtocol):
|
|
|
798
800
|
|
|
799
801
|
raise AgentInitializationException(
|
|
800
802
|
f"Failed to resolve agent card from {agent_card_path} after "
|
|
801
|
-
f"{max_retries} attempts."
|
|
803
|
+
f"{max_retries} attempts.",
|
|
802
804
|
)
|
|
803
805
|
|
|
804
806
|
# ============================================================================
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import json
|
|
2
2
|
from abc import abstractmethod
|
|
3
|
-
from datetime import datetime
|
|
3
|
+
from datetime import datetime, timedelta
|
|
4
4
|
from inspect import isawaitable
|
|
5
5
|
from typing import Any, Dict, List, Optional, Tuple
|
|
6
6
|
|
|
7
|
+
import anyio
|
|
7
8
|
import structlog
|
|
8
9
|
from jinja2 import Template
|
|
9
10
|
from mcp import ListToolsResult
|
|
@@ -75,6 +76,8 @@ class MCPBaseAgent(AgentProtocol):
|
|
|
75
76
|
|
|
76
77
|
MAX_ITERATIONS = 10
|
|
77
78
|
|
|
79
|
+
TOOL_CALL_DEFAULT_TIMEOUT = 10 # seconds
|
|
80
|
+
|
|
78
81
|
# ============================================================================
|
|
79
82
|
# Initialization & Setup
|
|
80
83
|
# ============================================================================
|
|
@@ -288,14 +291,16 @@ class MCPBaseAgent(AgentProtocol):
|
|
|
288
291
|
event_info="All connection attempts failed.",
|
|
289
292
|
)
|
|
290
293
|
raise AgentInitializationException(
|
|
291
|
-
f"
|
|
292
|
-
f"
|
|
294
|
+
f"Agent `{self._name}` failed to initialize. Failed to connect "
|
|
295
|
+
f"to MCP servers after {self._max_retries} attempts. {ce!s}"
|
|
293
296
|
) from ce
|
|
294
297
|
except (Exception, AuthenticationError) as e:
|
|
295
298
|
if isinstance(e, AuthenticationError):
|
|
296
|
-
event_info =
|
|
299
|
+
event_info = (
|
|
300
|
+
f"Authentication error during agent initialization. {e!s}"
|
|
301
|
+
)
|
|
297
302
|
else:
|
|
298
|
-
event_info = "Unexpected error during agent initialization."
|
|
303
|
+
event_info = f"Unexpected error during agent initialization. {e!s}"
|
|
299
304
|
structlogger.error(
|
|
300
305
|
"mcp_agent.connect.unexpected_exception",
|
|
301
306
|
event_info=event_info,
|
|
@@ -303,7 +308,7 @@ class MCPBaseAgent(AgentProtocol):
|
|
|
303
308
|
agent_name=self._name,
|
|
304
309
|
agent_id=str(make_agent_identifier(self._name, self.protocol_type)),
|
|
305
310
|
)
|
|
306
|
-
raise AgentInitializationException(
|
|
311
|
+
raise AgentInitializationException(event_info) from e
|
|
307
312
|
|
|
308
313
|
async def connect_to_server(self, server_config: AgentMCPServerConfig) -> None:
|
|
309
314
|
server_name = server_config.name
|
|
@@ -325,7 +330,7 @@ class MCPBaseAgent(AgentProtocol):
|
|
|
325
330
|
except Exception as e:
|
|
326
331
|
event_info = (
|
|
327
332
|
f"Agent `{self._name}` failed to connect to MCP server - "
|
|
328
|
-
f"`{server_name}` @ `{server_config.url}
|
|
333
|
+
f"`{server_name}` @ `{server_config.url}`"
|
|
329
334
|
)
|
|
330
335
|
structlogger.error(
|
|
331
336
|
"mcp_agent.connect.failed_to_connect",
|
|
@@ -335,7 +340,9 @@ class MCPBaseAgent(AgentProtocol):
|
|
|
335
340
|
agent_name=self._name,
|
|
336
341
|
agent_id=str(make_agent_identifier(self._name, self.protocol_type)),
|
|
337
342
|
)
|
|
338
|
-
|
|
343
|
+
|
|
344
|
+
# Wrap exceptions with extra info and raise the same type of exception.
|
|
345
|
+
raise type(e)(f"{event_info} : {e!s}") from e
|
|
339
346
|
|
|
340
347
|
async def connect_to_servers(self) -> None:
|
|
341
348
|
"""Connect to MCP servers."""
|
|
@@ -624,7 +631,11 @@ class MCPBaseAgent(AgentProtocol):
|
|
|
624
631
|
connection = self._server_connections[server_id]
|
|
625
632
|
try:
|
|
626
633
|
session = await connection.ensure_active_session()
|
|
627
|
-
result = await session.call_tool(
|
|
634
|
+
result = await session.call_tool(
|
|
635
|
+
tool_name,
|
|
636
|
+
arguments,
|
|
637
|
+
read_timeout_seconds=timedelta(seconds=self.TOOL_CALL_DEFAULT_TIMEOUT),
|
|
638
|
+
)
|
|
628
639
|
return AgentToolResult.from_mcp_tool_result(tool_name, result)
|
|
629
640
|
except Exception as e:
|
|
630
641
|
return AgentToolResult(
|
|
@@ -637,6 +648,21 @@ class MCPBaseAgent(AgentProtocol):
|
|
|
637
648
|
),
|
|
638
649
|
)
|
|
639
650
|
|
|
651
|
+
async def _run_custom_tool(
|
|
652
|
+
self, custom_tool: CustomToolSchema, arguments: Dict[str, Any]
|
|
653
|
+
) -> AgentToolResult:
|
|
654
|
+
"""Run a custom tool and return the result.
|
|
655
|
+
|
|
656
|
+
Args:
|
|
657
|
+
custom_tool: The custom tool schema containing the tool executor.
|
|
658
|
+
arguments: The arguments to pass to the tool executor.
|
|
659
|
+
|
|
660
|
+
Returns:
|
|
661
|
+
The result of the tool execution as an AgentToolResult.
|
|
662
|
+
"""
|
|
663
|
+
result = custom_tool.tool_executor(arguments)
|
|
664
|
+
return await result if isawaitable(result) else result
|
|
665
|
+
|
|
640
666
|
async def _execute_tool_call(
|
|
641
667
|
self, tool_name: str, arguments: Dict[str, Any]
|
|
642
668
|
) -> AgentToolResult:
|
|
@@ -655,8 +681,20 @@ class MCPBaseAgent(AgentProtocol):
|
|
|
655
681
|
try:
|
|
656
682
|
for custom_tool in self._custom_tools:
|
|
657
683
|
if custom_tool.tool_name == tool_name:
|
|
658
|
-
|
|
659
|
-
|
|
684
|
+
try:
|
|
685
|
+
with anyio.fail_after(self.TOOL_CALL_DEFAULT_TIMEOUT):
|
|
686
|
+
return await self._run_custom_tool(custom_tool, arguments)
|
|
687
|
+
|
|
688
|
+
except TimeoutError:
|
|
689
|
+
return AgentToolResult(
|
|
690
|
+
tool_name=tool_name,
|
|
691
|
+
result=None,
|
|
692
|
+
is_error=True,
|
|
693
|
+
error_message=(
|
|
694
|
+
f"Built-in tool `{tool_name}` timed out after "
|
|
695
|
+
f"{self.TOOL_CALL_DEFAULT_TIMEOUT} seconds."
|
|
696
|
+
),
|
|
697
|
+
)
|
|
660
698
|
except Exception as e:
|
|
661
699
|
return AgentToolResult(
|
|
662
700
|
tool_name=tool_name,
|
rasa/agents/validation.py
CHANGED
|
@@ -347,9 +347,11 @@ def _handle_pydantic_validation_error(
|
|
|
347
347
|
def _validate_endpoint_references(agent_config: AgentConfig) -> None:
|
|
348
348
|
"""Validate that LLM and MCP server references in agent config are valid."""
|
|
349
349
|
agent_name = agent_config.agent.name
|
|
350
|
-
|
|
351
|
-
# Get available endpoints
|
|
352
350
|
endpoints = Configuration.get_instance().endpoints
|
|
351
|
+
if not endpoints.config_file_path:
|
|
352
|
+
# If no endpoints were loaded (e.g., `data validate` without --endpoints), skip
|
|
353
|
+
# endpoint reference checks
|
|
354
|
+
return
|
|
353
355
|
|
|
354
356
|
# Validate LLM configuration references
|
|
355
357
|
if agent_config.configuration and agent_config.configuration.llm:
|
|
@@ -47,6 +47,10 @@ async def validate_project(importer: TrainingDataImporter) -> Optional[str]:
|
|
|
47
47
|
with capture_validation_logs() as captured_logs:
|
|
48
48
|
try:
|
|
49
49
|
with _mock_sys_exit() as exit_tracker:
|
|
50
|
+
from rasa.core.config.configuration import Configuration
|
|
51
|
+
|
|
52
|
+
Configuration.initialise_empty()
|
|
53
|
+
|
|
50
54
|
validate_files(
|
|
51
55
|
fail_on_warnings=config.VALIDATION_FAIL_ON_WARNINGS,
|
|
52
56
|
max_history=config.VALIDATION_MAX_HISTORY,
|
rasa/cli/arguments/data.py
CHANGED
|
@@ -4,8 +4,10 @@ from typing import Text
|
|
|
4
4
|
from rasa.cli.arguments.default_arguments import (
|
|
5
5
|
add_data_param,
|
|
6
6
|
add_domain_param,
|
|
7
|
+
add_endpoint_param,
|
|
7
8
|
add_nlu_data_param,
|
|
8
9
|
add_out_param,
|
|
10
|
+
add_sub_agents_param,
|
|
9
11
|
)
|
|
10
12
|
from rasa.shared.constants import DEFAULT_CONVERTED_DATA_PATH
|
|
11
13
|
|
|
@@ -91,6 +93,13 @@ def set_validator_arguments(parser: argparse.ArgumentParser) -> None:
|
|
|
91
93
|
)
|
|
92
94
|
add_domain_param(parser)
|
|
93
95
|
add_data_param(parser)
|
|
96
|
+
add_sub_agents_param(parser)
|
|
97
|
+
# Endpoints are optional for `data validate` command
|
|
98
|
+
add_endpoint_param(
|
|
99
|
+
parser,
|
|
100
|
+
help_text="Configuration file for the connectors as a yml file.",
|
|
101
|
+
default=None,
|
|
102
|
+
)
|
|
94
103
|
|
|
95
104
|
|
|
96
105
|
def set_migrate_arguments(parser: argparse.ArgumentParser) -> None:
|
rasa/cli/data.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import argparse
|
|
2
2
|
import logging
|
|
3
3
|
import pathlib
|
|
4
|
-
from typing import List
|
|
4
|
+
from typing import List, Optional
|
|
5
5
|
|
|
6
6
|
import rasa.cli.utils
|
|
7
7
|
import rasa.shared.core.domain
|
|
@@ -17,6 +17,7 @@ from rasa.cli.arguments import data as arguments
|
|
|
17
17
|
from rasa.cli.arguments import default_arguments
|
|
18
18
|
from rasa.cli.validation.bot_config import validate_files
|
|
19
19
|
from rasa.cli.validation.config_path_validation import get_validated_path
|
|
20
|
+
from rasa.core.config.configuration import Configuration
|
|
20
21
|
from rasa.e2e_test.e2e_config import create_llm_e2e_test_converter_config
|
|
21
22
|
from rasa.e2e_test.e2e_test_converter import E2ETestConverter
|
|
22
23
|
from rasa.e2e_test.utils.e2e_yaml_utils import E2ETestYAMLWriter
|
|
@@ -139,8 +140,12 @@ def _add_data_validate_parsers(
|
|
|
139
140
|
)
|
|
140
141
|
_append_story_structure_arguments(validate_parser)
|
|
141
142
|
validate_parser.set_defaults(
|
|
142
|
-
func=lambda args:
|
|
143
|
-
args.fail_on_warnings,
|
|
143
|
+
func=lambda args: _validate_files_with_configuration(
|
|
144
|
+
args.fail_on_warnings,
|
|
145
|
+
args.max_history,
|
|
146
|
+
_build_training_data_importer(args),
|
|
147
|
+
sub_agents=args.sub_agents,
|
|
148
|
+
endpoints=args.endpoints,
|
|
144
149
|
)
|
|
145
150
|
)
|
|
146
151
|
arguments.set_validator_arguments(validate_parser)
|
|
@@ -155,10 +160,12 @@ def _add_data_validate_parsers(
|
|
|
155
160
|
_append_story_structure_arguments(story_structure_parser)
|
|
156
161
|
|
|
157
162
|
story_structure_parser.set_defaults(
|
|
158
|
-
func=lambda args:
|
|
163
|
+
func=lambda args: _validate_files_with_configuration(
|
|
159
164
|
args.fail_on_warnings,
|
|
160
165
|
args.max_history,
|
|
161
166
|
_build_training_data_importer(args),
|
|
167
|
+
sub_agents=args.sub_agents,
|
|
168
|
+
endpoints=args.endpoints,
|
|
162
169
|
stories_only=True,
|
|
163
170
|
)
|
|
164
171
|
)
|
|
@@ -171,10 +178,12 @@ def _add_data_validate_parsers(
|
|
|
171
178
|
help="Checks for inconsistencies in the flows files.",
|
|
172
179
|
)
|
|
173
180
|
flows_structure_parser.set_defaults(
|
|
174
|
-
func=lambda args:
|
|
181
|
+
func=lambda args: _validate_files_with_configuration(
|
|
175
182
|
args.fail_on_warnings,
|
|
176
183
|
args.max_history,
|
|
177
184
|
_build_training_data_importer(args),
|
|
185
|
+
sub_agents=args.sub_agents,
|
|
186
|
+
endpoints=args.endpoints,
|
|
178
187
|
flows_only=True,
|
|
179
188
|
)
|
|
180
189
|
)
|
|
@@ -187,10 +196,12 @@ def _add_data_validate_parsers(
|
|
|
187
196
|
help="Checks for inconsistencies of the flow and response translation.",
|
|
188
197
|
)
|
|
189
198
|
translations_structure_parser.set_defaults(
|
|
190
|
-
func=lambda args:
|
|
199
|
+
func=lambda args: _validate_files_with_configuration(
|
|
191
200
|
args.fail_on_warnings,
|
|
192
201
|
args.max_history,
|
|
193
202
|
_build_training_data_importer(args),
|
|
203
|
+
sub_agents=args.sub_agents,
|
|
204
|
+
endpoints=args.endpoints,
|
|
194
205
|
translations_only=True,
|
|
195
206
|
)
|
|
196
207
|
)
|
|
@@ -376,3 +387,58 @@ def convert_data_to_e2e_tests(args: argparse.Namespace) -> None:
|
|
|
376
387
|
rasa.shared.utils.cli.print_error_and_exit(
|
|
377
388
|
f"Failed to convert the data into E2E tests. Error: {exc}"
|
|
378
389
|
)
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
def _initialize_configuration_for_validation(
|
|
393
|
+
sub_agents: Optional[str] = None, endpoints: Optional[str] = None
|
|
394
|
+
) -> None:
|
|
395
|
+
"""Initialize Configuration before validation.
|
|
396
|
+
|
|
397
|
+
Args:
|
|
398
|
+
sub_agents: Path to sub-agents directory for validation.
|
|
399
|
+
endpoints: Path to the endpoints configuration file.
|
|
400
|
+
"""
|
|
401
|
+
if endpoints:
|
|
402
|
+
Configuration.initialise_endpoints(endpoints_path=pathlib.Path(endpoints))
|
|
403
|
+
if sub_agents:
|
|
404
|
+
Configuration.initialise_sub_agents(pathlib.Path(sub_agents))
|
|
405
|
+
if not endpoints and not sub_agents:
|
|
406
|
+
Configuration.initialise_empty()
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
def _validate_files_with_configuration(
|
|
410
|
+
fail_on_warnings: bool,
|
|
411
|
+
max_history: int,
|
|
412
|
+
importer: TrainingDataImporter,
|
|
413
|
+
sub_agents: Optional[str] = None,
|
|
414
|
+
endpoints: Optional[str] = None,
|
|
415
|
+
stories_only: bool = False,
|
|
416
|
+
flows_only: bool = False,
|
|
417
|
+
translations_only: bool = False,
|
|
418
|
+
) -> None:
|
|
419
|
+
"""Wrapper for validate_files that ensures Configuration is initialized first.
|
|
420
|
+
|
|
421
|
+
Args:
|
|
422
|
+
fail_on_warnings: `True` if the process should exit with a non-zero status
|
|
423
|
+
max_history: The max history to use when validating the story structure.
|
|
424
|
+
importer: The `TrainingDataImporter` to use to load the training data.
|
|
425
|
+
sub_agents: Path to sub-agents directory for validation.
|
|
426
|
+
endpoints: Path to the endpoints configuration file.
|
|
427
|
+
stories_only: If `True`, only the story structure is validated.
|
|
428
|
+
flows_only: If `True`, only the flows are validated.
|
|
429
|
+
translations_only: If `True`, only the translations data is validated.
|
|
430
|
+
"""
|
|
431
|
+
# Initialize Configuration before calling validate_files
|
|
432
|
+
_initialize_configuration_for_validation(sub_agents=sub_agents, endpoints=endpoints)
|
|
433
|
+
|
|
434
|
+
# Call the original validate_files function
|
|
435
|
+
validate_files(
|
|
436
|
+
fail_on_warnings=fail_on_warnings,
|
|
437
|
+
max_history=max_history,
|
|
438
|
+
importer=importer,
|
|
439
|
+
stories_only=stories_only,
|
|
440
|
+
flows_only=flows_only,
|
|
441
|
+
translations_only=translations_only,
|
|
442
|
+
sub_agents=sub_agents,
|
|
443
|
+
endpoints=endpoints,
|
|
444
|
+
)
|
rasa/cli/interactive.py
CHANGED
|
@@ -12,6 +12,7 @@ from rasa import model
|
|
|
12
12
|
from rasa.cli import SubParsersAction
|
|
13
13
|
from rasa.cli.arguments import interactive as arguments
|
|
14
14
|
from rasa.cli.validation.config_path_validation import get_validated_path
|
|
15
|
+
from rasa.core.config.configuration import Configuration
|
|
15
16
|
from rasa.core.constants import DEFAULT_SUB_AGENTS
|
|
16
17
|
from rasa.engine.storage.local_model_storage import LocalModelStorage
|
|
17
18
|
from rasa.shared.constants import (
|
|
@@ -140,6 +141,8 @@ def perform_interactive_learning(
|
|
|
140
141
|
args.endpoints, "endpoints", DEFAULT_ENDPOINTS_PATH, True
|
|
141
142
|
)
|
|
142
143
|
|
|
144
|
+
Configuration.initialise_endpoints(endpoints_path=Path(args.endpoints))
|
|
145
|
+
|
|
143
146
|
do_interactive_learning(args, file_importer)
|
|
144
147
|
|
|
145
148
|
|
rasa/cli/llm_fine_tuning.py
CHANGED
|
@@ -414,6 +414,7 @@ def get_valid_endpoints(endpoints_file: str) -> AvailableEndpoints:
|
|
|
414
414
|
|
|
415
415
|
def set_up_e2e_test_runner(args: argparse.Namespace) -> E2ETestRunner:
|
|
416
416
|
endpoints = get_valid_endpoints(args.endpoints)
|
|
417
|
+
Configuration.initialise_sub_agents(args.sub_agents)
|
|
417
418
|
|
|
418
419
|
if endpoints.model is None:
|
|
419
420
|
args.model = validate_model_path(args.model, "model", DEFAULT_MODELS_PATH)
|
|
@@ -138,6 +138,7 @@ def get_rasa_defaults(config_yaml: Text, endpoints_yaml: Text) -> RasaDefaults:
|
|
|
138
138
|
A RasaDefaults object containing the default values for the project.
|
|
139
139
|
"""
|
|
140
140
|
config = read_yaml(config_yaml)
|
|
141
|
+
# Does not read from file, it converts the YAML content to a dictionary.
|
|
141
142
|
endpoints = read_yaml(endpoints_yaml)
|
|
142
143
|
|
|
143
144
|
prompts = get_system_default_prompts(config, endpoints)
|
|
@@ -149,6 +149,7 @@ def validate_files(
|
|
|
149
149
|
flows_only: bool = False,
|
|
150
150
|
translations_only: bool = False,
|
|
151
151
|
sub_agents: Optional[str] = None,
|
|
152
|
+
endpoints: Optional[str] = None,
|
|
152
153
|
) -> None:
|
|
153
154
|
"""Validates either the story structure or the entire project.
|
|
154
155
|
|
|
@@ -160,6 +161,7 @@ def validate_files(
|
|
|
160
161
|
flows_only: If `True`, only the flows are validated.
|
|
161
162
|
translations_only: If `True`, only the translations data is validated.
|
|
162
163
|
sub_agents: Path to sub-agents directory for validation.
|
|
164
|
+
endpoints: Path to the endpoints configuration file.
|
|
163
165
|
"""
|
|
164
166
|
from rasa.validator import Validator
|
|
165
167
|
|
rasa/constants.py
CHANGED
|
@@ -18,7 +18,7 @@ CONFIG_TELEMETRY_ID = "rasa_user_id"
|
|
|
18
18
|
CONFIG_TELEMETRY_ENABLED = "enabled"
|
|
19
19
|
CONFIG_TELEMETRY_DATE = "date"
|
|
20
20
|
|
|
21
|
-
MINIMUM_COMPATIBLE_VERSION = "3.14.
|
|
21
|
+
MINIMUM_COMPATIBLE_VERSION = "3.14.0rc3"
|
|
22
22
|
|
|
23
23
|
GLOBAL_USER_CONFIG_PATH = os.path.expanduser("~/.config/rasa/global.yml")
|
|
24
24
|
|
|
@@ -33,6 +33,7 @@ ENV_MCP_LOGGING_ENABLED = "MCP_LOGGING_ENABLED"
|
|
|
33
33
|
ENV_LOG_LEVEL_MATPLOTLIB = "LOG_LEVEL_MATPLOTLIB"
|
|
34
34
|
ENV_LOG_LEVEL_RABBITMQ = "LOG_LEVEL_RABBITMQ"
|
|
35
35
|
ENV_LOG_LEVEL_KAFKA = "LOG_LEVEL_KAFKA"
|
|
36
|
+
ENV_LOG_LEVEL_PYMONGO = "LOG_LEVEL_PYMONGO"
|
|
36
37
|
|
|
37
38
|
DEFAULT_SANIC_WORKERS = 1
|
|
38
39
|
ENV_SANIC_WORKERS = "SANIC_WORKERS"
|
|
@@ -4,9 +4,11 @@ from typing import Any, Dict, List, Optional
|
|
|
4
4
|
|
|
5
5
|
import structlog
|
|
6
6
|
|
|
7
|
+
import rasa.dialogue_understanding.stack.utils
|
|
7
8
|
from rasa.core.actions.action import Action
|
|
8
9
|
from rasa.core.channels import OutputChannel
|
|
9
10
|
from rasa.core.nlg import NaturalLanguageGenerator
|
|
11
|
+
from rasa.dialogue_understanding.patterns.code_change import FLOW_PATTERN_CODE_CHANGE_ID
|
|
10
12
|
from rasa.dialogue_understanding.stack.dialogue_stack import DialogueStack
|
|
11
13
|
from rasa.dialogue_understanding.stack.frames import (
|
|
12
14
|
BaseFlowStackFrame,
|
|
@@ -41,6 +43,15 @@ class ActionCleanStack(Action):
|
|
|
41
43
|
"""Clean the stack."""
|
|
42
44
|
structlogger.debug("action_clean_stack.run")
|
|
43
45
|
new_frames = []
|
|
46
|
+
top_flow_frame = rasa.dialogue_understanding.stack.utils.top_flow_frame(
|
|
47
|
+
tracker.stack, ignore_call_frames=False
|
|
48
|
+
)
|
|
49
|
+
top_user_flow_frame = (
|
|
50
|
+
rasa.dialogue_understanding.stack.utils.top_user_flow_frame(
|
|
51
|
+
tracker.stack, ignore_call_and_link_frames=False
|
|
52
|
+
)
|
|
53
|
+
)
|
|
54
|
+
|
|
44
55
|
# Set all frames to their end step, filter out any non-BaseFlowStackFrames
|
|
45
56
|
for frame in tracker.stack.frames:
|
|
46
57
|
if isinstance(frame, BaseFlowStackFrame):
|
|
@@ -56,4 +67,25 @@ class ActionCleanStack(Action):
|
|
|
56
67
|
new_frames.append(frame)
|
|
57
68
|
new_stack = DialogueStack.from_dict([frame.as_dict() for frame in new_frames])
|
|
58
69
|
|
|
70
|
+
# Check if the action is being called from within a user flow
|
|
71
|
+
if (
|
|
72
|
+
top_flow_frame
|
|
73
|
+
and top_flow_frame.flow_id != FLOW_PATTERN_CODE_CHANGE_ID
|
|
74
|
+
and top_user_flow_frame
|
|
75
|
+
and top_user_flow_frame.flow_id == top_flow_frame.flow_id
|
|
76
|
+
):
|
|
77
|
+
# The action is being called from within a user flow on the stack.
|
|
78
|
+
# If there are other frames on the stack, we need to make sure
|
|
79
|
+
# the last executed frame is the end step of the current user flow so
|
|
80
|
+
# that we can trigger pattern_completed for this user flow.
|
|
81
|
+
new_stack.pop()
|
|
82
|
+
structlogger.debug(
|
|
83
|
+
"action_clean_stack.pushing_user_frame_at_the_bottom_of_stack",
|
|
84
|
+
flow_id=top_user_flow_frame.flow_id,
|
|
85
|
+
)
|
|
86
|
+
new_stack.push(
|
|
87
|
+
top_user_flow_frame,
|
|
88
|
+
index=0,
|
|
89
|
+
)
|
|
90
|
+
|
|
59
91
|
return tracker.create_stack_updated_events(new_stack)
|
|
@@ -12,7 +12,7 @@ class ActionExecutionRejection(RasaException):
|
|
|
12
12
|
self.message = message or "Custom action '{}' rejected to run".format(
|
|
13
13
|
action_name
|
|
14
14
|
)
|
|
15
|
-
super(ActionExecutionRejection, self).__init__()
|
|
15
|
+
super(ActionExecutionRejection, self).__init__(self.message)
|
|
16
16
|
|
|
17
17
|
def __str__(self) -> str:
|
|
18
18
|
return self.message
|
rasa/core/actions/constants.py
CHANGED
|
@@ -3,3 +3,7 @@ SELECTIVE_DOMAIN = "enable_selective_domain"
|
|
|
3
3
|
|
|
4
4
|
SSL_CLIENT_CERT_FIELD = "ssl_client_cert"
|
|
5
5
|
SSL_CLIENT_KEY_FIELD = "ssl_client_key"
|
|
6
|
+
|
|
7
|
+
# Special marker key used by EndpointConfig to indicate 449 status
|
|
8
|
+
# without raising an exception
|
|
9
|
+
MISSING_DOMAIN_MARKER = "missing_domain"
|