waldiez 0.5.8__py3-none-any.whl → 0.5.10__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/_version.py +1 -1
- waldiez/cli.py +112 -24
- waldiez/exporting/agent/exporter.py +3 -0
- waldiez/exporting/agent/extras/captain_agent_extras.py +44 -7
- waldiez/exporting/agent/extras/handoffs/condition.py +3 -1
- waldiez/exporting/chats/utils/common.py +25 -23
- waldiez/exporting/core/__init__.py +0 -2
- waldiez/exporting/core/context.py +13 -13
- waldiez/exporting/core/protocols.py +0 -141
- waldiez/exporting/core/result.py +5 -5
- waldiez/exporting/flow/merger.py +2 -2
- waldiez/exporting/flow/orchestrator.py +1 -0
- waldiez/exporting/flow/utils/common.py +2 -2
- waldiez/exporting/flow/utils/importing.py +1 -0
- waldiez/exporting/flow/utils/logging.py +6 -7
- waldiez/exporting/tools/exporter.py +5 -0
- waldiez/exporting/tools/factory.py +4 -0
- waldiez/exporting/tools/processor.py +5 -1
- waldiez/io/_ws.py +13 -5
- waldiez/io/models/content/image.py +1 -0
- waldiez/io/models/user_input.py +4 -4
- waldiez/io/models/user_response.py +1 -0
- waldiez/io/mqtt.py +1 -1
- waldiez/io/structured.py +17 -17
- waldiez/io/utils.py +1 -1
- waldiez/io/ws.py +9 -11
- waldiez/logger.py +180 -63
- waldiez/models/agents/agent/update_system_message.py +0 -2
- waldiez/models/agents/doc_agent/doc_agent.py +8 -1
- waldiez/models/common/dict_utils.py +169 -40
- waldiez/models/flow/flow.py +6 -6
- waldiez/models/flow/info.py +5 -1
- waldiez/models/model/_llm.py +28 -14
- waldiez/models/model/model.py +4 -1
- waldiez/models/model/model_data.py +18 -5
- waldiez/models/tool/predefined/_config.py +5 -1
- waldiez/models/tool/predefined/_duckduckgo.py +4 -0
- waldiez/models/tool/predefined/_email.py +474 -0
- waldiez/models/tool/predefined/_google.py +8 -6
- waldiez/models/tool/predefined/_perplexity.py +3 -0
- waldiez/models/tool/predefined/_searxng.py +3 -0
- waldiez/models/tool/predefined/_tavily.py +4 -1
- waldiez/models/tool/predefined/_wikipedia.py +4 -1
- waldiez/models/tool/predefined/_youtube.py +4 -1
- waldiez/models/tool/predefined/protocol.py +3 -0
- waldiez/models/tool/tool.py +22 -4
- waldiez/models/waldiez.py +12 -0
- waldiez/runner.py +37 -54
- waldiez/running/__init__.py +6 -0
- waldiez/running/base_runner.py +310 -353
- waldiez/running/environment.py +1 -0
- waldiez/running/exceptions.py +9 -0
- waldiez/running/post_run.py +4 -4
- waldiez/running/pre_run.py +51 -40
- waldiez/running/protocol.py +21 -101
- waldiez/running/run_results.py +1 -1
- waldiez/running/standard_runner.py +84 -277
- waldiez/running/step_by_step/__init__.py +46 -0
- waldiez/running/step_by_step/breakpoints_mixin.py +188 -0
- waldiez/running/step_by_step/step_by_step_models.py +224 -0
- waldiez/running/step_by_step/step_by_step_runner.py +745 -0
- waldiez/running/subprocess_runner/__base__.py +282 -0
- waldiez/running/subprocess_runner/__init__.py +16 -0
- waldiez/running/subprocess_runner/_async_runner.py +362 -0
- waldiez/running/subprocess_runner/_sync_runner.py +455 -0
- waldiez/running/subprocess_runner/runner.py +561 -0
- waldiez/running/timeline_processor.py +1 -1
- waldiez/running/utils.py +376 -1
- waldiez/utils/version.py +2 -6
- waldiez/ws/__init__.py +70 -0
- waldiez/ws/__main__.py +15 -0
- waldiez/ws/_file_handler.py +201 -0
- waldiez/ws/cli.py +211 -0
- waldiez/ws/client_manager.py +835 -0
- waldiez/ws/errors.py +416 -0
- waldiez/ws/models.py +971 -0
- waldiez/ws/reloader.py +342 -0
- waldiez/ws/server.py +469 -0
- waldiez/ws/session_manager.py +393 -0
- waldiez/ws/session_stats.py +83 -0
- waldiez/ws/utils.py +385 -0
- {waldiez-0.5.8.dist-info → waldiez-0.5.10.dist-info}/METADATA +74 -74
- {waldiez-0.5.8.dist-info → waldiez-0.5.10.dist-info}/RECORD +87 -65
- waldiez/running/patch_io_stream.py +0 -210
- {waldiez-0.5.8.dist-info → waldiez-0.5.10.dist-info}/WHEEL +0 -0
- {waldiez-0.5.8.dist-info → waldiez-0.5.10.dist-info}/entry_points.txt +0 -0
- {waldiez-0.5.8.dist-info → waldiez-0.5.10.dist-info}/licenses/LICENSE +0 -0
- {waldiez-0.5.8.dist-info → waldiez-0.5.10.dist-info}/licenses/NOTICE.md +0 -0
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
# SPDX-License-Identifier: Apache-2.0.
|
|
2
2
|
# Copyright (c) 2024 - 2025 Waldiez and contributors.
|
|
3
3
|
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
# pyright: reportAttributeAccessIssue=false,reportUnknownArgumentType=false
|
|
7
|
-
# pylint: disable=too-many-try-statements,import-outside-toplevel,line-too-long,
|
|
8
|
-
# pylint: disable=too-complex,unused-argument,duplicate-code,broad-exception-caught
|
|
4
|
+
# pyright: reportUnknownMemberType=false, reportAttributeAccessIssue=false
|
|
5
|
+
# pylint: disable=duplicate-code
|
|
9
6
|
"""Run a waldiez flow.
|
|
10
7
|
|
|
11
8
|
The flow is first converted to an autogen flow with agents, chats, and tools.
|
|
@@ -15,26 +12,29 @@ variables specified in the waldiez file are set.
|
|
|
15
12
|
"""
|
|
16
13
|
|
|
17
14
|
import asyncio
|
|
18
|
-
import getpass
|
|
19
|
-
import importlib.util
|
|
20
|
-
import sys
|
|
21
|
-
import threading
|
|
22
15
|
import traceback
|
|
23
16
|
from pathlib import Path
|
|
24
|
-
from
|
|
25
|
-
from typing import TYPE_CHECKING, Any, Callable, Coroutine, Union
|
|
17
|
+
from typing import TYPE_CHECKING, Any, Union
|
|
26
18
|
|
|
27
19
|
from waldiez.models.waldiez import Waldiez
|
|
28
20
|
from waldiez.running.run_results import WaldiezRunResults
|
|
29
21
|
|
|
30
22
|
from .base_runner import WaldiezBaseRunner
|
|
31
|
-
from .utils import chdir
|
|
32
23
|
|
|
33
24
|
if TYPE_CHECKING:
|
|
34
25
|
from autogen.events import BaseEvent # type: ignore
|
|
35
26
|
from autogen.messages import BaseMessage # type: ignore
|
|
36
27
|
|
|
37
28
|
|
|
29
|
+
MESSAGES = {
|
|
30
|
+
"workflow_starting": "<Waldiez> - Starting workflow...",
|
|
31
|
+
"workflow_finished": "<Waldiez> - Workflow finished",
|
|
32
|
+
"workflow_stopped": "<Waldiez> - Workflow stopped by user",
|
|
33
|
+
"workflow_failed": "<Waldiez> - Workflow execution failed: {error}",
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# noinspection StrFormat
|
|
38
38
|
class WaldiezStandardRunner(WaldiezBaseRunner):
|
|
39
39
|
"""Run a waldiez flow in a standard way."""
|
|
40
40
|
|
|
@@ -56,49 +56,24 @@ class WaldiezStandardRunner(WaldiezBaseRunner):
|
|
|
56
56
|
dot_env=dot_env,
|
|
57
57
|
**kwargs,
|
|
58
58
|
)
|
|
59
|
-
self._execution_thread: threading.Thread | None = None
|
|
60
|
-
self._loaded_module: ModuleType | None = None
|
|
61
59
|
self._event_count = 0
|
|
62
60
|
self._processed_events = 0
|
|
63
61
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
spec = importlib.util.spec_from_file_location(
|
|
69
|
-
module_name, temp_dir / file_name
|
|
70
|
-
)
|
|
71
|
-
if not spec or not spec.loader:
|
|
72
|
-
raise ImportError("Could not import the flow")
|
|
73
|
-
module = importlib.util.module_from_spec(spec)
|
|
74
|
-
spec.loader.exec_module(module)
|
|
75
|
-
if not hasattr(module, "main"):
|
|
76
|
-
raise ImportError(
|
|
77
|
-
"The waldiez file does not contain a main() function"
|
|
78
|
-
)
|
|
79
|
-
self._loaded_module = module
|
|
80
|
-
return module
|
|
81
|
-
|
|
82
|
-
@staticmethod
|
|
83
|
-
def standard_input(prompt: str, *, password: bool = False) -> str:
|
|
84
|
-
"""Fallback / common input function for the workflow.
|
|
62
|
+
# pylint: disable=no-self-use
|
|
63
|
+
# noinspection PyMethodMayBeStatic
|
|
64
|
+
def print(self, *args: Any, **kwargs: Any) -> None:
|
|
65
|
+
"""Print.
|
|
85
66
|
|
|
86
67
|
Parameters
|
|
87
68
|
----------
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
Returns
|
|
94
|
-
-------
|
|
95
|
-
str
|
|
96
|
-
The user input as a string.
|
|
69
|
+
*args : Any
|
|
70
|
+
Positional arguments to print.
|
|
71
|
+
**kwargs : Any
|
|
72
|
+
Keyword arguments to print.
|
|
97
73
|
"""
|
|
98
|
-
|
|
99
|
-
return getpass.getpass(prompt)
|
|
100
|
-
return input(prompt)
|
|
74
|
+
WaldiezBaseRunner._print(*args, **kwargs)
|
|
101
75
|
|
|
76
|
+
# pylint: disable=unused-argument
|
|
102
77
|
def _run(
|
|
103
78
|
self,
|
|
104
79
|
temp_dir: Path,
|
|
@@ -109,48 +84,49 @@ class WaldiezStandardRunner(WaldiezBaseRunner):
|
|
|
109
84
|
**kwargs: Any,
|
|
110
85
|
) -> list[dict[str, Any]]:
|
|
111
86
|
"""Run the Waldiez workflow."""
|
|
87
|
+
# pylint: disable=import-outside-toplevel
|
|
112
88
|
from autogen.io import IOStream # type: ignore
|
|
113
89
|
|
|
114
90
|
from waldiez.io import StructuredIOStream
|
|
115
91
|
|
|
116
|
-
self._print: Callable[..., None] = print
|
|
117
|
-
self._input: (
|
|
118
|
-
Callable[..., str] | Callable[..., Coroutine[Any, Any, str]]
|
|
119
|
-
) = WaldiezStandardRunner.standard_input
|
|
120
92
|
results_container: WaldiezRunResults = {
|
|
121
93
|
"results": [],
|
|
122
94
|
"exception": None,
|
|
123
95
|
"completed": False,
|
|
124
96
|
}
|
|
97
|
+
# pylint: disable=too-many-try-statements,broad-exception-caught
|
|
125
98
|
try:
|
|
126
|
-
|
|
99
|
+
loaded_module = self._load_module(output_file, temp_dir)
|
|
127
100
|
if self._stop_requested.is_set():
|
|
128
|
-
self.log.
|
|
129
|
-
"
|
|
101
|
+
self.log.debug(
|
|
102
|
+
"Execution stopped before AG2 workflow start (sync)"
|
|
130
103
|
)
|
|
131
104
|
return []
|
|
105
|
+
# noinspection DuplicatedCode
|
|
132
106
|
if self.structured_io:
|
|
133
107
|
stream = StructuredIOStream(
|
|
134
108
|
uploads_root=uploads_root, is_async=False
|
|
135
109
|
)
|
|
136
110
|
else:
|
|
137
111
|
stream = IOStream.get_default()
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
self.
|
|
142
|
-
self.
|
|
143
|
-
results =
|
|
112
|
+
WaldiezBaseRunner._print = stream.print
|
|
113
|
+
WaldiezBaseRunner._input = stream.input
|
|
114
|
+
WaldiezBaseRunner._send = stream.send
|
|
115
|
+
self.print(MESSAGES["workflow_starting"])
|
|
116
|
+
self.print(self.waldiez.info.model_dump_json())
|
|
117
|
+
results = loaded_module.main(
|
|
144
118
|
on_event=self._on_event,
|
|
145
119
|
)
|
|
146
120
|
results_container["results"] = results
|
|
147
|
-
self.
|
|
148
|
-
except SystemExit:
|
|
149
|
-
self.
|
|
150
|
-
|
|
121
|
+
self.print(MESSAGES["workflow_finished"])
|
|
122
|
+
except SystemExit: # pragma: no cover
|
|
123
|
+
self.log.debug("Execution stopped by user (sync)")
|
|
124
|
+
self.print(MESSAGES["workflow_stopped"])
|
|
125
|
+
except BaseException as e: # pragma: no cover
|
|
151
126
|
results_container["exception"] = e
|
|
152
127
|
traceback.print_exc()
|
|
153
|
-
self.
|
|
128
|
+
self.print(MESSAGES["workflow_failed"].format(error=e))
|
|
129
|
+
|
|
154
130
|
finally:
|
|
155
131
|
results_container["completed"] = True
|
|
156
132
|
return results_container["results"]
|
|
@@ -162,153 +138,52 @@ class WaldiezStandardRunner(WaldiezBaseRunner):
|
|
|
162
138
|
"""Process an event from the workflow."""
|
|
163
139
|
self._event_count += 1
|
|
164
140
|
if self._stop_requested.is_set():
|
|
165
|
-
self.log.
|
|
166
|
-
"
|
|
141
|
+
self.log.debug(
|
|
142
|
+
"Execution stopped before AG2 workflow event processing (sync)"
|
|
167
143
|
)
|
|
168
144
|
return False
|
|
169
145
|
try:
|
|
170
|
-
|
|
171
|
-
if event.type == "input_request":
|
|
172
|
-
prompt = getattr(
|
|
173
|
-
event,
|
|
174
|
-
"prompt",
|
|
175
|
-
getattr(event.content, "prompt", "> "),
|
|
176
|
-
)
|
|
177
|
-
password = getattr(
|
|
178
|
-
event,
|
|
179
|
-
"password",
|
|
180
|
-
getattr(event.content, "password", False),
|
|
181
|
-
)
|
|
182
|
-
user_input = self._input(
|
|
183
|
-
prompt,
|
|
184
|
-
password=password,
|
|
185
|
-
)
|
|
186
|
-
event.content.respond(user_input)
|
|
187
|
-
else:
|
|
188
|
-
self._send(event) # pyright: ignore
|
|
146
|
+
WaldiezBaseRunner.process_event(event)
|
|
189
147
|
self._processed_events += 1
|
|
148
|
+
except SystemExit: # pragma: no cover
|
|
149
|
+
self.log.debug("Execution stopped by user (sync)")
|
|
150
|
+
return False
|
|
190
151
|
except Exception as e:
|
|
152
|
+
if self._stop_requested.is_set():
|
|
153
|
+
self.log.debug("Exception during stop, returning False")
|
|
154
|
+
return False
|
|
191
155
|
raise RuntimeError(
|
|
192
156
|
f"Error processing event {event}: {e}\n{traceback.format_exc()}"
|
|
193
157
|
) from e
|
|
194
|
-
if event.type == "run_completion":
|
|
195
|
-
self._signal_completion()
|
|
196
|
-
WaldiezBaseRunner._running = False
|
|
197
158
|
return not self._stop_requested.is_set()
|
|
198
159
|
|
|
199
|
-
def _start(
|
|
200
|
-
self,
|
|
201
|
-
temp_dir: Path,
|
|
202
|
-
output_file: Path,
|
|
203
|
-
uploads_root: Path | None,
|
|
204
|
-
skip_mmd: bool,
|
|
205
|
-
skip_timeline: bool,
|
|
206
|
-
) -> None:
|
|
207
|
-
"""Start the workflow in a non-blocking way."""
|
|
208
|
-
if self._execution_thread and self._execution_thread.is_alive():
|
|
209
|
-
raise RuntimeError("Non-blocking execution already in progress")
|
|
210
|
-
|
|
211
|
-
# Reset completion state
|
|
212
|
-
self._reset_completion_state()
|
|
213
|
-
|
|
214
|
-
# Create thread with proper integration
|
|
215
|
-
self._execution_thread = threading.Thread(
|
|
216
|
-
target=self._threaded_run,
|
|
217
|
-
args=(temp_dir, output_file, uploads_root, skip_mmd, skip_timeline),
|
|
218
|
-
name=f"WaldiezStandardRunner-{self.waldiez.name}",
|
|
219
|
-
daemon=False, # Not daemon so we can properly join
|
|
220
|
-
)
|
|
221
|
-
self._execution_thread.start()
|
|
222
|
-
|
|
223
|
-
def _threaded_run(
|
|
224
|
-
self,
|
|
225
|
-
temp_dir: Path,
|
|
226
|
-
output_file: Path,
|
|
227
|
-
uploads_root: Path | None,
|
|
228
|
-
skip_mmd: bool = False,
|
|
229
|
-
skip_timeline: bool = False,
|
|
230
|
-
) -> None:
|
|
231
|
-
"""Run in a separate thread with proper lifecycle."""
|
|
232
|
-
try:
|
|
233
|
-
# Change to temp directory and manage sys.path
|
|
234
|
-
with chdir(to=temp_dir):
|
|
235
|
-
sys.path.insert(0, str(temp_dir))
|
|
236
|
-
try:
|
|
237
|
-
results = self._run(
|
|
238
|
-
temp_dir=temp_dir,
|
|
239
|
-
output_file=output_file,
|
|
240
|
-
uploads_root=uploads_root,
|
|
241
|
-
skip_mmd=skip_mmd,
|
|
242
|
-
skip_timeline=skip_timeline,
|
|
243
|
-
)
|
|
244
|
-
|
|
245
|
-
# Store results
|
|
246
|
-
self._last_results = results
|
|
247
|
-
|
|
248
|
-
# Call after_run hooks
|
|
249
|
-
self.after_run(
|
|
250
|
-
results=results,
|
|
251
|
-
output_file=output_file,
|
|
252
|
-
uploads_root=uploads_root,
|
|
253
|
-
temp_dir=temp_dir,
|
|
254
|
-
skip_mmd=skip_mmd,
|
|
255
|
-
skip_timeline=skip_timeline,
|
|
256
|
-
)
|
|
257
|
-
|
|
258
|
-
finally:
|
|
259
|
-
# Clean up sys.path
|
|
260
|
-
if sys.path and sys.path[0] == str(temp_dir):
|
|
261
|
-
sys.path.pop(0)
|
|
262
|
-
|
|
263
|
-
except Exception as e:
|
|
264
|
-
self._last_exception = e
|
|
265
|
-
self.log.error("Threaded execution failed: %s", e)
|
|
266
|
-
|
|
267
|
-
finally:
|
|
268
|
-
# Signal completion and mark as not running
|
|
269
|
-
self._signal_completion()
|
|
270
|
-
WaldiezBaseRunner._running = False
|
|
271
|
-
|
|
272
160
|
async def _a_on_event(
|
|
273
161
|
self,
|
|
274
162
|
event: Union["BaseEvent", "BaseMessage"],
|
|
275
163
|
) -> bool:
|
|
276
164
|
"""Process an event from the workflow asynchronously."""
|
|
277
165
|
self._event_count += 1
|
|
278
|
-
if self._stop_requested.is_set():
|
|
279
|
-
self.log.
|
|
280
|
-
"
|
|
166
|
+
if self._stop_requested.is_set(): # pragma: no cover
|
|
167
|
+
self.log.debug(
|
|
168
|
+
"Execution stopped before AG2 workflow event processing (async)"
|
|
281
169
|
)
|
|
282
170
|
return False
|
|
283
171
|
try:
|
|
284
|
-
|
|
285
|
-
if event.type == "input_request":
|
|
286
|
-
prompt = getattr(
|
|
287
|
-
event,
|
|
288
|
-
"prompt",
|
|
289
|
-
getattr(event.content, "prompt", "> "),
|
|
290
|
-
)
|
|
291
|
-
password = getattr(
|
|
292
|
-
event,
|
|
293
|
-
"password",
|
|
294
|
-
getattr(event.content, "password", False),
|
|
295
|
-
)
|
|
296
|
-
user_input = self._input(
|
|
297
|
-
prompt,
|
|
298
|
-
password=password,
|
|
299
|
-
)
|
|
300
|
-
if asyncio.iscoroutine(user_input):
|
|
301
|
-
user_input = await user_input
|
|
302
|
-
await event.content.respond(user_input)
|
|
303
|
-
else:
|
|
304
|
-
self._send(event) # pyright: ignore
|
|
172
|
+
await WaldiezBaseRunner.a_process_event(event)
|
|
305
173
|
self._processed_events += 1
|
|
174
|
+
except SystemExit: # pragma: no cover
|
|
175
|
+
self.log.debug("Execution stopped by user (async)")
|
|
176
|
+
return False
|
|
306
177
|
except Exception as e:
|
|
178
|
+
if self._stop_requested.is_set():
|
|
179
|
+
self.log.debug("Exception during stop, returning False")
|
|
180
|
+
return False
|
|
307
181
|
raise RuntimeError(
|
|
308
182
|
f"Error processing event {event}: {e}\n{traceback.format_exc()}"
|
|
309
183
|
) from e
|
|
310
184
|
return not self._stop_requested.is_set()
|
|
311
185
|
|
|
186
|
+
# pylint: disable=too-complex
|
|
312
187
|
async def _a_run(
|
|
313
188
|
self,
|
|
314
189
|
temp_dir: Path,
|
|
@@ -324,16 +199,19 @@ class WaldiezStandardRunner(WaldiezBaseRunner):
|
|
|
324
199
|
async def _execute_workflow() -> list[dict[str, Any]]:
|
|
325
200
|
# fmt: on
|
|
326
201
|
"""Execute the workflow in an async context."""
|
|
202
|
+
# pylint: disable=import-outside-toplevel
|
|
327
203
|
from autogen.io import IOStream # pyright: ignore
|
|
328
204
|
|
|
329
205
|
from waldiez.io import StructuredIOStream
|
|
330
206
|
|
|
331
|
-
results: list[dict[str, Any]]
|
|
207
|
+
results: list[dict[str, Any]]
|
|
208
|
+
# pylint: disable=too-many-try-statements,broad-exception-caught
|
|
332
209
|
try:
|
|
333
|
-
|
|
334
|
-
if self._stop_requested.is_set():
|
|
335
|
-
self.log.
|
|
336
|
-
"
|
|
210
|
+
loaded_module = self._load_module(output_file, temp_dir)
|
|
211
|
+
if self._stop_requested.is_set(): # pragma: no cover
|
|
212
|
+
self.log.debug(
|
|
213
|
+
"Execution stopped before AG2 "
|
|
214
|
+
"workflow event processing (async)"
|
|
337
215
|
)
|
|
338
216
|
return []
|
|
339
217
|
if self.structured_io:
|
|
@@ -342,21 +220,21 @@ class WaldiezStandardRunner(WaldiezBaseRunner):
|
|
|
342
220
|
)
|
|
343
221
|
else:
|
|
344
222
|
stream = IOStream.get_default()
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
self.
|
|
349
|
-
self.
|
|
350
|
-
results = await
|
|
223
|
+
WaldiezBaseRunner._print = stream.print
|
|
224
|
+
WaldiezBaseRunner._input = stream.input
|
|
225
|
+
WaldiezBaseRunner._send = stream.send
|
|
226
|
+
self.print(MESSAGES["workflow_starting"])
|
|
227
|
+
self.print(self.waldiez.info.model_dump_json())
|
|
228
|
+
results = await loaded_module.main( # pyright: ignore
|
|
351
229
|
on_event=self._a_on_event
|
|
352
230
|
)
|
|
353
|
-
|
|
354
|
-
|
|
231
|
+
self.print(MESSAGES["workflow_finished"])
|
|
232
|
+
except SystemExit: # pragma: no cover
|
|
233
|
+
self.log.debug("Execution stopped by user (async)")
|
|
234
|
+
self.print(MESSAGES["workflow_stopped"])
|
|
355
235
|
return []
|
|
356
|
-
except Exception as e:
|
|
357
|
-
self.
|
|
358
|
-
f"<Waldiez> - Error loading workflow: {e}\n{traceback.format_exc()}"
|
|
359
|
-
)
|
|
236
|
+
except Exception as e: # pragma: no cover
|
|
237
|
+
self.print(MESSAGES["workflow_failed"].format(error=str(e)))
|
|
360
238
|
raise RuntimeError(
|
|
361
239
|
f"Error loading workflow: {e}\n{traceback.format_exc()}"
|
|
362
240
|
) from e
|
|
@@ -366,88 +244,17 @@ class WaldiezStandardRunner(WaldiezBaseRunner):
|
|
|
366
244
|
task = asyncio.create_task(_execute_workflow())
|
|
367
245
|
|
|
368
246
|
# Monitor for stop requests
|
|
247
|
+
# pylint: disable=too-many-try-statements
|
|
369
248
|
try:
|
|
370
249
|
while not task.done():
|
|
371
250
|
if self._stop_requested.is_set():
|
|
372
251
|
task.cancel()
|
|
373
|
-
self.log.
|
|
252
|
+
self.log.debug("Execution stopped by user (async)")
|
|
374
253
|
break
|
|
375
254
|
await asyncio.sleep(0.1)
|
|
376
255
|
# Return the task result when completed
|
|
377
256
|
return await task
|
|
378
257
|
|
|
379
258
|
except asyncio.CancelledError:
|
|
380
|
-
self.log.
|
|
259
|
+
self.log.debug("Execution cancelled (async)")
|
|
381
260
|
return []
|
|
382
|
-
|
|
383
|
-
async def _a_start(
|
|
384
|
-
self,
|
|
385
|
-
temp_dir: Path,
|
|
386
|
-
output_file: Path,
|
|
387
|
-
uploads_root: Path | None,
|
|
388
|
-
skip_mmd: bool = False,
|
|
389
|
-
skip_timeline: bool = False,
|
|
390
|
-
) -> None:
|
|
391
|
-
"""Start the Waldiez workflow asynchronously."""
|
|
392
|
-
|
|
393
|
-
async def run_in_background() -> None:
|
|
394
|
-
"""Run the Waldiez workflow in a background thread."""
|
|
395
|
-
try:
|
|
396
|
-
results = await self._a_run(
|
|
397
|
-
temp_dir,
|
|
398
|
-
output_file,
|
|
399
|
-
uploads_root,
|
|
400
|
-
skip_mmd=skip_mmd,
|
|
401
|
-
skip_timeline=skip_timeline,
|
|
402
|
-
)
|
|
403
|
-
if results:
|
|
404
|
-
self._print(f"<Waldiez> - Workflow completed: {results}")
|
|
405
|
-
except Exception as e:
|
|
406
|
-
self._print(
|
|
407
|
-
f"<Waldiez> - Error during workflow: {e}\n{traceback.format_exc()}"
|
|
408
|
-
)
|
|
409
|
-
|
|
410
|
-
asyncio.create_task(run_in_background())
|
|
411
|
-
|
|
412
|
-
def _stop(self) -> None:
|
|
413
|
-
"""Stop the Waldiez workflow."""
|
|
414
|
-
self.log.info("Stopping workflow execution...")
|
|
415
|
-
self._stop_requested.set()
|
|
416
|
-
|
|
417
|
-
# Wait for graceful shutdown
|
|
418
|
-
if self._execution_thread and self._execution_thread.is_alive():
|
|
419
|
-
self._execution_thread.join(timeout=5.0)
|
|
420
|
-
|
|
421
|
-
if self._execution_thread and self._execution_thread.is_alive():
|
|
422
|
-
self.log.warning("Workflow thread did not stop gracefully")
|
|
423
|
-
|
|
424
|
-
async def _a_stop(self) -> None:
|
|
425
|
-
"""Stop the Waldiez workflow asynchronously."""
|
|
426
|
-
self.log.info("Stopping workflow execution (async)...")
|
|
427
|
-
self._stop_requested.set()
|
|
428
|
-
|
|
429
|
-
# For async, we rely on the task cancellation in _a_run
|
|
430
|
-
# Let's give it a moment to respond
|
|
431
|
-
await asyncio.sleep(0.5)
|
|
432
|
-
|
|
433
|
-
def get_execution_stats(self) -> dict[str, Any]:
|
|
434
|
-
"""Get execution statistics for standard runner.
|
|
435
|
-
|
|
436
|
-
Returns
|
|
437
|
-
-------
|
|
438
|
-
dict[str, Any]
|
|
439
|
-
A dictionary containing execution statistics such as total events,
|
|
440
|
-
processed events, whether a module was loaded, and event processing rate.
|
|
441
|
-
"""
|
|
442
|
-
base_stats = super().get_execution_stats()
|
|
443
|
-
return {
|
|
444
|
-
**base_stats,
|
|
445
|
-
"total_events": self._event_count,
|
|
446
|
-
"processed_events": self._processed_events,
|
|
447
|
-
"has_loaded_module": self._loaded_module is not None,
|
|
448
|
-
"event_processing_rate": (
|
|
449
|
-
self._processed_events / self._event_count
|
|
450
|
-
if self._event_count > 0
|
|
451
|
-
else 0
|
|
452
|
-
),
|
|
453
|
-
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0.
|
|
2
|
+
# Copyright (c) 2024 - 2025 Waldiez and contributors.
|
|
3
|
+
|
|
4
|
+
"""Step-by-step runner for Waldiez workflows."""
|
|
5
|
+
|
|
6
|
+
from .breakpoints_mixin import BreakpointsMixin
|
|
7
|
+
from .step_by_step_models import (
|
|
8
|
+
WaldiezDebugBreakpointAdded,
|
|
9
|
+
WaldiezDebugBreakpointCleared,
|
|
10
|
+
WaldiezDebugBreakpointRemoved,
|
|
11
|
+
WaldiezDebugBreakpointsList,
|
|
12
|
+
WaldiezDebugError,
|
|
13
|
+
WaldiezDebugEventInfo,
|
|
14
|
+
WaldiezDebugHelp,
|
|
15
|
+
WaldiezDebugHelpCommand,
|
|
16
|
+
WaldiezDebugHelpCommandGroup,
|
|
17
|
+
WaldiezDebugInputRequest,
|
|
18
|
+
WaldiezDebugInputResponse,
|
|
19
|
+
WaldiezDebugMessage,
|
|
20
|
+
WaldiezDebugMessageWrapper,
|
|
21
|
+
WaldiezDebugPrint,
|
|
22
|
+
WaldiezDebugStats,
|
|
23
|
+
WaldiezDebugStepAction,
|
|
24
|
+
)
|
|
25
|
+
from .step_by_step_runner import WaldiezStepByStepRunner
|
|
26
|
+
|
|
27
|
+
__all__ = [
|
|
28
|
+
"BreakpointsMixin",
|
|
29
|
+
"WaldiezDebugError",
|
|
30
|
+
"WaldiezDebugEventInfo",
|
|
31
|
+
"WaldiezDebugHelp",
|
|
32
|
+
"WaldiezDebugHelpCommand",
|
|
33
|
+
"WaldiezDebugHelpCommandGroup",
|
|
34
|
+
"WaldiezDebugInputRequest",
|
|
35
|
+
"WaldiezDebugInputResponse",
|
|
36
|
+
"WaldiezDebugMessage",
|
|
37
|
+
"WaldiezDebugMessageWrapper",
|
|
38
|
+
"WaldiezDebugPrint",
|
|
39
|
+
"WaldiezDebugStats",
|
|
40
|
+
"WaldiezDebugStepAction",
|
|
41
|
+
"WaldiezStepByStepRunner",
|
|
42
|
+
"WaldiezDebugBreakpointAdded",
|
|
43
|
+
"WaldiezDebugBreakpointCleared",
|
|
44
|
+
"WaldiezDebugBreakpointRemoved",
|
|
45
|
+
"WaldiezDebugBreakpointsList",
|
|
46
|
+
]
|