waldiez 0.6.0__py3-none-any.whl → 0.6.1__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 waldiez might be problematic. Click here for more details.

Files changed (188) hide show
  1. waldiez/__init__.py +1 -1
  2. waldiez/_version.py +1 -1
  3. waldiez/cli.py +18 -7
  4. waldiez/cli_extras/jupyter.py +3 -0
  5. waldiez/cli_extras/runner.py +3 -1
  6. waldiez/cli_extras/studio.py +3 -1
  7. waldiez/exporter.py +9 -3
  8. waldiez/exporting/agent/exporter.py +9 -10
  9. waldiez/exporting/agent/extras/captain_agent_extras.py +6 -6
  10. waldiez/exporting/agent/extras/doc_agent_extras.py +6 -6
  11. waldiez/exporting/agent/extras/group_manager_agent_extas.py +34 -23
  12. waldiez/exporting/agent/extras/group_member_extras.py +6 -5
  13. waldiez/exporting/agent/extras/handoffs/after_work.py +1 -1
  14. waldiez/exporting/agent/extras/handoffs/available.py +1 -1
  15. waldiez/exporting/agent/extras/handoffs/condition.py +3 -2
  16. waldiez/exporting/agent/extras/handoffs/handoff.py +1 -1
  17. waldiez/exporting/agent/extras/handoffs/target.py +6 -4
  18. waldiez/exporting/agent/extras/rag/chroma_extras.py +27 -19
  19. waldiez/exporting/agent/extras/rag/mongo_extras.py +8 -8
  20. waldiez/exporting/agent/extras/rag/pgvector_extras.py +5 -5
  21. waldiez/exporting/agent/extras/rag/qdrant_extras.py +5 -4
  22. waldiez/exporting/agent/extras/rag/vector_db_extras.py +1 -1
  23. waldiez/exporting/agent/extras/rag_user_proxy_agent_extras.py +5 -7
  24. waldiez/exporting/agent/extras/reasoning_agent_extras.py +3 -5
  25. waldiez/exporting/chats/exporter.py +4 -4
  26. waldiez/exporting/chats/processor.py +1 -2
  27. waldiez/exporting/chats/utils/common.py +89 -48
  28. waldiez/exporting/chats/utils/group.py +9 -9
  29. waldiez/exporting/chats/utils/nested.py +7 -7
  30. waldiez/exporting/chats/utils/sequential.py +1 -1
  31. waldiez/exporting/chats/utils/single.py +2 -2
  32. waldiez/exporting/core/content.py +7 -7
  33. waldiez/exporting/core/context.py +5 -3
  34. waldiez/exporting/core/exporter.py +5 -3
  35. waldiez/exporting/core/exporters.py +2 -2
  36. waldiez/exporting/core/extras/agent_extras/captain_extras.py +2 -2
  37. waldiez/exporting/core/extras/agent_extras/group_manager_extras.py +2 -2
  38. waldiez/exporting/core/extras/agent_extras/rag_user_extras.py +2 -2
  39. waldiez/exporting/core/extras/agent_extras/standard_extras.py +3 -8
  40. waldiez/exporting/core/extras/base.py +7 -5
  41. waldiez/exporting/core/extras/flow_extras.py +4 -5
  42. waldiez/exporting/core/extras/model_extras.py +2 -2
  43. waldiez/exporting/core/extras/path_resolver.py +1 -2
  44. waldiez/exporting/core/extras/serializer.py +2 -2
  45. waldiez/exporting/core/protocols.py +6 -5
  46. waldiez/exporting/core/result.py +25 -28
  47. waldiez/exporting/core/types.py +10 -10
  48. waldiez/exporting/core/utils/llm_config.py +2 -2
  49. waldiez/exporting/core/validation.py +10 -11
  50. waldiez/exporting/flow/execution_generator.py +98 -10
  51. waldiez/exporting/flow/exporter.py +2 -2
  52. waldiez/exporting/flow/factory.py +2 -2
  53. waldiez/exporting/flow/file_generator.py +4 -2
  54. waldiez/exporting/flow/merger.py +5 -3
  55. waldiez/exporting/flow/orchestrator.py +72 -2
  56. waldiez/exporting/flow/utils/common.py +5 -5
  57. waldiez/exporting/flow/utils/importing.py +6 -7
  58. waldiez/exporting/flow/utils/linting.py +25 -9
  59. waldiez/exporting/flow/utils/logging.py +2 -2
  60. waldiez/exporting/models/exporter.py +8 -8
  61. waldiez/exporting/models/processor.py +5 -5
  62. waldiez/exporting/tools/exporter.py +2 -2
  63. waldiez/exporting/tools/processor.py +7 -4
  64. waldiez/io/__init__.py +8 -4
  65. waldiez/io/_ws.py +10 -6
  66. waldiez/io/models/constants.py +10 -10
  67. waldiez/io/models/content/audio.py +1 -0
  68. waldiez/io/models/content/base.py +20 -18
  69. waldiez/io/models/content/file.py +1 -0
  70. waldiez/io/models/content/image.py +1 -0
  71. waldiez/io/models/content/text.py +1 -0
  72. waldiez/io/models/content/video.py +1 -0
  73. waldiez/io/models/user_input.py +10 -5
  74. waldiez/io/models/user_response.py +17 -16
  75. waldiez/io/mqtt.py +18 -31
  76. waldiez/io/redis.py +18 -22
  77. waldiez/io/structured.py +52 -53
  78. waldiez/io/utils.py +3 -0
  79. waldiez/io/ws.py +5 -1
  80. waldiez/logger.py +16 -3
  81. waldiez/models/agents/__init__.py +3 -0
  82. waldiez/models/agents/agent/agent.py +23 -16
  83. waldiez/models/agents/agent/agent_data.py +25 -22
  84. waldiez/models/agents/agent/code_execution.py +9 -11
  85. waldiez/models/agents/agent/termination_message.py +10 -12
  86. waldiez/models/agents/agent/update_system_message.py +2 -4
  87. waldiez/models/agents/agents.py +8 -8
  88. waldiez/models/agents/assistant/assistant.py +6 -3
  89. waldiez/models/agents/assistant/assistant_data.py +2 -2
  90. waldiez/models/agents/captain/captain_agent.py +7 -4
  91. waldiez/models/agents/captain/captain_agent_data.py +5 -7
  92. waldiez/models/agents/doc_agent/doc_agent.py +7 -4
  93. waldiez/models/agents/doc_agent/doc_agent_data.py +9 -10
  94. waldiez/models/agents/doc_agent/rag_query_engine.py +10 -12
  95. waldiez/models/agents/extra_requirements.py +3 -3
  96. waldiez/models/agents/group_manager/group_manager.py +12 -7
  97. waldiez/models/agents/group_manager/group_manager_data.py +13 -12
  98. waldiez/models/agents/group_manager/speakers.py +17 -19
  99. waldiez/models/agents/rag_user_proxy/rag_user_proxy.py +7 -4
  100. waldiez/models/agents/rag_user_proxy/rag_user_proxy_data.py +4 -1
  101. waldiez/models/agents/rag_user_proxy/retrieve_config.py +69 -63
  102. waldiez/models/agents/rag_user_proxy/vector_db_config.py +19 -19
  103. waldiez/models/agents/reasoning/reasoning_agent.py +7 -4
  104. waldiez/models/agents/reasoning/reasoning_agent_data.py +3 -2
  105. waldiez/models/agents/reasoning/reasoning_agent_reason_config.py +8 -8
  106. waldiez/models/agents/user_proxy/user_proxy.py +6 -3
  107. waldiez/models/agents/user_proxy/user_proxy_data.py +1 -1
  108. waldiez/models/chat/chat.py +27 -20
  109. waldiez/models/chat/chat_data.py +22 -19
  110. waldiez/models/chat/chat_message.py +9 -9
  111. waldiez/models/chat/chat_nested.py +9 -9
  112. waldiez/models/chat/chat_summary.py +6 -6
  113. waldiez/models/common/__init__.py +2 -0
  114. waldiez/models/common/ag2_version.py +2 -0
  115. waldiez/models/common/dict_utils.py +8 -6
  116. waldiez/models/common/handoff.py +18 -17
  117. waldiez/models/common/method_utils.py +7 -7
  118. waldiez/models/common/naming.py +49 -0
  119. waldiez/models/flow/flow.py +11 -6
  120. waldiez/models/flow/flow_data.py +23 -17
  121. waldiez/models/flow/info.py +3 -3
  122. waldiez/models/flow/naming.py +2 -1
  123. waldiez/models/model/_aws.py +11 -13
  124. waldiez/models/model/_llm.py +5 -0
  125. waldiez/models/model/_price.py +2 -4
  126. waldiez/models/model/extra_requirements.py +1 -3
  127. waldiez/models/model/model.py +2 -2
  128. waldiez/models/model/model_data.py +21 -21
  129. waldiez/models/tool/extra_requirements.py +2 -4
  130. waldiez/models/tool/predefined/_duckduckgo.py +1 -0
  131. waldiez/models/tool/predefined/_email.py +1 -0
  132. waldiez/models/tool/predefined/_google.py +1 -0
  133. waldiez/models/tool/predefined/_perplexity.py +1 -0
  134. waldiez/models/tool/predefined/_searxng.py +1 -0
  135. waldiez/models/tool/predefined/_tavily.py +1 -0
  136. waldiez/models/tool/predefined/_wikipedia.py +1 -0
  137. waldiez/models/tool/predefined/_youtube.py +1 -0
  138. waldiez/models/tool/tool.py +8 -5
  139. waldiez/models/tool/tool_data.py +2 -2
  140. waldiez/models/waldiez.py +152 -4
  141. waldiez/runner.py +11 -5
  142. waldiez/running/async_utils.py +192 -0
  143. waldiez/running/base_runner.py +117 -264
  144. waldiez/running/dir_utils.py +52 -0
  145. waldiez/running/environment.py +10 -44
  146. waldiez/running/events_mixin.py +252 -0
  147. waldiez/running/exceptions.py +20 -0
  148. waldiez/running/gen_seq_diagram.py +18 -15
  149. waldiez/running/io_utils.py +216 -0
  150. waldiez/running/protocol.py +11 -5
  151. waldiez/running/requirements_mixin.py +65 -0
  152. waldiez/running/results_mixin.py +926 -0
  153. waldiez/running/standard_runner.py +22 -25
  154. waldiez/running/step_by_step/breakpoints_mixin.py +192 -60
  155. waldiez/running/step_by_step/command_handler.py +3 -0
  156. waldiez/running/step_by_step/events_processor.py +194 -14
  157. waldiez/running/step_by_step/step_by_step_models.py +110 -43
  158. waldiez/running/step_by_step/step_by_step_runner.py +107 -57
  159. waldiez/running/subprocess_runner/__base__.py +9 -1
  160. waldiez/running/subprocess_runner/_async_runner.py +5 -3
  161. waldiez/running/subprocess_runner/_sync_runner.py +6 -2
  162. waldiez/running/subprocess_runner/runner.py +39 -23
  163. waldiez/running/timeline_processor.py +1 -1
  164. waldiez/utils/__init__.py +2 -0
  165. waldiez/utils/conflict_checker.py +4 -4
  166. waldiez/utils/python_manager.py +415 -0
  167. waldiez/ws/_file_handler.py +18 -18
  168. waldiez/ws/_mock.py +2 -1
  169. waldiez/ws/cli.py +36 -12
  170. waldiez/ws/client_manager.py +35 -27
  171. waldiez/ws/errors.py +3 -0
  172. waldiez/ws/models.py +43 -52
  173. waldiez/ws/reloader.py +12 -4
  174. waldiez/ws/server.py +85 -55
  175. waldiez/ws/session_manager.py +8 -9
  176. waldiez/ws/session_stats.py +1 -1
  177. waldiez/ws/utils.py +4 -1
  178. {waldiez-0.6.0.dist-info → waldiez-0.6.1.dist-info}/METADATA +82 -93
  179. waldiez-0.6.1.dist-info/RECORD +254 -0
  180. waldiez/running/post_run.py +0 -186
  181. waldiez/running/pre_run.py +0 -281
  182. waldiez/running/run_results.py +0 -14
  183. waldiez/running/utils.py +0 -625
  184. waldiez-0.6.0.dist-info/RECORD +0 -251
  185. {waldiez-0.6.0.dist-info → waldiez-0.6.1.dist-info}/WHEEL +0 -0
  186. {waldiez-0.6.0.dist-info → waldiez-0.6.1.dist-info}/entry_points.txt +0 -0
  187. {waldiez-0.6.0.dist-info → waldiez-0.6.1.dist-info}/licenses/LICENSE +0 -0
  188. {waldiez-0.6.0.dist-info → waldiez-0.6.1.dist-info}/licenses/NOTICE.md +0 -0
