waldiez 0.5.10__py3-none-any.whl → 0.6.1__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 (192) hide show
  1. waldiez/__init__.py +1 -1
  2. waldiez/_version.py +1 -1
  3. waldiez/cli.py +19 -7
  4. waldiez/cli_extras/jupyter.py +3 -0
  5. waldiez/cli_extras/runner.py +3 -1
  6. waldiez/cli_extras/studio.py +3 -1
  7. waldiez/exporter.py +9 -3
  8. waldiez/exporting/agent/exporter.py +15 -16
  9. waldiez/exporting/agent/extras/captain_agent_extras.py +6 -6
  10. waldiez/exporting/agent/extras/doc_agent_extras.py +6 -6
  11. waldiez/exporting/agent/extras/group_manager_agent_extas.py +40 -24
  12. waldiez/exporting/agent/extras/group_member_extras.py +6 -5
  13. waldiez/exporting/agent/extras/handoffs/after_work.py +2 -1
  14. waldiez/exporting/agent/extras/handoffs/available.py +2 -1
  15. waldiez/exporting/agent/extras/handoffs/condition.py +3 -2
  16. waldiez/exporting/agent/extras/handoffs/handoff.py +2 -1
  17. waldiez/exporting/agent/extras/handoffs/target.py +7 -4
  18. waldiez/exporting/agent/extras/rag/chroma_extras.py +27 -19
  19. waldiez/exporting/agent/extras/rag/mongo_extras.py +8 -8
  20. waldiez/exporting/agent/extras/rag/pgvector_extras.py +5 -5
  21. waldiez/exporting/agent/extras/rag/qdrant_extras.py +5 -4
  22. waldiez/exporting/agent/extras/rag/vector_db_extras.py +1 -1
  23. waldiez/exporting/agent/extras/rag_user_proxy_agent_extras.py +5 -7
  24. waldiez/exporting/agent/extras/reasoning_agent_extras.py +3 -5
  25. waldiez/exporting/agent/termination.py +1 -0
  26. waldiez/exporting/chats/exporter.py +4 -4
  27. waldiez/exporting/chats/processor.py +1 -2
  28. waldiez/exporting/chats/utils/common.py +89 -48
  29. waldiez/exporting/chats/utils/group.py +9 -9
  30. waldiez/exporting/chats/utils/nested.py +7 -7
  31. waldiez/exporting/chats/utils/sequential.py +1 -1
  32. waldiez/exporting/chats/utils/single.py +2 -2
  33. waldiez/exporting/core/constants.py +3 -1
  34. waldiez/exporting/core/content.py +7 -7
  35. waldiez/exporting/core/context.py +5 -3
  36. waldiez/exporting/core/exporter.py +5 -3
  37. waldiez/exporting/core/exporters.py +2 -2
  38. waldiez/exporting/core/extras/agent_extras/captain_extras.py +2 -2
  39. waldiez/exporting/core/extras/agent_extras/group_manager_extras.py +2 -2
  40. waldiez/exporting/core/extras/agent_extras/rag_user_extras.py +2 -2
  41. waldiez/exporting/core/extras/agent_extras/standard_extras.py +3 -8
  42. waldiez/exporting/core/extras/base.py +7 -5
  43. waldiez/exporting/core/extras/flow_extras.py +4 -5
  44. waldiez/exporting/core/extras/model_extras.py +2 -2
  45. waldiez/exporting/core/extras/path_resolver.py +1 -2
  46. waldiez/exporting/core/extras/serializer.py +13 -11
  47. waldiez/exporting/core/protocols.py +6 -5
  48. waldiez/exporting/core/result.py +25 -28
  49. waldiez/exporting/core/types.py +11 -10
  50. waldiez/exporting/core/utils/llm_config.py +4 -4
  51. waldiez/exporting/core/validation.py +10 -11
  52. waldiez/exporting/flow/execution_generator.py +99 -10
  53. waldiez/exporting/flow/exporter.py +2 -2
  54. waldiez/exporting/flow/factory.py +2 -2
  55. waldiez/exporting/flow/file_generator.py +4 -2
  56. waldiez/exporting/flow/merger.py +5 -3
  57. waldiez/exporting/flow/orchestrator.py +72 -2
  58. waldiez/exporting/flow/utils/common.py +6 -6
  59. waldiez/exporting/flow/utils/importing.py +7 -8
  60. waldiez/exporting/flow/utils/linting.py +25 -9
  61. waldiez/exporting/flow/utils/logging.py +5 -77
  62. waldiez/exporting/models/exporter.py +8 -8
  63. waldiez/exporting/models/processor.py +5 -5
  64. waldiez/exporting/tools/exporter.py +2 -2
  65. waldiez/exporting/tools/processor.py +7 -4
  66. waldiez/io/__init__.py +11 -5
  67. waldiez/io/_ws.py +12 -6
  68. waldiez/io/models/constants.py +10 -10
  69. waldiez/io/models/content/audio.py +1 -0
  70. waldiez/io/models/content/base.py +20 -18
  71. waldiez/io/models/content/file.py +1 -0
  72. waldiez/io/models/content/image.py +1 -0
  73. waldiez/io/models/content/text.py +1 -0
  74. waldiez/io/models/content/video.py +1 -0
  75. waldiez/io/models/user_input.py +10 -5
  76. waldiez/io/models/user_response.py +17 -16
  77. waldiez/io/mqtt.py +18 -31
  78. waldiez/io/redis.py +18 -22
  79. waldiez/io/structured.py +122 -70
  80. waldiez/io/utils.py +19 -10
  81. waldiez/io/ws.py +7 -3
  82. waldiez/logger.py +16 -3
  83. waldiez/models/agents/__init__.py +3 -0
  84. waldiez/models/agents/agent/agent.py +25 -17
  85. waldiez/models/agents/agent/agent_data.py +25 -22
  86. waldiez/models/agents/agent/code_execution.py +9 -11
  87. waldiez/models/agents/agent/termination_message.py +10 -12
  88. waldiez/models/agents/agent/update_system_message.py +2 -4
  89. waldiez/models/agents/agents.py +8 -8
  90. waldiez/models/agents/assistant/assistant.py +6 -3
  91. waldiez/models/agents/assistant/assistant_data.py +2 -2
  92. waldiez/models/agents/captain/captain_agent.py +7 -4
  93. waldiez/models/agents/captain/captain_agent_data.py +5 -7
  94. waldiez/models/agents/doc_agent/doc_agent.py +7 -4
  95. waldiez/models/agents/doc_agent/doc_agent_data.py +9 -10
  96. waldiez/models/agents/doc_agent/rag_query_engine.py +10 -12
  97. waldiez/models/agents/extra_requirements.py +3 -3
  98. waldiez/models/agents/group_manager/group_manager.py +12 -7
  99. waldiez/models/agents/group_manager/group_manager_data.py +13 -12
  100. waldiez/models/agents/group_manager/speakers.py +17 -19
  101. waldiez/models/agents/rag_user_proxy/rag_user_proxy.py +7 -4
  102. waldiez/models/agents/rag_user_proxy/rag_user_proxy_data.py +4 -1
  103. waldiez/models/agents/rag_user_proxy/retrieve_config.py +69 -63
  104. waldiez/models/agents/rag_user_proxy/vector_db_config.py +19 -19
  105. waldiez/models/agents/reasoning/reasoning_agent.py +7 -4
  106. waldiez/models/agents/reasoning/reasoning_agent_data.py +3 -2
  107. waldiez/models/agents/reasoning/reasoning_agent_reason_config.py +8 -8
  108. waldiez/models/agents/user_proxy/user_proxy.py +6 -3
  109. waldiez/models/agents/user_proxy/user_proxy_data.py +1 -1
  110. waldiez/models/chat/chat.py +28 -20
  111. waldiez/models/chat/chat_data.py +22 -21
  112. waldiez/models/chat/chat_message.py +9 -9
  113. waldiez/models/chat/chat_nested.py +9 -9
  114. waldiez/models/chat/chat_summary.py +6 -6
  115. waldiez/models/common/__init__.py +2 -0
  116. waldiez/models/common/ag2_version.py +2 -0
  117. waldiez/models/common/base.py +2 -0
  118. waldiez/models/common/dict_utils.py +8 -6
  119. waldiez/models/common/handoff.py +20 -17
  120. waldiez/models/common/method_utils.py +9 -7
  121. waldiez/models/common/naming.py +49 -0
  122. waldiez/models/flow/flow.py +11 -6
  123. waldiez/models/flow/flow_data.py +23 -17
  124. waldiez/models/flow/info.py +3 -3
  125. waldiez/models/flow/naming.py +2 -1
  126. waldiez/models/model/_aws.py +11 -13
  127. waldiez/models/model/_llm.py +8 -0
  128. waldiez/models/model/_price.py +2 -4
  129. waldiez/models/model/extra_requirements.py +1 -3
  130. waldiez/models/model/model.py +2 -2
  131. waldiez/models/model/model_data.py +21 -21
  132. waldiez/models/tool/extra_requirements.py +2 -4
  133. waldiez/models/tool/predefined/_duckduckgo.py +1 -0
  134. waldiez/models/tool/predefined/_email.py +4 -0
  135. waldiez/models/tool/predefined/_google.py +1 -0
  136. waldiez/models/tool/predefined/_perplexity.py +2 -1
  137. waldiez/models/tool/predefined/_searxng.py +2 -1
  138. waldiez/models/tool/predefined/_tavily.py +1 -0
  139. waldiez/models/tool/predefined/_wikipedia.py +2 -1
  140. waldiez/models/tool/predefined/_youtube.py +1 -0
  141. waldiez/models/tool/tool.py +8 -5
  142. waldiez/models/tool/tool_data.py +2 -2
  143. waldiez/models/waldiez.py +152 -4
  144. waldiez/runner.py +11 -5
  145. waldiez/running/async_utils.py +192 -0
  146. waldiez/running/base_runner.py +155 -241
  147. waldiez/running/dir_utils.py +52 -0
  148. waldiez/running/environment.py +10 -44
  149. waldiez/running/events_mixin.py +252 -0
  150. waldiez/running/exceptions.py +20 -0
  151. waldiez/running/gen_seq_diagram.py +18 -15
  152. waldiez/running/io_utils.py +216 -0
  153. waldiez/running/protocol.py +11 -5
  154. waldiez/running/requirements_mixin.py +65 -0
  155. waldiez/running/results_mixin.py +926 -0
  156. waldiez/running/standard_runner.py +24 -27
  157. waldiez/running/step_by_step/breakpoints_mixin.py +503 -47
  158. waldiez/running/step_by_step/command_handler.py +154 -0
  159. waldiez/running/step_by_step/events_processor.py +379 -0
  160. waldiez/running/step_by_step/step_by_step_models.py +425 -41
  161. waldiez/running/step_by_step/step_by_step_runner.py +437 -382
  162. waldiez/running/subprocess_runner/__base__.py +13 -8
  163. waldiez/running/subprocess_runner/_async_runner.py +6 -4
  164. waldiez/running/subprocess_runner/_sync_runner.py +11 -6
  165. waldiez/running/subprocess_runner/runner.py +48 -23
  166. waldiez/running/timeline_processor.py +1 -1
  167. waldiez/utils/__init__.py +2 -0
  168. waldiez/utils/conflict_checker.py +4 -4
  169. waldiez/utils/python_manager.py +415 -0
  170. waldiez/ws/__init__.py +8 -7
  171. waldiez/ws/_file_handler.py +18 -20
  172. waldiez/ws/_mock.py +75 -0
  173. waldiez/ws/cli.py +58 -10
  174. waldiez/ws/client_manager.py +77 -53
  175. waldiez/ws/errors.py +3 -0
  176. waldiez/ws/models.py +61 -53
  177. waldiez/ws/reloader.py +33 -4
  178. waldiez/ws/server.py +121 -52
  179. waldiez/ws/session_manager.py +8 -9
  180. waldiez/ws/session_stats.py +1 -1
  181. waldiez/ws/utils.py +33 -5
  182. {waldiez-0.5.10.dist-info → waldiez-0.6.1.dist-info}/METADATA +107 -109
  183. waldiez-0.6.1.dist-info/RECORD +254 -0
  184. waldiez/running/post_run.py +0 -180
  185. waldiez/running/pre_run.py +0 -159
  186. waldiez/running/run_results.py +0 -14
  187. waldiez/running/utils.py +0 -511
  188. waldiez-0.5.10.dist-info/RECORD +0 -248
  189. {waldiez-0.5.10.dist-info → waldiez-0.6.1.dist-info}/WHEEL +0 -0
  190. {waldiez-0.5.10.dist-info → waldiez-0.6.1.dist-info}/entry_points.txt +0 -0
  191. {waldiez-0.5.10.dist-info → waldiez-0.6.1.dist-info}/licenses/LICENSE +0 -0
  192. {waldiez-0.5.10.dist-info → waldiez-0.6.1.dist-info}/licenses/NOTICE.md +0 -0
