waldiez 0.5.9__py3-none-any.whl → 0.6.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of waldiez might be problematic. Click here for more details.

Files changed (109) hide show
  1. waldiez/_version.py +1 -1
  2. waldiez/cli.py +113 -24
  3. waldiez/exporting/agent/exporter.py +9 -6
  4. waldiez/exporting/agent/extras/captain_agent_extras.py +44 -7
  5. waldiez/exporting/agent/extras/group_manager_agent_extas.py +6 -1
  6. waldiez/exporting/agent/extras/handoffs/after_work.py +1 -0
  7. waldiez/exporting/agent/extras/handoffs/available.py +1 -0
  8. waldiez/exporting/agent/extras/handoffs/condition.py +3 -1
  9. waldiez/exporting/agent/extras/handoffs/handoff.py +1 -0
  10. waldiez/exporting/agent/extras/handoffs/target.py +1 -0
  11. waldiez/exporting/agent/termination.py +1 -0
  12. waldiez/exporting/chats/utils/common.py +25 -23
  13. waldiez/exporting/core/__init__.py +0 -2
  14. waldiez/exporting/core/constants.py +3 -1
  15. waldiez/exporting/core/context.py +13 -13
  16. waldiez/exporting/core/extras/serializer.py +12 -10
  17. waldiez/exporting/core/protocols.py +0 -141
  18. waldiez/exporting/core/result.py +5 -5
  19. waldiez/exporting/core/types.py +1 -0
  20. waldiez/exporting/core/utils/llm_config.py +2 -2
  21. waldiez/exporting/flow/execution_generator.py +1 -0
  22. waldiez/exporting/flow/merger.py +2 -2
  23. waldiez/exporting/flow/orchestrator.py +1 -0
  24. waldiez/exporting/flow/utils/common.py +3 -3
  25. waldiez/exporting/flow/utils/importing.py +1 -0
  26. waldiez/exporting/flow/utils/logging.py +7 -80
  27. waldiez/exporting/tools/exporter.py +5 -0
  28. waldiez/exporting/tools/factory.py +4 -0
  29. waldiez/exporting/tools/processor.py +5 -1
  30. waldiez/io/__init__.py +3 -1
  31. waldiez/io/_ws.py +15 -5
  32. waldiez/io/models/content/image.py +1 -0
  33. waldiez/io/models/user_input.py +4 -4
  34. waldiez/io/models/user_response.py +1 -0
  35. waldiez/io/mqtt.py +1 -1
  36. waldiez/io/structured.py +98 -45
  37. waldiez/io/utils.py +17 -11
  38. waldiez/io/ws.py +10 -12
  39. waldiez/logger.py +180 -63
  40. waldiez/models/agents/agent/agent.py +2 -1
  41. waldiez/models/agents/agent/update_system_message.py +0 -2
  42. waldiez/models/agents/doc_agent/doc_agent.py +8 -1
  43. waldiez/models/chat/chat.py +1 -0
  44. waldiez/models/chat/chat_data.py +0 -2
  45. waldiez/models/common/base.py +2 -0
  46. waldiez/models/common/dict_utils.py +169 -40
  47. waldiez/models/common/handoff.py +2 -0
  48. waldiez/models/common/method_utils.py +2 -0
  49. waldiez/models/flow/flow.py +6 -6
  50. waldiez/models/flow/info.py +5 -1
  51. waldiez/models/model/_llm.py +31 -14
  52. waldiez/models/model/model.py +4 -1
  53. waldiez/models/model/model_data.py +18 -5
  54. waldiez/models/tool/predefined/_config.py +5 -1
  55. waldiez/models/tool/predefined/_duckduckgo.py +4 -0
  56. waldiez/models/tool/predefined/_email.py +477 -0
  57. waldiez/models/tool/predefined/_google.py +4 -1
  58. waldiez/models/tool/predefined/_perplexity.py +4 -1
  59. waldiez/models/tool/predefined/_searxng.py +4 -1
  60. waldiez/models/tool/predefined/_tavily.py +4 -1
  61. waldiez/models/tool/predefined/_wikipedia.py +5 -2
  62. waldiez/models/tool/predefined/_youtube.py +4 -1
  63. waldiez/models/tool/predefined/protocol.py +3 -0
  64. waldiez/models/tool/tool.py +22 -4
  65. waldiez/models/waldiez.py +12 -0
  66. waldiez/runner.py +37 -54
  67. waldiez/running/__init__.py +6 -0
  68. waldiez/running/base_runner.py +381 -363
  69. waldiez/running/environment.py +1 -0
  70. waldiez/running/exceptions.py +9 -0
  71. waldiez/running/post_run.py +10 -4
  72. waldiez/running/pre_run.py +199 -66
  73. waldiez/running/protocol.py +21 -101
  74. waldiez/running/run_results.py +1 -1
  75. waldiez/running/standard_runner.py +83 -276
  76. waldiez/running/step_by_step/__init__.py +46 -0
  77. waldiez/running/step_by_step/breakpoints_mixin.py +512 -0
  78. waldiez/running/step_by_step/command_handler.py +151 -0
  79. waldiez/running/step_by_step/events_processor.py +199 -0
  80. waldiez/running/step_by_step/step_by_step_models.py +541 -0
  81. waldiez/running/step_by_step/step_by_step_runner.py +750 -0
  82. waldiez/running/subprocess_runner/__base__.py +279 -0
  83. waldiez/running/subprocess_runner/__init__.py +16 -0
  84. waldiez/running/subprocess_runner/_async_runner.py +362 -0
  85. waldiez/running/subprocess_runner/_sync_runner.py +456 -0
  86. waldiez/running/subprocess_runner/runner.py +570 -0
  87. waldiez/running/timeline_processor.py +1 -1
  88. waldiez/running/utils.py +492 -3
  89. waldiez/utils/version.py +2 -6
  90. waldiez/ws/__init__.py +71 -0
  91. waldiez/ws/__main__.py +15 -0
  92. waldiez/ws/_file_handler.py +199 -0
  93. waldiez/ws/_mock.py +74 -0
  94. waldiez/ws/cli.py +235 -0
  95. waldiez/ws/client_manager.py +851 -0
  96. waldiez/ws/errors.py +416 -0
  97. waldiez/ws/models.py +988 -0
  98. waldiez/ws/reloader.py +363 -0
  99. waldiez/ws/server.py +508 -0
  100. waldiez/ws/session_manager.py +393 -0
  101. waldiez/ws/session_stats.py +83 -0
  102. waldiez/ws/utils.py +410 -0
  103. {waldiez-0.5.9.dist-info → waldiez-0.6.0.dist-info}/METADATA +105 -96
  104. {waldiez-0.5.9.dist-info → waldiez-0.6.0.dist-info}/RECORD +108 -83
  105. waldiez/running/patch_io_stream.py +0 -210
  106. {waldiez-0.5.9.dist-info → waldiez-0.6.0.dist-info}/WHEEL +0 -0
  107. {waldiez-0.5.9.dist-info → waldiez-0.6.0.dist-info}/entry_points.txt +0 -0
  108. {waldiez-0.5.9.dist-info → waldiez-0.6.0.dist-info}/licenses/LICENSE +0 -0
  109. {waldiez-0.5.9.dist-info → waldiez-0.6.0.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
- # flake8: noqa: C901, E501
5
- # pyright: reportUnknownMemberType=false, reportUnknownVariableType=false
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,too-few-public-methods
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 types import ModuleType
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,23 @@ 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
- def _load_module(self, output_file: Path, temp_dir: Path) -> ModuleType:
65
- """Load the module from the waldiez file."""
66
- file_name = output_file.name
67
- module_name = file_name.replace(".py", "")
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
62
  @staticmethod
83
- def standard_input(prompt: str, *, password: bool = False) -> str:
84
- """Fallback / common input function for the workflow.
63
+ def print(*args: Any, **kwargs: Any) -> None:
64
+ """Print.
85
65
 
86
66
  Parameters
87
67
  ----------
88
- prompt : str
89
- The prompt to display to the user.
90
- password : bool, optional
91
- If True, use getpass to hide input (default is False).
92
-
93
- Returns
94
- -------
95
- str
96
- The user input as a string.
68
+ *args : Any
69
+ Positional arguments to print.
70
+ **kwargs : Any
71
+ Keyword arguments to print.
97
72
  """
98
- if password:
99
- return getpass.getpass(prompt)
100
- return input(prompt)
73
+ WaldiezBaseRunner.print(*args, **kwargs)
101
74
 
75
+ # pylint: disable=unused-argument
102
76
  def _run(
103
77
  self,
104
78
  temp_dir: Path,
@@ -109,48 +83,49 @@ class WaldiezStandardRunner(WaldiezBaseRunner):
109
83
  **kwargs: Any,
110
84
  ) -> list[dict[str, Any]]:
111
85
  """Run the Waldiez workflow."""
86
+ # pylint: disable=import-outside-toplevel
112
87
  from autogen.io import IOStream # type: ignore
113
88
 
114
89
  from waldiez.io import StructuredIOStream
115
90
 
116
- self._print: Callable[..., None] = print
117
- self._input: (
118
- Callable[..., str] | Callable[..., Coroutine[Any, Any, str]]
119
- ) = WaldiezStandardRunner.standard_input
120
91
  results_container: WaldiezRunResults = {
121
92
  "results": [],
122
93
  "exception": None,
123
94
  "completed": False,
124
95
  }
96
+ # pylint: disable=too-many-try-statements,broad-exception-caught
125
97
  try:
126
- self._loaded_module = self._load_module(output_file, temp_dir)
98
+ loaded_module = self._load_module(output_file, temp_dir)
127
99
  if self._stop_requested.is_set():
128
- self.log.info(
129
- "Async execution stopped before AG2 workflow start"
100
+ self.log.debug(
101
+ "Execution stopped before AG2 workflow start (sync)"
130
102
  )
131
103
  return []
104
+ # noinspection DuplicatedCode
132
105
  if self.structured_io:
133
106
  stream = StructuredIOStream(
134
107
  uploads_root=uploads_root, is_async=False
135
108
  )
136
109
  else:
137
110
  stream = IOStream.get_default()
138
- self._print = stream.print
139
- self._input = stream.input
140
- self._send = stream.send
141
- self._print("<Waldiez> - Starting workflow...")
142
- self._print(self.waldiez.info.model_dump_json())
143
- results = self._loaded_module.main(
111
+ WaldiezBaseRunner._print = stream.print
112
+ WaldiezBaseRunner._input = stream.input
113
+ WaldiezBaseRunner._send = stream.send
114
+ self.print(MESSAGES["workflow_starting"])
115
+ self.print(self.waldiez.info.model_dump_json())
116
+ results = loaded_module.main(
144
117
  on_event=self._on_event,
145
118
  )
146
119
  results_container["results"] = results
147
- self._print("<Waldiez> - Workflow finished")
148
- except SystemExit:
149
- self._print("<Waldiez> - Workflow stopped by user")
150
- except Exception as e: # pylint: disable=broad-exception-caught
120
+ self.print(MESSAGES["workflow_finished"])
121
+ except SystemExit: # pragma: no cover
122
+ self.log.debug("Execution stopped by user (sync)")
123
+ self.print(MESSAGES["workflow_stopped"])
124
+ except BaseException as e: # pragma: no cover
151
125
  results_container["exception"] = e
152
126
  traceback.print_exc()
153
- self._print(f"<Waldiez> - Workflow execution failed: {e}")
127
+ self.print(MESSAGES["workflow_failed"].format(error=e))
128
+
154
129
  finally:
155
130
  results_container["completed"] = True
156
131
  return results_container["results"]
@@ -162,153 +137,52 @@ class WaldiezStandardRunner(WaldiezBaseRunner):
162
137
  """Process an event from the workflow."""
163
138
  self._event_count += 1
164
139
  if self._stop_requested.is_set():
165
- self.log.info(
166
- "Async execution stopped before AG2 workflow event processing"
140
+ self.log.debug(
141
+ "Execution stopped before AG2 workflow event processing (sync)"
167
142
  )
168
143
  return False
169
144
  try:
170
- if hasattr(event, "type"):
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
145
+ WaldiezBaseRunner.process_event(event)
189
146
  self._processed_events += 1
147
+ except SystemExit: # pragma: no cover
148
+ self.log.debug("Execution stopped by user (sync)")
149
+ return False
190
150
  except Exception as e:
151
+ if self._stop_requested.is_set():
152
+ self.log.debug("Exception during stop, returning False")
153
+ return False
191
154
  raise RuntimeError(
192
155
  f"Error processing event {event}: {e}\n{traceback.format_exc()}"
193
156
  ) from e
194
- if event.type == "run_completion":
195
- self._signal_completion()
196
- WaldiezBaseRunner._running = False
197
157
  return not self._stop_requested.is_set()
198
158
 
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
159
  async def _a_on_event(
273
160
  self,
274
161
  event: Union["BaseEvent", "BaseMessage"],
275
162
  ) -> bool:
276
163
  """Process an event from the workflow asynchronously."""
277
164
  self._event_count += 1
278
- if self._stop_requested.is_set():
279
- self.log.info(
280
- "Async execution stopped before AG2 workflow event processing"
165
+ if self._stop_requested.is_set(): # pragma: no cover
166
+ self.log.debug(
167
+ "Execution stopped before AG2 workflow event processing (async)"
281
168
  )
282
169
  return False
283
170
  try:
284
- if hasattr(event, "type"):
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
171
+ await WaldiezBaseRunner.a_process_event(event)
305
172
  self._processed_events += 1
173
+ except SystemExit: # pragma: no cover
174
+ self.log.debug("Execution stopped by user (async)")
175
+ return False
306
176
  except Exception as e:
177
+ if self._stop_requested.is_set():
178
+ self.log.debug("Exception during stop, returning False")
179
+ return False
307
180
  raise RuntimeError(
308
181
  f"Error processing event {event}: {e}\n{traceback.format_exc()}"
309
182
  ) from e
310
183
  return not self._stop_requested.is_set()
311
184
 
185
+ # pylint: disable=too-complex
312
186
  async def _a_run(
313
187
  self,
314
188
  temp_dir: Path,
@@ -324,39 +198,43 @@ class WaldiezStandardRunner(WaldiezBaseRunner):
324
198
  async def _execute_workflow() -> list[dict[str, Any]]:
325
199
  # fmt: on
326
200
  """Execute the workflow in an async context."""
201
+ # pylint: disable=import-outside-toplevel
327
202
  from autogen.io import IOStream # pyright: ignore
328
203
 
329
204
  from waldiez.io import StructuredIOStream
330
205
 
331
- results: list[dict[str, Any]] = []
206
+ results: list[dict[str, Any]]
207
+ # pylint: disable=too-many-try-statements,broad-exception-caught
332
208
  try:
333
- self._loaded_module = self._load_module(output_file, temp_dir)
334
- if self._stop_requested.is_set():
335
- self.log.info(
336
- "Async execution stopped before AG2 workflow start"
209
+ loaded_module = self._load_module(output_file, temp_dir)
210
+ if self._stop_requested.is_set(): # pragma: no cover
211
+ self.log.debug(
212
+ "Execution stopped before AG2 "
213
+ "workflow event processing (async)"
337
214
  )
338
215
  return []
216
+ # noinspection DuplicatedCode
339
217
  if self.structured_io:
340
218
  stream = StructuredIOStream(
341
219
  uploads_root=uploads_root, is_async=True
342
220
  )
343
221
  else:
344
222
  stream = IOStream.get_default()
345
- self._print = stream.print
346
- self._input = stream.input
347
- self._send = stream.send
348
- self._print("<Waldiez> - Starting workflow...")
349
- self._print(self.waldiez.info.model_dump_json())
350
- results = await self._loaded_module.main(
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
- except SystemExit:
354
- self._print("<Waldiez> - Workflow stopped by user")
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._print(
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.info("Async execution stopped by user")
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.info("Async execution cancelled")
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
+ ]