@@ -2,6 +2,7 @@
2
2
  # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
3
 
4
4
  # pyright: reportUnknownMemberType=false, reportAttributeAccessIssue=false
5
+ # pyright: reportMissingTypeStubs=false, reportDeprecated=false
5
6
  # pylint: disable=duplicate-code,too-few-public-methods
6
7
  """Run a waldiez flow.
7
8
 
@@ -16,12 +17,16 @@ import traceback
16
17
  from pathlib import Path
17
18
  from typing import TYPE_CHECKING, Any, Union
18
19
 
20
+ from typing_extensions import override
21
+
19
22
  from waldiez.models.waldiez import Waldiez
20
- from waldiez.running.run_results import WaldiezRunResults
21
23
 
22
24
  from .base_runner import WaldiezBaseRunner
25
+ from .events_mixin import EventsMixin
26
+ from .results_mixin import WaldiezRunResults
23
27
 
24
28
  if TYPE_CHECKING:
29
+ from autogen.agentchat import ConversableAgent # type: ignore
25
30
  from autogen.events import BaseEvent # type: ignore
26
31
  from autogen.messages import BaseMessage # type: ignore
27
32
 
@@ -59,20 +64,8 @@ class WaldiezStandardRunner(WaldiezBaseRunner):
59
64
  self._event_count = 0
60
65
  self._processed_events = 0
61
66
 
62
- @staticmethod
63
- def print(*args: Any, **kwargs: Any) -> None:
64
- """Print.
65
-
66
- Parameters
67
- ----------
68
- *args : Any
69
- Positional arguments to print.
70
- **kwargs : Any
71
- Keyword arguments to print.
72
- """
73
- WaldiezBaseRunner.print(*args, **kwargs)
74
-
75
67
  # pylint: disable=unused-argument
68
+ @override
76
69
  def _run(
77
70
  self,
78
71
  temp_dir: Path,
@@ -108,9 +101,9 @@ class WaldiezStandardRunner(WaldiezBaseRunner):
108
101
  )
109
102
  else:
110
103
  stream = IOStream.get_default()
111
- WaldiezBaseRunner._print = stream.print
112
- WaldiezBaseRunner._input = stream.input
113
- WaldiezBaseRunner._send = stream.send
104
+ EventsMixin.set_print_function(stream.print)
105
+ EventsMixin.set_input_function(stream.input)
106
+ EventsMixin.set_send_function(stream.send)
114
107
  self.print(MESSAGES["workflow_starting"])
115
108
  self.print(self.waldiez.info.model_dump_json())
116
109
  results = loaded_module.main(
@@ -133,6 +126,7 @@ class WaldiezStandardRunner(WaldiezBaseRunner):
133
126
  def _on_event(
134
127
  self,
135
128
  event: Union["BaseEvent", "BaseMessage"],
129
+ agents: list["ConversableAgent"],
136
130
  ) -> bool:
137
131
  """Process an event from the workflow."""
138
132
  self._event_count += 1
@@ -142,7 +136,7 @@ class WaldiezStandardRunner(WaldiezBaseRunner):
142
136
  )
143
137
  return False
144
138
  try:
145
- WaldiezBaseRunner.process_event(event)
139
+ EventsMixin.process_event(event, agents)
146
140
  self._processed_events += 1
147
141
  except SystemExit: # pragma: no cover
148
142
  self.log.debug("Execution stopped by user (sync)")
@@ -159,6 +153,7 @@ class WaldiezStandardRunner(WaldiezBaseRunner):
159
153
  async def _a_on_event(
160
154
  self,
161
155
  event: Union["BaseEvent", "BaseMessage"],
156
+ agents: list["ConversableAgent"],
162
157
  ) -> bool:
163
158
  """Process an event from the workflow asynchronously."""
164
159
  self._event_count += 1
@@ -168,7 +163,7 @@ class WaldiezStandardRunner(WaldiezBaseRunner):
168
163
  )
169
164
  return False
170
165
  try:
171
- await WaldiezBaseRunner.a_process_event(event)
166
+ await EventsMixin.a_process_event(event, agents)
172
167
  self._processed_events += 1
173
168
  except SystemExit: # pragma: no cover
174
169
  self.log.debug("Execution stopped by user (async)")
@@ -183,6 +178,7 @@ class WaldiezStandardRunner(WaldiezBaseRunner):
183
178
  return not self._stop_requested.is_set()
184
179
 
185
180
  # pylint: disable=too-complex
181
+ @override
186
182
  async def _a_run(
187
183
  self,
188
184
  temp_dir: Path,
@@ -199,7 +195,7 @@ class WaldiezStandardRunner(WaldiezBaseRunner):
199
195
  # fmt: on
200
196
  """Execute the workflow in an async context."""
201
197
  # pylint: disable=import-outside-toplevel
202
- from autogen.io import IOStream # pyright: ignore
198
+ from autogen.io import IOStream
203
199
 
204
200
  from waldiez.io import StructuredIOStream
205
201
 
@@ -208,10 +204,11 @@ class WaldiezStandardRunner(WaldiezBaseRunner):
208
204
  try:
209
205
  loaded_module = self._load_module(output_file, temp_dir)
210
206
  if self._stop_requested.is_set(): # pragma: no cover
211
- self.log.debug(
207
+ msg = (
212
208
  "Execution stopped before AG2 "
213
209
  "workflow event processing (async)"
214
210
  )
211
+ self.log.debug(msg)
215
212
  return []
216
213
  # noinspection DuplicatedCode
217
214
  if self.structured_io:
@@ -220,12 +217,12 @@ class WaldiezStandardRunner(WaldiezBaseRunner):
220
217
  )
221
218
  else:
222
219
  stream = IOStream.get_default()
223
- WaldiezBaseRunner._print = stream.print
224
- WaldiezBaseRunner._input = stream.input
225
- WaldiezBaseRunner._send = stream.send
220
+ EventsMixin.set_print_function(stream.print)
221
+ EventsMixin.set_input_function(stream.input)
222
+ EventsMixin.set_send_function(stream.send)
226
223
  self.print(MESSAGES["workflow_starting"])
227
224
  self.print(self.waldiez.info.model_dump_json())
228
- results = await loaded_module.main( # pyright: ignore
225
+ results = await loaded_module.main(
229
226
  on_event=self._a_on_event
230
227
  )
231
228
  self.print(MESSAGES["workflow_finished"])
@@ -1,11 +1,18 @@
1
1
  # SPDX-License-Identifier: Apache-2.0.
2
2
  # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
+
3
4
  # pylint: disable=unused-argument
5
+ # pyright: reportDeprecated=false, reportMissingTypeStubs=false
6
+ # pyright: reportUnusedParameter=false, reportUnnecessaryIsInstance=false
7
+ # pyright: reportUnknownMemberType=false, reportUnknownVariableType=false
8
+ # pyright: reportUnknownArgumentType=false
9
+
4
10
  """Breakpoints management mixin for step-by-step debugging."""
5
11
 
6
12
  import logging
13
+ from collections.abc import Iterable
7
14
  from functools import lru_cache
8
- from typing import TYPE_CHECKING, Any, Callable, Iterable, Union
15
+ from typing import TYPE_CHECKING, Any, Callable, Union
9
16
 
10
17
  from .step_by_step_models import (
11
18
  WaldiezBreakpoint,
@@ -13,6 +20,7 @@ from .step_by_step_models import (
13
20
  WaldiezDebugBreakpointCleared,
14
21
  WaldiezDebugBreakpointRemoved,
15
22
  WaldiezDebugBreakpointsList,
23
+ WaldiezDebugConfig,
16
24
  WaldiezDebugError,
17
25
  WaldiezDebugMessage,
18
26
  )
@@ -57,9 +65,14 @@ def handle_breakpoint_errors(func: Callable[..., bool]) -> Callable[..., bool]:
57
65
  class BreakpointsMixin:
58
66
  """Mixin class for managing breakpoints in step-by-step debugging."""
59
67
 
68
+ _breakpoints: set[WaldiezBreakpoint]
69
+ _agent_id_to_name: dict[str, str]
70
+ _config: WaldiezDebugConfig
71
+
60
72
  def __init__(self, *args: Any, **kwargs: Any) -> None:
61
73
  """Initialize breakpoints storage."""
62
- self._breakpoints: set[WaldiezBreakpoint] = set()
74
+ self._breakpoints = set()
75
+ self._agent_id_to_name = {}
63
76
 
64
77
  # Statistics for monitoring
65
78
  self._breakpoint_stats = {
@@ -72,6 +85,45 @@ class BreakpointsMixin:
72
85
  self._check_breakpoint_match_cached = lru_cache(maxsize=1000)(
73
86
  self._check_breakpoint_match_impl
74
87
  )
88
+ self._config = kwargs.get("config", WaldiezDebugConfig())
89
+
90
+ @staticmethod
91
+ def get_initial_breakpoints(
92
+ items: Iterable[Any],
93
+ ) -> set[WaldiezBreakpoint]:
94
+ """Get initial breakpoints.
95
+
96
+ Parameters
97
+ ----------
98
+ items : Iterable[Any]
99
+ The items to parse for getting the endpoints.
100
+
101
+ Returns
102
+ -------
103
+ set[WaldiezBreakpoint]
104
+ The parsed breakpoints.
105
+ """
106
+ breakpoints: set[WaldiezBreakpoint] = set()
107
+ for item in items:
108
+ if isinstance(item, str):
109
+ try:
110
+ entry = WaldiezBreakpoint.from_string(item)
111
+ breakpoints.add(entry)
112
+ except BaseException: # pylint: disable=broad-exception-caught
113
+ pass
114
+ elif isinstance(item, WaldiezBreakpoint):
115
+ breakpoints.add(item)
116
+ return breakpoints
117
+
118
+ def set_agent_id_to_name(self, mapping: dict[str, str]) -> None:
119
+ """Set the agent id to agent name mapping.
120
+
121
+ Parameters
122
+ ----------
123
+ mapping : dict[str, str]
124
+ The agent id to agent name mapping.
125
+ """
126
+ self._agent_id_to_name = mapping
75
127
 
76
128
  # noinspection PyTypeHints
77
129
  def emit(self, message: WaldiezDebugMessage) -> None:
@@ -97,6 +149,7 @@ class BreakpointsMixin:
97
149
  event_type: str,
98
150
  sender: str,
99
151
  recipient: str,
152
+ sender_only: bool,
100
153
  breakpoints_sig: frozenset[str],
101
154
  ) -> bool:
102
155
  """Check if the event matches any breakpoints.