@@ -2,79 +2,46 @@
2
2
  # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
3
 
4
4
  # pyright: reportUnknownMemberType=false, reportAttributeAccessIssue=false
5
- # pyright: reportUnknownArgumentType=false
5
+ # pyright: reportUnknownArgumentType=false, reportUnusedParameter=false
6
6
  """Base runner for Waldiez workflows."""
7
7
 
8
8
  import importlib.util
9
- import inspect
9
+ import json
10
10
  import shutil
11
11
  import sys
12
12
  import tempfile
13
13
  import threading
14
14
  from pathlib import Path
15
15
  from types import ModuleType, TracebackType
16
- from typing import TYPE_CHECKING, Any, Callable, Coroutine, Type, Union
16
+ from typing import Any
17
17
 
18
18
  from aiofiles.os import wrap
19
19
  from anyio.from_thread import start_blocking_portal
20
- from typing_extensions import Self
20
+ from typing_extensions import Self, override
21
21
 
22
22
  from waldiez.exporter import WaldiezExporter
23
23
  from waldiez.logger import WaldiezLogger, get_logger
24
24
  from waldiez.models import Waldiez
25
25
 
26
- from .environment import refresh_environment, reset_env_vars, set_env_vars
26
+ from .dir_utils import a_chdir, chdir
27
+ from .environment import reset_env_vars, set_env_vars
28
+ from .events_mixin import EventsMixin
27
29
  from .exceptions import StopRunningException
