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