@@ -109,6 +162,8 @@ class BreakpointsMixin:
109
162
  The event sender.
110
163
  recipient : str
111
164
  The event recipient.
165
+ sender_only : bool
166
+ Only check for event's sender agent.
112
167
  breakpoints_sig : frozenset[str]
113
168
  Signature of current breakpoints for cache invalidation.
114
169
 
@@ -119,8 +174,8 @@ class BreakpointsMixin:
119
174
  """
120
175
  event_dict = {
121
176
  "type": event_type,
122
- "sender": sender,
123
- "recipient": recipient,
177
+ "sender": self._agent_id_to_name.get(sender, sender),
178
+ "recipient": self._agent_id_to_name.get(recipient, recipient),
124
179
  }
125
180
 
126
181
  # Reconstruct breakpoints from signature for cache safety
@@ -130,10 +185,24 @@ class BreakpointsMixin:
130
185
  WaldiezBreakpoint.from_string(bp_str)
131
186
  for bp_str in breakpoints_sig
132
187
  }
133
- return any(bp.matches(event_dict) for bp in breakpoints)
188
+ return any(
189
+ bp.matches(
190
+ event_dict,
191
+ self._agent_id_to_name,
192
+ sender_only=sender_only,
193
+ )
194
+ for bp in breakpoints
195
+ )
134
196
  except Exception: # pylint: disable=broad-exception-caught
135
197
  # Fallback to current breakpoints if signature is malformed
136
- return any(bp.matches(event_dict) for bp in self._breakpoints)
198
+ return any(
199
+ bp.matches(
200
+ event_dict,
201
+ self._agent_id_to_name,
202
+ sender_only=sender_only,
203
+ )
204
+ for bp in self._breakpoints
205
+ )
137
206
 
138
207
  @handle_breakpoint_errors
139
208
  def add_breakpoint(self, spec: str) -> bool:
@@ -149,7 +218,7 @@ class BreakpointsMixin:
149
218
  bool
150
219
  True if the breakpoint was added successfully, False otherwise.
151
220
  """