28
- from .post_run import after_run
29
- from .pre_run import RequirementsMixin
30
30
  from .protocol import WaldiezRunnerProtocol
31
- from .utils import (
32
- a_chdir,
33
- chdir,
34
- input_async,
35
- input_sync,
36
- is_async_callable,
37
- syncify,
38
- )
39
-
40
- if TYPE_CHECKING:
41
- from autogen.events import BaseEvent # type: ignore[import-untyped]
42
- from autogen.messages import BaseMessage # type: ignore[import-untyped]
31
+ from .requirements_mixin import RequirementsMixin
32
+ from .results_mixin import ResultsMixin
43
33
 
44
34
 
45
35
  # pylint: disable=too-many-public-methods
46
- class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
47
- """Base runner for Waldiez.
48
-
49
- Initialization parameters:
50
- - waldiez: The Waldiez flow to run.
51
- - output_path: Path to save the output file.
52
- - uploads_root: Root directory for uploads.
53
- - structured_io: Whether to use structured I/O.
54
- - skip_patch_io: Whether to skip patching I/O functions.
55
- - dot_env: Path to a .env file for environment variables.
56
-
57
- Methods to possibly override:
58
- - prepare: Prepare the environment and paths for running the flow.
59
- - _before_run: Actions to perform before running the flow.
60
- - a_prepare: Async version of the prepare method.
61
- - _a_before_run: Async actions to perform before running the flow.
62
- - _run: Actual implementation of the run logic.
63
- - _a_run: Async implementation of the run logic.
64
- - _after_run: Actions to perform after running the flow.
65
- - _a_after_run: Async actions to perform after running the flow.
66
- """
36
+ class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin, ResultsMixin):
37
+ """Base runner for Waldiez."""
67
38
 
