rasa-pro 3.14.0rc4__py3-none-any.whl → 3.15.0a1__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/config.py +4 -0
- rasa/builder/copilot/copilot.py +28 -9
- rasa/builder/copilot/copilot_templated_message_provider.py +1 -1
- rasa/builder/copilot/models.py +171 -4
- rasa/builder/document_retrieval/inkeep_document_retrieval.py +2 -0
- rasa/builder/download.py +1 -1
- rasa/builder/service.py +101 -24
- rasa/builder/telemetry/__init__.py +0 -0
- rasa/builder/telemetry/copilot_langfuse_telemetry.py +384 -0
- rasa/builder/{copilot/telemetry.py → telemetry/copilot_segment_telemetry.py} +21 -3
- 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_exceptions.py +1 -1
- rasa/core/agent.py +4 -1
- rasa/core/available_agents.py +1 -1
- 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 +23 -8
- rasa/core/policies/flows/mcp_tool_executor.py +7 -1
- rasa/core/policies/rule_policy.py +1 -1
- rasa/core/run.py +15 -4
- rasa/dialogue_understanding/commands/cancel_flow_command.py +1 -1
- rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +1 -1
- 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/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/llm/litellm_router_llm_client.py +2 -2
- rasa/shared/utils/common.py +9 -1
- 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 +2 -0
- rasa/utils/installation_utils.py +111 -0
- rasa/utils/log_utils.py +20 -1
- rasa/utils/tensorflow/callback.py +2 -0
- rasa/utils/train_utils.py +2 -0
- rasa/version.py +1 -1
- {rasa_pro-3.14.0rc4.dist-info → rasa_pro-3.15.0a1.dist-info}/METADATA +4 -2
- {rasa_pro-3.14.0rc4.dist-info → rasa_pro-3.15.0a1.dist-info}/RECORD +65 -62
- {rasa_pro-3.14.0rc4.dist-info → rasa_pro-3.15.0a1.dist-info}/NOTICE +0 -0
- {rasa_pro-3.14.0rc4.dist-info → rasa_pro-3.15.0a1.dist-info}/WHEEL +0 -0
- {rasa_pro-3.14.0rc4.dist-info → rasa_pro-3.15.0a1.dist-info}/entry_points.txt +0 -0
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"
|
|
@@ -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/agent.py
CHANGED
|
@@ -297,7 +297,10 @@ async def load_agent(
|
|
|
297
297
|
return agent
|
|
298
298
|
|
|
299
299
|
except AgentInitializationException as e:
|
|
300
|
-
|
|
300
|
+
if e.suppress_stack_trace:
|
|
301
|
+
raise e from None
|
|
302
|
+
else:
|
|
303
|
+
raise e
|
|
301
304
|
except Exception as e:
|
|
302
305
|
logger.error(f"Could not load model due to {e}.", exc_info=True)
|
|
303
306
|
return agent
|
rasa/core/available_agents.py
CHANGED
|
@@ -114,7 +114,7 @@ class AvailableAgents:
|
|
|
114
114
|
else:
|
|
115
115
|
# We are using the default folder, it may not be created yet
|
|
116
116
|
# Init with an empty agents in this case
|
|
117
|
-
structlogger.
|
|
117
|
+
structlogger.debug(
|
|
118
118
|
f"Default agents config folder '{sub_agents_folder}' does not "
|
|
119
119
|
f"exist. Agent configurations won't be loaded."
|
|
120
120
|
)
|
rasa/core/exceptions.py
CHANGED
|
@@ -14,7 +14,7 @@ class AgentNotReady(RasaCoreException):
|
|
|
14
14
|
def __init__(self, message: Text) -> None:
|
|
15
15
|
"""Initialize message attribute."""
|
|
16
16
|
self.message = message
|
|
17
|
-
super(AgentNotReady, self).__init__()
|
|
17
|
+
super(AgentNotReady, self).__init__(message)
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
class ChannelConfigError(RasaCoreException):
|
|
@@ -58,7 +58,7 @@ class InvalidStory(RasaException):
|
|
|
58
58
|
message: a custom exception message.
|
|
59
59
|
"""
|
|
60
60
|
self.message = message
|
|
61
|
-
super(InvalidStory, self).__init__()
|
|
61
|
+
super(InvalidStory, self).__init__(message)
|
|
62
62
|
|
|
63
63
|
def __str__(self) -> Text:
|
|
64
64
|
return self.message
|
|
@@ -174,6 +174,7 @@ class TrackerFeaturizer:
|
|
|
174
174
|
|
|
175
175
|
Args:
|
|
176
176
|
trackers_as_actions: A list of tracker labels.
|
|
177
|
+
domain: The domain containing action names.
|
|
177
178
|
|
|
178
179
|
Returns:
|
|
179
180
|
Label IDs for each tracker
|
|
@@ -854,7 +855,7 @@ class MaxHistoryTrackerFeaturizer(TrackerFeaturizer):
|
|
|
854
855
|
"""Creates an iterator over training examples from a tracker.
|
|
855
856
|
|
|
856
857
|
Args:
|
|
857
|
-
|
|
858
|
+
tracker: The tracker from which to extract training examples.
|
|
858
859
|
domain: The domain of the training data.
|
|
859
860
|
omit_unset_slots: If `True` do not include the initial values of slots.
|
|
860
861
|
ignore_action_unlikely_intent: Whether to remove `action_unlikely_intent`
|
rasa/core/persistor.py
CHANGED
|
@@ -314,7 +314,7 @@ class AWSPersistor(Persistor):
|
|
|
314
314
|
obj = self.s3.Object(self.bucket_name, model_path)
|
|
315
315
|
return obj.content_length
|
|
316
316
|
except Exception:
|
|
317
|
-
raise ModelNotFound()
|
|
317
|
+
raise ModelNotFound("Model not found")
|
|
318
318
|
|
|
319
319
|
def _retrieve_tar(
|
|
320
320
|
self, target_filename: str, target_path: Optional[str] = None
|
|
@@ -349,7 +349,7 @@ class AWSPersistor(Persistor):
|
|
|
349
349
|
target_filename=target_filename,
|
|
350
350
|
event_info=log,
|
|
351
351
|
)
|
|
352
|
-
raise ModelNotFound() from exc
|
|
352
|
+
raise ModelNotFound("Model not found") from exc
|
|
353
353
|
except exceptions.BotoCoreError as exc:
|
|
354
354
|
structlogger.error(
|
|
355
355
|
"aws_persistor.retrieve_tar.model_download_error",
|
|
@@ -357,7 +357,7 @@ class AWSPersistor(Persistor):
|
|
|
357
357
|
target_filename=target_filename,
|
|
358
358
|
event_info=log,
|
|
359
359
|
)
|
|
360
|
-
raise ModelNotFound() from exc
|
|
360
|
+
raise ModelNotFound("Model not found") from exc
|
|
361
361
|
|
|
362
362
|
|
|
363
363
|
class GCSPersistor(Persistor):
|
|
@@ -447,7 +447,7 @@ class GCSPersistor(Persistor):
|
|
|
447
447
|
blob = self.bucket.blob(target_filename)
|
|
448
448
|
return blob.size
|
|
449
449
|
except Exception:
|
|
450
|
-
raise ModelNotFound()
|
|
450
|
+
raise ModelNotFound("Model not found")
|
|
451
451
|
|
|
452
452
|
def _retrieve_tar(
|
|
453
453
|
self, target_filename: str, target_path: Optional[str] = None
|
|
@@ -481,7 +481,7 @@ class GCSPersistor(Persistor):
|
|
|
481
481
|
target_filename=target_filename,
|
|
482
482
|
event_info=log,
|
|
483
483
|
)
|
|
484
|
-
raise ModelNotFound() from exc
|
|
484
|
+
raise ModelNotFound("Model not found") from exc
|
|
485
485
|
|
|
486
486
|
|
|
487
487
|
class AzurePersistor(Persistor):
|
|
@@ -534,7 +534,7 @@ class AzurePersistor(Persistor):
|
|
|
534
534
|
properties = blob_client.get_blob_properties()
|
|
535
535
|
return properties.size
|
|
536
536
|
except Exception:
|
|
537
|
-
raise ModelNotFound()
|
|
537
|
+
raise ModelNotFound("Model not found")
|
|
538
538
|
|
|
539
539
|
def _retrieve_tar(
|
|
540
540
|
self, target_filename: Text, target_path: Optional[str] = None
|
|
@@ -570,4 +570,4 @@ class AzurePersistor(Persistor):
|
|
|
570
570
|
event_info=log,
|
|
571
571
|
exception=exc,
|
|
572
572
|
)
|
|
573
|
-
raise ModelNotFound() from exc
|
|
573
|
+
raise ModelNotFound("Model not found") from exc
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
|
-
from typing import Any, Dict, List, Optional, cast
|
|
4
|
+
from typing import Any, Dict, List, Optional, Tuple, cast
|
|
5
5
|
|
|
6
6
|
import structlog
|
|
7
7
|
|
|
@@ -24,6 +24,7 @@ from rasa.core.policies.flows.flow_step_result import (
|
|
|
24
24
|
PauseFlowReturnPrediction,
|
|
25
25
|
)
|
|
26
26
|
from rasa.core.utils import get_slot_names_from_exit_conditions
|
|
27
|
+
from rasa.dialogue_understanding.patterns.cancel import CancelPatternFlowStackFrame
|
|
27
28
|
from rasa.dialogue_understanding.patterns.internal_error import (
|
|
28
29
|
InternalErrorPatternFlowStackFrame,
|
|
29
30
|
)
|
|
@@ -31,6 +32,7 @@ from rasa.dialogue_understanding.stack.dialogue_stack import DialogueStack
|
|
|
31
32
|
from rasa.dialogue_understanding.stack.frames.flow_stack_frame import (
|
|
32
33
|
AgentStackFrame,
|
|
33
34
|
AgentState,
|
|
35
|
+
BaseFlowStackFrame,
|
|
34
36
|
)
|
|
35
37
|
from rasa.shared.agents.utils import get_protocol_type
|
|
36
38
|
from rasa.shared.core.constants import (
|
|
@@ -46,9 +48,11 @@ from rasa.shared.core.events import (
|
|
|
46
48
|
AgentResumed,
|
|
47
49
|
AgentStarted,
|
|
48
50
|
Event,
|
|
51
|
+
FlowCancelled,
|
|
49
52
|
SlotSet,
|
|
50
53
|
deserialise_events,
|
|
51
54
|
)
|
|
55
|
+
from rasa.shared.core.flows.flows_list import FlowsList
|
|
52
56
|
from rasa.shared.core.flows.steps import (
|
|
53
57
|
CallFlowStep,
|
|
54
58
|
)
|
|
@@ -85,6 +89,7 @@ async def run_agent(
|
|
|
85
89
|
step: CallFlowStep,
|
|
86
90
|
tracker: DialogueStateTracker,
|
|
87
91
|
slots: List[Slot],
|
|
92
|
+
flows: FlowsList,
|
|
88
93
|
) -> FlowStepResult:
|
|
89
94
|
"""Run an agent call step."""
|
|
90
95
|
structlogger.debug(
|
|
@@ -177,9 +182,13 @@ async def run_agent(
|
|
|
177
182
|
elif output.status == AgentStatus.COMPLETED:
|
|
178
183
|
return _handle_agent_completed(output, final_events, stack, step)
|
|
179
184
|
elif output.status == AgentStatus.FATAL_ERROR:
|
|
180
|
-
return _handle_agent_fatal_error(
|
|
185
|
+
return _handle_agent_fatal_error(
|
|
186
|
+
output, final_events, stack, step, flows, tracker
|
|
187
|
+
)
|
|
181
188
|
else:
|
|
182
|
-
return _handle_agent_unknown_status(
|
|
189
|
+
return _handle_agent_unknown_status(
|
|
190
|
+
output, final_events, stack, step, flows, tracker
|
|
191
|
+
)
|
|
183
192
|
|
|
184
193
|
|
|
185
194
|
async def _call_agent_with_retry(
|
|
@@ -299,6 +308,8 @@ def _handle_agent_unknown_status(
|
|
|
299
308
|
final_events: List[Event],
|
|
300
309
|
stack: DialogueStack,
|
|
301
310
|
step: CallFlowStep,
|
|
311
|
+
flows: FlowsList,
|
|
312
|
+
tracker: DialogueStateTracker,
|
|
302
313
|
) -> FlowStepResult:
|
|
303
314
|
"""Handle unknown agent status.
|
|
304
315
|
|
|
@@ -307,6 +318,8 @@ def _handle_agent_unknown_status(
|
|
|
307
318
|
final_events: List of events to be added to the final result
|
|
308
319
|
stack: The dialogue stack
|
|
309
320
|
step: The flow step that called the agent
|
|
321
|
+
flows: All flows
|
|
322
|
+
tracker: The dialogue state tracker
|
|
310
323
|
|
|
311
324
|
Returns:
|
|
312
325
|
FlowStepResult indicating to continue with internal error pattern
|
|
@@ -320,8 +333,21 @@ def _handle_agent_unknown_status(
|
|
|
320
333
|
flow_id=step.flow_id,
|
|
321
334
|
status=output.status,
|
|
322
335
|
)
|
|
336
|
+
# remove the agent stack frame
|
|
323
337
|
remove_agent_stack_frame(stack, step.call)
|
|
324
338
|
final_events.append(AgentCancelled(agent_id=step.call, flow_id=step.flow_id))
|
|
339
|
+
|
|
340
|
+
# cancel the current active flow:
|
|
341
|
+
# push the cancel pattern stack frame and add the flow cancelled event
|
|
342
|
+
cancel_pattern_stack_frame, flow_cancelled_event = _cancel_flow(
|
|
343
|
+
stack, flows, tracker, step
|
|
344
|
+
)
|
|
345
|
+
if cancel_pattern_stack_frame:
|
|
346
|
+
stack.push(cancel_pattern_stack_frame)
|
|
347
|
+
if flow_cancelled_event:
|
|
348
|
+
final_events.append(flow_cancelled_event)
|
|
349
|
+
|
|
350
|
+
# trigger the internal error pattern
|
|
325
351
|
stack.push(InternalErrorPatternFlowStackFrame())
|
|
326
352
|
return ContinueFlowWithNextStep(events=final_events)
|
|
327
353
|
|
|
@@ -418,6 +444,8 @@ def _handle_agent_fatal_error(
|
|
|
418
444
|
final_events: List[Event],
|
|
419
445
|
stack: DialogueStack,
|
|
420
446
|
step: CallFlowStep,
|
|
447
|
+
flows: FlowsList,
|
|
448
|
+
tracker: DialogueStateTracker,
|
|
421
449
|
) -> FlowStepResult:
|
|
422
450
|
"""Handle fatal error from agent execution.
|
|
423
451
|
|
|
@@ -426,13 +454,15 @@ def _handle_agent_fatal_error(
|
|
|
426
454
|
final_events: List of events to be added to the final result
|
|
427
455
|
stack: The dialogue stack
|
|
428
456
|
step: The flow step that called the agent
|
|
457
|
+
flows: All flows
|
|
458
|
+
tracker: The dialogue state tracker
|
|
429
459
|
|
|
430
460
|
Returns:
|
|
431
461
|
FlowStepResult indicating to continue with internal error pattern
|
|
432
462
|
"""
|
|
433
463
|
output.metadata = output.metadata or {}
|
|
434
464
|
_update_agent_events(final_events, output.metadata)
|
|
435
|
-
# the agent failed, trigger pattern_internal_error
|
|
465
|
+
# the agent failed, cancel the current flow and trigger pattern_internal_error
|
|
436
466
|
structlogger.error(
|
|
437
467
|
"flow.step.run_agent.fatal_error",
|
|
438
468
|
agent_name=step.call,
|
|
@@ -440,16 +470,66 @@ def _handle_agent_fatal_error(
|
|
|
440
470
|
flow_id=step.flow_id,
|
|
441
471
|
error_message=output.error_message,
|
|
442
472
|
)
|
|
473
|
+
# remove the agent stack frame
|
|
443
474
|
remove_agent_stack_frame(stack, step.call)
|
|
444
475
|
final_events.append(
|
|
445
476
|
AgentCancelled(
|
|
446
477
|
agent_id=step.call, flow_id=step.flow_id, reason=output.error_message
|
|
447
478
|
)
|
|
448
479
|
)
|
|
480
|
+
|
|
481
|
+
# cancel the current active flow:
|
|
482
|
+
# push the cancel pattern stack frame and add the flow cancelled event
|
|
483
|
+
cancel_pattern_stack_frame, flow_cancelled_event = _cancel_flow(
|
|
484
|
+
stack, flows, tracker, step
|
|
485
|
+
)
|
|
486
|
+
if cancel_pattern_stack_frame:
|
|
487
|
+
stack.push(cancel_pattern_stack_frame)
|
|
488
|
+
if flow_cancelled_event:
|
|
489
|
+
final_events.append(flow_cancelled_event)
|
|
490
|
+
|
|
491
|
+
# push the internal error pattern stack frame
|
|
449
492
|
stack.push(InternalErrorPatternFlowStackFrame())
|
|
450
493
|
return ContinueFlowWithNextStep(events=final_events)
|
|
451
494
|
|
|
452
495
|
|
|
496
|
+
def _cancel_flow(
|
|
497
|
+
stack: DialogueStack,
|
|
498
|
+
flows: FlowsList,
|
|
499
|
+
tracker: DialogueStateTracker,
|
|
500
|
+
step: CallFlowStep,
|
|
501
|
+
) -> Tuple[Optional[CancelPatternFlowStackFrame], Optional[FlowCancelled]]:
|
|
502
|
+
"""Cancel the current active flow.
|
|
503
|
+
|
|
504
|
+
Creates a cancel pattern stack frame and a flow cancelled event.
|
|
505
|
+
"""
|
|
506
|
+
from rasa.dialogue_understanding.commands import CancelFlowCommand
|
|
507
|
+
|
|
508
|
+
cancel_pattern_stack_frame = None
|
|
509
|
+
flow_cancelled_event = None
|
|
510
|
+
|
|
511
|
+
top_frame = stack.top()
|
|
512
|
+
|
|
513
|
+
if isinstance(top_frame, BaseFlowStackFrame):
|
|
514
|
+
flow = flows.flow_by_id(step.flow_id)
|
|
515
|
+
flow_name = (
|
|
516
|
+
flow.readable_name(language=tracker.current_language)
|
|
517
|
+
if flow
|
|
518
|
+
else step.flow_id
|
|
519
|
+
)
|
|
520
|
+
|
|
521
|
+
canceled_frames = CancelFlowCommand.select_canceled_frames(stack)
|
|
522
|
+
|
|
523
|
+
cancel_pattern_stack_frame = CancelPatternFlowStackFrame(
|
|
524
|
+
canceled_name=flow_name,
|
|
525
|
+
canceled_frames=canceled_frames,
|
|
526
|
+
)
|
|
527
|
+
|
|
528
|
+
flow_cancelled_event = FlowCancelled(step.flow_id, step.id)
|
|
529
|
+
|
|
530
|
+
return cancel_pattern_stack_frame, flow_cancelled_event
|
|
531
|
+
|
|
532
|
+
|
|
453
533
|
################################################################################
|
|
454
534
|
# Create predictions
|
|
455
535
|
################################################################################
|
|
@@ -5,14 +5,17 @@ from rasa.shared.exceptions import RasaException
|
|
|
5
5
|
class FlowException(RasaException):
|
|
6
6
|
"""Exception that is raised when there is a problem with a flow."""
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
def __init__(self, message: str = "Flow error occurred") -> None:
|
|
9
|
+
"""Initialize FlowException with a message."""
|
|
10
|
+
super().__init__(message)
|
|
9
11
|
|
|
10
12
|
|
|
11
13
|
class FlowCircuitBreakerTrippedException(FlowException):
|
|
12
14
|
"""Exception that is raised when the flow circuit breaker tripped.
|
|
13
15
|
|
|
14
16
|
The circuit breaker gets tripped when a flow seems to be stuck in
|
|
15
|
-
executing steps and does not make any progress.
|
|
17
|
+
executing steps and does not make any progress.
|
|
18
|
+
"""
|
|
16
19
|
|
|
17
20
|
def __init__(
|
|
18
21
|
self, dialogue_stack: DialogueStack, number_of_steps_taken: int
|
|
@@ -357,6 +357,10 @@ def reset_scoped_slots(
|
|
|
357
357
|
flow_persistable_slots = current_flow.persisted_slots
|
|
358
358
|
|
|
359
359
|
for step in current_flow.steps_with_calls_resolved:
|
|
360
|
+
# take persisted slots from called flows into consideration
|
|
361
|
+
# before resetting slots
|
|
362
|
+
if isinstance(step, CallFlowStep) and step.called_flow_reference:
|
|
363
|
+
flow_persistable_slots.extend(step.called_flow_reference.persisted_slots)
|
|
360
364
|
if isinstance(step, CollectInformationFlowStep):
|
|
361
365
|
# reset all slots scoped to the flow
|
|
362
366
|
slot_name = step.collect
|
|
@@ -368,7 +372,22 @@ def reset_scoped_slots(
|
|
|
368
372
|
# slots set by the set slots step should be reset after the flow ends
|
|
369
373
|
# unless they are also used in a collect step where `reset_after_flow_ends`
|
|
370
374
|
# is set to `False` or set in the `persisted_slots` list.
|
|
371
|
-
resettable_set_slots =
|
|
375
|
+
resettable_set_slots = _get_resettable_set_slots(
|
|
376
|
+
current_flow, not_resettable_slot_names, flow_persistable_slots
|
|
377
|
+
)
|
|
378
|
+
for name in resettable_set_slots:
|
|
379
|
+
_reset_slot(name, tracker)
|
|
380
|
+
|
|
381
|
+
return events
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
def _get_resettable_set_slots(
|
|
385
|
+
current_flow: Flow,
|
|
386
|
+
not_resettable_slot_names: set[Text],
|
|
387
|
+
flow_persistable_slots: List[Text],
|
|
388
|
+
) -> List[Text]:
|
|
389
|
+
"""Get list of slot names from SetSlotsFlowStep that should be reset."""
|
|
390
|
+
return [
|
|
372
391
|
slot["key"]
|
|
373
392
|
for step in current_flow.steps_with_calls_resolved
|
|
374
393
|
if isinstance(step, SetSlotsFlowStep)
|
|
@@ -377,11 +396,6 @@ def reset_scoped_slots(
|
|
|
377
396
|
and slot["key"] not in flow_persistable_slots
|
|
378
397
|
]
|
|
379
398
|
|
|
380
|
-
for name in resettable_set_slots:
|
|
381
|
-
_reset_slot(name, tracker)
|
|
382
|
-
|
|
383
|
-
return events
|
|
384
|
-
|
|
385
399
|
|
|
386
400
|
async def advance_flows(
|
|
387
401
|
tracker: DialogueStateTracker,
|
|
@@ -650,7 +664,7 @@ async def run_step(
|
|
|
650
664
|
return _run_link_step(initial_events, stack, step)
|
|
651
665
|
|
|
652
666
|
elif isinstance(step, CallFlowStep):
|
|
653
|
-
return await _run_call_step(initial_events, stack, step, tracker, slots)
|
|
667
|
+
return await _run_call_step(initial_events, stack, step, tracker, slots, flows)
|
|
654
668
|
|
|
655
669
|
elif isinstance(step, SetSlotsFlowStep):
|
|
656
670
|
return _run_set_slot_step(initial_events, step)
|
|
@@ -723,12 +737,13 @@ async def _run_call_step(
|
|
|
723
737
|
step: CallFlowStep,
|
|
724
738
|
tracker: DialogueStateTracker,
|
|
725
739
|
slots: List[Slot],
|
|
740
|
+
flows: FlowsList,
|
|
726
741
|
) -> FlowStepResult:
|
|
727
742
|
structlogger.debug("flow.step.run.call")
|
|
728
743
|
if step.is_calling_mcp_tool():
|
|
729
744
|
return await call_mcp_tool(initial_events, stack, step, tracker)
|
|
730
745
|
elif step.is_calling_agent():
|
|
731
|
-
return await run_agent(initial_events, stack, step, tracker, slots)
|
|
746
|
+
return await run_agent(initial_events, stack, step, tracker, slots, flows)
|
|
732
747
|
else:
|
|
733
748
|
stack.push(
|
|
734
749
|
UserFlowStackFrame(
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import json
|
|
2
|
+
from datetime import timedelta
|
|
2
3
|
from typing import Any, Dict, List, Optional
|
|
3
4
|
|
|
4
5
|
import structlog
|
|
@@ -24,6 +25,7 @@ structlogger = structlog.get_logger()
|
|
|
24
25
|
|
|
25
26
|
CONFIG_VALUE = "value"
|
|
26
27
|
CONFIG_SLOT = "slot"
|
|
28
|
+
TOOL_CALL_DEFATULT_TIMEOUT = 10 # seconds
|
|
27
29
|
|
|
28
30
|
|
|
29
31
|
async def call_mcp_tool(
|
|
@@ -102,7 +104,11 @@ async def _execute_mcp_tool_call(
|
|
|
102
104
|
|
|
103
105
|
# Call the tool with parameters
|
|
104
106
|
mcp_server = await mcp_server_connection.ensure_active_session()
|
|
105
|
-
result = await mcp_server.call_tool(
|
|
107
|
+
result = await mcp_server.call_tool(
|
|
108
|
+
step.call,
|
|
109
|
+
arguments,
|
|
110
|
+
read_timeout_seconds=timedelta(seconds=TOOL_CALL_DEFATULT_TIMEOUT),
|
|
111
|
+
)
|
|
106
112
|
|
|
107
113
|
# Handle tool execution result
|
|
108
114
|
if result is None or result.isError:
|