waldiez 0.5.10__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 +19 -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 +15 -16
- 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 +40 -24
- waldiez/exporting/agent/extras/group_member_extras.py +6 -5
- waldiez/exporting/agent/extras/handoffs/after_work.py +2 -1
- waldiez/exporting/agent/extras/handoffs/available.py +2 -1
- waldiez/exporting/agent/extras/handoffs/condition.py +3 -2
- waldiez/exporting/agent/extras/handoffs/handoff.py +2 -1
- waldiez/exporting/agent/extras/handoffs/target.py +7 -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/agent/termination.py +1 -0
- 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/constants.py +3 -1
- 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 +13 -11
- waldiez/exporting/core/protocols.py +6 -5
- waldiez/exporting/core/result.py +25 -28
- waldiez/exporting/core/types.py +11 -10
- waldiez/exporting/core/utils/llm_config.py +4 -4
- waldiez/exporting/core/validation.py +10 -11
- waldiez/exporting/flow/execution_generator.py +99 -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 +6 -6
- waldiez/exporting/flow/utils/importing.py +7 -8
- waldiez/exporting/flow/utils/linting.py +25 -9
- waldiez/exporting/flow/utils/logging.py +5 -77
- 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 +11 -5
- waldiez/io/_ws.py +12 -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 +122 -70
- waldiez/io/utils.py +19 -10
- waldiez/io/ws.py +7 -3
- waldiez/logger.py +16 -3
- waldiez/models/agents/__init__.py +3 -0
- waldiez/models/agents/agent/agent.py +25 -17
- 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 +28 -20
- waldiez/models/chat/chat_data.py +22 -21
- 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/base.py +2 -0
- waldiez/models/common/dict_utils.py +8 -6
- waldiez/models/common/handoff.py +20 -17
- waldiez/models/common/method_utils.py +9 -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 +8 -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 +4 -0
- waldiez/models/tool/predefined/_google.py +1 -0
- waldiez/models/tool/predefined/_perplexity.py +2 -1
- waldiez/models/tool/predefined/_searxng.py +2 -1
- waldiez/models/tool/predefined/_tavily.py +1 -0
- waldiez/models/tool/predefined/_wikipedia.py +2 -1
- 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 +155 -241
- 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 +24 -27
- waldiez/running/step_by_step/breakpoints_mixin.py +503 -47
- waldiez/running/step_by_step/command_handler.py +154 -0
- waldiez/running/step_by_step/events_processor.py +379 -0
- waldiez/running/step_by_step/step_by_step_models.py +425 -41
- waldiez/running/step_by_step/step_by_step_runner.py +437 -382
- waldiez/running/subprocess_runner/__base__.py +13 -8
- waldiez/running/subprocess_runner/_async_runner.py +6 -4
- waldiez/running/subprocess_runner/_sync_runner.py +11 -6
- waldiez/running/subprocess_runner/runner.py +48 -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/__init__.py +8 -7
- waldiez/ws/_file_handler.py +18 -20
- waldiez/ws/_mock.py +75 -0
- waldiez/ws/cli.py +58 -10
- waldiez/ws/client_manager.py +77 -53
- waldiez/ws/errors.py +3 -0
- waldiez/ws/models.py +61 -53
- waldiez/ws/reloader.py +33 -4
- waldiez/ws/server.py +121 -52
- waldiez/ws/session_manager.py +8 -9
- waldiez/ws/session_stats.py +1 -1
- waldiez/ws/utils.py +33 -5
- {waldiez-0.5.10.dist-info → waldiez-0.6.1.dist-info}/METADATA +107 -109
- waldiez-0.6.1.dist-info/RECORD +254 -0
- waldiez/running/post_run.py +0 -180
- waldiez/running/pre_run.py +0 -159
- waldiez/running/run_results.py +0 -14
- waldiez/running/utils.py +0 -511
- waldiez-0.5.10.dist-info/RECORD +0 -248
- {waldiez-0.5.10.dist-info → waldiez-0.6.1.dist-info}/WHEEL +0 -0
- {waldiez-0.5.10.dist-info → waldiez-0.6.1.dist-info}/entry_points.txt +0 -0
- {waldiez-0.5.10.dist-info → waldiez-0.6.1.dist-info}/licenses/LICENSE +0 -0
- {waldiez-0.5.10.dist-info → waldiez-0.6.1.dist-info}/licenses/NOTICE.md +0 -0
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# SPDX-License-Identifier: Apache-2.0.
|
|
2
2
|
# Copyright (c) 2024 - 2025 Waldiez and contributors.
|
|
3
3
|
|
|
4
|
-
# pylint: disable=line-too-long
|
|
4
|
+
# pylint: disable=duplicate-code,line-too-long
|
|
5
5
|
# pyright: reportUnknownMemberType=false, reportAttributeAccessIssue=false
|
|
6
6
|
# pyright: reportUnknownArgumentType=false, reportOptionalMemberAccess=false
|
|
7
|
-
#
|
|
7
|
+
# pyright: reportDeprecated=false, reportMissingTypeStubs=false
|
|
8
|
+
# pyright: reportUnsafeMultipleInheritance=false
|
|
8
9
|
# flake8: noqa: E501
|
|
9
10
|
|
|
10
11
|
"""Step-by-step Waldiez runner with user interaction capabilities."""
|
|
@@ -13,20 +14,26 @@ import asyncio
|
|
|
13
14
|
import threading
|
|
14
15
|
import traceback
|
|
15
16
|
import uuid
|
|
17
|
+
from collections import deque
|
|
18
|
+
from collections.abc import Iterable
|
|
16
19
|
from pathlib import Path
|
|
17
|
-
from typing import TYPE_CHECKING, Any,
|
|
20
|
+
from typing import TYPE_CHECKING, Any, Union
|
|
18
21
|
|
|
19
22
|
from pydantic import ValidationError
|
|
23
|
+
from typing_extensions import override
|
|
20
24
|
|
|
21
25
|
from waldiez.models.waldiez import Waldiez
|
|
26
|
+
from waldiez.running.step_by_step.command_handler import CommandHandler
|
|
27
|
+
from waldiez.running.step_by_step.events_processor import EventProcessor
|
|
22
28
|
|
|
23
29
|
from ..base_runner import WaldiezBaseRunner
|
|
30
|
+
from ..events_mixin import EventsMixin
|
|
24
31
|
from ..exceptions import StopRunningException
|
|
25
|
-
from ..
|
|
32
|
+
from ..results_mixin import WaldiezRunResults
|
|
26
33
|
from .breakpoints_mixin import BreakpointsMixin
|
|
27
34
|
from .step_by_step_models import (
|
|
28
|
-
HELP_MESSAGE,
|
|
29
35
|
VALID_CONTROL_COMMANDS,
|
|
36
|
+
WaldiezDebugConfig,
|
|
30
37
|
WaldiezDebugError,
|
|
31
38
|
WaldiezDebugEventInfo,
|
|
32
39
|
WaldiezDebugInputRequest,
|
|
@@ -37,13 +44,10 @@ from .step_by_step_models import (
|
|
|
37
44
|
)
|
|
38
45
|
|
|
39
46
|
if TYPE_CHECKING:
|
|
47
|
+
from autogen.agentchat import ConversableAgent # type: ignore
|
|
40
48
|
from autogen.events import BaseEvent # type: ignore
|
|
41
49
|
from autogen.messages import BaseMessage # type: ignore
|
|
42
50
|
|
|
43
|
-
|
|
44
|
-
DEBUG_INPUT_PROMPT = (
|
|
45
|
-
"[Step] (c)ontinue, (r)un, (q)uit, (i)nfo, (h)elp, (st)ats: "
|
|
46
|
-
)
|
|
47
51
|
MESSAGES = {
|
|
48
52
|
"workflow_starting": "<Waldiez step-by-step> - Starting workflow...",
|
|
49
53
|
"workflow_finished": "<Waldiez step-by-step> - Workflow finished",
|
|
@@ -52,12 +56,27 @@ MESSAGES = {
|
|
|
52
56
|
"<Waldiez step-by-step> - Workflow execution failed: {error}"
|
|
53
57
|
),
|
|
54
58
|
}
|
|
59
|
+
DEBUG_INPUT_PROMPT = (
|
|
60
|
+
# cspell: disable-next-line
|
|
61
|
+
"[Step] (c)ontinue, (r)un, (q)uit, (i)nfo, (h)elp, (st)ats: "
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def gen_id() -> str:
|
|
66
|
+
"""Generate a new id.
|
|
67
|
+
|
|
68
|
+
Returns
|
|
69
|
+
-------
|
|
70
|
+
str
|
|
71
|
+
The new id.
|
|
72
|
+
"""
|
|
73
|
+
return str(uuid.uuid4())
|
|
55
74
|
|
|
56
75
|
|
|
57
76
|
# pylint: disable=too-many-instance-attributes
|
|
58
77
|
# noinspection DuplicatedCode,StrFormat
|
|
59
78
|
class WaldiezStepByStepRunner(WaldiezBaseRunner, BreakpointsMixin):
|
|
60
|
-
"""
|
|
79
|
+
"""Refactored step-by-step runner with improved architecture."""
|
|
61
80
|
|
|
62
81
|
def __init__(
|
|
63
82
|
self,
|
|
@@ -67,7 +86,8 @@ class WaldiezStepByStepRunner(WaldiezBaseRunner, BreakpointsMixin):
|
|
|
67
86
|
structured_io: bool = False,
|
|
68
87
|
dot_env: str | Path | None = None,
|
|
69
88
|
auto_continue: bool = False,
|
|
70
|
-
breakpoints: Iterable[
|
|
89
|
+
breakpoints: Iterable[Any] | None = None,
|
|
90
|
+
config: WaldiezDebugConfig | None = None,
|
|
71
91
|
**kwargs: Any,
|
|
72
92
|
) -> None:
|
|
73
93
|
"""Initialize the step-by-step runner."""
|
|
@@ -79,24 +99,46 @@ class WaldiezStepByStepRunner(WaldiezBaseRunner, BreakpointsMixin):
|
|
|
79
99
|
dot_env=dot_env,
|
|
80
100
|
**kwargs,
|
|
81
101
|
)
|
|
82
|
-
BreakpointsMixin.__init__(self)
|
|
102
|
+
BreakpointsMixin.__init__(self, config=config)
|
|
103
|
+
self.set_agent_id_to_name(waldiez.flow.unique_names["agent_names"])
|
|
104
|
+
|
|
105
|
+
# Configuration
|
|
106
|
+
self._config = config or WaldiezDebugConfig()
|
|
107
|
+
self._config.auto_continue = auto_continue
|
|
108
|
+
|
|
109
|
+
# Core state
|
|
83
110
|
self._event_count = 0
|
|
84
111
|
self._processed_events = 0
|
|
85
|
-
self._step_mode =
|
|
86
|
-
|
|
87
|
-
#
|
|
88
|
-
|
|
89
|
-
self.
|
|
90
|
-
|
|
112
|
+
self._step_mode = self._config.step_mode
|
|
113
|
+
|
|
114
|
+
# Use deque for efficient FIFO operations on event history
|
|
115
|
+
self._event_history: deque[dict[str, Any]] = deque(
|
|
116
|
+
maxlen=self._config.max_event_history
|
|
117
|
+
)
|
|
91
118
|
self._current_event: Union["BaseEvent", "BaseMessage", None] = None
|
|
119
|
+
|
|
120
|
+
# Participant tracking
|
|
92
121
|
self._known_participants = self.waldiez.info.participants
|
|
93
122
|
self._last_sender: str | None = None
|
|
94
123
|
self._last_recipient: str | None = None
|
|
95
124
|
|
|
125
|
+
# Initialize breakpoints
|
|
126
|
+
if breakpoints:
|
|
127
|
+
_, errors = self.import_breakpoints(list(breakpoints))
|
|
128
|
+
if errors:
|
|
129
|
+
for error in errors:
|
|
130
|
+
self.log.warning("Breakpoint import error: %s", error)
|
|
131
|
+
|
|
132
|
+
# Command handling
|
|
133
|
+
self._command_handler = CommandHandler(self)
|
|
134
|
+
self._event_processor = EventProcessor(self)
|
|
135
|
+
auto_run = self.is_auto_run()
|
|
136
|
+
self._config.auto_continue = auto_run
|
|
137
|
+
|
|
96
138
|
@property
|
|
97
139
|
def auto_continue(self) -> bool:
|
|
98
140
|
"""Get whether auto-continue is enabled."""
|
|
99
|
-
return self.
|
|
141
|
+
return self._config.auto_continue
|
|
100
142
|
|
|
101
143
|
@auto_continue.setter
|
|
102
144
|
def auto_continue(self, value: bool) -> None:
|
|
@@ -107,321 +149,438 @@ class WaldiezStepByStepRunner(WaldiezBaseRunner, BreakpointsMixin):
|
|
|
107
149
|
value : bool
|
|
108
150
|
Whether to enable auto-continue.
|
|
109
151
|
"""
|
|
110
|
-
self.
|
|
152
|
+
self._config.auto_continue = value
|
|
153
|
+
self.log.debug("Auto-continue mode set to: %s", value)
|
|
111
154
|
|
|
112
155
|
@property
|
|
113
|
-
def
|
|
114
|
-
"""Get the
|
|
115
|
-
|
|
156
|
+
def step_mode(self) -> bool:
|
|
157
|
+
"""Get the step mode.
|
|
158
|
+
|
|
159
|
+
Returns
|
|
160
|
+
-------
|
|
161
|
+
bool
|
|
162
|
+
Whether the step mode is enabled.
|
|
163
|
+
"""
|
|
164
|
+
return self._step_mode
|
|
165
|
+
|
|
166
|
+
@step_mode.setter
|
|
167
|
+
def step_mode(self, value: bool) -> None:
|
|
168
|
+
"""Set the step mode.
|
|
169
|
+
|
|
170
|
+
Parameters
|
|
171
|
+
----------
|
|
172
|
+
value : bool
|
|
173
|
+
Whether to enable step mode.
|
|
174
|
+
"""
|
|
175
|
+
self._step_mode = value
|
|
176
|
+
self.log.debug("Step mode set to: %s", value)
|
|
177
|
+
|
|
178
|
+
@property
|
|
179
|
+
def last_sender(self) -> str | None:
|
|
180
|
+
"""Get the last sender.
|
|
181
|
+
|
|
182
|
+
Returns
|
|
183
|
+
-------
|
|
184
|
+
str | None
|
|
185
|
+
The last sender, if available.
|
|
186
|
+
"""
|
|
187
|
+
return self._last_sender
|
|
188
|
+
|
|
189
|
+
@last_sender.setter
|
|
190
|
+
def last_sender(self, value: str | None) -> None:
|
|
191
|
+
"""Set the last sender.
|
|
192
|
+
|
|
193
|
+
Parameters
|
|
194
|
+
----------
|
|
195
|
+
value : str | None
|
|
196
|
+
The last sender to set.
|
|
197
|
+
"""
|
|
198
|
+
self._last_sender = value
|
|
199
|
+
|
|
200
|
+
@property
|
|
201
|
+
def last_recipient(self) -> str | None:
|
|
202
|
+
"""Get the last recipient.
|
|
203
|
+
|
|
204
|
+
Returns
|
|
205
|
+
-------
|
|
206
|
+
str | None
|
|
207
|
+
The last recipient, if available.
|
|
208
|
+
"""
|
|
209
|
+
return self._last_recipient
|
|
116
210
|
|
|
117
|
-
|
|
118
|
-
|
|
211
|
+
@last_recipient.setter
|
|
212
|
+
def last_recipient(self, value: str | None) -> None:
|
|
213
|
+
"""Set the last recipient.
|
|
119
214
|
|
|
120
215
|
Parameters
|
|
121
216
|
----------
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
without waiting for user input.
|
|
217
|
+
value : str | None
|
|
218
|
+
The last recipient to set.
|
|
125
219
|
"""
|
|
126
|
-
self.
|
|
127
|
-
|
|
220
|
+
self._last_recipient = value
|
|
221
|
+
|
|
222
|
+
@property
|
|
223
|
+
def stop_requested(self) -> threading.Event:
|
|
224
|
+
"""Get the stop requested event."""
|
|
225
|
+
return self._stop_requested
|
|
226
|
+
|
|
227
|
+
@property
|
|
228
|
+
def max_event_history(self) -> int:
|
|
229
|
+
"""Get the maximum event history size."""
|
|
230
|
+
return self._config.max_event_history
|
|
128
231
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
def print(self, *args: Any, **kwargs: Any) -> None:
|
|
132
|
-
"""Print.
|
|
232
|
+
def add_to_history(self, event_info: dict[str, Any]) -> None:
|
|
233
|
+
"""Add an event to the history.
|
|
133
234
|
|
|
134
235
|
Parameters
|
|
135
236
|
----------
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
**kwargs : Any
|
|
139
|
-
Keyword arguments to print.
|
|
237
|
+
event_info : dict[str, Any]
|
|
238
|
+
The event information to add to the history.
|
|
140
239
|
"""
|
|
141
|
-
|
|
240
|
+
self._event_history.append(event_info)
|
|
241
|
+
|
|
242
|
+
def pop_event(self) -> None:
|
|
243
|
+
"""Pop event from the history."""
|
|
244
|
+
if self._event_history:
|
|
245
|
+
self._event_history.popleft()
|
|
142
246
|
|
|
143
|
-
def emit_event(
|
|
247
|
+
def emit_event(
|
|
248
|
+
self, event: Union["BaseEvent", "BaseMessage", dict[str, Any]]
|
|
249
|
+
) -> None:
|
|
144
250
|
"""Emit an event.
|
|
145
251
|
|
|
146
252
|
Parameters
|
|
147
253
|
----------
|
|
148
|
-
event :
|
|
254
|
+
event : BaseEvent | BaseMessage | dict[str, Any]
|
|
149
255
|
The event to emit.
|
|
150
256
|
"""
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
257
|
+
if not isinstance(event, dict):
|
|
258
|
+
event_info = event.model_dump(
|
|
259
|
+
mode="json", exclude_none=True, fallback=str
|
|
260
|
+
)
|
|
261
|
+
event_info["count"] = self._event_count
|
|
262
|
+
event_info["sender"] = getattr(event, "sender", self._last_sender)
|
|
263
|
+
event_info["recipient"] = getattr(
|
|
264
|
+
event, "recipient", self._last_recipient
|
|
265
|
+
)
|
|
266
|
+
else:
|
|
267
|
+
event_info = event
|
|
159
268
|
self.emit(WaldiezDebugEventInfo(event=event_info))
|
|
160
269
|
|
|
161
270
|
# noinspection PyTypeHints
|
|
271
|
+
@override
|
|
162
272
|
def emit(self, message: WaldiezDebugMessage) -> None:
|
|
163
273
|
"""Emit a debug message.
|
|
164
274
|
|
|
165
275
|
Parameters
|
|
166
276
|
----------
|
|
167
277
|
message : WaldiezDebugMessage
|
|
168
|
-
The
|
|
278
|
+
The message to emit.
|
|
169
279
|
"""
|
|
170
280
|
message_dump = message.model_dump(
|
|
171
281
|
mode="json", exclude_none=True, fallback=str
|
|
172
282
|
)
|
|
173
283
|
self.print(message_dump)
|
|
174
284
|
|
|
175
|
-
|
|
285
|
+
@property
|
|
286
|
+
def current_event(self) -> Union["BaseEvent", "BaseMessage", None]:
|
|
287
|
+
"""Get the current event.
|
|
288
|
+
|
|
289
|
+
Returns
|
|
290
|
+
-------
|
|
291
|
+
Union["BaseEvent", "BaseMessage", None]
|
|
292
|
+
The current event, if available.
|
|
293
|
+
"""
|
|
294
|
+
return self._current_event
|
|
295
|
+
|
|
296
|
+
@current_event.setter
|
|
297
|
+
def current_event(
|
|
298
|
+
self, value: Union["BaseEvent", "BaseMessage", None]
|
|
299
|
+
) -> None:
|
|
300
|
+
"""Set the current event.
|
|
301
|
+
|
|
302
|
+
Parameters
|
|
303
|
+
----------
|
|
304
|
+
value : Union["BaseEvent", "BaseMessage", None]
|
|
305
|
+
The event to set as the current event.
|
|
306
|
+
"""
|
|
307
|
+
self._current_event = value
|
|
308
|
+
|
|
309
|
+
@property
|
|
310
|
+
def event_count(self) -> int:
|
|
311
|
+
"""Get the current event count.
|
|
312
|
+
|
|
313
|
+
Returns
|
|
314
|
+
-------
|
|
315
|
+
int
|
|
316
|
+
The current event count.
|
|
317
|
+
"""
|
|
318
|
+
return self._event_count
|
|
319
|
+
|
|
320
|
+
def event_plus_one(self) -> None:
|
|
321
|
+
"""Increment the current event count."""
|
|
322
|
+
self._event_count += 1
|
|
323
|
+
|
|
324
|
+
def show_event_info(self) -> None:
|
|
325
|
+
"""Show detailed information about the current event."""
|
|
176
326
|
if not self._current_event:
|
|
327
|
+
self.emit(WaldiezDebugError(error="No current event to display"))
|
|
177
328
|
return
|
|
329
|
+
|
|
178
330
|
event_info = self._current_event.model_dump(
|
|
179
331
|
mode="json", exclude_none=True, fallback=str
|
|
180
332
|
)
|
|
333
|
+
# Add additional context
|
|
334
|
+
event_info["_meta"] = {
|
|
335
|
+
"event_number": self._event_count,
|
|
336
|
+
"processed_events": self._processed_events,
|
|
337
|
+
"step_mode": self._step_mode,
|
|
338
|
+
"has_breakpoints": len(self._breakpoints) > 0,
|
|
339
|
+
}
|
|
181
340
|
self.emit(WaldiezDebugEventInfo(event=event_info))
|
|
182
341
|
|
|
183
|
-
def
|
|
342
|
+
def show_stats(self) -> None:
|
|
343
|
+
"""Show comprehensive execution statistics."""
|
|
344
|
+
base_stats: dict[str, Any] = {
|
|
345
|
+
"execution": {
|
|
346
|
+
"events_processed": self._processed_events,
|
|
347
|
+
"total_events": self._event_count,
|
|
348
|
+
"processing_rate": (
|
|
349
|
+
f"{(self._processed_events / self._event_count * 100):.1f}%"
|
|
350
|
+
if self._event_count > 0
|
|
351
|
+
else "0%"
|
|
352
|
+
),
|
|
353
|
+
},
|
|
354
|
+
"mode": {
|
|
355
|
+
"step_mode": self._step_mode,
|
|
356
|
+
"auto_continue": self._config.auto_continue,
|
|
357
|
+
},
|
|
358
|
+
"history": {
|
|
359
|
+
"event_history_count": len(self._event_history),
|
|
360
|
+
"max_history_size": self._config.max_event_history,
|
|
361
|
+
"memory_usage": f"{len(self._event_history) * 200}B (est.)",
|
|
362
|
+
},
|
|
363
|
+
"participants": {
|
|
364
|
+
"last_sender": self._last_sender,
|
|
365
|
+
"last_recipient": self._last_recipient,
|
|
366
|
+
"known_participants": len(self._known_participants),
|
|
367
|
+
},
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
# Merge with breakpoint stats
|
|
371
|
+
breakpoint_stats = self.get_breakpoint_stats()
|
|
184
372
|
stats_dict: dict[str, Any] = {
|
|
185
|
-
|
|
373
|
+
**base_stats,
|
|
374
|
+
"breakpoints": breakpoint_stats,
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
self.emit(WaldiezDebugStats(stats=stats_dict))
|
|
378
|
+
|
|
379
|
+
@property
|
|
380
|
+
def execution_stats(self) -> dict[str, Any]:
|
|
381
|
+
"""Get comprehensive execution statistics.
|
|
382
|
+
|
|
383
|
+
Returns
|
|
384
|
+
-------
|
|
385
|
+
dict[str, Any]
|
|
386
|
+
A dictionary containing execution statistics.
|
|
387
|
+
"""
|
|
388
|
+
base_stats: dict[str, Any] = {
|
|
186
389
|
"total_events": self._event_count,
|
|
390
|
+
"processed_events": self._processed_events,
|
|
391
|
+
"event_processing_rate": (
|
|
392
|
+
self._processed_events / self._event_count
|
|
393
|
+
if self._event_count > 0
|
|
394
|
+
else 0
|
|
395
|
+
),
|
|
187
396
|
"step_mode": self._step_mode,
|
|
188
|
-
"auto_continue": self.
|
|
189
|
-
"breakpoints": sorted(self.get_breakpoints()),
|
|
397
|
+
"auto_continue": self._config.auto_continue,
|
|
190
398
|
"event_history_count": len(self._event_history),
|
|
399
|
+
"last_sender": self._last_sender,
|
|
400
|
+
"last_recipient": self._last_recipient,
|
|
401
|
+
"known_participants": [
|
|
402
|
+
p.model_dump() for p in self._known_participants
|
|
403
|
+
],
|
|
404
|
+
"config": self._config.model_dump(),
|
|
191
405
|
}
|
|
192
|
-
self.emit(WaldiezDebugStats(stats=stats_dict))
|
|
193
406
|
|
|
194
|
-
|
|
195
|
-
self, user_response: str, request_id: str
|
|
196
|
-
) -> tuple[str | None, bool]:
|
|
197
|
-
"""Get user response for step-by-step execution.
|
|
407
|
+
return {**base_stats, "breakpoints": self.get_breakpoint_stats()}
|
|
198
408
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
The user's response.
|
|
203
|
-
request_id : str
|
|
204
|
-
The ID of the input request.
|
|
409
|
+
@property
|
|
410
|
+
def event_history(self) -> list[dict[str, Any]]:
|
|
411
|
+
"""Get the history of processed events.
|
|
205
412
|
|
|
206
413
|
Returns
|
|
207
414
|
-------
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
and a boolean indicating validity.
|
|
415
|
+
list[dict[str, Any]]
|
|
416
|
+
A list of dictionaries containing event history.
|
|
211
417
|
"""
|
|
418
|
+
return list(self._event_history)
|
|
419
|
+
|
|
420
|
+
def reset_session(self) -> None:
|
|
421
|
+
"""Reset the debugging session state."""
|
|
422
|
+
self._event_count = 0
|
|
423
|
+
self._processed_events = 0
|
|
424
|
+
self._event_history.clear()
|
|
425
|
+
self._current_event = None
|
|
426
|
+
self._last_sender = None
|
|
427
|
+
self._last_recipient = None
|
|
428
|
+
self.reset_stats()
|
|
429
|
+
self.log.info("Debug session reset")
|
|
430
|
+
|
|
431
|
+
def _get_user_response(
|
|
432
|
+
self,
|
|
433
|
+
user_response: str,
|
|
434
|
+
request_id: str,
|
|
435
|
+
skip_id_check: bool = False,
|
|
436
|
+
) -> tuple[str | None, bool]:
|
|
437
|
+
"""Get and validate user response."""
|
|
212
438
|
try:
|
|
213
439
|
response = WaldiezDebugInputResponse.model_validate_json(
|
|
214
440
|
user_response
|
|
215
441
|
)
|
|
216
442
|
except ValidationError as exc:
|
|
443
|
+
# Handle raw CLI input
|
|
217
444
|
got = user_response.strip().lower()
|
|
218
|
-
# in cli mode, let's see if got raw response
|
|
219
|
-
# instead of a structured one
|
|
220
445
|
if got in VALID_CONTROL_COMMANDS:
|
|
221
446
|
return got, True
|
|
222
447
|
self.emit(WaldiezDebugError(error=f"Invalid input: {exc}"))
|
|
223
448
|
return None, False
|
|
224
449
|
|
|
225
|
-
if response.request_id != request_id:
|
|
450
|
+
if not skip_id_check and response.request_id != request_id:
|
|
226
451
|
self.emit(
|
|
227
452
|
WaldiezDebugError(
|
|
228
|
-
error=
|
|
229
|
-
"Stale input received: "
|
|
230
|
-
f"{response.request_id} != {request_id}"
|
|
231
|
-
)
|
|
453
|
+
error=f"Stale input received: {response.request_id} != {request_id}"
|
|
232
454
|
)
|
|
233
455
|
)
|
|
234
456
|
return None, False
|
|
457
|
+
|
|
235
458
|
return response.data, True
|
|
236
459
|
|
|
237
|
-
|
|
238
|
-
def _parse_user_action( # noqa: C901
|
|
460
|
+
def _parse_user_action(
|
|
239
461
|
self, user_response: str, request_id: str
|
|
240
462
|
) -> WaldiezDebugStepAction:
|
|
241
|
-
"""Parse user action
|
|
242
|
-
|
|
243
|
-
Parameters
|
|
244
|
-
----------
|
|
245
|
-
user_response : str
|
|
246
|
-
The user's response.
|
|
247
|
-
request_id : str
|
|
248
|
-
The ID of the input request.
|
|
249
|
-
|
|
250
|
-
Returns
|
|
251
|
-
-------
|
|
252
|
-
WaldiezDebugStepAction
|
|
253
|
-
The action chosen by the user.
|
|
254
|
-
"""
|
|
463
|
+
"""Parse user action using the command handler."""
|
|
255
464
|
self.log.debug("Parsing user action... '%s'", user_response)
|
|
465
|
+
|
|
256
466
|
user_input, is_valid = self._get_user_response(
|
|
257
|
-
user_response,
|
|
467
|
+
user_response,
|
|
468
|
+
request_id=request_id,
|
|
469
|
+
skip_id_check=True,
|
|
258
470
|
)
|
|
259
471
|
if not is_valid:
|
|
260
472
|
return WaldiezDebugStepAction.UNKNOWN
|
|
261
|
-
if not user_input:
|
|
262
|
-
return WaldiezDebugStepAction.CONTINUE
|
|
263
|
-
match user_input:
|
|
264
|
-
case "c":
|
|
265
|
-
self._step_mode = True
|
|
266
|
-
return WaldiezDebugStepAction.CONTINUE
|
|
267
|
-
case "s":
|
|
268
|
-
self._step_mode = True
|
|
269
|
-
return WaldiezDebugStepAction.STEP
|
|
270
|
-
case "r":
|
|
271
|
-
self._step_mode = False
|
|
272
|
-
return WaldiezDebugStepAction.RUN
|
|
273
|
-
case "q":
|
|
274
|
-
self._stop_requested.set()
|
|
275
|
-
return WaldiezDebugStepAction.QUIT
|
|
276
|
-
case "i":
|
|
277
|
-
self._show_event_info()
|
|
278
|
-
return WaldiezDebugStepAction.INFO
|
|
279
|
-
case "h":
|
|
280
|
-
self.emit(HELP_MESSAGE)
|
|
281
|
-
return WaldiezDebugStepAction.HELP
|
|
282
|
-
case "st":
|
|
283
|
-
self._show_stats()
|
|
284
|
-
return WaldiezDebugStepAction.STATS
|
|
285
|
-
case "ab":
|
|
286
|
-
if self._current_event and hasattr(self._current_event, "type"):
|
|
287
|
-
self.add_breakpoint(self._current_event.type)
|
|
288
|
-
else:
|
|
289
|
-
self.emit(
|
|
290
|
-
WaldiezDebugError(
|
|
291
|
-
error="No current event to add breakpoint for"
|
|
292
|
-
)
|
|
293
|
-
)
|
|
294
|
-
return WaldiezDebugStepAction.ADD_BREAKPOINT
|
|
295
|
-
case "rb":
|
|
296
|
-
if self._current_event and hasattr(self._current_event, "type"):
|
|
297
|
-
self.remove_breakpoint(self._current_event.type)
|
|
298
|
-
else:
|
|
299
|
-
self.emit(
|
|
300
|
-
WaldiezDebugError(
|
|
301
|
-
error="No current event to remove breakpoint for"
|
|
302
|
-
)
|
|
303
|
-
)
|
|
304
|
-
return WaldiezDebugStepAction.REMOVE_BREAKPOINT
|
|
305
|
-
case "lb":
|
|
306
|
-
self.list_breakpoints()
|
|
307
|
-
return WaldiezDebugStepAction.LIST_BREAKPOINTS
|
|
308
|
-
case "cb":
|
|
309
|
-
self.clear_breakpoints()
|
|
310
|
-
return WaldiezDebugStepAction.CLEAR_BREAKPOINTS
|
|
311
|
-
case _:
|
|
312
|
-
self.emit(
|
|
313
|
-
WaldiezDebugError(
|
|
314
|
-
error=f"Unknown command: {user_input}, use 'h' for help"
|
|
315
|
-
)
|
|
316
|
-
)
|
|
317
|
-
return WaldiezDebugStepAction.UNKNOWN
|
|
318
473
|
|
|
319
|
-
|
|
320
|
-
"""Get user action for step-by-step execution.
|
|
474
|
+
return self._command_handler.handle_command(user_input or "")
|
|
321
475
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
WaldiezDebugStepAction
|
|
325
|
-
The action chosen by the user.
|
|
326
|
-
"""
|
|
327
|
-
if self._auto_continue:
|
|
328
|
-
return WaldiezDebugStepAction.CONTINUE
|
|
476
|
+
def _get_user_action(self, force: bool) -> WaldiezDebugStepAction:
|
|
477
|
+
"""Get user action with timeout support.
|
|
329
478
|
|
|
479
|
+
Parameters
|
|
480
|
+
----------
|
|
481
|
+
force : bool
|
|
482
|
+
Force getting the user's action, even if in auto-run mode.
|
|
483
|
+
"""
|
|
484
|
+
if self._config.auto_continue:
|
|
485
|
+
self.step_mode = True
|
|
486
|
+
if force:
|
|
487
|
+
self._config.auto_continue = False
|
|
488
|
+
else:
|
|
489
|
+
return WaldiezDebugStepAction.CONTINUE
|
|
330
490
|
while True:
|
|
331
|
-
|
|
332
|
-
request_id = str(uuid.uuid4())
|
|
491
|
+
request_id = gen_id()
|
|
333
492
|
try:
|
|
334
|
-
self.
|
|
335
|
-
|
|
336
|
-
|
|
493
|
+
if not self.structured_io:
|
|
494
|
+
# if structured, we already do this (print the prompt)
|
|
495
|
+
self.emit(
|
|
496
|
+
WaldiezDebugInputRequest(
|
|
497
|
+
prompt=DEBUG_INPUT_PROMPT, request_id=request_id
|
|
498
|
+
)
|
|
337
499
|
)
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
500
|
+
except Exception as e: # pylint: disable=broad-exception-caught
|
|
501
|
+
self.log.warning("Failed to emit input request: %s", e)
|
|
502
|
+
try:
|
|
503
|
+
user_input = EventsMixin.get_user_input(
|
|
504
|
+
DEBUG_INPUT_PROMPT,
|
|
505
|
+
request_id=request_id,
|
|
506
|
+
).strip()
|
|
344
507
|
return self._parse_user_action(
|
|
345
508
|
user_input, request_id=request_id
|
|
346
509
|
)
|
|
510
|
+
|
|
347
511
|
except (KeyboardInterrupt, EOFError):
|
|
348
512
|
self._stop_requested.set()
|
|
349
513
|
return WaldiezDebugStepAction.QUIT
|
|
350
514
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
"""
|
|
360
|
-
if self._auto_continue:
|
|
361
|
-
return WaldiezDebugStepAction.CONTINUE
|
|
515
|
+
async def _a_get_user_action(self, force: bool) -> WaldiezDebugStepAction:
|
|
516
|
+
"""Get user action asynchronously."""
|
|
517
|
+
if self._config.auto_continue:
|
|
518
|
+
self.step_mode = True
|
|
519
|
+
if force:
|
|
520
|
+
self._config.auto_continue = False
|
|
521
|
+
else:
|
|
522
|
+
return WaldiezDebugStepAction.CONTINUE
|
|
362
523
|
|
|
363
524
|
while True:
|
|
525
|
+
request_id = gen_id()
|
|
364
526
|
# pylint: disable=too-many-try-statements
|
|
365
|
-
request_id = str(uuid.uuid4())
|
|
366
527
|
try:
|
|
367
528
|
self.emit(
|
|
368
529
|
WaldiezDebugInputRequest(
|
|
369
530
|
prompt=DEBUG_INPUT_PROMPT, request_id=request_id
|
|
370
531
|
)
|
|
371
532
|
)
|
|
372
|
-
|
|
533
|
+
|
|
534
|
+
user_input = await EventsMixin.a_get_user_input(
|
|
373
535
|
DEBUG_INPUT_PROMPT
|
|
374
536
|
)
|
|
375
|
-
user_input = user_input.strip()
|
|
537
|
+
user_input = user_input.strip()
|
|
376
538
|
return self._parse_user_action(
|
|
377
539
|
user_input, request_id=request_id
|
|
378
540
|
)
|
|
541
|
+
|
|
379
542
|
except (KeyboardInterrupt, EOFError):
|
|
380
543
|
return WaldiezDebugStepAction.QUIT
|
|
381
544
|
|
|
382
|
-
def _handle_step_interaction(self) -> bool:
|
|
545
|
+
def _handle_step_interaction(self, force: bool) -> bool:
|
|
383
546
|
"""Handle step-by-step user interaction.
|
|
384
547
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
bool
|
|
388
|
-
|
|
548
|
+
Parameters
|
|
549
|
+
----------
|
|
550
|
+
force : bool
|
|
551
|
+
Force getting the user's action, even if in auto-run mode.
|
|
389
552
|
"""
|
|
390
|
-
while True:
|
|
391
|
-
action = self._get_user_action()
|
|
392
|
-
|
|
553
|
+
while True:
|
|
554
|
+
action = self._get_user_action(force)
|
|
393
555
|
if action in (
|
|
394
556
|
WaldiezDebugStepAction.CONTINUE,
|
|
395
557
|
WaldiezDebugStepAction.STEP,
|
|
396
558
|
):
|
|
397
559
|
return True
|
|
398
560
|
if action == WaldiezDebugStepAction.RUN:
|
|
561
|
+
self._config.auto_continue = True
|
|
399
562
|
return True
|
|
400
|
-
if action == WaldiezDebugStepAction.QUIT:
|
|
563
|
+
if action == WaldiezDebugStepAction.QUIT:
|
|
401
564
|
return False
|
|
565
|
+
# For other actions (info, help, etc.), continue the loop
|
|
402
566
|
|
|
403
|
-
async def _a_handle_step_interaction(self) -> bool:
|
|
404
|
-
"""Handle step-by-step user interaction asynchronously.
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
-------
|
|
408
|
-
bool
|
|
409
|
-
True to continue execution, False to stop.
|
|
410
|
-
"""
|
|
411
|
-
while True: # pragma: no branch
|
|
412
|
-
action = await self._a_get_user_action()
|
|
413
|
-
|
|
567
|
+
async def _a_handle_step_interaction(self, force: bool) -> bool:
|
|
568
|
+
"""Handle step-by-step user interaction asynchronously."""
|
|
569
|
+
while True:
|
|
570
|
+
action = await self._a_get_user_action(force)
|
|
414
571
|
if action in (
|
|
415
572
|
WaldiezDebugStepAction.CONTINUE,
|
|
416
573
|
WaldiezDebugStepAction.STEP,
|
|
417
574
|
):
|
|
418
575
|
return True
|
|
419
576
|
if action == WaldiezDebugStepAction.RUN:
|
|
577
|
+
self._config.auto_continue = True
|
|
420
578
|
return True
|
|
421
|
-
if action == WaldiezDebugStepAction.QUIT:
|
|
579
|
+
if action == WaldiezDebugStepAction.QUIT:
|
|
422
580
|
return False
|
|
581
|
+
# For other actions (info, help, etc.), continue the loop
|
|
423
582
|
|
|
424
|
-
|
|
583
|
+
@override
|
|
425
584
|
def _run(
|
|
426
585
|
self,
|
|
427
586
|
temp_dir: Path,
|
|
@@ -445,13 +604,13 @@ class WaldiezStepByStepRunner(WaldiezBaseRunner, BreakpointsMixin):
|
|
|
445
604
|
# pylint: disable=too-many-try-statements,broad-exception-caught
|
|
446
605
|
try:
|
|
447
606
|
loaded_module = self._load_module(output_file, temp_dir)
|
|
448
|
-
if self._stop_requested.is_set():
|
|
607
|
+
if self._stop_requested.is_set():
|
|
449
608
|
self.log.debug(
|
|
450
609
|
"Step-by-step execution stopped before workflow start"
|
|
451
610
|
)
|
|
452
611
|
return []
|
|
453
612
|
|
|
454
|
-
#
|
|
613
|
+
# Setup I/O
|
|
455
614
|
if self.structured_io:
|
|
456
615
|
stream = StructuredIOStream(
|
|
457
616
|
uploads_root=uploads_root, is_async=False
|
|
@@ -459,9 +618,9 @@ class WaldiezStepByStepRunner(WaldiezBaseRunner, BreakpointsMixin):
|
|
|
459
618
|
else:
|
|
460
619
|
stream = IOStream.get_default()
|
|
461
620
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
621
|
+
EventsMixin.set_print_function(stream.print)
|
|
622
|
+
EventsMixin.set_input_function(stream.input)
|
|
623
|
+
EventsMixin.set_send_function(stream.send)
|
|
465
624
|
|
|
466
625
|
self.print(MESSAGES["workflow_starting"])
|
|
467
626
|
self.print(self.waldiez.info.model_dump_json())
|
|
@@ -475,77 +634,63 @@ class WaldiezStepByStepRunner(WaldiezBaseRunner, BreakpointsMixin):
|
|
|
475
634
|
raise StopRunningException(StopRunningException.reason) from e
|
|
476
635
|
results_container["exception"] = e
|
|
477
636
|
traceback.print_exc()
|
|
478
|
-
# noinspection StrFormat
|
|
479
637
|
self.print(MESSAGES["workflow_failed"].format(error=str(e)))
|
|
480
638
|
finally:
|
|
481
639
|
results_container["completed"] = True
|
|
482
640
|
|
|
483
641
|
return results_container["results"]
|
|
484
642
|
|
|
485
|
-
def
|
|
486
|
-
|
|
643
|
+
def _re_emit_if_needed(self, event_info: dict[str, Any]) -> None:
|
|
644
|
+
# emit again if type is text, swapping the sender and without recipient
|
|
645
|
+
if event_info.get("type", "") == "text":
|
|
646
|
+
event_info["sender"] = event_info["recipient"]
|
|
647
|
+
event_info["recipient"] = None
|
|
648
|
+
event_info["agents"]["sender"] = event_info["agents"]["recipient"]
|
|
649
|
+
event_info["agents"]["recipient"] = None
|
|
650
|
+
self.emit_event(event_info)
|
|
487
651
|
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
event
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
bool
|
|
496
|
-
True to continue, False to stop.
|
|
497
|
-
|
|
498
|
-
Raises
|
|
499
|
-
------
|
|
500
|
-
RuntimeError
|
|
501
|
-
If an error occurs while processing the event.
|
|
502
|
-
StopRunningException
|
|
503
|
-
If execution is stopped by user request.
|
|
504
|
-
"""
|
|
505
|
-
self._event_count += 1
|
|
506
|
-
self._current_event = event
|
|
507
|
-
|
|
508
|
-
if self._stop_requested.is_set():
|
|
509
|
-
self.log.debug(
|
|
510
|
-
"Step-by-step execution stopped before event processing"
|
|
511
|
-
)
|
|
512
|
-
return False
|
|
513
|
-
|
|
514
|
-
# Store event in history
|
|
515
|
-
event_info = event.model_dump(
|
|
516
|
-
mode="json", exclude_none=True, fallback=str
|
|
517
|
-
)
|
|
518
|
-
event_info["count"] = self._event_count
|
|
519
|
-
self._event_history.append(event_info)
|
|
520
|
-
|
|
521
|
-
# pylint: disable=too-many-try-statements
|
|
652
|
+
def _on_event(
|
|
653
|
+
self,
|
|
654
|
+
event: Union["BaseEvent", "BaseMessage"],
|
|
655
|
+
agents: list["ConversableAgent"],
|
|
656
|
+
) -> bool:
|
|
657
|
+
"""Process an event with step-by-step debugging."""
|
|
658
|
+
# pylint: disable=too-many-try-statements,broad-exception-caught
|
|
522
659
|
try:
|
|
523
|
-
#
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
self.
|
|
528
|
-
|
|
660
|
+
# Use the event processor for core logic
|
|
661
|
+
result = self._event_processor.process_event(event, agents)
|
|
662
|
+
|
|
663
|
+
if result["action"] == "stop":
|
|
664
|
+
self.log.debug(
|
|
665
|
+
"Step-by-step execution stopped before event processing"
|
|
666
|
+
)
|
|
667
|
+
return False
|
|
668
|
+
event_info = result["event_info"]
|
|
669
|
+
self.emit_event(event_info)
|
|
670
|
+
# Handle breakpoint logic
|
|
671
|
+
if result["action"] == "break":
|
|
672
|
+
if not self._handle_step_interaction(force=True):
|
|
529
673
|
self._stop_requested.set()
|
|
530
674
|
if hasattr(event, "type") and event.type == "input_request":
|
|
531
675
|
event.content.respond("exit")
|
|
532
676
|
return True
|
|
533
677
|
raise StopRunningException(StopRunningException.reason)
|
|
534
|
-
|
|
535
|
-
# Process the event
|
|
536
|
-
|
|
678
|
+
self._re_emit_if_needed(event_info)
|
|
679
|
+
# Process the actual event
|
|
680
|
+
EventsMixin.process_event(event, agents, skip_send=True)
|
|
537
681
|
self._processed_events += 1
|
|
538
682
|
|
|
539
683
|
except Exception as e:
|
|
540
684
|
if not isinstance(e, StopRunningException):
|
|
541
685
|
raise RuntimeError(
|
|
542
|
-
f"Error processing event {event}: "
|
|
543
|
-
f"{e}\n{traceback.format_exc()}"
|
|
686
|
+
f"Error processing event {event}: {e}\n{traceback.format_exc()}"
|
|
544
687
|
) from e
|
|
545
688
|
raise StopRunningException(StopRunningException.reason) from e
|
|
689
|
+
|
|
546
690
|
return not self._stop_requested.is_set()
|
|
547
691
|
|
|
548
692
|
# pylint: disable=too-complex
|
|
693
|
+
@override
|
|
549
694
|
async def _a_run(
|
|
550
695
|
self,
|
|
551
696
|
temp_dir: Path,
|
|
@@ -558,19 +703,17 @@ class WaldiezStepByStepRunner(WaldiezBaseRunner, BreakpointsMixin):
|
|
|
558
703
|
"""Run the Waldiez workflow with step-by-step debugging (async)."""
|
|
559
704
|
|
|
560
705
|
async def _execute_workflow() -> list[dict[str, Any]]:
|
|
561
|
-
"""Execute the workflow in an async context."""
|
|
562
706
|
# pylint: disable=import-outside-toplevel
|
|
563
|
-
from autogen.io import IOStream
|
|
707
|
+
from autogen.io import IOStream
|
|
564
708
|
|
|
565
709
|
from waldiez.io import StructuredIOStream
|
|
566
710
|
|
|
567
|
-
results: list[dict[str, Any]]
|
|
568
711
|
# pylint: disable=too-many-try-statements,broad-exception-caught
|
|
569
712
|
try:
|
|
570
713
|
loaded_module = self._load_module(output_file, temp_dir)
|
|
571
|
-
if self._stop_requested.is_set():
|
|
714
|
+
if self._stop_requested.is_set():
|
|
572
715
|
self.log.debug(
|
|
573
|
-
"
|
|
716
|
+
"Step-by-step execution stopped before workflow start"
|
|
574
717
|
)
|
|
575
718
|
return []
|
|
576
719
|
|
|
@@ -581,15 +724,16 @@ class WaldiezStepByStepRunner(WaldiezBaseRunner, BreakpointsMixin):
|
|
|
581
724
|
else:
|
|
582
725
|
stream = IOStream.get_default()
|
|
583
726
|
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
727
|
+
EventsMixin.set_print_function(stream.print)
|
|
728
|
+
EventsMixin.set_input_function(stream.input)
|
|
729
|
+
EventsMixin.set_send_function(stream.send)
|
|
587
730
|
|
|
588
731
|
self.print(MESSAGES["workflow_starting"])
|
|
589
732
|
self.print(self.waldiez.info.model_dump_json())
|
|
590
733
|
|
|
591
734
|
results = await loaded_module.main(on_event=self._a_on_event)
|
|
592
735
|
self.print(MESSAGES["workflow_finished"])
|
|
736
|
+
return results
|
|
593
737
|
|
|
594
738
|
except Exception as e:
|
|
595
739
|
if StopRunningException.reason in str(e):
|
|
@@ -600,13 +744,9 @@ class WaldiezStepByStepRunner(WaldiezBaseRunner, BreakpointsMixin):
|
|
|
600
744
|
traceback.print_exc()
|
|
601
745
|
return []
|
|
602
746
|
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
# Create cancellable task
|
|
747
|
+
# Create and monitor cancellable task
|
|
606
748
|
task = asyncio.create_task(_execute_workflow())
|
|
607
|
-
|
|
608
|
-
# Monitor for stop requests
|
|
609
|
-
# pylint: disable=too-many-try-statements
|
|
749
|
+
# pylint: disable=too-many-try-statements,broad-exception-caught
|
|
610
750
|
try:
|
|
611
751
|
while not task.done():
|
|
612
752
|
if self._stop_requested.is_set():
|
|
@@ -615,131 +755,46 @@ class WaldiezStepByStepRunner(WaldiezBaseRunner, BreakpointsMixin):
|
|
|
615
755
|
break
|
|
616
756
|
await asyncio.sleep(0.1)
|
|
617
757
|
return await task
|
|
618
|
-
|
|
619
758
|
except asyncio.CancelledError:
|
|
620
759
|
self.log.debug("Step-by-step execution cancelled")
|
|
621
760
|
return []
|
|
622
761
|
|
|
623
762
|
async def _a_on_event(
|
|
624
|
-
self,
|
|
763
|
+
self,
|
|
764
|
+
event: Union["BaseEvent", "BaseMessage"],
|
|
765
|
+
agents: list["ConversableAgent"],
|
|
625
766
|
) -> bool:
|
|
626
|
-
"""Process an event with step-by-step debugging asynchronously.
|
|
627
|
-
|
|
628
|
-
Parameters
|
|
629
|
-
----------
|
|
630
|
-
event : Union[BaseEvent, BaseMessage]
|
|
631
|
-
The event to process.
|
|
632
|
-
|
|
633
|
-
Returns
|
|
634
|
-
-------
|
|
635
|
-
bool
|
|
636
|
-
True to continue, False to stop.
|
|
637
|
-
|
|
638
|
-
Raises
|
|
639
|
-
------
|
|
640
|
-
RuntimeError
|
|
641
|
-
If an error occurs while processing the event.
|
|
642
|
-
StopRunningException
|
|
643
|
-
If execution is stopped by user request.
|
|
644
|
-
"""
|
|
645
|
-
self._event_count += 1
|
|
646
|
-
self._current_event = event
|
|
647
|
-
|
|
648
|
-
if self._stop_requested.is_set():
|
|
649
|
-
self.log.debug(
|
|
650
|
-
"Async step-by-step execution stopped before event processing"
|
|
651
|
-
)
|
|
652
|
-
return False
|
|
653
|
-
|
|
654
|
-
# Store event in history
|
|
655
|
-
event_info = event.model_dump(
|
|
656
|
-
mode="json", exclude_none=True, fallback=str
|
|
657
|
-
)
|
|
658
|
-
event_info["count"] = self._event_count
|
|
659
|
-
self._event_history.append(event_info)
|
|
660
|
-
|
|
661
|
-
# pylint: disable=too-many-try-statements
|
|
767
|
+
"""Process an event with step-by-step debugging asynchronously."""
|
|
768
|
+
# pylint: disable=too-many-try-statements,broad-exception-caught
|
|
662
769
|
try:
|
|
663
|
-
#
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
self.
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
770
|
+
# Use the event processor for core logic
|
|
771
|
+
result = self._event_processor.process_event(event, agents)
|
|
772
|
+
|
|
773
|
+
if result["action"] == "stop":
|
|
774
|
+
self.log.debug(
|
|
775
|
+
"Async step-by-step execution stopped before event processing"
|
|
776
|
+
)
|
|
777
|
+
return False
|
|
778
|
+
event_info = result["event_info"]
|
|
779
|
+
self.emit_event(event_info)
|
|
780
|
+
# Handle breakpoint logic
|
|
781
|
+
if result["action"] == "break":
|
|
782
|
+
if not await self._a_handle_step_interaction(force=True):
|
|
671
783
|
self._stop_requested.set()
|
|
672
784
|
if hasattr(event, "type") and event.type == "input_request":
|
|
673
|
-
event.content.respond("exit")
|
|
785
|
+
await event.content.respond("exit")
|
|
674
786
|
return True
|
|
675
787
|
raise StopRunningException(StopRunningException.reason)
|
|
676
|
-
|
|
677
|
-
# Process the event
|
|
678
|
-
await
|
|
788
|
+
self._re_emit_if_needed(event_info)
|
|
789
|
+
# Process the actual event
|
|
790
|
+
await EventsMixin.a_process_event(event, agents, skip_send=True)
|
|
679
791
|
self._processed_events += 1
|
|
680
792
|
|
|
681
793
|
except Exception as e:
|
|
682
794
|
if not isinstance(e, StopRunningException):
|
|
683
795
|
raise RuntimeError(
|
|
684
|
-
f"Error processing event {event}: "
|
|
685
|
-
f"{e}\n{traceback.format_exc()}"
|
|
796
|
+
f"Error processing event {event}: {e}\n{traceback.format_exc()}"
|
|
686
797
|
) from e
|
|
687
798
|
raise StopRunningException(StopRunningException.reason) from e
|
|
688
|
-
return not self._stop_requested.is_set()
|
|
689
799
|
|
|
690
|
-
|
|
691
|
-
"""Get execution statistics for step-by-step runner.
|
|
692
|
-
|
|
693
|
-
Returns
|
|
694
|
-
-------
|
|
695
|
-
dict[str, Any]
|
|
696
|
-
A dictionary containing execution statistics.
|
|
697
|
-
"""
|
|
698
|
-
return {
|
|
699
|
-
"total_events": self._event_count,
|
|
700
|
-
"processed_events": self._processed_events,
|
|
701
|
-
"event_processing_rate": (
|
|
702
|
-
self._processed_events / self._event_count
|
|
703
|
-
if self._event_count > 0
|
|
704
|
-
else 0
|
|
705
|
-
),
|
|
706
|
-
"step_mode": self._step_mode,
|
|
707
|
-
"auto_continue": self._auto_continue,
|
|
708
|
-
"breakpoints": sorted(self.get_breakpoints()),
|
|
709
|
-
"event_history_count": len(self._event_history),
|
|
710
|
-
"last_sender": self._last_sender,
|
|
711
|
-
"last_recipient": self._last_recipient,
|
|
712
|
-
"known_participants": [
|
|
713
|
-
p.model_dump() for p in self._known_participants
|
|
714
|
-
],
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
def get_event_history(self) -> list[dict[str, Any]]:
|
|
718
|
-
"""Get the history of processed events.
|
|
719
|
-
|
|
720
|
-
Returns
|
|
721
|
-
-------
|
|
722
|
-
list[dict[str, Any]]
|
|
723
|
-
List of event information dictionaries.
|
|
724
|
-
"""
|
|
725
|
-
return self._event_history.copy()
|
|
726
|
-
|
|
727
|
-
def enable_auto_continue(self, enabled: bool = True) -> None:
|
|
728
|
-
"""Enable or disable auto-continue mode.
|
|
729
|
-
|
|
730
|
-
Parameters
|
|
731
|
-
----------
|
|
732
|
-
enabled : bool, optional
|
|
733
|
-
Whether to enable auto-continue, by default True.
|
|
734
|
-
"""
|
|
735
|
-
self._auto_continue = enabled
|
|
736
|
-
|
|
737
|
-
def enable_step_mode(self, enabled: bool = True) -> None:
|
|
738
|
-
"""Enable or disable step mode.
|
|
739
|
-
|
|
740
|
-
Parameters
|
|
741
|
-
----------
|
|
742
|
-
enabled : bool, optional
|
|
743
|
-
Whether to enable step mode, by default True.
|
|
744
|
-
"""
|
|
745
|
-
self._step_mode = enabled
|
|
800
|
+
return not self._stop_requested.is_set()
|