68
39
  _structured_io: bool
69
40
  _output_path: str | Path | None
70
41
  _uploads_root: str | Path | None
71
42
  _dot_env_path: str | Path | None
72
- _skip_patch_io: bool
73
43
  _running: bool
74
- _is_async: bool
75
- _input: Callable[..., str] | Callable[..., Coroutine[Any, Any, str]]
76
- _print: Callable[..., None]
77
- _send: Callable[[Union["BaseEvent", "BaseMessage"]], None]
44
+ _waldiez_file: Path
78
45
 
79
46
  def __init__(
80
47
  self,
@@ -82,7 +49,6 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
82
49
  output_path: str | Path | None,
83
50
  uploads_root: str | Path | None,
84
51
  structured_io: bool,
85
- skip_patch_io: bool = False,
86
52
  dot_env: str | Path | None = None,
87
53
  **kwargs: Any,
88
54
  ) -> None:
@@ -91,12 +57,12 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
91
57
  WaldiezBaseRunner._structured_io = structured_io
92
58
  WaldiezBaseRunner._output_path = output_path
93
59
  WaldiezBaseRunner._uploads_root = uploads_root
94
- WaldiezBaseRunner._skip_patch_io = skip_patch_io
95
60
  WaldiezBaseRunner._dot_env_path = dot_env
96
- WaldiezBaseRunner._input = input
97
- WaldiezBaseRunner._print = print
98
- WaldiezBaseRunner._send = print
99
- WaldiezBaseRunner._is_async = waldiez.is_async
61
+ EventsMixin.set_input_function(input)
62
+ EventsMixin.set_print_function(print)
63
+ EventsMixin.set_send_function(print)
64
+ EventsMixin.set_async(waldiez.is_async)
65
+ RequirementsMixin.__init__(self)
100
66
  self._waldiez = waldiez
101
67
  self._called_install_requirements = False
102
68
  self._exporter = WaldiezExporter(waldiez)
@@ -110,100 +76,46 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
110
76
  self._logger = logger
111
77
  else:
112
78
  self._logger = get_logger()
79
+ waldiez_file = kwargs.get("waldiez_file", "")
80
+ if isinstance(waldiez_file, str) and waldiez_file:
81
+ waldiez_file_path = Path(waldiez_file).resolve()
82
+ elif isinstance(waldiez_file, Path):
83
+ waldiez_file_path = waldiez_file.resolve()
84
+ else:
85
+ waldiez_file_path = waldiez.dump(to=output_path)
86
+ if not waldiez_file_path or not waldiez_file_path.is_file():
87
+ raise ValueError("Could not resolve a waldiez file path")
88
+ WaldiezBaseRunner._waldiez_file = waldiez_file_path
113
89
 