152
- if not spec or not isinstance(spec, str): # pyright: ignore
221
+ if not spec or not isinstance(spec, str):
153
222
  self.emit(
154
223
  WaldiezDebugError(
155
224
  error="Invalid event type: must be a non-empty string"
@@ -197,7 +266,7 @@ class BreakpointsMixin:
197
266
  if isinstance(spec, WaldiezBreakpoint):
198
267
  breakpoint_obj = spec
199
268
  spec_str = str(spec)
200
- elif isinstance(spec, str) and spec: # pyright: ignore
269
+ elif isinstance(spec, str) and spec:
201
270
  try:
202
271
  breakpoint_obj = WaldiezBreakpoint.from_string(spec)
203
272
  spec_str = spec
@@ -320,8 +389,83 @@ class BreakpointsMixin:
320
389
  except ValueError:
321
390
  return False
322
391
 
392
+ @staticmethod
393
+ def _get_event_core(event_dict: dict[str, Any]) -> tuple[str, str, str]:
394
+ event_type = event_dict.get("type", "unknown")
395
+ sender = event_dict.get("sender", "")
396
+ if not sender:
397
+ event_content = event_dict.get("content", {})
398
+ if isinstance(event_content, dict):
399
+ sender = event_content.get(
400
+ "sender",
401
+ event_content.get("speaker", ""),
402
+ )
403
+ if not isinstance(sender, str):
404
+ sender = ""
405
+ recipient = event_dict.get("recipient", "")
406
+ if not recipient:
407
+ event_content = event_dict.get("content", {})
408
+ if isinstance(event_content, dict):
409
+ recipient = event_content.get("recipient")
410
+ if not isinstance(recipient, str):
411
+ recipient = ""
412
+ return event_type, sender, recipient
413
+
414
+ def _got_breakpoint_match(
415
+ self, event_dump: dict[str, Any], sender_only: bool
416
+ ) -> bool:
417
+ event_type, sender, recipient = BreakpointsMixin._get_event_core(
418
+ event_dump
419
+ )
420
+ event_dict = {
421
+ "type": event_type,
422
+ "sender": sender,
423
+ "recipient": recipient,
424
+ }
425
+
426
+ has_agent_breakpoints = any(
427
+ bp.agent is not None for bp in self._breakpoints
428
+ )
429
+ if has_agent_breakpoints:
430
+ # Don't use cache for agent-based breakpoints
431
+ # (we might have a mix of agent ids and names)
432
+ matches_breakpoint = any(
433
+ bp.matches(
434
+ event_dict,
435
+ self._agent_id_to_name,
436
+ sender_only=sender_only,
437
+ )
438
+ for bp in self._breakpoints
439
+ )
440
+ else:
441
+ # Get current breakpoints signature for cache invalidation
442
+ breakpoints_sig = self._get_breakpoints_signature()
443
+
444
+ # Check cached result
445
+ # noinspection PyBroadException
446
+ try:
447
+ matches_breakpoint = self._check_breakpoint_match_cached(
448
+ event_type, sender, recipient, sender_only, breakpoints_sig
449
+ )
450
+ self._breakpoint_stats["cache_hits"] += 1
451
+ except Exception: # pylint: disable=broad-exception-caught
452
+ # Fallback to non-cached check
453
+ matches_breakpoint = any(
454
+ bp.matches(
455
+ event_dict,
456
+ self._agent_id_to_name,
457
+ sender_only=sender_only,
458
+ )
459
+ for bp in self._breakpoints
460
+ )
461
+ self._breakpoint_stats["cache_misses"] += 1
462
+
463
+ return matches_breakpoint
464
+
323
465
  def should_break_on_event(
324
- self, event: Union["BaseEvent", "BaseMessage"], step_mode: bool = True
466
+ self,
467
+ event: Union["BaseEvent", "BaseMessage"],
468
+ sender_only: bool,
325
469
  ) -> bool:
326
470
  """Determine if we should break on this event.
327
471
 
@@ -329,8 +473,8 @@ class BreakpointsMixin:
329
473
  ----------
330
474
  event : Union[BaseEvent, BaseMessage]
331
475
  The event to check.
332
- step_mode : bool, optional
333
- Whether step mode is enabled, by default True.
476
+ sender_only : bool
477
+ Only check for event's sender agent.
334
478
 
335
479
  Returns
336
480
  -------
@@ -344,59 +488,30 @@ class BreakpointsMixin:
344
488
  if event_type == "input_request":
345
489
  return False
346
490
 
347
- # Quick path: if no breakpoints and not in step mode, don't break
348
- if not self._breakpoints and not step_mode:
349
- return False
350
-
351
- # Quick path: if step mode and no specific breakpoints,
352
- # break on everything
353
- if step_mode and not self._breakpoints:
354
- return True
491
+ if not self._breakpoints:
492
+ return not bool(self._config.auto_continue)
355
493
 
356
494
  # Check if this event matches any breakpoint using caching
357
- if hasattr(event, "model_dump"):
358
- # pylint: disable=too-many-try-statements,broad-exception-caught
359
- try:
360
- event_dict = event.model_dump(
361
- mode="python", exclude_none=True, fallback=str
362
- )
363
-
364
- # Extract event details for cache key
365
- event_type_key = event_dict.get("type", "unknown")
366
- sender = event_dict.get("sender", "")
367
- recipient = event_dict.get("recipient", "")
368
-
369
- # Get current breakpoints signature for cache invalidation
370
- breakpoints_sig = self._get_breakpoints_signature()
371
-
372
- # Check cached result
373
- # noinspection PyBroadException
374
- try:
375
- matches_breakpoint = self._check_breakpoint_match_cached(
376
- event_type_key, sender, recipient, breakpoints_sig
377
- )
378
- self._breakpoint_stats["cache_hits"] += 1
379
- except Exception:
380
- # Fallback to non-cached check
381
- matches_breakpoint = any(
382
- bp.matches(event_dict) for bp in self._breakpoints
383
- )
384
- self._breakpoint_stats["cache_misses"] += 1
385
-
495
+ if not hasattr(event, "model_dump"):
496
+ return not bool(self._config.auto_continue)
497
+ # pylint: disable=too-many-try-statements,broad-exception-caught
498
+ try:
499
+ event_dump = event.model_dump(
500
+ mode="python", exclude_none=True, fallback=str
501
+ )
502
+ matches_breakpoint = self._got_breakpoint_match(
503
+ event_dump, sender_only=sender_only
504
+ )
505
+ # If any breakpoint matches: break regardless of step_mode
506
+ if matches_breakpoint:
386
507
  self._breakpoint_stats["total_matches"] += 1
508
+ return True
387
509
 
388
- # If any breakpoint matches: break regardless of step_mode
389
- if matches_breakpoint:
390
- return True
391
-
392
- except Exception as e: # pylint: disable=broad-exception-caught
393
- logging.warning("Error processing event for breakpoints: %s", e)
394
- self._breakpoint_stats["cache_misses"] += 1
510
+ except Exception as e: # pylint: disable=broad-exception-caught
511
+ logging.warning("Error processing event for breakpoints: %s", e)
512
+ self._breakpoint_stats["cache_misses"] += 1
395
513
 
396
- # No specific breakpoints matched:
397
- # - If step_mode, break on every event (single-step behavior)
398
- # - If not step_mode, do not break
399
- return bool(step_mode)
514
+ return not bool(self._config.auto_continue)
400
515
 
401
516
  def get_breakpoint_stats(self) -> dict[str, Any]:
402
517
  """Get breakpoint statistics including performance metrics.
@@ -410,7 +525,7 @@ class BreakpointsMixin:
410
525
  {
411
526
  "type": bp.type.value,
412
527
  "event_type": bp.event_type,
413
- "agent_name": bp.agent_name,
528
+ "agent": bp.agent,
414
529
  "description": bp.description,
415
530
  "string_repr": str(bp),
416
531
  }
@@ -476,6 +591,23 @@ class BreakpointsMixin:
476
591
  """
477
592
  return [str(bp) for bp in sorted(self._breakpoints, key=str)]
478
593
 
594
+ def is_auto_run(self) -> bool:
595
+ """Check if we are in auto-run mode.
596
+
597
+ Returns
598
+ -------
599
+ bool
600
+ False if we don't have any breakpoints and
601
+ we don't have any 'all' breakpoints, True otherwise.
602
+ """
603
+ if not self._breakpoints:
604
+ return False
605
+ if any(bp.type.value == "all" for bp in self._breakpoints):
606
+ self._breakpoints.clear()
607
+ self._invalidate_cache()
608
+ return False
609
+ return True
610
+
479
611
  def import_breakpoints(
480
612
  self, breakpoint_specs: list[str]
481
613
  ) -> tuple[int, list[str]]:
@@ -1,6 +1,9 @@
1
1
  # SPDX-License-Identifier: Apache-2.0.
2
2
  # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
+
3
4
  # pylint: disable=unused-argument
5
+ # pyright: reportImportCycles=false, reportUnusedParameter=false
6
+
4
7
  """Command handler for step-by-step execution."""
5
8
 
6
9
  from typing import TYPE_CHECKING, Callable