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
@@ -0,0 +1,570 @@
1
+ # SPDX-License-Identifier: Apache-2.0.
2
+ # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
+ # flake8: noqa: G004
4
+ """Waldiez subprocess runner that inherits from BaseRunner."""
5
+
6
+ import asyncio
7
+ import re
8
+ from pathlib import Path
9
+ from typing import Any, Callable, Literal
10
+
11
+ from waldiez.models import Waldiez
12
+
13
+ from ..base_runner import WaldiezBaseRunner
14
+ from ._async_runner import AsyncSubprocessRunner
15
+ from ._sync_runner import SyncSubprocessRunner
16
+
17
+ # TODO: check output directory and return the results from the JSON logs
18
+ # in self._run and self._a_run
19
+
20
+
21
+ # noinspection PyUnusedLocal
22
+ class WaldiezSubprocessRunner(WaldiezBaseRunner):
23
+ """Waldiez runner that uses subprocess execution via standalone runners."""
24
+
25
+ def __init__(
26
+ self,
27
+ waldiez: Waldiez,
28
+ on_output: Callable[[dict[str, Any]], None] | None = None,
29
+ on_input_request: Callable[[str], None] | None = None,
30
+ on_async_output: Callable[[dict[str, Any]], Any] | None = None,
31
+ on_async_input_request: Callable[[str], Any] | None = None,
32
+ output_path: str | Path | None = None,
33
+ uploads_root: str | Path | None = None,
34
+ structured_io: bool = True,
35
+ dot_env: str | Path | None = None,
36
+ input_timeout: float = 120.0,
37
+ **kwargs: Any,
38
+ ) -> None:
39
+ """Initialize subprocess runner that inherits from BaseRunner.
40
+
41
+ Parameters
42
+ ----------
43
+ waldiez : Waldiez
44
+ The Waldiez workflow to run
45
+ on_output : Callable[[dict[str, Any]], None] | None
46
+ Sync callback for handling output messages
47
+ on_input_request : Callable[[str], None] | None
48
+ Sync callback for handling input requests
49
+ on_async_output : Callable[[dict[str, Any]], Any] | None
50
+ Async callback for handling output messages
51
+ on_async_input_request : Callable[[str], Any] | None
52
+ Async callback for handling input requests
53
+ output_path : str | Path | None
54
+ Output path for the workflow
55
+ uploads_root : str | Path | None
56
+ Root directory for uploads
57
+ structured_io : bool
58
+ Whether to use structured I/O (forced to True for subprocess)
59
+ dot_env : str | Path | None
60
+ Path to .env file
61
+ input_timeout : float
62
+ Timeout for user input in seconds
63
+ **kwargs : Any
64
+ Additional arguments for BaseRunner
65
+ """
66
+ super().__init__(
67
+ waldiez=waldiez,
68
+ output_path=output_path,
69
+ uploads_root=uploads_root,
70
+ structured_io=True, # Always force structured I/O for subprocess
71
+ dot_env=dot_env,
72
+ **kwargs,
73
+ )
74
+
75
+ # Store callbacks
76
+ self.sync_on_output = on_output or self._default_sync_output
77
+ self.sync_on_input_request = (
78
+ on_input_request or self._default_sync_input_request
79
+ )
80
+ self.async_on_output = on_async_output or self._default_async_output
81
+ self.async_on_input_request = (
82
+ on_async_input_request or self._default_async_input_request
83
+ )
84
+ self.input_timeout = input_timeout
85
+
86
+ # Subprocess runner instances
87
+ self.async_runner: AsyncSubprocessRunner | None = None
88
+ self.sync_runner: SyncSubprocessRunner | None = None
89
+ self.temp_flow_file: Path | None = None
90
+ mode = kwargs.get("mode", "run")
91
+ if mode not in ["run", "debug"]:
92
+ raise ValueError(f"Invalid mode: {mode}")
93
+ # noinspection PyTypeChecker
94
+ self.mode: Literal["run", "debug"] = mode
95
+ waldiez_file = kwargs.get("waldiez_file")
96
+ self._waldiez_file = self._ensure_waldiez_file(waldiez_file)
97
+
98
+ def _ensure_waldiez_file(self, waldiez_file: str | Path | None) -> Path:
99
+ """Ensure the Waldiez file is a Path object."""
100
+ if isinstance(waldiez_file, str):
101
+ waldiez_file = Path(waldiez_file)
102
+ if waldiez_file and waldiez_file.is_file():
103
+ return waldiez_file.resolve()
104
+ file_name = self.waldiez.name
105
+ # sanitize file name
106
+ # noinspection RegExpRedundantEscape
107
+ file_name = re.sub(r"[^a-zA-Z0-9_\-\.]", "_", file_name)[:30]
108
+ file_name = f"{file_name}.waldiez"
109
+ with open(file_name, "w", encoding="utf-8") as f:
110
+ f.write(self.waldiez.model_dump_json())
111
+ return Path(file_name).resolve()
112
+
113
+ def _default_sync_output(self, data: dict[str, Any]) -> None:
114
+ """Get the default sync output handler."""
115
+ if data.get("type") == "error":
116
+ self.log.error(data.get("data", ""))
117
+ else:
118
+ content = data.get("data", data)
119
+ self._print(content)
120
+
121
+ def _default_sync_input_request(self, prompt: str) -> None:
122
+ """Get the default sync input request handler."""
123
+ input_value = input(prompt)
124
+ self.log.debug("User input received: %s", input_value)
125
+ self.provide_user_input(input_value)
126
+
127
+ async def _default_async_output(self, data: dict[str, Any]) -> None:
128
+ """Get the default async output handler."""
129
+ self._default_sync_output(data)
130
+
131
+ async def _default_async_input_request(self, prompt: str) -> None:
132
+ """Get the default async input request handler."""
133
+ await asyncio.to_thread(self._default_sync_input_request, prompt)
134
+
135
+ def _create_async_subprocess_runner(self) -> AsyncSubprocessRunner:
136
+ """Create async subprocess runner."""
137
+ self.async_runner = AsyncSubprocessRunner(
138
+ on_output=self.async_on_output,
139
+ on_input_request=self.async_on_input_request,
140
+ input_timeout=self.input_timeout,
141
+ uploads_root=self.uploads_root,
142
+ dot_env=self.dot_env_path,
143
+ logger=self.log,
144
+ )
145
+ return self.async_runner
146
+
147
+ def _create_sync_subprocess_runner(self) -> SyncSubprocessRunner:
148
+ """Create sync subprocess runner."""
149
+ self.sync_runner = SyncSubprocessRunner(
150
+ on_output=self.sync_on_output,
151
+ on_input_request=self.sync_on_input_request,
152
+ input_timeout=self.input_timeout,
153
+ uploads_root=self.uploads_root,
154
+ dot_env=self.dot_env_path,
155
+ logger=self.log,
156
+ )
157
+ return self.sync_runner
158
+
159
+ def run(
160
+ self,
161
+ output_path: str | Path | None = None,
162
+ uploads_root: str | Path | None = None,
163
+ structured_io: bool | None = None,
164
+ skip_mmd: bool = False,
165
+ skip_timeline: bool = False,
166
+ dot_env: str | Path | None = None,
167
+ **kwargs: Any,
168
+ ) -> list[dict[str, Any]]:
169
+ """Run the Waldiez flow.
170
+
171
+ Parameters
172
+ ----------
173
+ output_path : str | Path | None
174
+ The output path, by default None.
175
+ uploads_root : str | Path | None
176
+ The runtime uploads root, by default None.
177
+ structured_io : bool
178
+ Whether to use structured IO instead of the default 'input/print'.
179
+ skip_mmd : bool
180
+ Whether to skip generating the mermaid diagram.
181
+ skip_timeline : bool
182
+ Whether to skip generating the timeline JSON.
183
+ dot_env : str | Path | None
184
+ The path to the .env file, if any.
185
+ **kwargs : Any
186
+ Additional keyword arguments for the run method.
187
+
188
+ Returns
189
+ -------
190
+ list[dict[str, Any]]
191
+ The results of the run.
192
+ """
193
+ temp_dir = Path.cwd()
194
+ output_file = self._get_output_file(output_path)
195
+ if dot_env is not None: # pragma: no cover
196
+ resolved = Path(dot_env).resolve()
197
+ if resolved.is_file():
198
+ WaldiezBaseRunner._dot_env_path = resolved
199
+ return self._run(
200
+ temp_dir=temp_dir,
201
+ output_file=output_file,
202
+ uploads_root=Path(uploads_root) if uploads_root else None,
203
+ skip_mmd=skip_mmd,
204
+ skip_timeline=skip_timeline,
205
+ dot_env=dot_env,
206
+ **kwargs,
207
+ )
208
+
209
+ def _get_output_file(
210
+ self,
211
+ output_path: str | Path | None,
212
+ ) -> Path:
213
+ """Get the output file path."""
214
+ if output_path:
215
+ _output_path = Path(output_path)
216
+ if _output_path.is_file():
217
+ return _output_path
218
+ if _output_path.is_dir(): # pragma: no branch
219
+ filename = self._waldiez_file.stem
220
+ return _output_path / f"{filename}.py"
221
+ return self._waldiez_file.with_suffix(".py")
222
+
223
+ def _run(
224
+ self,
225
+ temp_dir: Path,
226
+ output_file: Path,
227
+ uploads_root: Path | None,
228
+ skip_mmd: bool,
229
+ skip_timeline: bool,
230
+ **kwargs: Any,
231
+ ) -> list[dict[str, Any]]:
232
+ # pylint: disable=too-many-try-statements,broad-exception-caught
233
+ mode = kwargs.get("mode", self.mode)
234
+ if not isinstance(mode, str) or mode not in [
235
+ "run",
236
+ "debug",
237
+ ]: # pragma: no cover
238
+ mode = "run"
239
+ self.mode = mode # type: ignore
240
+ try:
241
+ # Create sync subprocess runner
242
+ runner = self._create_sync_subprocess_runner()
243
+
244
+ # Run subprocess
245
+ success = runner.run_subprocess(self._waldiez_file, mode=self.mode)
246
+ return [
247
+ {
248
+ "success": success,
249
+ "runner": "sync_subprocess",
250
+ "mode": self.mode,
251
+ }
252
+ ]
253
+
254
+ except Exception as e:
255
+ self.log.error("Error in sync subprocess execution: %s", e)
256
+ return [
257
+ {
258
+ "error": str(e),
259
+ "runner": "sync_subprocess",
260
+ "mode": self.mode,
261
+ }
262
+ ]
263
+
264
+ async def a_run(
265
+ self,
266
+ output_path: str | Path | None = None,
267
+ uploads_root: str | Path | None = None,
268
+ structured_io: bool | None = None,
269
+ skip_mmd: bool = False,
270
+ skip_timeline: bool = False,
271
+ dot_env: str | Path | None = None,
272
+ **kwargs: Any,
273
+ ) -> list[dict[str, Any]]:
274
+ """Run the Waldiez flow asynchronously.
275
+
276
+ Parameters
277
+ ----------
278
+ output_path : str | Path | None
279
+ The output path, by default None.
280
+ uploads_root : str | Path | None
281
+ The runtime uploads root.
282
+ structured_io : bool
283
+ Whether to use structured IO instead of the default 'input/print'.
284
+ skip_mmd : bool
285
+ Whether to skip generating the mermaid diagram.
286
+ skip_timeline : bool
287
+ Whether to skip generating the timeline JSON.
288
+ dot_env : str | Path | None
289
+ The path to the .env file, if any.
290
+ **kwargs : Any
291
+ Additional keyword arguments for the a_run method.
292
+
293
+ Returns
294
+ -------
295
+ list[dict[str, Any]]
296
+ The results of the run.
297
+ """
298
+ if dot_env is not None: # pragma: no cover
299
+ resolved = Path(dot_env).resolve()
300
+ if resolved.is_file():
301
+ WaldiezBaseRunner._dot_env_path = resolved
302
+ temp_dir = Path.cwd()
303
+ output_file = self._get_output_file(output_path)
304
+ return await self._a_run(
305
+ temp_dir=temp_dir,
306
+ output_file=output_file,
307
+ uploads_root=Path(uploads_root) if uploads_root else None,
308
+ skip_mmd=skip_mmd,
309
+ skip_timeline=skip_timeline,
310
+ dot_env=dot_env,
311
+ **kwargs,
312
+ )
313
+
314
+ async def _a_run(
315
+ self,
316
+ temp_dir: Path,
317
+ output_file: Path,
318
+ uploads_root: Path | None,
319
+ skip_mmd: bool,
320
+ skip_timeline: bool,
321
+ **kwargs: Any,
322
+ ) -> list[dict[str, Any]]:
323
+ """Run workflow using async subprocess runner.
324
+
325
+ Parameters
326
+ ----------
327
+ temp_dir : Path
328
+ Temporary directory
329
+ output_file : Path
330
+ Output file path
331
+ uploads_root : Path | None
332
+ Uploads root directory
333
+ skip_mmd : bool
334
+ Skip mermaid diagram generation
335
+ skip_timeline : bool
336
+ Skip timeline generation
337
+ **kwargs : Any
338
+ Additional arguments
339
+
340
+ Returns
341
+ -------
342
+ list[dict[str, Any]]
343
+ Results of the workflow execution
344
+ """
345
+ mode = kwargs.get("mode", self.mode)
346
+ if not isinstance(mode, str) or mode not in [
347
+ "run",
348
+ "debug",
349
+ ]: # pragma: no cover
350
+ mode = "run"
351
+ self.mode = mode # type: ignore
352
+
353
+ # pylint: disable=too-many-try-statements,broad-exception-caught
354
+ try:
355
+ # Create async subprocess runner
356
+ runner = self._create_async_subprocess_runner()
357
+
358
+ # Run subprocess
359
+ success = await runner.run_subprocess(
360
+ self._waldiez_file,
361
+ mode=self.mode,
362
+ )
363
+ return [
364
+ {
365
+ "success": success,
366
+ "runner": "async_subprocess",
367
+ "mode": self.mode,
368
+ }
369
+ ]
370
+
371
+ except Exception as e:
372
+ self.log.error("Error in async subprocess execution: %s", e)
373
+ return [
374
+ {
375
+ "error": str(e),
376
+ "runner": "async_subprocess",
377
+ "mode": self.mode,
378
+ }
379
+ ]
380
+
381
+ def provide_user_input(self, user_input: str) -> None:
382
+ """Provide user input to the active subprocess runner.
383
+
384
+ Parameters
385
+ ----------
386
+ user_input : str
387
+ User input response
388
+ """
389
+ if self.async_runner and self.async_runner.is_running():
390
+ asyncio.create_task(
391
+ self.async_runner.provide_user_input(user_input)
392
+ )
393
+ if self.sync_runner and self.sync_runner.is_running():
394
+ self.sync_runner.provide_user_input(user_input)
395
+
396
+ async def a_provide_user_input(self, user_input: str) -> None:
397
+ """Provide user input to the active subprocess runner (async version).
398
+
399
+ Parameters
400
+ ----------
401
+ user_input : str
402
+ User input response
403
+ """
404
+ if self.async_runner and self.async_runner.is_running():
405
+ await self.async_runner.provide_user_input(user_input)
406
+ if self.sync_runner and self.sync_runner.is_running():
407
+ await asyncio.to_thread(
408
+ self.sync_runner.provide_user_input, user_input
409
+ )
410
+
411
+ def stop(self) -> None:
412
+ """Stop the workflow execution."""
413
+ super().stop() # Set the base runner stop flag
414
+
415
+ # Stop active subprocess runners
416
+ if self.async_runner and self.async_runner.is_running():
417
+ asyncio.create_task(self.async_runner.stop())
418
+ if self.sync_runner and self.sync_runner.is_running():
419
+ self.sync_runner.stop()
420
+
421
+ async def a_stop(self) -> None:
422
+ """Stop the workflow execution (async version)."""
423
+ super().stop() # Set the base runner stop flag
424
+
425
+ # Stop active subprocess runners
426
+ if self.async_runner and self.async_runner.is_running():
427
+ await self.async_runner.stop()
428
+ if self.sync_runner and self.sync_runner.is_running():
429
+ await asyncio.to_thread(self.sync_runner.stop)
430
+
431
+ def is_subprocess_running(self) -> bool:
432
+ """Check if a subprocess is currently running.
433
+
434
+ Returns
435
+ -------
436
+ bool
437
+ True if a subprocess is running, False otherwise
438
+ """
439
+ return (
440
+ self.async_runner is not None and self.async_runner.is_running()
441
+ ) or (self.sync_runner is not None and self.sync_runner.is_running())
442
+
443
+ def get_subprocess_exit_code(self) -> int | None:
444
+ """Get the exit code of the subprocess.
445
+
446
+ Returns
447
+ -------
448
+ int | None
449
+ Exit code if available, None otherwise
450
+ """
451
+ if self.sync_runner:
452
+ return self.sync_runner.get_exit_code()
453
+ return None
454
+
455
+ def _cleanup_subprocess_runners(self) -> None:
456
+ """Cleanup subprocess runner instances."""
457
+ if self.async_runner and self.async_runner.is_running():
458
+ asyncio.create_task(self.async_runner.stop())
459
+ self.async_runner = None
460
+ if self.sync_runner and self.sync_runner.is_running():
461
+ self.sync_runner.stop()
462
+ self.sync_runner = None
463
+
464
+ def _after_run(
465
+ self,
466
+ results: list[dict[str, Any]],
467
+ output_file: Path,
468
+ waldiez_file: Path,
469
+ uploads_root: Path | None,
470
+ temp_dir: Path,
471
+ skip_mmd: bool,
472
+ skip_timeline: bool,
473
+ ) -> None:
474
+ """Actions to perform after running the flow.
475
+
476
+ Parameters
477
+ ----------
478
+ results : list[dict[str, Any]]
479
+ Results from the workflow execution
480
+ output_file : Path
481
+ Output file path
482
+ waldiez_file : Path
483
+ The waldiez file used/dumped for the run.
484
+ uploads_root : Path | None
485
+ Uploads root directory
486
+ temp_dir : Path
487
+ Temporary directory
488
+ skip_mmd : bool
489
+ Skip mermaid diagram generation
490
+ skip_timeline : bool
491
+ Skip timeline generation
492
+ """
493
+ # Cleanup subprocess runners
494
+ self._cleanup_subprocess_runners()
495
+
496
+ async def _a_after_run(
497
+ self,
498
+ results: list[dict[str, Any]],
499
+ output_file: Path,
500
+ waldiez_file: Path,
501
+ uploads_root: Path | None,
502
+ temp_dir: Path,
503
+ skip_mmd: bool,
504
+ skip_timeline: bool,
505
+ ) -> None:
506
+ """Actions to perform after running the flow (async version).
507
+
508
+ Parameters
509
+ ----------
510
+ results : list[dict[str, Any]]
511
+ Results from the workflow execution
512
+ output_file : Path
513
+ Output file path
514
+ waldiez_file : Path
515
+ The waldiez file used/dumped for the run.
516
+ uploads_root : Path | None
517
+ Uploads root directory
518
+ temp_dir : Path
519
+ Temporary directory
520
+ skip_mmd : bool
521
+ Skip mermaid diagram generation
522
+ skip_timeline : bool
523
+ Skip timeline generation
524
+ """
525
+ # Cleanup subprocess runners
526
+ self._cleanup_subprocess_runners()
527
+
528
+ @classmethod
529
+ def create_with_callbacks(
530
+ cls,
531
+ waldiez_file: str | Path,
532
+ on_output: Callable[[dict[str, Any]], None] | None = None,
533
+ on_input_request: Callable[[str], None] | None = None,
534
+ on_async_output: Callable[[dict[str, Any]], Any] | None = None,
535
+ on_async_input_request: Callable[[str], Any] | None = None,
536
+ **kwargs: Any,
537
+ ) -> "WaldiezSubprocessRunner":
538
+ """Create subprocess runner from waldiez file with callbacks.
539
+
540
+ Parameters
541
+ ----------
542
+ waldiez_file : str | Path
543
+ Path to waldiez file
544
+ on_output : Callable[[dict[str, Any]], None] | None
545
+ Sync output callback
546
+ on_input_request : Callable[[str], None] | None
547
+ Sync input request callback
548
+ on_async_output : Callable[[dict[str, Any]], Any] | None
549
+ Async output callback
550
+ on_async_input_request : Callable[[str], Any] | None
551
+ Async input request callback
552
+ **kwargs : Any
553
+ Additional arguments
554
+
555
+ Returns
556
+ -------
557
+ WaldiezSubprocessRunner
558
+ Configured subprocess runner
559
+ """
560
+ waldiez = Waldiez.load(waldiez_file)
561
+ kwargs.pop("waldiez_file", None)
562
+ return cls(
563
+ waldiez=waldiez,
564
+ on_output=on_output,
565
+ on_input_request=on_input_request,
566
+ on_async_output=on_async_output,
567
+ on_async_input_request=on_async_input_request,
568
+ waldiez_file=waldiez_file,
569
+ **kwargs,
570
+ )
@@ -64,7 +64,7 @@ DEFAULT_AGENT_COLOR = "#E5E7EB"
64
64
  LOG = WaldiezLogger()
65
65
 
66
66
 
67
- # noinspection PyMethodMayBeStatic
67
+ # noinspection PyMethodMayBeStatic,PyTypeHints
68
68
  class TimelineProcessor:
69
69
  """Class to process timeline data from CSV files."""
70
70