114
- def is_running(self) -> bool:
115
- """Check if the workflow is currently running.
116
-
117
- Returns
118
- -------
119
- bool
120
- True if the workflow is running, False otherwise.
121
- """
122
- with self._running_lock:
123
- return WaldiezBaseRunner._running
124
-
125
- @staticmethod
126
- def get_input_function() -> (
127
- Callable[..., str] | Callable[..., Coroutine[Any, Any, str]]
128
- ):
129
- """Get the input function for user interaction.
130
-
131
- Returns
132
- -------
133
- Callable[[str, bool], str]
134
- A function that takes a prompt and a password flag,
135
- returning user input.
136
- """
137
- if hasattr(WaldiezBaseRunner, "_input") and callable(
138
- WaldiezBaseRunner._input
139
- ):
140
- return WaldiezBaseRunner._input
141
- if WaldiezBaseRunner._is_async:
142
- return input_async
143
- return input_sync
144
-
90
+ @override
145
91
  @staticmethod
146
- async def a_get_user_input(prompt: str, *, password: bool = False) -> str:
147
- """Get user input with an optional password prompt.
92
+ def print(*args: Any, **kwargs: Any) -> None:
93
+ """Print a message to the output stream.
148
94
 
149
95
  Parameters
150
96
  ----------
151
- prompt : str
152
- The prompt to display to the user.
153
- password : bool, optional
154
- If True, the input will be hidden (default is False).
155
-
156
- Returns
157
- -------
158
- str
159
- The user input.
97
+ *args : Any
98
+ Positional arguments to print.
99
+ **kwargs : Any
100
+ Keyword arguments to print.
160
101
  """
161
- input_function = WaldiezBaseRunner.get_input_function()
162
- if is_async_callable(input_function):
163
- try:
164
- result = await input_function( # type: ignore
165
- prompt,
166
- password=password,
167
- )
168
- except TypeError:
169
- result = await input_function(prompt) # type: ignore
102
+ if len(args) == 1 and isinstance(args[0], dict):
103
+ arg = json.dumps(args[0], default=str, ensure_ascii=False)
104
+ EventsMixin.do_print(arg, **kwargs)
170
105
  else:
171
- try:
172
- result = input_function(prompt, password=password)
173
- except TypeError:
174
- result = input_function(prompt)
175
- return result # pyright: ignore
176
-
177
- @staticmethod
178
- def get_user_input(
179
- prompt: str,
180
- *,
181
- password: bool = False,
182
- ) -> str:
183
- """Get user input with an optional password prompt.
106
+ EventsMixin.do_print(*args, **kwargs)
184
107
 
185
- Parameters
186
- ----------
187
- prompt : str
188
- The prompt to display to the user.
189
- password : bool, optional
190
- If True, the input will be hidden (default is False).
108
+ @override
109
+ def is_running(self) -> bool:
110
+ """Check if the workflow is currently running.
191
111
 
192
112
  Returns
193
113
  -------
194
- str
195
- The user input.
114
+ bool
115
+ True if the workflow is running, False otherwise.
196
116
  """
197
- input_function = WaldiezBaseRunner.get_input_function()
198
- if inspect.iscoroutinefunction(input_function):
199
- try:
200
- return syncify(input_function)(prompt, password=password)
201
- except TypeError:
202
- return syncify(input_function)(prompt)
203
- try:
204
- return str(input_function(prompt, password=password))
205
- except TypeError:
206
- return str(input_function(prompt))
117
+ with self._running_lock:
118
+ return WaldiezBaseRunner._running
207
119
 
208
120
  def _load_module(self, output_file: Path, temp_dir: Path) -> ModuleType:
209
121
  """Load the module from the waldiez file."""
@@ -230,7 +142,7 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
230
142
  ) -> Path:
231
143
  """Run before the flow execution."""
232
144
  self.log.info("Preparing workflow file: %s", output_file)
233
- temp_dir = Path(tempfile.mkdtemp())
145
+ temp_dir = Path(tempfile.mkdtemp(prefix="wlz-"))
234
146
  file_name = output_file.name
235
147
  with chdir(to=temp_dir):
236
148
  self._exporter.export(
@@ -300,12 +212,14 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
300
212
  def _after_run(
301
213
  self,
302
214
  results: list[dict[str, Any]],
215
+ error: BaseException | None,
303
216
  output_file: Path,
217
+ waldiez_file: Path,
304
218
  uploads_root: Path | None,
305
219
  temp_dir: Path,
306
220
  skip_mmd: bool,
307
221
  skip_timeline: bool,
308
- ) -> None:
222
+ ) -> Path | None:
309
223
  """Run after the flow execution."""
310
224
  # Save results
311
225
  self._last_results = results
@@ -314,10 +228,13 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
314
228
  self._stop_requested.clear()
315
229
  # pylint: disable=broad-exception-caught
316
230
  try:
