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.

Files changed (88) hide show
  1. waldiez/_version.py +1 -1
  2. waldiez/cli.py +112 -24
  3. waldiez/exporting/agent/exporter.py +3 -0
  4. waldiez/exporting/agent/extras/captain_agent_extras.py +44 -7
  5. waldiez/exporting/agent/extras/handoffs/condition.py +3 -1
  6. waldiez/exporting/chats/utils/common.py +25 -23
  7. waldiez/exporting/core/__init__.py +0 -2
  8. waldiez/exporting/core/context.py +13 -13
  9. waldiez/exporting/core/protocols.py +0 -141
  10. waldiez/exporting/core/result.py +5 -5
  11. waldiez/exporting/flow/merger.py +2 -2
  12. waldiez/exporting/flow/orchestrator.py +1 -0
  13. waldiez/exporting/flow/utils/common.py +2 -2
  14. waldiez/exporting/flow/utils/importing.py +1 -0
  15. waldiez/exporting/flow/utils/logging.py +6 -7
  16. waldiez/exporting/tools/exporter.py +5 -0
  17. waldiez/exporting/tools/factory.py +4 -0
  18. waldiez/exporting/tools/processor.py +5 -1
  19. waldiez/io/_ws.py +13 -5
  20. waldiez/io/models/content/image.py +1 -0
  21. waldiez/io/models/user_input.py +4 -4
  22. waldiez/io/models/user_response.py +1 -0
  23. waldiez/io/mqtt.py +1 -1
  24. waldiez/io/structured.py +17 -17
  25. waldiez/io/utils.py +1 -1
  26. waldiez/io/ws.py +9 -11
  27. waldiez/logger.py +180 -63
  28. waldiez/models/agents/agent/update_system_message.py +0 -2
  29. waldiez/models/agents/doc_agent/doc_agent.py +8 -1
  30. waldiez/models/common/dict_utils.py +169 -40
  31. waldiez/models/flow/flow.py +6 -6
  32. waldiez/models/flow/info.py +5 -1
  33. waldiez/models/model/_llm.py +28 -14
  34. waldiez/models/model/model.py +4 -1
  35. waldiez/models/model/model_data.py +18 -5
  36. waldiez/models/tool/predefined/_config.py +5 -1
  37. waldiez/models/tool/predefined/_duckduckgo.py +4 -0
  38. waldiez/models/tool/predefined/_email.py +474 -0
  39. waldiez/models/tool/predefined/_google.py +8 -6
  40. waldiez/models/tool/predefined/_perplexity.py +3 -0
  41. waldiez/models/tool/predefined/_searxng.py +3 -0
  42. waldiez/models/tool/predefined/_tavily.py +4 -1
  43. waldiez/models/tool/predefined/_wikipedia.py +4 -1
  44. waldiez/models/tool/predefined/_youtube.py +4 -1
  45. waldiez/models/tool/predefined/protocol.py +3 -0
  46. waldiez/models/tool/tool.py +22 -4
  47. waldiez/models/waldiez.py +12 -0
  48. waldiez/runner.py +37 -54
  49. waldiez/running/__init__.py +6 -0
  50. waldiez/running/base_runner.py +310 -353
  51. waldiez/running/environment.py +1 -0
  52. waldiez/running/exceptions.py +9 -0
  53. waldiez/running/post_run.py +4 -4
  54. waldiez/running/pre_run.py +51 -40
  55. waldiez/running/protocol.py +21 -101
  56. waldiez/running/run_results.py +1 -1
  57. waldiez/running/standard_runner.py +84 -277
  58. waldiez/running/step_by_step/__init__.py +46 -0
  59. waldiez/running/step_by_step/breakpoints_mixin.py +188 -0
  60. waldiez/running/step_by_step/step_by_step_models.py +224 -0
  61. waldiez/running/step_by_step/step_by_step_runner.py +745 -0
  62. waldiez/running/subprocess_runner/__base__.py +282 -0
  63. waldiez/running/subprocess_runner/__init__.py +16 -0
  64. waldiez/running/subprocess_runner/_async_runner.py +362 -0
  65. waldiez/running/subprocess_runner/_sync_runner.py +455 -0
  66. waldiez/running/subprocess_runner/runner.py +561 -0
  67. waldiez/running/timeline_processor.py +1 -1
  68. waldiez/running/utils.py +376 -1
  69. waldiez/utils/version.py +2 -6
  70. waldiez/ws/__init__.py +70 -0
  71. waldiez/ws/__main__.py +15 -0
  72. waldiez/ws/_file_handler.py +201 -0
  73. waldiez/ws/cli.py +211 -0
  74. waldiez/ws/client_manager.py +835 -0
  75. waldiez/ws/errors.py +416 -0
  76. waldiez/ws/models.py +971 -0
  77. waldiez/ws/reloader.py +342 -0
  78. waldiez/ws/server.py +469 -0
  79. waldiez/ws/session_manager.py +393 -0
  80. waldiez/ws/session_stats.py +83 -0
  81. waldiez/ws/utils.py +385 -0
  82. {waldiez-0.5.8.dist-info → waldiez-0.5.10.dist-info}/METADATA +74 -74
  83. {waldiez-0.5.8.dist-info → waldiez-0.5.10.dist-info}/RECORD +87 -65
  84. waldiez/running/patch_io_stream.py +0 -210
  85. {waldiez-0.5.8.dist-info → waldiez-0.5.10.dist-info}/WHEEL +0 -0
  86. {waldiez-0.5.8.dist-info → waldiez-0.5.10.dist-info}/entry_points.txt +0 -0
  87. {waldiez-0.5.8.dist-info → waldiez-0.5.10.dist-info}/licenses/LICENSE +0 -0
  88. {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
- # 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
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,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
- 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
- @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
- 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.
69
+ *args : Any
70
+ Positional arguments to print.
71
+ **kwargs : Any
72
+ Keyword arguments to print.
97
73
  """
98
- if password:
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
- self._loaded_module = self._load_module(output_file, temp_dir)
99
+ loaded_module = self._load_module(output_file, temp_dir)
127
100
  if self._stop_requested.is_set():
128
- self.log.info(
129
- "Async execution stopped before AG2 workflow start"
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
- 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(
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._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
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._print(f"<Waldiez> - Workflow execution failed: {e}")
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.info(
166
- "Async execution stopped before AG2 workflow event processing"
141
+ self.log.debug(
142
+ "Execution stopped before AG2 workflow event processing (sync)"
167
143
  )
168
144
  return False
169
145
  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
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.info(
280
- "Async execution stopped before AG2 workflow event processing"
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
- 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
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
- 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"
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
- 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
+ ]