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.
- waldiez/__init__.py +1 -1
- waldiez/_version.py +1 -1
- waldiez/cli.py +18 -7
- waldiez/cli_extras/jupyter.py +3 -0
- waldiez/cli_extras/runner.py +3 -1
- waldiez/cli_extras/studio.py +3 -1
- waldiez/exporter.py +9 -3
- waldiez/exporting/agent/exporter.py +9 -10
- waldiez/exporting/agent/extras/captain_agent_extras.py +6 -6
- waldiez/exporting/agent/extras/doc_agent_extras.py +6 -6
- waldiez/exporting/agent/extras/group_manager_agent_extas.py +34 -23
- waldiez/exporting/agent/extras/group_member_extras.py +6 -5
- waldiez/exporting/agent/extras/handoffs/after_work.py +1 -1
- waldiez/exporting/agent/extras/handoffs/available.py +1 -1
- waldiez/exporting/agent/extras/handoffs/condition.py +3 -2
- waldiez/exporting/agent/extras/handoffs/handoff.py +1 -1
- waldiez/exporting/agent/extras/handoffs/target.py +6 -4
- waldiez/exporting/agent/extras/rag/chroma_extras.py +27 -19
- waldiez/exporting/agent/extras/rag/mongo_extras.py +8 -8
- waldiez/exporting/agent/extras/rag/pgvector_extras.py +5 -5
- waldiez/exporting/agent/extras/rag/qdrant_extras.py +5 -4
- waldiez/exporting/agent/extras/rag/vector_db_extras.py +1 -1
- waldiez/exporting/agent/extras/rag_user_proxy_agent_extras.py +5 -7
- waldiez/exporting/agent/extras/reasoning_agent_extras.py +3 -5
- waldiez/exporting/chats/exporter.py +4 -4
- waldiez/exporting/chats/processor.py +1 -2
- waldiez/exporting/chats/utils/common.py +89 -48
- waldiez/exporting/chats/utils/group.py +9 -9
- waldiez/exporting/chats/utils/nested.py +7 -7
- waldiez/exporting/chats/utils/sequential.py +1 -1
- waldiez/exporting/chats/utils/single.py +2 -2
- waldiez/exporting/core/content.py +7 -7
- waldiez/exporting/core/context.py +5 -3
- waldiez/exporting/core/exporter.py +5 -3
- waldiez/exporting/core/exporters.py +2 -2
- waldiez/exporting/core/extras/agent_extras/captain_extras.py +2 -2
- waldiez/exporting/core/extras/agent_extras/group_manager_extras.py +2 -2
- waldiez/exporting/core/extras/agent_extras/rag_user_extras.py +2 -2
- waldiez/exporting/core/extras/agent_extras/standard_extras.py +3 -8
- waldiez/exporting/core/extras/base.py +7 -5
- waldiez/exporting/core/extras/flow_extras.py +4 -5
- waldiez/exporting/core/extras/model_extras.py +2 -2
- waldiez/exporting/core/extras/path_resolver.py +1 -2
- waldiez/exporting/core/extras/serializer.py +2 -2
- waldiez/exporting/core/protocols.py +6 -5
- waldiez/exporting/core/result.py +25 -28
- waldiez/exporting/core/types.py +10 -10
- waldiez/exporting/core/utils/llm_config.py +2 -2
- waldiez/exporting/core/validation.py +10 -11
- waldiez/exporting/flow/execution_generator.py +98 -10
- waldiez/exporting/flow/exporter.py +2 -2
- waldiez/exporting/flow/factory.py +2 -2
- waldiez/exporting/flow/file_generator.py +4 -2
- waldiez/exporting/flow/merger.py +5 -3
- waldiez/exporting/flow/orchestrator.py +72 -2
- waldiez/exporting/flow/utils/common.py +5 -5
- waldiez/exporting/flow/utils/importing.py +6 -7
- waldiez/exporting/flow/utils/linting.py +25 -9
- waldiez/exporting/flow/utils/logging.py +2 -2
- waldiez/exporting/models/exporter.py +8 -8
- waldiez/exporting/models/processor.py +5 -5
- waldiez/exporting/tools/exporter.py +2 -2
- waldiez/exporting/tools/processor.py +7 -4
- waldiez/io/__init__.py +8 -4
- waldiez/io/_ws.py +10 -6
- waldiez/io/models/constants.py +10 -10
- waldiez/io/models/content/audio.py +1 -0
- waldiez/io/models/content/base.py +20 -18
- waldiez/io/models/content/file.py +1 -0
- waldiez/io/models/content/image.py +1 -0
- waldiez/io/models/content/text.py +1 -0
- waldiez/io/models/content/video.py +1 -0
- waldiez/io/models/user_input.py +10 -5
- waldiez/io/models/user_response.py +17 -16
- waldiez/io/mqtt.py +18 -31
- waldiez/io/redis.py +18 -22
- waldiez/io/structured.py +52 -53
- waldiez/io/utils.py +3 -0
- waldiez/io/ws.py +5 -1
- waldiez/logger.py +16 -3
- waldiez/models/agents/__init__.py +3 -0
- waldiez/models/agents/agent/agent.py +23 -16
- waldiez/models/agents/agent/agent_data.py +25 -22
- waldiez/models/agents/agent/code_execution.py +9 -11
- waldiez/models/agents/agent/termination_message.py +10 -12
- waldiez/models/agents/agent/update_system_message.py +2 -4
- waldiez/models/agents/agents.py +8 -8
- waldiez/models/agents/assistant/assistant.py +6 -3
- waldiez/models/agents/assistant/assistant_data.py +2 -2
- waldiez/models/agents/captain/captain_agent.py +7 -4
- waldiez/models/agents/captain/captain_agent_data.py +5 -7
- waldiez/models/agents/doc_agent/doc_agent.py +7 -4
- waldiez/models/agents/doc_agent/doc_agent_data.py +9 -10
- waldiez/models/agents/doc_agent/rag_query_engine.py +10 -12
- waldiez/models/agents/extra_requirements.py +3 -3
- waldiez/models/agents/group_manager/group_manager.py +12 -7
- waldiez/models/agents/group_manager/group_manager_data.py +13 -12
- waldiez/models/agents/group_manager/speakers.py +17 -19
- waldiez/models/agents/rag_user_proxy/rag_user_proxy.py +7 -4
- waldiez/models/agents/rag_user_proxy/rag_user_proxy_data.py +4 -1
- waldiez/models/agents/rag_user_proxy/retrieve_config.py +69 -63
- waldiez/models/agents/rag_user_proxy/vector_db_config.py +19 -19
- waldiez/models/agents/reasoning/reasoning_agent.py +7 -4
- waldiez/models/agents/reasoning/reasoning_agent_data.py +3 -2
- waldiez/models/agents/reasoning/reasoning_agent_reason_config.py +8 -8
- waldiez/models/agents/user_proxy/user_proxy.py +6 -3
- waldiez/models/agents/user_proxy/user_proxy_data.py +1 -1
- waldiez/models/chat/chat.py +27 -20
- waldiez/models/chat/chat_data.py +22 -19
- waldiez/models/chat/chat_message.py +9 -9
- waldiez/models/chat/chat_nested.py +9 -9
- waldiez/models/chat/chat_summary.py +6 -6
- waldiez/models/common/__init__.py +2 -0
- waldiez/models/common/ag2_version.py +2 -0
- waldiez/models/common/dict_utils.py +8 -6
- waldiez/models/common/handoff.py +18 -17
- waldiez/models/common/method_utils.py +7 -7
- waldiez/models/common/naming.py +49 -0
- waldiez/models/flow/flow.py +11 -6
- waldiez/models/flow/flow_data.py +23 -17
- waldiez/models/flow/info.py +3 -3
- waldiez/models/flow/naming.py +2 -1
- waldiez/models/model/_aws.py +11 -13
- waldiez/models/model/_llm.py +5 -0
- waldiez/models/model/_price.py +2 -4
- waldiez/models/model/extra_requirements.py +1 -3
- waldiez/models/model/model.py +2 -2
- waldiez/models/model/model_data.py +21 -21
- waldiez/models/tool/extra_requirements.py +2 -4
- waldiez/models/tool/predefined/_duckduckgo.py +1 -0
- waldiez/models/tool/predefined/_email.py +1 -0
- waldiez/models/tool/predefined/_google.py +1 -0
- waldiez/models/tool/predefined/_perplexity.py +1 -0
- waldiez/models/tool/predefined/_searxng.py +1 -0
- waldiez/models/tool/predefined/_tavily.py +1 -0
- waldiez/models/tool/predefined/_wikipedia.py +1 -0
- waldiez/models/tool/predefined/_youtube.py +1 -0
- waldiez/models/tool/tool.py +8 -5
- waldiez/models/tool/tool_data.py +2 -2
- waldiez/models/waldiez.py +152 -4
- waldiez/runner.py +11 -5
- waldiez/running/async_utils.py +192 -0
- waldiez/running/base_runner.py +117 -264
- waldiez/running/dir_utils.py +52 -0
- waldiez/running/environment.py +10 -44
- waldiez/running/events_mixin.py +252 -0
- waldiez/running/exceptions.py +20 -0
- waldiez/running/gen_seq_diagram.py +18 -15
- waldiez/running/io_utils.py +216 -0
- waldiez/running/protocol.py +11 -5
- waldiez/running/requirements_mixin.py +65 -0
- waldiez/running/results_mixin.py +926 -0
- waldiez/running/standard_runner.py +22 -25
- waldiez/running/step_by_step/breakpoints_mixin.py +192 -60
- waldiez/running/step_by_step/command_handler.py +3 -0
- waldiez/running/step_by_step/events_processor.py +194 -14
- waldiez/running/step_by_step/step_by_step_models.py +110 -43
- waldiez/running/step_by_step/step_by_step_runner.py +107 -57
- waldiez/running/subprocess_runner/__base__.py +9 -1
- waldiez/running/subprocess_runner/_async_runner.py +5 -3
- waldiez/running/subprocess_runner/_sync_runner.py +6 -2
- waldiez/running/subprocess_runner/runner.py +39 -23
- waldiez/running/timeline_processor.py +1 -1
- waldiez/utils/__init__.py +2 -0
- waldiez/utils/conflict_checker.py +4 -4
- waldiez/utils/python_manager.py +415 -0
- waldiez/ws/_file_handler.py +18 -18
- waldiez/ws/_mock.py +2 -1
- waldiez/ws/cli.py +36 -12
- waldiez/ws/client_manager.py +35 -27
- waldiez/ws/errors.py +3 -0
- waldiez/ws/models.py +43 -52
- waldiez/ws/reloader.py +12 -4
- waldiez/ws/server.py +85 -55
- waldiez/ws/session_manager.py +8 -9
- waldiez/ws/session_stats.py +1 -1
- waldiez/ws/utils.py +4 -1
- {waldiez-0.6.0.dist-info → waldiez-0.6.1.dist-info}/METADATA +82 -93
- waldiez-0.6.1.dist-info/RECORD +254 -0
- waldiez/running/post_run.py +0 -186
- waldiez/running/pre_run.py +0 -281
- waldiez/running/run_results.py +0 -14
- waldiez/running/utils.py +0 -625
- waldiez-0.6.0.dist-info/RECORD +0 -251
- {waldiez-0.6.0.dist-info → waldiez-0.6.1.dist-info}/WHEEL +0 -0
- {waldiez-0.6.0.dist-info → waldiez-0.6.1.dist-info}/entry_points.txt +0 -0
- {waldiez-0.6.0.dist-info → waldiez-0.6.1.dist-info}/licenses/LICENSE +0 -0
- {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
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
224
|
-
|
|
225
|
-
|
|
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(
|
|
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,
|
|
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
|
|
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(
|
|
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(
|
|
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):
|
|
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:
|
|
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,
|
|
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
|
-
|
|
333
|
-
|
|
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
|
-
|
|
348
|
-
|
|
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
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
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
|
-
|
|
389
|
-
|
|
390
|
-
|
|
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
|
-
|
|
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
|
-
"
|
|
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
|