317
- after_run(
231
+ return ResultsMixin.post_run(
232
+ results=results,
233
+ error=error,
318
234
  temp_dir=temp_dir,
319
235
  output_file=output_file,
320
236
  flow_name=self._waldiez.name,
237
+ waldiez_file=waldiez_file,
321
238
  uploads_root=uploads_root,
322
239
  skip_mmd=skip_mmd,
323
240
  skip_timeline=skip_timeline,
@@ -325,25 +242,39 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
325
242
  except BaseException as exc: # pragma: no cover
326
243
  self.log.warning("Error occurred during after_run: %s", exc)
327
244
  self.log.info("Cleanup completed")
245
+ return None
328
246
 
329
247
  async def _a_after_run(
330
248
  self,
331
249
  results: list[dict[str, Any]],
250
+ error: BaseException | None,
332
251
  output_file: Path,
252
+ waldiez_file: Path,
333
253
  uploads_root: Path | None,
334
254
  temp_dir: Path,
335
255
  skip_mmd: bool,
336
256
  skip_timeline: bool,
337
- ) -> None:
257
+ ) -> Path | None:
338
258
  """Run after the flow execution asynchronously."""
339
- self._after_run(
340
- results=results,
341
- output_file=output_file,
342
- uploads_root=uploads_root,
343
- temp_dir=temp_dir,
344
- skip_mmd=skip_mmd,
345
- skip_timeline=skip_timeline,
346
- )
259
+ self._last_results = results
260
+ self._stop_requested.clear()
261
+ # pylint: disable=broad-exception-caught
262
+ try:
263
+ return await ResultsMixin.a_post_run(
264
+ results=results,
265
+ error=error,
266
+ temp_dir=temp_dir,
267
+ output_file=output_file,
268
+ flow_name=self._waldiez.name,
269
+ waldiez_file=waldiez_file,
270
+ uploads_root=uploads_root,
271
+ skip_mmd=skip_mmd,
272
+ skip_timeline=skip_timeline,
273
+ )
274
+ except BaseException as exc: # pragma: no cover
275
+ self.log.warning("Error occurred during a_after_run: %s", exc)
276
+ self.log.info("Cleanup completed")
277
+ return None
347
278
 
348
279
  @staticmethod
349
280
  def _prepare_paths(
@@ -364,58 +295,7 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
364
295
  output_file: Path = Path(WaldiezBaseRunner._output_path)
365
296
  return output_file, uploads_root_path
366
297
 
367
- @staticmethod
368
- async def a_process_event(event: Union["BaseEvent", "BaseMessage"]) -> None:
369
- """Process an event or message asynchronously.
370
-
371
- Parameters
372
- ----------
373
- event : Union[BaseEvent, BaseMessage]
374
- The event or message to process.
375
- """
376
- if hasattr(event, "type"): # pragma: no branch
377
- if event.type == "input_request":
378
- prompt = getattr(
379
- event, "prompt", getattr(event.content, "prompt", "> ")
380
- )
381
- password = getattr(
382
- event,
383
- "password",
384
- getattr(event.content, "password", False),
385
- )
386
- user_input = await WaldiezBaseRunner.a_get_user_input(
387
- prompt, password=password
388
- )
389
- await event.content.respond(user_input)
390
- else:
391
- WaldiezBaseRunner._send(event)
392
-
393
- @staticmethod
394
- def process_event(event: Union["BaseEvent", "BaseMessage"]) -> None:
395
- """Process an event or message synchronously.
396
-
397
- Parameters
398
- ----------
399
- event : Union[BaseEvent, BaseMessage]
400
- The event or message to process.
401
- """
402
- if hasattr(event, "type"): # pragma: no branch
403
- if event.type == "input_request":
404
- prompt = getattr(
405
- event, "prompt", getattr(event.content, "prompt", "> ")
406
- )
407
- password = getattr(
408
- event,
409
- "password",
410
- getattr(event.content, "password", False),
411
- )
412
- user_input = WaldiezBaseRunner.get_user_input(
413
- prompt, password=password
414
- )
415
- event.content.respond(user_input)
416
- else:
417
- WaldiezBaseRunner._send(event)
418
-
298
+ @override
419
299
  def before_run(
420
300
  self,
421
301
  output_file: Path,
@@ -440,6 +320,7 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
440
320
  uploads_root=uploads_root,
441
321
  )
442
322
 
323
+ @override
443
324
  async def a_before_run(
444
325
  self,
445
326
  output_file: Path,
@@ -495,11 +376,11 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
495
376
  uploads_root=uploads_root_path,
496
377
  )
497
378
  self.install_requirements()
498
- refresh_environment()
499
379
  return temp_dir, output_file, uploads_root_path
500
380
 
501
381
  # noinspection PyProtocol
502
382
  # pylint: disable=too-many-locals,unused-argument
383
+ @override
503
384
  def run(
504
385
  self,
505
386
  output_path: str | Path | None = None,
@@ -564,8 +445,10 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
564
445
  uploads_root=uploads_root,
565
446
  )
566
447
  WaldiezBaseRunner._running = True
567
- results: list[dict[str, Any]]
448
+ results: list[dict[str, Any]] = []
449
+ error: BaseException | None = None
568
450
  old_env_vars = set_env_vars(self._waldiez.get_flow_env_vars())
451
+ output_dir = output_file.parent
569
452
  try:
570
453
  with chdir(to=temp_dir):
571
454
  sys.path.insert(0, str(temp_dir))
@@ -577,25 +460,30 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
577
460
  skip_timeline=skip_timeline,
578
461
  )
579
462
  except (SystemExit, StopRunningException, KeyboardInterrupt) as exc:
463
+ error = exc
464
+ self.log.warning("Execution stopped: %s", exc)
580
465
  raise StopRunningException(StopRunningException.reason) from exc
581
466
  except BaseException as exc: # pylint: disable=broad-exception-caught
582
467
  self.log.error("Error occurred while running workflow: %s", exc)
583
- results = [{"error": str(exc)}]
468
+ error = exc
584
469
  finally:
585
470
  WaldiezBaseRunner._running = False
586
471
  reset_env_vars(old_env_vars)
587
- self.after_run(
588
- results=results,
589
- output_file=output_file,
590
- uploads_root=uploads_root_path,
591
- temp_dir=temp_dir,
592
- skip_mmd=skip_mmd,
593
- skip_timeline=skip_timeline,
594
- )
595
- self._print("<Waldiez> - Done running the flow.")
472
+ output = self.after_run(
473
+ results=results,
474
+ error=error,
475
+ output_file=output_file,
476
+ uploads_root=uploads_root_path,
477
+ temp_dir=temp_dir,
478
+ skip_mmd=skip_mmd,
479
+ skip_timeline=skip_timeline,
480
+ )
481
+ if output:
482
+ output_dir = output
483
+ EventsMixin.do_print("<Waldiez> - Done running the flow.")
596
484
  if sys.path[0] == str(temp_dir):
597
485
  sys.path.pop(0)
598
- return results
486
+ return self.get_results(results, output_dir)
599
487
 
600
488
  async def a_prepare(
601
489
  self,
@@ -625,11 +513,11 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
625
513
  uploads_root=uploads_root_path,
626
514
  )
627
515
  await self.a_install_requirements()
628
- refresh_environment()
629
516
  return temp_dir, output_file, uploads_root_path
630
517
 
631
518
  # noinspection DuplicatedCode
632
519
  # noinspection PyProtocol
520
+ @override
633
521
  async def a_run(
634
522
  self,
635
523
  output_path: str | Path | None = None,
@@ -685,7 +573,9 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
685
573
  uploads_root=uploads_root,
686
574
  )
687
575
  WaldiezBaseRunner._running = True
688
- results: list[dict[str, Any]]
576
+ results: list[dict[str, Any]] = []
577
+ error: BaseException | None = None
578
+ output_dir = output_file.parent
689
579
  old_env_vars = set_env_vars(self._waldiez.get_flow_env_vars())
690
580
  try:
691
581
  async with a_chdir(to=temp_dir):
@@ -698,39 +588,49 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
698
588
  skip_timeline=skip_timeline,
699
589
  )
700
590
  except (SystemExit, StopRunningException, KeyboardInterrupt) as exc:
591
+ self.log.warning("Execution stopped: %s", exc)
592
+ error = exc
701
593
  raise StopRunningException(StopRunningException.reason) from exc
702
594
  except BaseException as exc: # pylint: disable=broad-exception-caught
703
- results = [{"error": str(exc)}]
595
+ self.log.error("Error occurred while running workflow: %s", exc)
596
+ error = exc
704
597
  finally:
705
598
  WaldiezBaseRunner._running = False
706
599
  reset_env_vars(old_env_vars)
707
- await self._a_after_run(
708
- results=results,
709
- output_file=output_file,
710
- uploads_root=uploads_root_path,
711
- temp_dir=temp_dir,
712
- skip_mmd=skip_mmd,
713
- skip_timeline=skip_timeline,
714
- )
600
+ output = await self.a_after_run(
601
+ results=results,
602
+ error=error,
603
+ output_file=output_file,
604
+ uploads_root=uploads_root_path,
605
+ temp_dir=temp_dir,
606
+ skip_mmd=skip_mmd,
607
+ skip_timeline=skip_timeline,
608
+ )
609
+ if output:
610
+ output_dir = output
715
611
  if sys.path[0] == str(temp_dir):
716
612
  sys.path.pop(0)
717
- return results
613
+ return await self.a_get_results(results, output_dir)
718
614
 
615
+ @override
719
616
  def after_run(
720
617
  self,
721
618
  results: list[dict[str, Any]],
619
+ error: BaseException | None,
722
620
  output_file: Path,
723
621
  uploads_root: Path | None,
724
622
  temp_dir: Path,
725
623
  skip_mmd: bool,
726
624
  skip_timeline: bool,
727
- ) -> None:
625
+ ) -> Path | None:
728
626
  """Actions to perform after running the flow.
729
627
 
730
628
  Parameters
731
629
  ----------
732
630
  results : list[dict[str, Any]]
733
631
  The results of the flow run.
632
+ error : BaseException | None
633
+ Optional error during the run.
734
634
  output_file : Path
735
635
  The path to the output file.
736
636
  uploads_root : Path | None
@@ -741,31 +641,42 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
741
641
  Whether to skip generating the mermaid diagram.
742
642
  skip_timeline : bool
743
643
  Whether to skip generating the timeline JSON.
644
+
645
+ Returns
646
+ -------
647
+ Path | None
648
+ The destination directory if output file, else None
744
649
  """
