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.

Files changed (65) hide show
  1. rasa/agents/agent_manager.py +7 -5
  2. rasa/agents/protocol/a2a/a2a_agent.py +13 -11
  3. rasa/agents/protocol/mcp/mcp_base_agent.py +49 -11
  4. rasa/agents/validation.py +4 -2
  5. rasa/builder/config.py +4 -0
  6. rasa/builder/copilot/copilot.py +28 -9
  7. rasa/builder/copilot/copilot_templated_message_provider.py +1 -1
  8. rasa/builder/copilot/models.py +171 -4
  9. rasa/builder/document_retrieval/inkeep_document_retrieval.py +2 -0
  10. rasa/builder/download.py +1 -1
  11. rasa/builder/service.py +101 -24
  12. rasa/builder/telemetry/__init__.py +0 -0
  13. rasa/builder/telemetry/copilot_langfuse_telemetry.py +384 -0
  14. rasa/builder/{copilot/telemetry.py → telemetry/copilot_segment_telemetry.py} +21 -3
  15. rasa/builder/validation_service.py +4 -0
  16. rasa/cli/arguments/data.py +9 -0
  17. rasa/cli/data.py +72 -6
  18. rasa/cli/interactive.py +3 -0
  19. rasa/cli/llm_fine_tuning.py +1 -0
  20. rasa/cli/project_templates/defaults.py +1 -0
  21. rasa/cli/validation/bot_config.py +2 -0
  22. rasa/constants.py +2 -1
  23. rasa/core/actions/action_exceptions.py +1 -1
  24. rasa/core/agent.py +4 -1
  25. rasa/core/available_agents.py +1 -1
  26. rasa/core/exceptions.py +1 -1
  27. rasa/core/featurizers/tracker_featurizers.py +3 -2
  28. rasa/core/persistor.py +7 -7
  29. rasa/core/policies/flows/agent_executor.py +84 -4
  30. rasa/core/policies/flows/flow_exceptions.py +5 -2
  31. rasa/core/policies/flows/flow_executor.py +23 -8
  32. rasa/core/policies/flows/mcp_tool_executor.py +7 -1
  33. rasa/core/policies/rule_policy.py +1 -1
  34. rasa/core/run.py +15 -4
  35. rasa/dialogue_understanding/commands/cancel_flow_command.py +1 -1
  36. rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +1 -1
  37. rasa/e2e_test/e2e_config.py +4 -3
  38. rasa/engine/recipes/default_components.py +16 -6
  39. rasa/graph_components/validators/default_recipe_validator.py +10 -4
  40. rasa/model_manager/runner_service.py +1 -1
  41. rasa/nlu/classifiers/diet_classifier.py +2 -0
  42. rasa/privacy/privacy_config.py +1 -1
  43. rasa/shared/agents/auth/auth_strategy/oauth2_auth_strategy.py +4 -7
  44. rasa/shared/core/slots.py +55 -24
  45. rasa/shared/core/training_data/story_reader/story_reader.py +1 -1
  46. rasa/shared/exceptions.py +23 -2
  47. rasa/shared/providers/llm/litellm_router_llm_client.py +2 -2
  48. rasa/shared/utils/common.py +9 -1
  49. rasa/shared/utils/llm.py +21 -4
  50. rasa/shared/utils/mcp/server_connection.py +7 -4
  51. rasa/studio/download.py +3 -0
  52. rasa/studio/prompts.py +1 -0
  53. rasa/studio/upload.py +4 -0
  54. rasa/utils/common.py +9 -0
  55. rasa/utils/endpoints.py +2 -0
  56. rasa/utils/installation_utils.py +111 -0
  57. rasa/utils/log_utils.py +20 -1
  58. rasa/utils/tensorflow/callback.py +2 -0
  59. rasa/utils/train_utils.py +2 -0
  60. rasa/version.py +1 -1
  61. {rasa_pro-3.14.0rc4.dist-info → rasa_pro-3.15.0a1.dist-info}/METADATA +4 -2
  62. {rasa_pro-3.14.0rc4.dist-info → rasa_pro-3.15.0a1.dist-info}/RECORD +65 -62
  63. {rasa_pro-3.14.0rc4.dist-info → rasa_pro-3.15.0a1.dist-info}/NOTICE +0 -0
  64. {rasa_pro-3.14.0rc4.dist-info → rasa_pro-3.15.0a1.dist-info}/WHEEL +0 -0
  65. {rasa_pro-3.14.0rc4.dist-info → rasa_pro-3.15.0a1.dist-info}/entry_points.txt +0 -0
@@ -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: validate_files(
143
- args.fail_on_warnings, args.max_history, _build_training_data_importer(args)
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: validate_files(
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: validate_files(
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: validate_files(
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
 
@@ -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.0.dev10"
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
- raise e
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
@@ -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.info(
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
- trackers: The tracker from which to extract training examples.
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(output, final_events, stack, step)
185
+ return _handle_agent_fatal_error(
186
+ output, final_events, stack, step, flows, tracker
187
+ )
181
188
  else:
182
- return _handle_agent_unknown_status(output, final_events, stack, step)
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
- pass
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(step.call, arguments)
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:
@@ -84,7 +84,7 @@ class InvalidRule(RasaException):
84
84
  """Exception that can be raised when rules are not valid."""
85
85
 
86
86
  def __init__(self, message: Text) -> None:
87
- super().__init__()
87
+ super().__init__(message)
88
88
  self.message = message
89
89
 
90
90
  def __str__(self) -> Text: