waldiez 0.4.9__py3-none-any.whl → 0.4.11__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 (38) hide show
  1. waldiez/__init__.py +1 -2
  2. waldiez/_version.py +1 -1
  3. waldiez/cli.py +88 -50
  4. waldiez/exporter.py +64 -9
  5. waldiez/exporting/core/context.py +12 -0
  6. waldiez/exporting/core/extras/flow_extras.py +2 -14
  7. waldiez/exporting/core/types.py +21 -0
  8. waldiez/exporting/flow/exporter.py +4 -0
  9. waldiez/exporting/flow/factory.py +16 -0
  10. waldiez/exporting/flow/orchestrator.py +12 -0
  11. waldiez/exporting/flow/utils/__init__.py +2 -0
  12. waldiez/exporting/flow/utils/common.py +96 -2
  13. waldiez/exporting/flow/utils/logging.py +5 -6
  14. waldiez/io/mqtt.py +7 -3
  15. waldiez/io/structured.py +5 -1
  16. waldiez/models/common/method_utils.py +1 -1
  17. waldiez/models/tool/tool.py +2 -1
  18. waldiez/runner.py +402 -321
  19. waldiez/running/__init__.py +6 -34
  20. waldiez/running/base_runner.py +907 -0
  21. waldiez/running/environment.py +74 -0
  22. waldiez/running/import_runner.py +424 -0
  23. waldiez/running/patch_io_stream.py +208 -0
  24. waldiez/running/post_run.py +26 -24
  25. waldiez/running/pre_run.py +2 -46
  26. waldiez/running/protocol.py +281 -0
  27. waldiez/running/run_results.py +22 -0
  28. waldiez/running/subprocess_runner.py +100 -0
  29. waldiez/utils/__init__.py +0 -2
  30. waldiez/utils/version.py +4 -2
  31. {waldiez-0.4.9.dist-info → waldiez-0.4.11.dist-info}/METADATA +11 -11
  32. {waldiez-0.4.9.dist-info → waldiez-0.4.11.dist-info}/RECORD +37 -32
  33. waldiez/utils/flaml_warnings.py +0 -17
  34. /waldiez/running/{util.py → utils.py} +0 -0
  35. {waldiez-0.4.9.dist-info → waldiez-0.4.11.dist-info}/WHEEL +0 -0
  36. {waldiez-0.4.9.dist-info → waldiez-0.4.11.dist-info}/entry_points.txt +0 -0
  37. {waldiez-0.4.9.dist-info → waldiez-0.4.11.dist-info}/licenses/LICENSE +0 -0
  38. {waldiez-0.4.9.dist-info → waldiez-0.4.11.dist-info}/licenses/NOTICE.md +0 -0
@@ -0,0 +1,208 @@
1
+ # SPDX-License-Identifier: Apache-2.0.
2
+ # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
+
4
+ # pylint: disable=import-outside-toplevel, broad-exception-caught
5
+ # pylint: disable=too-many-try-statements, too-complex
6
+ # noqa: C901
7
+ """Patch ag2's IOStream if a flow is async.
8
+
9
+ # let's keep an eye here:
10
+ # https://github.com/ag2ai/ag2/blob/main/autogen/agentchat/conversable_agent.py#L2973
11
+ # reply = await iostream.input(prompt) ???? (await???)
12
+ """
13
+
14
+ import inspect
15
+ import sys
16
+ from typing import Any, Callable
17
+
18
+ from asyncer import syncify
19
+
20
+
21
+ def patch_io_stream(is_async: bool) -> None:
22
+ """Patch the IOStream to handle async flows.
23
+
24
+ Parameters
25
+ ----------
26
+ is_async : bool
27
+ Whether the flow is async or not.
28
+ """
29
+ if is_async:
30
+ patch_async_io_stream()
31
+ else:
32
+ patch_sync_io_stream()
33
+
34
+
35
+ def patch_sync_io_stream() -> None:
36
+ """Patch the IOStream to handle async flows."""
37
+ from autogen.io import IOStream # type: ignore
38
+
39
+ iostream = IOStream.get_default()
40
+ original_input = iostream.input
41
+
42
+ def _safe_input(prompt: str = "", *, password: bool = False) -> str:
43
+ """Async input method."""
44
+ try:
45
+ input_or_coro = original_input(prompt, password=password)
46
+ if inspect.iscoroutine(input_or_coro):
47
+
48
+ async def _async_input() -> str:
49
+ reply = await input_or_coro
50
+ return reply
51
+
52
+ return syncify(_async_input)()
53
+ return input_or_coro
54
+
55
+ except EOFError:
56
+ # Handle EOFError gracefully
57
+ return ""
58
+
59
+ iostream.input = _safe_input # pyright: ignore
60
+ iostream.print = get_printer() # pyright: ignore
61
+ IOStream.set_default(iostream)
62
+
63
+
64
+ def patch_async_io_stream() -> None:
65
+ """Patch the IOStream to handle async flows."""
66
+ from autogen.io import IOStream # pyright: ignore
67
+
68
+ iostream = IOStream.get_default()
69
+ original_input = iostream.input
70
+
71
+ async def _async_input(prompt: str = "", *, password: bool = False) -> str:
72
+ """Async input method."""
73
+ try:
74
+ input_or_coro = original_input(prompt, password=password)
75
+ if inspect.iscoroutine(input_or_coro):
76
+ reply = await input_or_coro
77
+ else:
78
+ reply = input_or_coro
79
+ return reply
80
+ except EOFError:
81
+ # Handle EOFError gracefully
82
+ return ""
83
+
84
+ iostream.input = _async_input # pyright: ignore
85
+ iostream.print = get_printer() # pyright: ignore
86
+ IOStream.set_default(iostream)
87
+
88
+
89
+ def get_printer() -> Callable[..., None]: # noqa: C901
90
+ """Get the printer function.
91
+
92
+ Returns
93
+ -------
94
+ Callable[..., None]
95
+ The printer function that handles Unicode encoding errors gracefully.
96
+ """
97
+ try:
98
+ from autogen.io import IOStream # pyright: ignore
99
+
100
+ printer = IOStream.get_default().print
101
+ except ImportError:
102
+ # Fallback to standard print if autogen is not available
103
+ printer = print
104
+
105
+ def safe_printer(*args: Any, **kwargs: Any) -> None: # noqa: C901
106
+ """Safe printer that handles Unicode encoding errors.
107
+
108
+ Parameters
109
+ ----------
110
+ *args : Any
111
+ Arguments to pass to the printer
112
+ **kwargs : Any
113
+ Keyword arguments to pass to the printer
114
+ """
115
+ try:
116
+ printer(*args, **kwargs)
117
+ except UnicodeEncodeError:
118
+ # First fallback: try to get a safe string representation
119
+ try:
120
+ msg, flush = get_what_to_print(*args, **kwargs)
121
+ # Convert problematic characters to safe representations
122
+ safe_msg = msg.encode("utf-8", errors="replace").decode("utf-8")
123
+ printer(safe_msg, end="", flush=flush)
124
+ except (UnicodeEncodeError, UnicodeDecodeError):
125
+ # Second fallback: use built-in print with safe encoding
126
+ try:
127
+ # Convert args to safe string representations
128
+ safe_args: list[str] = []
129
+ for arg in args:
130
+ try:
131
+ safe_args.append(
132
+ str(arg)
133
+ .encode("utf-8", errors="replace")
134
+ .decode("utf-8")
135
+ )
136
+ except (UnicodeEncodeError, UnicodeDecodeError):
137
+ safe_args.append(repr(arg))
138
+
139
+ # Use built-in print instead of the custom printer
140
+ print(*safe_args, **kwargs)
141
+
142
+ except Exception:
143
+ # Final fallback: write directly to stderr buffer
144
+ try:
145
+ error_msg = (
146
+ "Could not print the message "
147
+ "due to encoding issues.\n"
148
+ )
149
+ if hasattr(sys.stderr, "buffer"):
150
+ sys.stderr.buffer.write(
151
+ error_msg.encode("utf-8", errors="replace")
152
+ )
153
+ sys.stderr.buffer.flush()
154
+ else:
155
+ sys.stderr.write(error_msg)
156
+ sys.stderr.flush()
157
+ except Exception:
158
+ # If even this fails, we're in a very bad state
159
+ pass
160
+ except Exception as e:
161
+ # Handle any other unexpected errors
162
+ try:
163
+ error_msg = f"Unexpected error in printer: {str(e)}\n"
164
+ if hasattr(sys.stderr, "buffer"):
165
+ sys.stderr.buffer.write(
166
+ error_msg.encode("utf-8", errors="replace")
167
+ )
168
+ sys.stderr.buffer.flush()
169
+ else:
170
+ sys.stderr.write(error_msg)
171
+ sys.stderr.flush()
172
+ except Exception:
173
+ pass
174
+
175
+ return safe_printer
176
+
177
+
178
+ def get_what_to_print(*args: Any, **kwargs: Any) -> tuple[str, bool]:
179
+ """Extract message and flush flag from print arguments.
180
+
181
+ Parameters
182
+ ----------
183
+ *args : Any
184
+ Arguments to print
185
+ **kwargs : Any
186
+ Keyword arguments for print function
187
+
188
+ Returns
189
+ -------
190
+ tuple[str, bool]
191
+ Message to print and flush flag
192
+ """
193
+ # Convert all args to strings and join with spaces (like print does)
194
+ msg = " ".join(str(arg) for arg in args)
195
+
196
+ # Handle sep parameter
197
+ sep = kwargs.get("sep", " ")
198
+ if len(args) > 1:
199
+ msg = sep.join(str(arg) for arg in args)
200
+
201
+ # Handle end parameter
202
+ end = kwargs.get("end", "\n")
203
+ msg += end
204
+
205
+ # Handle flush parameter
206
+ flush = kwargs.get("flush", False)
207
+
208
+ return msg, flush
@@ -1,5 +1,7 @@
1
1
  # SPDX-License-Identifier: Apache-2.0.
2
2
  # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
+
4
+ # pylint: disable=unused-argument
3
5
  """Utilities for running code."""
4
6
 
5
7
  import datetime
@@ -12,8 +14,9 @@ from .gen_seq_diagram import generate_sequence_diagram
12
14
 
13
15
  def after_run(
14
16
  temp_dir: Path,
15
- output_path: Optional[Union[str, Path]],
17
+ output_file: Optional[Union[str, Path]],
16
18
  flow_name: str,
19
+ uploads_root: Optional[Path] = None,
17
20
  skip_mmd: bool = False,
18
21
  ) -> None:
19
22
  """Actions to perform after running the flow.
@@ -22,17 +25,19 @@ def after_run(
22
25
  ----------
23
26
  temp_dir : Path
24
27
  The temporary directory.
25
- output_path : Optional[Union[str, Path]]
26
- The output path.
28
+ output_file : Optional[Union[str, Path]]
29
+ The output file.
27
30
  flow_name : str
28
31
  The flow name.
32
+ uploads_root : Optional[Path], optional
33
+ The runtime uploads root, by default None
29
34
  skip_mmd : bool, optional
30
35
  Whether to skip the mermaid sequence diagram generation,
31
36
  by default, False
32
37
  """
33
- if isinstance(output_path, str):
34
- output_path = Path(output_path)
35
- mmd_dir = output_path.parent if output_path else Path.cwd()
38
+ if isinstance(output_file, str):
39
+ output_file = Path(output_file)
40
+ mmd_dir = output_file.parent if output_file else Path.cwd()
36
41
  if skip_mmd is False:
37
42
  events_csv_path = temp_dir / "logs" / "events.csv"
38
43
  if events_csv_path.exists():
@@ -40,7 +45,7 @@ def after_run(
40
45
  mmd_path = temp_dir / f"{flow_name}.mmd"
41
46
  generate_sequence_diagram(events_csv_path, mmd_path)
42
47
  if (
43
- not output_path
48
+ not output_file
44
49
  and mmd_path.exists()
45
50
  and mmd_path != mmd_dir / f"{flow_name}.mmd"
46
51
  ):
@@ -48,8 +53,8 @@ def after_run(
48
53
  shutil.copyfile(mmd_path, mmd_dir / f"{flow_name}.mmd")
49
54
  except BaseException: # pylint: disable=broad-exception-caught
50
55
  pass
51
- if output_path:
52
- destination_dir = output_path.parent
56
+ if output_file:
57
+ destination_dir = output_file.parent
53
58
  destination_dir = (
54
59
  destination_dir
55
60
  / "waldiez_out"
@@ -60,8 +65,7 @@ def after_run(
60
65
  print(f"Copying the results to {destination_dir}")
61
66
  copy_results(
62
67
  temp_dir=temp_dir,
63
- output_path=output_path,
64
- output_dir=output_path.parent,
68
+ output_file=output_file,
65
69
  destination_dir=destination_dir,
66
70
  )
67
71
  shutil.rmtree(temp_dir)
@@ -69,8 +73,7 @@ def after_run(
69
73
 
70
74
  def copy_results(
71
75
  temp_dir: Path,
72
- output_path: Path,
73
- output_dir: Path,
76
+ output_file: Path,
74
77
  destination_dir: Path,
75
78
  ) -> None:
76
79
  """Copy the results to the output directory.
@@ -79,14 +82,13 @@ def copy_results(
79
82
  ----------
80
83
  temp_dir : Path
81
84
  The temporary directory.
82
- output_path : Path
83
- The output path.
84
- output_dir : Path
85
- The output directory.
85
+ output_file : Path
86
+ The output file.
86
87
  destination_dir : Path
87
88
  The destination directory.
88
89
  """
89
90
  temp_dir.mkdir(parents=True, exist_ok=True)
91
+ output_dir = output_file.parent
90
92
  for item in temp_dir.iterdir():
91
93
  # skip cache files
92
94
  if (
@@ -107,13 +109,13 @@ def copy_results(
107
109
  shutil.copy(item, destination_dir)
108
110
  else:
109
111
  shutil.copytree(item, destination_dir / item.name)
110
- if output_path.is_file():
111
- if output_path.suffix == ".waldiez":
112
- output_path = output_path.with_suffix(".py")
113
- if output_path.suffix == ".py":
114
- src = temp_dir / output_path.name
112
+ if output_file.is_file():
113
+ if output_file.suffix == ".waldiez":
114
+ output_file = output_file.with_suffix(".py")
115
+ if output_file.suffix == ".py":
116
+ src = temp_dir / output_file.name
115
117
  if src.exists():
116
- dst = destination_dir / output_path.name
118
+ dst = destination_dir / output_file.name
117
119
  if dst.exists():
118
120
  dst.unlink()
119
- shutil.copyfile(src, output_dir / output_path.name)
121
+ shutil.copyfile(src, output_dir / output_file.name)
@@ -7,54 +7,10 @@ import io
7
7
  import os
8
8
  import subprocess
9
9
  import sys
10
- import tempfile
11
- from pathlib import Path
12
- from typing import Callable, Optional, Union
10
+ from typing import Callable
13
11
 
14
12
  from .environment import in_virtualenv, is_root
15
- from .util import strip_ansi
16
-
17
-
18
- def before_run(
19
- output_path: Optional[Union[str, Path]],
20
- uploads_root: Optional[Union[str, Path]],
21
- ) -> str:
22
- """Actions to perform before running the flow.
23
-
24
- Parameters
25
- ----------
26
- output_path : Optional[Union[str, Path]]
27
- The output path.
28
- uploads_root : Optional[Union[str, Path]]
29
- The runtime uploads root.
30
-
31
- Returns
32
- -------
33
- str
34
- The file name.
35
- """
36
- if not uploads_root:
37
- uploads_root = Path(tempfile.mkdtemp())
38
- else:
39
- uploads_root = Path(uploads_root)
40
- if not uploads_root.exists():
41
- uploads_root.mkdir(parents=True)
42
- output_dir = Path.cwd()
43
- if output_path and isinstance(output_path, str):
44
- output_path = Path(output_path)
45
- if output_path:
46
- if output_path.is_dir():
47
- output_dir = output_path
48
- else:
49
- output_dir = output_path.parent if output_path else Path.cwd()
50
- if not output_dir.exists():
51
- output_dir.mkdir(parents=True, exist_ok=True)
52
- file_name = Path(output_path).name if output_path else "waldiez_flow.py"
53
- if file_name.endswith((".json", ".waldiez")):
54
- file_name = file_name.replace(".json", ".py").replace(".waldiez", ".py")
55
- if not file_name.endswith(".py"):
56
- file_name += ".py"
57
- return file_name
13
+ from .utils import strip_ansi
58
14
 
59
15
 
60
16
  def install_requirements(
@@ -0,0 +1,281 @@
1
+ # SPDX-License-Identifier: Apache-2.0.
2
+ # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
+
4
+ """Waldiez Runner protocol."""
5
+
6
+ from pathlib import Path
7
+ from typing import TYPE_CHECKING, Protocol, Union, runtime_checkable
8
+
9
+ if TYPE_CHECKING:
10
+ from autogen import ChatResult # type: ignore[import-untyped]
11
+
12
+
13
+ @runtime_checkable
14
+ class WaldiezRunnerProtocol(Protocol):
15
+ """Waldiez Runner protocol."""
16
+
17
+ def before_run(
18
+ self,
19
+ output_file: Path,
20
+ uploads_root: Path | None,
21
+ ) -> Path: # pyright: ignore
22
+ """Actions to perform before running the flow.
23
+
24
+ Parameters
25
+ ----------
26
+ output_file : Path
27
+ The output file.
28
+ uploads_root : Path | None
29
+ The runtime uploads root.
30
+
31
+ Returns
32
+ -------
33
+ Path
34
+ The path to the temporary directory created for the run.
35
+ """
36
+
37
+ async def a_before_run(
38
+ self,
39
+ output_file: Path,
40
+ uploads_root: Path | None,
41
+ ) -> Path: # pyright: ignore
42
+ """Asynchronously perform actions before running the flow.
43
+
44
+ Parameters
45
+ ----------
46
+ output_file : Path
47
+ The output file.
48
+ uploads_root : Path | None
49
+ The runtime uploads root.
50
+
51
+ Returns
52
+ -------
53
+ Path
54
+ The path to the temporary directory created for the run.
55
+ """
56
+
57
+ def start(
58
+ self,
59
+ output_path: str | Path | None,
60
+ uploads_root: str | Path | None,
61
+ structured_io: bool | None = None,
62
+ skip_patch_io: bool | None = None,
63
+ skip_mmd: bool = False,
64
+ ) -> None:
65
+ """Start running the Waldiez flow in a non-blocking way.
66
+
67
+ To allow "stoping" it later.
68
+
69
+ Parameters
70
+ ----------
71
+ output_path : str | Path | None
72
+ The output path.
73
+ uploads_root : str | Path | None
74
+ The runtime uploads root.
75
+ structured_io : bool
76
+ Whether to use structured IO instead of the default 'input/print'.
77
+ skip_patch_io : bool | None
78
+ Whether to skip patching I/O, by default None.
79
+ If None, it will use the value from the context.
80
+ skip_mmd : bool
81
+ Whether to skip generating the mermaid diagram.
82
+
83
+ Raises
84
+ ------
85
+ RuntimeError
86
+ If the runner is already running.
87
+ """
88
+
89
+ async def a_start(
90
+ self,
91
+ output_path: str | Path | None,
92
+ uploads_root: str | Path | None,
93
+ structured_io: bool | None = None,
94
+ skip_patch_io: bool | None = None,
95
+ skip_mmd: bool = False,
96
+ ) -> None:
97
+ """Asynchronously start running the Waldiez flow in a non-blocking way.
98
+
99
+ To allow "stoping" it later.
100
+
101
+ Parameters
102
+ ----------
103
+ output_path : str | Path | None
104
+ The output path.
105
+ uploads_root : str | Path | None
106
+ The runtime uploads root.
107
+ structured_io : bool
108
+ Whether to use structured IO instead of the default 'input/print'.
109
+ skip_patch_io : bool | None
110
+ Whether to skip patching I/O, by default None.
111
+ If None, it will use the value from the context.
112
+ skip_mmd : bool
113
+ Whether to skip generating the mermaid diagram.
114
+
115
+ Raises
116
+ ------
117
+ RuntimeError
118
+ If the runner is already running.
119
+ """
120
+
121
+ def run(
122
+ self,
123
+ output_path: str | Path | None,
124
+ uploads_root: str | Path | None,
125
+ structured_io: bool | None = None,
126
+ threaded: bool | None = None,
127
+ skip_patch_io: bool | None = None,
128
+ skip_mmd: bool = False,
129
+ ) -> Union[
130
+ "ChatResult",
131
+ list["ChatResult"],
132
+ dict[int, "ChatResult"],
133
+ ]: # pyright: ignore
134
+ """Run the Waldiez flow in a blocking way.
135
+
136
+ Parameters
137
+ ----------
138
+ output_path : str | Path | None
139
+ The output path, by default None.
140
+ uploads_root : str | Path | None
141
+ The runtime uploads root.
142
+ structured_io : bool
143
+ Whether to use structured IO instead of the default 'input/print'.
144
+ threaded : bool | None
145
+ Whether to run the flow in a separate thread.
146
+ skip_patch_io : bool
147
+ Whether to skip patching I/O, by default None.
148
+ If None, it will use the value from the context.
149
+ skip_mmd : bool
150
+ Whether to skip generating the mermaid diagram.
151
+
152
+ Returns
153
+ -------
154
+ Union[ChatResult, list[ChatResult], dict[int, ChatResult]]
155
+ The result of the run, which can be a single ChatResult,
156
+ a list of ChatResults,
157
+ or a dictionary mapping indices to ChatResults.
158
+ """
159
+
160
+ async def a_run(
161
+ self,
162
+ output_path: str | Path | None,
163
+ uploads_root: str | Path | None,
164
+ structured_io: bool | None = None,
165
+ skip_patch_io: bool | None = None,
166
+ skip_mmd: bool = False,
167
+ ) -> Union[
168
+ "ChatResult",
169
+ list["ChatResult"],
170
+ dict[int, "ChatResult"],
171
+ ]: # pyright: ignore
172
+ """Run the Waldiez flow.
173
+
174
+ Parameters
175
+ ----------
176
+ output_path : str | Path | None
177
+ The output path, by default None.
178
+ uploads_root : str | Path | None
179
+ The runtime uploads root.
180
+ structured_io : bool
181
+ Whether to use structured IO instead of the default 'input/print'.
182
+ skip_patch_io : bool
183
+ Whether to skip patching I/O, by default None.
184
+ If None, it will use the value from the context.
185
+ skip_mmd : bool
186
+ Whether to skip generating the mermaid diagram.
187
+
188
+ Returns
189
+ -------
190
+ Union[ChatResult, list[ChatResult], dict[int, ChatResult]]
191
+ The result of the run, which can be a single ChatResult,
192
+ a list of ChatResults,
193
+ or a dictionary mapping indices to ChatResults.
194
+ """
195
+
196
+ def after_run(
197
+ self,
198
+ results: Union[
199
+ "ChatResult",
200
+ list["ChatResult"],
201
+ dict[int, "ChatResult"],
202
+ ],
203
+ output_file: Path,
204
+ uploads_root: Path | None,
205
+ temp_dir: Path,
206
+ skip_mmd: bool,
207
+ ) -> None:
208
+ """Actions to perform after running the flow.
209
+
210
+ Parameters
211
+ ----------
212
+ results : Union[ChatResult, list[ChatResult], dict[int, ChatResult]]
213
+ The results of the run, which can be a single ChatResult,
214
+ a list of ChatResults,
215
+ or a dictionary mapping indices to ChatResults.
216
+ output_file : Path
217
+ The path to the output file.
218
+ uploads_root : Path | None
219
+ The runtime uploads root.
220
+ temp_dir : Path
221
+ The path to the temporary directory.
222
+ skip_mmd : bool
223
+ Whether to skip generating the mermaid diagram.
224
+ """
225
+
226
+ async def a_after_run(
227
+ self,
228
+ results: Union[
229
+ "ChatResult",
230
+ list["ChatResult"],
231
+ dict[int, "ChatResult"],
232
+ ],
233
+ output_file: Path,
234
+ uploads_root: Path | None,
235
+ temp_dir: Path,
236
+ skip_mmd: bool,
237
+ ) -> None:
238
+ """Asynchronously perform actions after running the flow.
239
+
240
+ Parameters
241
+ ----------
242
+ results : Union[ChatResult, list[ChatResult], dict[int, ChatResult]]
243
+ The results of the run, which can be a single ChatResult,
244
+ a list of ChatResults,
245
+ or a dictionary mapping indices to ChatResults.
246
+ output_file : Path
247
+ The path to the output file.
248
+ uploads_root : Path | None
249
+ The runtime uploads root.
250
+ temp_dir : Path
251
+ The path to the temporary directory.
252
+ skip_mmd : bool
253
+ Whether to skip generating the mermaid diagram.
254
+ """
255
+
256
+ def is_running(self) -> bool: # pyright: ignore
257
+ """Check if the runner is currently running.
258
+
259
+ Returns
260
+ -------
261
+ bool
262
+ True if the runner is running, False otherwise.
263
+ """
264
+
265
+ def stop(self) -> None:
266
+ """Stop the runner if it is running.
267
+
268
+ Raises
269
+ ------
270
+ RuntimeError
271
+ If the runner is not running.
272
+ """
273
+
274
+ async def a_stop(self) -> None:
275
+ """Asynchronously stop the runner if it is running.
276
+
277
+ Raises
278
+ ------
279
+ RuntimeError
280
+ If the runner is not running.
281
+ """
@@ -0,0 +1,22 @@
1
+ # SPDX-License-Identifier: Apache-2.0.
2
+ # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
+
4
+ """Waldiez run results module."""
5
+
6
+ from typing import TYPE_CHECKING, TypedDict, Union
7
+
8
+ if TYPE_CHECKING:
9
+ from autogen import ChatResult # type: ignore[import-untyped]
10
+
11
+
12
+ class WaldiezRunResults(TypedDict):
13
+ """Results of the Waldiez run."""
14
+
15
+ results: Union[
16
+ "ChatResult",
17
+ list["ChatResult"],
18
+ dict[int, "ChatResult"],
19
+ None,
20
+ ]
21
+ exception: Exception | None
22
+ completed: bool