745
- self._after_run(
650
+ return self._after_run(
746
651
  results=results,
652
+ error=error,
747
653
  output_file=output_file,
654
+ waldiez_file=WaldiezBaseRunner._waldiez_file,
748
655
  uploads_root=uploads_root,
749
656
  temp_dir=temp_dir,
750
657
  skip_mmd=skip_mmd,
751
658
  skip_timeline=skip_timeline,
752
659
  )
753
660
 
661
+ @override
754
662
  async def a_after_run(
755
663
  self,
756
664
  results: list[dict[str, Any]],
665
+ error: BaseException | None,
757
666
  output_file: Path,
758
667
  uploads_root: Path | None,
759
668
  temp_dir: Path,
760
669
  skip_mmd: bool,
761
670
  skip_timeline: bool,
762
- ) -> None:
671
+ ) -> Path | None:
763
672
  """Asynchronously perform actions after running the flow.
764
673
 
765
674
  Parameters
766
675
  ----------
767
676
  results : list[dict[str, Any]]
768
677
  The results of the flow run.
678
+ error : BaseException | None
679
+ Optional error during the run.
769
680
  output_file : Path
770
681
  The path to the output file.
771
682
  uploads_root : Path | None
@@ -776,14 +687,20 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
776
687
  Whether to skip generating the mermaid diagram.
777
688
  skip_timeline : bool
778
689
 
690
+ Returns
691
+ -------
692
+ Path | None
693
+ The destination directory if output file, else None
779
694
  """
780
- await self._a_after_run(
695
+ return await self._a_after_run(
781
696
  results=results,
697
+ error=error,
782
698
  output_file=output_file,
783
699
  uploads_root=uploads_root,
784
700
  temp_dir=temp_dir,
785
701
  skip_mmd=skip_mmd,
786
702
  skip_timeline=skip_timeline,
703
+ waldiez_file=WaldiezBaseRunner._waldiez_file,
787
704
  )
788
705
 
789
706
  @property
@@ -791,6 +708,11 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
791
708
  """Get the Waldiez instance."""
792
709
  return self._waldiez
793
710
 
711
+ @property
712
+ def waldiez_file(self) -> Path:
713
+ """Get the path to the waldiez file."""
714
+ return self._waldiez_file
715
+
794
716
  @property
795
717
  def is_async(self) -> bool:
796
718
  """Check if the workflow is async."""
@@ -826,11 +748,6 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
826
748
  """Get the uploads root path for the runner."""
827
749
  return WaldiezBaseRunner._uploads_root
828
750
 
829
- @property
830
- def skip_patch_io(self) -> bool:
831
- """Check if the runner is skipping patching IO."""
832
- return WaldiezBaseRunner._skip_patch_io
833
-
834
751
  @classmethod
835
752
  def load(
836
753
  cls,
@@ -889,14 +806,7 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
889
806
  )
890
807
 
891
808
  def stop(self) -> None:
892
- """Stop the workflow execution.
893
-
894
- This method sets the stop flag that will be checked by the event
895
- handlers and other parts of the workflow execution to gracefully
896
- terminate the workflow execution.
897
- Note: Stopping will occur at the "next" AutoGen event, not immediately.
898
- """
899
- self.log.info("Stop requested - setting stop flag")
809
+ """Stop the workflow execution."""
900
810
  self._stop_requested.set()
901
811
 
902
812
  def is_stop_requested(self) -> bool:
@@ -909,6 +819,10 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
909
819
  """
910
820
  return self._stop_requested.is_set()
911
821
 
822
+ def set_stop_requested(self) -> None:
823
+ """Set the stop requested flag."""
824
+ self._stop_requested.set()
825
+
912
826
  def __enter__(self) -> Self:
913
827
  """Enter the context manager."""
914
828
  return self
@@ -919,7 +833,7 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
919
833
 
920
834
  def __exit__(
921
835
  self,
922
- exc_type: Type[BaseException],
836
+ exc_type: type[BaseException],
923
837
  exc_value: BaseException,
924
838
  traceback: TracebackType,
925
839
  ) -> None:
@@ -929,7 +843,7 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
929
843
 
930
844
  async def __aexit__(
931
845
  self,
932
- exc_type: Type[BaseException],
846
+ exc_type: type[BaseException],
933
847
  exc_value: BaseException,
934
848
  traceback: TracebackType,
935
849
  ) -> None: