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.
- waldiez/_version.py +1 -1
- waldiez/cli.py +113 -24
- waldiez/exporting/agent/exporter.py +9 -6
- waldiez/exporting/agent/extras/captain_agent_extras.py +44 -7
- waldiez/exporting/agent/extras/group_manager_agent_extas.py +6 -1
- waldiez/exporting/agent/extras/handoffs/after_work.py +1 -0
- waldiez/exporting/agent/extras/handoffs/available.py +1 -0
- waldiez/exporting/agent/extras/handoffs/condition.py +3 -1
- waldiez/exporting/agent/extras/handoffs/handoff.py +1 -0
- waldiez/exporting/agent/extras/handoffs/target.py +1 -0
- waldiez/exporting/agent/termination.py +1 -0
- waldiez/exporting/chats/utils/common.py +25 -23
- waldiez/exporting/core/__init__.py +0 -2
- waldiez/exporting/core/constants.py +3 -1
- waldiez/exporting/core/context.py +13 -13
- waldiez/exporting/core/extras/serializer.py +12 -10
- waldiez/exporting/core/protocols.py +0 -141
- waldiez/exporting/core/result.py +5 -5
- waldiez/exporting/core/types.py +1 -0
- waldiez/exporting/core/utils/llm_config.py +2 -2
- waldiez/exporting/flow/execution_generator.py +1 -0
- waldiez/exporting/flow/merger.py +2 -2
- waldiez/exporting/flow/orchestrator.py +1 -0
- waldiez/exporting/flow/utils/common.py +3 -3
- waldiez/exporting/flow/utils/importing.py +1 -0
- waldiez/exporting/flow/utils/logging.py +7 -80
- waldiez/exporting/tools/exporter.py +5 -0
- waldiez/exporting/tools/factory.py +4 -0
- waldiez/exporting/tools/processor.py +5 -1
- waldiez/io/__init__.py +3 -1
- waldiez/io/_ws.py +15 -5
- waldiez/io/models/content/image.py +1 -0
- waldiez/io/models/user_input.py +4 -4
- waldiez/io/models/user_response.py +1 -0
- waldiez/io/mqtt.py +1 -1
- waldiez/io/structured.py +98 -45
- waldiez/io/utils.py +17 -11
- waldiez/io/ws.py +10 -12
- waldiez/logger.py +180 -63
- waldiez/models/agents/agent/agent.py +2 -1
- waldiez/models/agents/agent/update_system_message.py +0 -2
- waldiez/models/agents/doc_agent/doc_agent.py +8 -1
- waldiez/models/chat/chat.py +1 -0
- waldiez/models/chat/chat_data.py +0 -2
- waldiez/models/common/base.py +2 -0
- waldiez/models/common/dict_utils.py +169 -40
- waldiez/models/common/handoff.py +2 -0
- waldiez/models/common/method_utils.py +2 -0
- waldiez/models/flow/flow.py +6 -6
- waldiez/models/flow/info.py +5 -1
- waldiez/models/model/_llm.py +31 -14
- waldiez/models/model/model.py +4 -1
- waldiez/models/model/model_data.py +18 -5
- waldiez/models/tool/predefined/_config.py +5 -1
- waldiez/models/tool/predefined/_duckduckgo.py +4 -0
- waldiez/models/tool/predefined/_email.py +477 -0
- waldiez/models/tool/predefined/_google.py +4 -1
- waldiez/models/tool/predefined/_perplexity.py +4 -1
- waldiez/models/tool/predefined/_searxng.py +4 -1
- waldiez/models/tool/predefined/_tavily.py +4 -1
- waldiez/models/tool/predefined/_wikipedia.py +5 -2
- waldiez/models/tool/predefined/_youtube.py +4 -1
- waldiez/models/tool/predefined/protocol.py +3 -0
- waldiez/models/tool/tool.py +22 -4
- waldiez/models/waldiez.py +12 -0
- waldiez/runner.py +37 -54
- waldiez/running/__init__.py +6 -0
- waldiez/running/base_runner.py +381 -363
- waldiez/running/environment.py +1 -0
- waldiez/running/exceptions.py +9 -0
- waldiez/running/post_run.py +10 -4
- waldiez/running/pre_run.py +199 -66
- waldiez/running/protocol.py +21 -101
- waldiez/running/run_results.py +1 -1
- waldiez/running/standard_runner.py +83 -276
- waldiez/running/step_by_step/__init__.py +46 -0
- waldiez/running/step_by_step/breakpoints_mixin.py +512 -0
- waldiez/running/step_by_step/command_handler.py +151 -0
- waldiez/running/step_by_step/events_processor.py +199 -0
- waldiez/running/step_by_step/step_by_step_models.py +541 -0
- waldiez/running/step_by_step/step_by_step_runner.py +750 -0
- waldiez/running/subprocess_runner/__base__.py +279 -0
- waldiez/running/subprocess_runner/__init__.py +16 -0
- waldiez/running/subprocess_runner/_async_runner.py +362 -0
- waldiez/running/subprocess_runner/_sync_runner.py +456 -0
- waldiez/running/subprocess_runner/runner.py +570 -0
- waldiez/running/timeline_processor.py +1 -1
- waldiez/running/utils.py +492 -3
- waldiez/utils/version.py +2 -6
- waldiez/ws/__init__.py +71 -0
- waldiez/ws/__main__.py +15 -0
- waldiez/ws/_file_handler.py +199 -0
- waldiez/ws/_mock.py +74 -0
- waldiez/ws/cli.py +235 -0
- waldiez/ws/client_manager.py +851 -0
- waldiez/ws/errors.py +416 -0
- waldiez/ws/models.py +988 -0
- waldiez/ws/reloader.py +363 -0
- waldiez/ws/server.py +508 -0
- waldiez/ws/session_manager.py +393 -0
- waldiez/ws/session_stats.py +83 -0
- waldiez/ws/utils.py +410 -0
- {waldiez-0.5.9.dist-info → waldiez-0.6.0.dist-info}/METADATA +105 -96
- {waldiez-0.5.9.dist-info → waldiez-0.6.0.dist-info}/RECORD +108 -83
- waldiez/running/patch_io_stream.py +0 -210
- {waldiez-0.5.9.dist-info → waldiez-0.6.0.dist-info}/WHEEL +0 -0
- {waldiez-0.5.9.dist-info → waldiez-0.6.0.dist-info}/entry_points.txt +0 -0
- {waldiez-0.5.9.dist-info → waldiez-0.6.0.dist-info}/licenses/LICENSE +0 -0
- {waldiez-0.5.9.dist-info → waldiez-0.6.0.dist-info}/licenses/NOTICE.md +0 -0
waldiez/running/base_runner.py
CHANGED
|
@@ -1,26 +1,20 @@
|
|
|
1
1
|
# SPDX-License-Identifier: Apache-2.0.
|
|
2
2
|
# Copyright (c) 2024 - 2025 Waldiez and contributors.
|
|
3
3
|
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
# pylint: disable=too-many-public-methods,too-many-locals
|
|
7
|
-
|
|
4
|
+
# pyright: reportUnknownMemberType=false, reportAttributeAccessIssue=false
|
|
5
|
+
# pyright: reportUnknownArgumentType=false
|
|
8
6
|
"""Base runner for Waldiez workflows."""
|
|
9
7
|
|
|
8
|
+
import importlib.util
|
|
9
|
+
import inspect
|
|
10
|
+
import json
|
|
10
11
|
import shutil
|
|
11
12
|
import sys
|
|
12
13
|
import tempfile
|
|
13
14
|
import threading
|
|
14
15
|
from pathlib import Path
|
|
15
|
-
from types import TracebackType
|
|
16
|
-
from typing import
|
|
17
|
-
TYPE_CHECKING,
|
|
18
|
-
Any,
|
|
19
|
-
Callable,
|
|
20
|
-
Coroutine,
|
|
21
|
-
Optional,
|
|
22
|
-
Type,
|
|
23
|
-
)
|
|
16
|
+
from types import ModuleType, TracebackType
|
|
17
|
+
from typing import TYPE_CHECKING, Any, Callable, Coroutine, Type, Union
|
|
24
18
|
|
|
25
19
|
from aiofiles.os import wrap
|
|
26
20
|
from anyio.from_thread import start_blocking_portal
|
|
@@ -31,22 +25,31 @@ from waldiez.logger import WaldiezLogger, get_logger
|
|
|
31
25
|
from waldiez.models import Waldiez
|
|
32
26
|
|
|
33
27
|
from .environment import refresh_environment, reset_env_vars, set_env_vars
|
|
28
|
+
from .exceptions import StopRunningException
|
|
34
29
|
from .post_run import after_run
|
|
35
|
-
from .pre_run import
|
|
36
|
-
a_install_requirements,
|
|
37
|
-
install_requirements,
|
|
38
|
-
)
|
|
30
|
+
from .pre_run import RequirementsMixin, dump_waldiez
|
|
39
31
|
from .protocol import WaldiezRunnerProtocol
|
|
40
32
|
from .utils import (
|
|
41
33
|
a_chdir,
|
|
42
34
|
chdir,
|
|
35
|
+
input_async,
|
|
36
|
+
input_sync,
|
|
37
|
+
is_async_callable,
|
|
38
|
+
syncify,
|
|
43
39
|
)
|
|
44
40
|
|
|
45
41
|
if TYPE_CHECKING:
|
|
46
|
-
|
|
42
|
+
# pylint: disable=line-too-long
|
|
43
|
+
from autogen.events import ( # type: ignore[import-untyped,import-not-found,unused-ignore] # noqa: E501
|
|
44
|
+
BaseEvent,
|
|
45
|
+
)
|
|
46
|
+
from autogen.messages import ( # type: ignore[import-untyped,import-not-found,unused-ignore] # noqa: E501
|
|
47
|
+
BaseMessage,
|
|
48
|
+
)
|
|
47
49
|
|
|
48
50
|
|
|
49
|
-
|
|
51
|
+
# pylint: disable=too-many-public-methods
|
|
52
|
+
class WaldiezBaseRunner(WaldiezRunnerProtocol, RequirementsMixin):
|
|
50
53
|
"""Base runner for Waldiez.
|
|
51
54
|
|
|
52
55
|
Initialization parameters:
|
|
@@ -54,28 +57,29 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol):
|
|
|
54
57
|
- output_path: Path to save the output file.
|
|
55
58
|
- uploads_root: Root directory for uploads.
|
|
56
59
|
- structured_io: Whether to use structured I/O.
|
|
57
|
-
- skip_patch_io: Whether to skip patching I/O functions.
|
|
58
60
|
- dot_env: Path to a .env file for environment variables.
|
|
59
61
|
|
|
60
|
-
Methods to override:
|
|
62
|
+
Methods to possibly override:
|
|
63
|
+
- prepare: Prepare the environment and paths for running the flow.
|
|
61
64
|
- _before_run: Actions to perform before running the flow.
|
|
65
|
+
- a_prepare: Async version of the prepare method.
|
|
62
66
|
- _a_before_run: Async actions to perform before running the flow.
|
|
63
67
|
- _run: Actual implementation of the run logic.
|
|
64
68
|
- _a_run: Async implementation of the run logic.
|
|
65
69
|
- _after_run: Actions to perform after running the flow.
|
|
66
70
|
- _a_after_run: Async actions to perform after running the flow.
|
|
67
|
-
- _start: Implementation of non-blocking start logic.
|
|
68
|
-
- _a_start: Async implementation of non-blocking start logic.
|
|
69
|
-
- _stop: Actions to perform when stopping the flow.
|
|
70
|
-
- _a_stop: Async actions to perform when stopping the flow.
|
|
71
71
|
"""
|
|
72
72
|
|
|
73
73
|
_structured_io: bool
|
|
74
74
|
_output_path: str | Path | None
|
|
75
75
|
_uploads_root: str | Path | None
|
|
76
76
|
_dot_env_path: str | Path | None
|
|
77
|
-
_skip_patch_io: bool
|
|
78
77
|
_running: bool
|
|
78
|
+
_is_async: bool
|
|
79
|
+
_waldiez_file: Path
|
|
80
|
+
_input: Callable[..., str] | Callable[..., Coroutine[Any, Any, str]]
|
|
81
|
+
_print: Callable[..., None]
|
|
82
|
+
_send: Callable[[Union["BaseEvent", "BaseMessage"]], None]
|
|
79
83
|
|
|
80
84
|
def __init__(
|
|
81
85
|
self,
|
|
@@ -83,30 +87,59 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol):
|
|
|
83
87
|
output_path: str | Path | None,
|
|
84
88
|
uploads_root: str | Path | None,
|
|
85
89
|
structured_io: bool,
|
|
86
|
-
skip_patch_io: bool = False,
|
|
87
90
|
dot_env: str | Path | None = None,
|
|
91
|
+
**kwargs: Any,
|
|
88
92
|
) -> None:
|
|
89
93
|
"""Initialize the Waldiez manager."""
|
|
90
|
-
self._waldiez = waldiez
|
|
91
94
|
WaldiezBaseRunner._running = False
|
|
92
95
|
WaldiezBaseRunner._structured_io = structured_io
|
|
93
96
|
WaldiezBaseRunner._output_path = output_path
|
|
94
97
|
WaldiezBaseRunner._uploads_root = uploads_root
|
|
95
|
-
WaldiezBaseRunner._skip_patch_io = skip_patch_io
|
|
96
98
|
WaldiezBaseRunner._dot_env_path = dot_env
|
|
99
|
+
WaldiezBaseRunner._input = input
|
|
100
|
+
WaldiezBaseRunner._print = print
|
|
101
|
+
WaldiezBaseRunner._send = print
|
|
102
|
+
WaldiezBaseRunner._is_async = waldiez.is_async
|
|
103
|
+
self._waldiez = waldiez
|
|
97
104
|
self._called_install_requirements = False
|
|
98
105
|
self._exporter = WaldiezExporter(waldiez)
|
|
99
106
|
self._stop_requested = threading.Event()
|
|
100
|
-
self._logger = get_logger()
|
|
101
|
-
self._print: Callable[..., None] = print
|
|
102
|
-
self._send: Callable[["BaseEvent"], None] = self._print
|
|
103
|
-
self._input: (
|
|
104
|
-
Callable[..., str] | Callable[..., Coroutine[Any, Any, str]]
|
|
105
|
-
) = input
|
|
106
107
|
self._last_results: list[dict[str, Any]] = []
|
|
107
108
|
self._last_exception: Exception | None = None
|
|
108
|
-
self.
|
|
109
|
-
self.
|
|
109
|
+
self._running_lock = threading.Lock()
|
|
110
|
+
self._loaded_module: ModuleType | None = None
|
|
111
|
+
logger = kwargs.get("logger")
|
|
112
|
+
if isinstance(logger, WaldiezLogger):
|
|
113
|
+
self._logger = logger
|
|
114
|
+
else:
|
|
115
|
+
self._logger = get_logger()
|
|
116
|
+
waldiez_file = kwargs.get("waldiez_file", "")
|
|
117
|
+
if isinstance(waldiez_file, str) and waldiez_file:
|
|
118
|
+
waldiez_file_path = Path(waldiez_file).resolve()
|
|
119
|
+
elif isinstance(waldiez_file, Path):
|
|
120
|
+
waldiez_file_path = waldiez_file.resolve()
|
|
121
|
+
else:
|
|
122
|
+
waldiez_file_path = dump_waldiez(waldiez, output_path=output_path)
|
|
123
|
+
if not waldiez_file_path or not waldiez_file_path.is_file():
|
|
124
|
+
raise ValueError("Could not resolve a waldiez file path")
|
|
125
|
+
WaldiezBaseRunner._waldiez_file = waldiez_file_path
|
|
126
|
+
|
|
127
|
+
@staticmethod
|
|
128
|
+
def print(*args: Any, **kwargs: Any) -> None:
|
|
129
|
+
"""Print a message to the console.
|
|
130
|
+
|
|
131
|
+
Parameters
|
|
132
|
+
----------
|
|
133
|
+
*args : Any
|
|
134
|
+
Positional arguments to print.
|
|
135
|
+
**kwargs : Any
|
|
136
|
+
Keyword arguments to print.
|
|
137
|
+
"""
|
|
138
|
+
if len(args) == 1 and isinstance(args[0], dict):
|
|
139
|
+
arg = json.dumps(args[0], default=str, ensure_ascii=False)
|
|
140
|
+
WaldiezBaseRunner._print(arg, **kwargs)
|
|
141
|
+
else:
|
|
142
|
+
WaldiezBaseRunner._print(*args, **kwargs)
|
|
110
143
|
|
|
111
144
|
def is_running(self) -> bool:
|
|
112
145
|
"""Check if the workflow is currently running.
|
|
@@ -116,65 +149,119 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol):
|
|
|
116
149
|
bool
|
|
117
150
|
True if the workflow is running, False otherwise.
|
|
118
151
|
"""
|
|
119
|
-
|
|
152
|
+
with self._running_lock:
|
|
153
|
+
return WaldiezBaseRunner._running
|
|
120
154
|
|
|
121
|
-
|
|
122
|
-
|
|
155
|
+
@staticmethod
|
|
156
|
+
def get_input_function() -> (
|
|
157
|
+
Callable[..., str] | Callable[..., Coroutine[Any, Any, str]]
|
|
158
|
+
):
|
|
159
|
+
"""Get the input function for user interaction.
|
|
123
160
|
|
|
124
|
-
|
|
161
|
+
Returns
|
|
162
|
+
-------
|
|
163
|
+
Callable[[str, bool], str]
|
|
164
|
+
A function that takes a prompt and a password flag,
|
|
165
|
+
returning user input.
|
|
166
|
+
"""
|
|
167
|
+
if hasattr(WaldiezBaseRunner, "_input") and callable(
|
|
168
|
+
WaldiezBaseRunner._input
|
|
169
|
+
):
|
|
170
|
+
return WaldiezBaseRunner._input
|
|
171
|
+
if WaldiezBaseRunner._is_async:
|
|
172
|
+
return input_async
|
|
173
|
+
return input_sync
|
|
174
|
+
|
|
175
|
+
@staticmethod
|
|
176
|
+
async def a_get_user_input(
|
|
177
|
+
prompt: str, *, password: bool = False, **kwargs: Any
|
|
178
|
+
) -> str:
|
|
179
|
+
"""Get user input with an optional password prompt.
|
|
125
180
|
|
|
126
181
|
Parameters
|
|
127
182
|
----------
|
|
128
|
-
|
|
129
|
-
The
|
|
130
|
-
|
|
183
|
+
prompt : str
|
|
184
|
+
The prompt to display to the user.
|
|
185
|
+
password : bool, optional
|
|
186
|
+
If True, the input will be hidden (default is False).
|
|
187
|
+
**kwargs : Any
|
|
188
|
+
Additional keyword arguments to pass to the input function.
|
|
131
189
|
|
|
132
190
|
Returns
|
|
133
191
|
-------
|
|
134
|
-
|
|
135
|
-
|
|
192
|
+
str
|
|
193
|
+
The user input.
|
|
136
194
|
"""
|
|
137
|
-
|
|
138
|
-
if
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
195
|
+
input_function = WaldiezBaseRunner.get_input_function()
|
|
196
|
+
if is_async_callable(input_function):
|
|
197
|
+
try:
|
|
198
|
+
result = await input_function( # type: ignore
|
|
199
|
+
prompt,
|
|
200
|
+
password=password,
|
|
201
|
+
**kwargs,
|
|
202
|
+
)
|
|
203
|
+
except TypeError:
|
|
204
|
+
result = await input_function(prompt) # type: ignore
|
|
205
|
+
else:
|
|
206
|
+
try:
|
|
207
|
+
result = input_function(prompt, password=password, **kwargs)
|
|
208
|
+
except TypeError:
|
|
209
|
+
result = input_function(prompt)
|
|
210
|
+
return result # pyright: ignore
|
|
144
211
|
|
|
145
|
-
|
|
212
|
+
@staticmethod
|
|
213
|
+
def get_user_input(
|
|
214
|
+
prompt: str,
|
|
215
|
+
*,
|
|
216
|
+
password: bool = False,
|
|
217
|
+
**kwargs: Any,
|
|
218
|
+
) -> str:
|
|
219
|
+
"""Get user input with an optional password prompt.
|
|
146
220
|
|
|
147
|
-
|
|
148
|
-
|
|
221
|
+
Parameters
|
|
222
|
+
----------
|
|
223
|
+
prompt : str
|
|
224
|
+
The prompt to display to the user.
|
|
225
|
+
password : bool, optional
|
|
226
|
+
If True, the input will be hidden (default is False).
|
|
227
|
+
**kwargs : Any
|
|
228
|
+
Additional keyword arguments to pass to the input function.
|
|
149
229
|
|
|
150
230
|
Returns
|
|
151
231
|
-------
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
- is_running: Whether the runner is currently running.
|
|
155
|
-
- has_thread: Whether there is an execution thread.
|
|
156
|
-
- thread_alive: Whether the execution thread is alive.
|
|
157
|
-
- has_error: Whether there was an error during execution.
|
|
232
|
+
str
|
|
233
|
+
The user input.
|
|
158
234
|
"""
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
235
|
+
input_function = WaldiezBaseRunner.get_input_function()
|
|
236
|
+
if inspect.iscoroutinefunction(input_function):
|
|
237
|
+
try:
|
|
238
|
+
return syncify(input_function)(
|
|
239
|
+
prompt, password=password, **kwargs
|
|
240
|
+
)
|
|
241
|
+
except TypeError:
|
|
242
|
+
return syncify(input_function)(prompt)
|
|
243
|
+
try:
|
|
244
|
+
return str(input_function(prompt, password=password, **kwargs))
|
|
245
|
+
except TypeError:
|
|
246
|
+
return str(input_function(prompt))
|
|
247
|
+
|
|
248
|
+
def _load_module(self, output_file: Path, temp_dir: Path) -> ModuleType:
|
|
249
|
+
"""Load the module from the waldiez file."""
|
|
250
|
+
file_name = output_file.name
|
|
251
|
+
module_name = file_name.replace(".py", "")
|
|
252
|
+
spec = importlib.util.spec_from_file_location(
|
|
253
|
+
module_name, temp_dir / file_name
|
|
254
|
+
)
|
|
255
|
+
if not spec or not spec.loader:
|
|
256
|
+
raise ImportError("Could not import the flow")
|
|
257
|
+
module = importlib.util.module_from_spec(spec)
|
|
258
|
+
spec.loader.exec_module(module)
|
|
259
|
+
if not hasattr(module, "main"):
|
|
260
|
+
raise ImportError(
|
|
261
|
+
"The waldiez file does not contain a main() function"
|
|
262
|
+
)
|
|
263
|
+
self._loaded_module = module
|
|
264
|
+
return module
|
|
178
265
|
|
|
179
266
|
def _before_run(
|
|
180
267
|
self,
|
|
@@ -250,48 +337,11 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol):
|
|
|
250
337
|
"The _a_run method must be implemented in the subclass."
|
|
251
338
|
)
|
|
252
339
|
|
|
253
|
-
def _start(
|
|
254
|
-
self,
|
|
255
|
-
temp_dir: Path,
|
|
256
|
-
output_file: Path,
|
|
257
|
-
uploads_root: Path | None,
|
|
258
|
-
skip_mmd: bool,
|
|
259
|
-
skip_timeline: bool,
|
|
260
|
-
) -> None:
|
|
261
|
-
"""Start running the Waldiez flow in a non-blocking way."""
|
|
262
|
-
raise NotImplementedError(
|
|
263
|
-
"The _start method must be implemented in the subclass."
|
|
264
|
-
)
|
|
265
|
-
|
|
266
|
-
async def _a_start(
|
|
267
|
-
self,
|
|
268
|
-
temp_dir: Path,
|
|
269
|
-
output_file: Path,
|
|
270
|
-
uploads_root: Path | None,
|
|
271
|
-
skip_mmd: bool,
|
|
272
|
-
skip_timeline: bool,
|
|
273
|
-
) -> None:
|
|
274
|
-
"""Start running the Waldiez flow in a non-blocking way asynchronously.
|
|
275
|
-
|
|
276
|
-
Parameters
|
|
277
|
-
----------
|
|
278
|
-
temp_dir : Path
|
|
279
|
-
The path to the temporary directory created for the run.
|
|
280
|
-
output_file : Path
|
|
281
|
-
The path to the output file.
|
|
282
|
-
uploads_root : Path | None
|
|
283
|
-
The root path for uploads, if any.
|
|
284
|
-
skip_mmd : bool
|
|
285
|
-
Whether to skip generating the mermaid diagram.
|
|
286
|
-
"""
|
|
287
|
-
raise NotImplementedError(
|
|
288
|
-
"The _a_start method must be implemented in the subclass."
|
|
289
|
-
)
|
|
290
|
-
|
|
291
340
|
def _after_run(
|
|
292
341
|
self,
|
|
293
342
|
results: list[dict[str, Any]],
|
|
294
343
|
output_file: Path,
|
|
344
|
+
waldiez_file: Path,
|
|
295
345
|
uploads_root: Path | None,
|
|
296
346
|
temp_dir: Path,
|
|
297
347
|
skip_mmd: bool,
|
|
@@ -303,20 +353,26 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol):
|
|
|
303
353
|
|
|
304
354
|
# Reset stop flag for next run
|
|
305
355
|
self._stop_requested.clear()
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
356
|
+
# pylint: disable=broad-exception-caught
|
|
357
|
+
try:
|
|
358
|
+
after_run(
|
|
359
|
+
temp_dir=temp_dir,
|
|
360
|
+
output_file=output_file,
|
|
361
|
+
flow_name=self._waldiez.name,
|
|
362
|
+
waldiez_file=waldiez_file,
|
|
363
|
+
uploads_root=uploads_root,
|
|
364
|
+
skip_mmd=skip_mmd,
|
|
365
|
+
skip_timeline=skip_timeline,
|
|
366
|
+
)
|
|
367
|
+
except BaseException as exc: # pragma: no cover
|
|
368
|
+
self.log.warning("Error occurred during after_run: %s", exc)
|
|
314
369
|
self.log.info("Cleanup completed")
|
|
315
370
|
|
|
316
371
|
async def _a_after_run(
|
|
317
372
|
self,
|
|
318
373
|
results: list[dict[str, Any]],
|
|
319
374
|
output_file: Path,
|
|
375
|
+
waldiez_file: Path,
|
|
320
376
|
uploads_root: Path | None,
|
|
321
377
|
temp_dir: Path,
|
|
322
378
|
skip_mmd: bool,
|
|
@@ -330,23 +386,9 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol):
|
|
|
330
386
|
temp_dir=temp_dir,
|
|
331
387
|
skip_mmd=skip_mmd,
|
|
332
388
|
skip_timeline=skip_timeline,
|
|
389
|
+
waldiez_file=waldiez_file,
|
|
333
390
|
)
|
|
334
391
|
|
|
335
|
-
def _stop(self) -> None:
|
|
336
|
-
"""Actions to perform when stopping the flow."""
|
|
337
|
-
raise NotImplementedError(
|
|
338
|
-
"The _stop method must be implemented in the subclass."
|
|
339
|
-
)
|
|
340
|
-
|
|
341
|
-
async def _a_stop(self) -> None:
|
|
342
|
-
"""Asynchronously perform actions when stopping the flow."""
|
|
343
|
-
raise NotImplementedError(
|
|
344
|
-
"The _a_stop method must be implemented in the subclass."
|
|
345
|
-
)
|
|
346
|
-
|
|
347
|
-
# ===================================================================
|
|
348
|
-
# HELPER METHODS
|
|
349
|
-
# ===================================================================
|
|
350
392
|
@staticmethod
|
|
351
393
|
def _prepare_paths(
|
|
352
394
|
output_path: str | Path | None = None,
|
|
@@ -366,43 +408,67 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol):
|
|
|
366
408
|
output_file: Path = Path(WaldiezBaseRunner._output_path)
|
|
367
409
|
return output_file, uploads_root_path
|
|
368
410
|
|
|
369
|
-
|
|
370
|
-
|
|
411
|
+
@staticmethod
|
|
412
|
+
async def a_process_event(
|
|
413
|
+
event: Union["BaseEvent", "BaseMessage"],
|
|
414
|
+
skip_send: bool = False,
|
|
415
|
+
) -> None:
|
|
416
|
+
"""Process an event or message asynchronously.
|
|
371
417
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
418
|
+
Parameters
|
|
419
|
+
----------
|
|
420
|
+
event : Union[BaseEvent, BaseMessage]
|
|
421
|
+
The event or message to process.
|
|
422
|
+
skip_send : bool
|
|
423
|
+
Skip sending the event.
|
|
424
|
+
"""
|
|
425
|
+
if hasattr(event, "type"): # pragma: no branch
|
|
426
|
+
if event.type == "input_request":
|
|
427
|
+
prompt = getattr(
|
|
428
|
+
event, "prompt", getattr(event.content, "prompt", "> ")
|
|
429
|
+
)
|
|
430
|
+
password = getattr(
|
|
431
|
+
event,
|
|
432
|
+
"password",
|
|
433
|
+
getattr(event.content, "password", False),
|
|
434
|
+
)
|
|
435
|
+
user_input = await WaldiezBaseRunner.a_get_user_input(
|
|
436
|
+
prompt, password=password
|
|
437
|
+
)
|
|
438
|
+
await event.content.respond(user_input)
|
|
439
|
+
elif not skip_send:
|
|
440
|
+
WaldiezBaseRunner._send(event)
|
|
441
|
+
|
|
442
|
+
@staticmethod
|
|
443
|
+
def process_event(
|
|
444
|
+
event: Union["BaseEvent", "BaseMessage"],
|
|
445
|
+
skip_send: bool = False,
|
|
446
|
+
) -> None:
|
|
447
|
+
"""Process an event or message synchronously.
|
|
448
|
+
|
|
449
|
+
Parameters
|
|
450
|
+
----------
|
|
451
|
+
event : Union[BaseEvent, BaseMessage]
|
|
452
|
+
The event or message to process.
|
|
453
|
+
skip_send : bool
|
|
454
|
+
Skip sending the event.
|
|
377
455
|
"""
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
async def a_install_requirements(self) -> None:
|
|
396
|
-
"""Install the requirements for the flow asynchronously."""
|
|
397
|
-
if not self._called_install_requirements:
|
|
398
|
-
self._called_install_requirements = True
|
|
399
|
-
extra_requirements = self.gather_requirements()
|
|
400
|
-
if extra_requirements:
|
|
401
|
-
await a_install_requirements(extra_requirements)
|
|
402
|
-
|
|
403
|
-
# ===================================================================
|
|
404
|
-
# PUBLIC PROTOCOL IMPLEMENTATION
|
|
405
|
-
# ===================================================================
|
|
456
|
+
if hasattr(event, "type"): # pragma: no branch
|
|
457
|
+
if event.type == "input_request":
|
|
458
|
+
prompt = getattr(
|
|
459
|
+
event, "prompt", getattr(event.content, "prompt", "> ")
|
|
460
|
+
)
|
|
461
|
+
password = getattr(
|
|
462
|
+
event,
|
|
463
|
+
"password",
|
|
464
|
+
getattr(event.content, "password", False),
|
|
465
|
+
)
|
|
466
|
+
user_input = WaldiezBaseRunner.get_user_input(
|
|
467
|
+
prompt, password=password
|
|
468
|
+
)
|
|
469
|
+
event.content.respond(user_input)
|
|
470
|
+
elif not skip_send:
|
|
471
|
+
WaldiezBaseRunner._send(event)
|
|
406
472
|
|
|
407
473
|
def before_run(
|
|
408
474
|
self,
|
|
@@ -452,6 +518,42 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol):
|
|
|
452
518
|
uploads_root=uploads_root,
|
|
453
519
|
)
|
|
454
520
|
|
|
521
|
+
def prepare(
|
|
522
|
+
self,
|
|
523
|
+
output_path: str | Path | None,
|
|
524
|
+
uploads_root: str | Path | None,
|
|
525
|
+
) -> tuple[Path, Path, Path | None]:
|
|
526
|
+
"""Prepare the paths and environment for running the flow.
|
|
527
|
+
|
|
528
|
+
Parameters
|
|
529
|
+
----------
|
|
530
|
+
output_path : str | Path | None
|
|
531
|
+
The output path for the flow, by default None.
|
|
532
|
+
uploads_root : str | Path | None
|
|
533
|
+
The root path for uploads, by default None.
|
|
534
|
+
|
|
535
|
+
Returns
|
|
536
|
+
-------
|
|
537
|
+
tuple[Path, Path, Path | None]
|
|
538
|
+
A tuple containing:
|
|
539
|
+
- The path to the output file.
|
|
540
|
+
- The path to the temporary directory created for the run.
|
|
541
|
+
- The root path for uploads, if specified, otherwise None.
|
|
542
|
+
"""
|
|
543
|
+
output_file, uploads_root_path = self._prepare_paths(
|
|
544
|
+
output_path=output_path,
|
|
545
|
+
uploads_root=uploads_root,
|
|
546
|
+
)
|
|
547
|
+
temp_dir = self.before_run(
|
|
548
|
+
output_file=output_file,
|
|
549
|
+
uploads_root=uploads_root_path,
|
|
550
|
+
)
|
|
551
|
+
self.install_requirements()
|
|
552
|
+
refresh_environment()
|
|
553
|
+
return temp_dir, output_file, uploads_root_path
|
|
554
|
+
|
|
555
|
+
# noinspection PyProtocol
|
|
556
|
+
# pylint: disable=too-many-locals,unused-argument
|
|
455
557
|
def run(
|
|
456
558
|
self,
|
|
457
559
|
output_path: str | Path | None = None,
|
|
@@ -462,7 +564,7 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol):
|
|
|
462
564
|
dot_env: str | Path | None = None,
|
|
463
565
|
**kwargs: Any,
|
|
464
566
|
) -> list[dict[str, Any]]:
|
|
465
|
-
"""Run the Waldiez flow
|
|
567
|
+
"""Run the Waldiez flow.
|
|
466
568
|
|
|
467
569
|
Parameters
|
|
468
570
|
----------
|
|
@@ -471,10 +573,9 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol):
|
|
|
471
573
|
uploads_root : str | Path | None
|
|
472
574
|
The runtime uploads root, by default None.
|
|
473
575
|
structured_io : bool
|
|
474
|
-
Whether to use structured IO instead of the default 'input/print'
|
|
475
|
-
by default False.
|
|
576
|
+
Whether to use structured IO instead of the default 'input/print'.
|
|
476
577
|
skip_mmd : bool
|
|
477
|
-
Whether to skip generating the mermaid diagram
|
|
578
|
+
Whether to skip generating the mermaid diagram.
|
|
478
579
|
skip_timeline : bool
|
|
479
580
|
Whether to skip generating the timeline JSON.
|
|
480
581
|
dot_env : str | Path | None
|
|
@@ -485,12 +586,15 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol):
|
|
|
485
586
|
Returns
|
|
486
587
|
-------
|
|
487
588
|
list[dict[str, Any]]
|
|
488
|
-
The
|
|
589
|
+
The results of the run.
|
|
489
590
|
|
|
490
591
|
Raises
|
|
491
592
|
------
|
|
492
593
|
RuntimeError
|
|
493
|
-
If the runner is already running
|
|
594
|
+
If the runner is already running, the workflow is not async,
|
|
595
|
+
or an error occurs during the run.
|
|
596
|
+
StopRunningException
|
|
597
|
+
If the run is stopped by the user.
|
|
494
598
|
"""
|
|
495
599
|
if dot_env is not None:
|
|
496
600
|
resolved = Path(dot_env).resolve()
|
|
@@ -500,7 +604,7 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol):
|
|
|
500
604
|
WaldiezBaseRunner._structured_io = structured_io
|
|
501
605
|
if self.is_running():
|
|
502
606
|
raise RuntimeError("Workflow already running")
|
|
503
|
-
if self.
|
|
607
|
+
if self.is_async:
|
|
504
608
|
with start_blocking_portal(backend="asyncio") as portal:
|
|
505
609
|
return portal.call(
|
|
506
610
|
self.a_run,
|
|
@@ -509,19 +613,13 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol):
|
|
|
509
613
|
structured_io,
|
|
510
614
|
skip_mmd,
|
|
511
615
|
)
|
|
512
|
-
output_file, uploads_root_path = self.
|
|
616
|
+
temp_dir, output_file, uploads_root_path = self.prepare(
|
|
513
617
|
output_path=output_path,
|
|
514
618
|
uploads_root=uploads_root,
|
|
515
619
|
)
|
|
516
|
-
temp_dir = self.before_run(
|
|
517
|
-
output_file=output_file,
|
|
518
|
-
uploads_root=uploads_root_path,
|
|
519
|
-
)
|
|
520
|
-
self.install_requirements()
|
|
521
|
-
refresh_environment()
|
|
522
620
|
WaldiezBaseRunner._running = True
|
|
523
|
-
results: list[dict[str, Any]]
|
|
524
|
-
old_env_vars = set_env_vars(self.
|
|
621
|
+
results: list[dict[str, Any]]
|
|
622
|
+
old_env_vars = set_env_vars(self._waldiez.get_flow_env_vars())
|
|
525
623
|
try:
|
|
526
624
|
with chdir(to=temp_dir):
|
|
527
625
|
sys.path.insert(0, str(temp_dir))
|
|
@@ -532,6 +630,11 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol):
|
|
|
532
630
|
skip_mmd=skip_mmd,
|
|
533
631
|
skip_timeline=skip_timeline,
|
|
534
632
|
)
|
|
633
|
+
except (SystemExit, StopRunningException, KeyboardInterrupt) as exc:
|
|
634
|
+
raise StopRunningException(StopRunningException.reason) from exc
|
|
635
|
+
except BaseException as exc: # pylint: disable=broad-exception-caught
|
|
636
|
+
self.log.error("Error occurred while running workflow: %s", exc)
|
|
637
|
+
results = [{"error": str(exc)}]
|
|
535
638
|
finally:
|
|
536
639
|
WaldiezBaseRunner._running = False
|
|
537
640
|
reset_env_vars(old_env_vars)
|
|
@@ -548,7 +651,39 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol):
|
|
|
548
651
|
sys.path.pop(0)
|
|
549
652
|
return results
|
|
550
653
|
|
|
654
|
+
async def a_prepare(
|
|
655
|
+
self,
|
|
656
|
+
output_path: str | Path | None,
|
|
657
|
+
uploads_root: str | Path | None,
|
|
658
|
+
) -> tuple[Path, Path, Path | None]:
|
|
659
|
+
"""Prepare the paths for the async run.
|
|
660
|
+
|
|
661
|
+
Parameters
|
|
662
|
+
----------
|
|
663
|
+
output_path : str | Path | None
|
|
664
|
+
The output path, by default None.
|
|
665
|
+
uploads_root : str | Path | None
|
|
666
|
+
The uploads root path, by default None.
|
|
667
|
+
|
|
668
|
+
Returns
|
|
669
|
+
-------
|
|
670
|
+
tuple[Path, Path, Path | None]
|
|
671
|
+
The temporary directory, output file, and uploads root path.
|
|
672
|
+
"""
|
|
673
|
+
output_file, uploads_root_path = self._prepare_paths(
|
|
674
|
+
output_path=output_path,
|
|
675
|
+
uploads_root=uploads_root,
|
|
676
|
+
)
|
|
677
|
+
temp_dir = await self._a_before_run(
|
|
678
|
+
output_file=output_file,
|
|
679
|
+
uploads_root=uploads_root_path,
|
|
680
|
+
)
|
|
681
|
+
await self.a_install_requirements()
|
|
682
|
+
refresh_environment()
|
|
683
|
+
return temp_dir, output_file, uploads_root_path
|
|
684
|
+
|
|
551
685
|
# noinspection DuplicatedCode
|
|
686
|
+
# noinspection PyProtocol
|
|
552
687
|
async def a_run(
|
|
553
688
|
self,
|
|
554
689
|
output_path: str | Path | None = None,
|
|
@@ -566,28 +701,30 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol):
|
|
|
566
701
|
output_path : str | Path | None
|
|
567
702
|
The output path, by default None.
|
|
568
703
|
uploads_root : str | Path | None
|
|
569
|
-
The runtime uploads root
|
|
704
|
+
The runtime uploads root.
|
|
570
705
|
structured_io : bool
|
|
571
|
-
Whether to use structured IO instead of the default 'input/print'
|
|
572
|
-
by default False.
|
|
706
|
+
Whether to use structured IO instead of the default 'input/print'.
|
|
573
707
|
skip_mmd : bool
|
|
574
|
-
Whether to skip generating the mermaid diagram
|
|
708
|
+
Whether to skip generating the mermaid diagram.
|
|
575
709
|
skip_timeline : bool
|
|
576
|
-
Whether to skip generating the timeline JSON
|
|
710
|
+
Whether to skip generating the timeline JSON.
|
|
577
711
|
dot_env : str | Path | None
|
|
578
712
|
The path to the .env file, if any.
|
|
579
713
|
**kwargs : Any
|
|
580
|
-
Additional keyword arguments for the
|
|
714
|
+
Additional keyword arguments for the a_run method.
|
|
581
715
|
|
|
582
716
|
Returns
|
|
583
717
|
-------
|
|
584
718
|
list[dict[str, Any]]
|
|
585
|
-
The
|
|
719
|
+
The results of the run.
|
|
586
720
|
|
|
587
721
|
Raises
|
|
588
722
|
------
|
|
589
723
|
RuntimeError
|
|
590
|
-
If the runner is already running
|
|
724
|
+
If the runner is already running, the workflow is not async
|
|
725
|
+
or an error occurs during the run.
|
|
726
|
+
StopRunningException
|
|
727
|
+
If the run is stopped by the user.
|
|
591
728
|
"""
|
|
592
729
|
if dot_env is not None:
|
|
593
730
|
resolved = Path(dot_env).resolve()
|
|
@@ -597,19 +734,13 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol):
|
|
|
597
734
|
WaldiezBaseRunner._structured_io = structured_io
|
|
598
735
|
if self.is_running():
|
|
599
736
|
raise RuntimeError("Workflow already running")
|
|
600
|
-
output_file, uploads_root_path = self.
|
|
737
|
+
temp_dir, output_file, uploads_root_path = await self.a_prepare(
|
|
601
738
|
output_path=output_path,
|
|
602
739
|
uploads_root=uploads_root,
|
|
603
740
|
)
|
|
604
|
-
temp_dir = await self._a_before_run(
|
|
605
|
-
output_file=output_file,
|
|
606
|
-
uploads_root=uploads_root_path,
|
|
607
|
-
)
|
|
608
|
-
await self.a_install_requirements()
|
|
609
|
-
refresh_environment()
|
|
610
741
|
WaldiezBaseRunner._running = True
|
|
611
|
-
results: list[dict[str, Any]]
|
|
612
|
-
old_env_vars = set_env_vars(self.
|
|
742
|
+
results: list[dict[str, Any]]
|
|
743
|
+
old_env_vars = set_env_vars(self._waldiez.get_flow_env_vars())
|
|
613
744
|
try:
|
|
614
745
|
async with a_chdir(to=temp_dir):
|
|
615
746
|
sys.path.insert(0, str(temp_dir))
|
|
@@ -620,6 +751,10 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol):
|
|
|
620
751
|
skip_mmd=skip_mmd,
|
|
621
752
|
skip_timeline=skip_timeline,
|
|
622
753
|
)
|
|
754
|
+
except (SystemExit, StopRunningException, KeyboardInterrupt) as exc:
|
|
755
|
+
raise StopRunningException(StopRunningException.reason) from exc
|
|
756
|
+
except BaseException as exc: # pylint: disable=broad-exception-caught
|
|
757
|
+
results = [{"error": str(exc)}]
|
|
623
758
|
finally:
|
|
624
759
|
WaldiezBaseRunner._running = False
|
|
625
760
|
reset_env_vars(old_env_vars)
|
|
@@ -627,6 +762,7 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol):
|
|
|
627
762
|
results=results,
|
|
628
763
|
output_file=output_file,
|
|
629
764
|
uploads_root=uploads_root_path,
|
|
765
|
+
waldiez_file=WaldiezBaseRunner._waldiez_file,
|
|
630
766
|
temp_dir=temp_dir,
|
|
631
767
|
skip_mmd=skip_mmd,
|
|
632
768
|
skip_timeline=skip_timeline,
|
|
@@ -635,129 +771,6 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol):
|
|
|
635
771
|
sys.path.pop(0)
|
|
636
772
|
return results
|
|
637
773
|
|
|
638
|
-
def start(
|
|
639
|
-
self,
|
|
640
|
-
output_path: str | Path | None,
|
|
641
|
-
uploads_root: str | Path | None,
|
|
642
|
-
structured_io: bool | None = None,
|
|
643
|
-
skip_mmd: bool = False,
|
|
644
|
-
skip_timeline: bool = False,
|
|
645
|
-
dot_env: str | Path | None = None,
|
|
646
|
-
**kwargs: Any,
|
|
647
|
-
) -> None:
|
|
648
|
-
"""Start running the Waldiez flow in a non-blocking way.
|
|
649
|
-
|
|
650
|
-
Parameters
|
|
651
|
-
----------
|
|
652
|
-
output_path : str | Path | None
|
|
653
|
-
The output path.
|
|
654
|
-
uploads_root : str | Path | None
|
|
655
|
-
The runtime uploads root.
|
|
656
|
-
structured_io : bool | None
|
|
657
|
-
Whether to use structured IO instead of the default 'input/print'.
|
|
658
|
-
skip_mmd : bool
|
|
659
|
-
Whether to skip generating the mermaid diagram, by default False.
|
|
660
|
-
skip_timeline : bool
|
|
661
|
-
Whether to skip generating the timeline JSON, by default False.
|
|
662
|
-
dot_env : str | Path | None
|
|
663
|
-
The path to the .env file, if any.
|
|
664
|
-
**kwargs : Any
|
|
665
|
-
Additional keyword arguments for the start method.
|
|
666
|
-
|
|
667
|
-
Raises
|
|
668
|
-
------
|
|
669
|
-
RuntimeError
|
|
670
|
-
If the runner is already running.
|
|
671
|
-
"""
|
|
672
|
-
if dot_env is not None:
|
|
673
|
-
resolved = Path(dot_env).resolve()
|
|
674
|
-
if resolved.is_file():
|
|
675
|
-
WaldiezBaseRunner._dot_env_path = resolved
|
|
676
|
-
if structured_io is not None:
|
|
677
|
-
WaldiezBaseRunner._structured_io = structured_io
|
|
678
|
-
if self.is_running():
|
|
679
|
-
raise RuntimeError("Workflow already running")
|
|
680
|
-
output_file, uploads_root_path = self._prepare_paths(
|
|
681
|
-
output_path=output_path,
|
|
682
|
-
uploads_root=uploads_root,
|
|
683
|
-
)
|
|
684
|
-
temp_dir = self.before_run(
|
|
685
|
-
output_file=output_file,
|
|
686
|
-
uploads_root=uploads_root_path,
|
|
687
|
-
)
|
|
688
|
-
self.install_requirements()
|
|
689
|
-
refresh_environment()
|
|
690
|
-
WaldiezBaseRunner._running = True
|
|
691
|
-
self._start(
|
|
692
|
-
temp_dir=temp_dir,
|
|
693
|
-
output_file=output_file,
|
|
694
|
-
uploads_root=uploads_root_path,
|
|
695
|
-
skip_mmd=skip_mmd,
|
|
696
|
-
skip_timeline=skip_timeline,
|
|
697
|
-
)
|
|
698
|
-
|
|
699
|
-
# noinspection DuplicatedCode
|
|
700
|
-
async def a_start(
|
|
701
|
-
self,
|
|
702
|
-
output_path: str | Path | None,
|
|
703
|
-
uploads_root: str | Path | None,
|
|
704
|
-
structured_io: bool | None = None,
|
|
705
|
-
skip_mmd: bool = False,
|
|
706
|
-
skip_timeline: bool = False,
|
|
707
|
-
dot_env: str | Path | None = None,
|
|
708
|
-
**kwargs: Any,
|
|
709
|
-
) -> None:
|
|
710
|
-
"""Asynchronously start running the Waldiez flow in a non-blocking way.
|
|
711
|
-
|
|
712
|
-
Parameters
|
|
713
|
-
----------
|
|
714
|
-
output_path : str | Path | None
|
|
715
|
-
The output path.
|
|
716
|
-
uploads_root : str | Path | None
|
|
717
|
-
The runtime uploads root.
|
|
718
|
-
structured_io : bool | None = None
|
|
719
|
-
Whether to use structured IO instead of the default 'input/print'.
|
|
720
|
-
skip_mmd : bool = False
|
|
721
|
-
Whether to skip generating the mermaid diagram, by default False.
|
|
722
|
-
skip_timeline : bool = False
|
|
723
|
-
Whether to skip generating the timeline JSON, by default False.
|
|
724
|
-
dot_env : str | Path | None = None
|
|
725
|
-
The path to the .env file, if any.
|
|
726
|
-
**kwargs : Any
|
|
727
|
-
Additional keyword arguments for the start method.
|
|
728
|
-
|
|
729
|
-
Raises
|
|
730
|
-
------
|
|
731
|
-
RuntimeError
|
|
732
|
-
If the runner is already running.
|
|
733
|
-
"""
|
|
734
|
-
if dot_env is not None:
|
|
735
|
-
resolved = Path(dot_env).resolve()
|
|
736
|
-
if resolved.is_file():
|
|
737
|
-
WaldiezBaseRunner._dot_env_path = resolved
|
|
738
|
-
if structured_io is not None:
|
|
739
|
-
WaldiezBaseRunner._structured_io = structured_io
|
|
740
|
-
if self.is_running():
|
|
741
|
-
raise RuntimeError("Workflow already running")
|
|
742
|
-
output_file, uploads_root_path = self._prepare_paths(
|
|
743
|
-
output_path=output_path,
|
|
744
|
-
uploads_root=uploads_root,
|
|
745
|
-
)
|
|
746
|
-
temp_dir = await self._a_before_run(
|
|
747
|
-
output_file=output_file,
|
|
748
|
-
uploads_root=uploads_root_path,
|
|
749
|
-
)
|
|
750
|
-
await self.a_install_requirements()
|
|
751
|
-
refresh_environment()
|
|
752
|
-
WaldiezBaseRunner._running = True
|
|
753
|
-
await self._a_start(
|
|
754
|
-
temp_dir=temp_dir,
|
|
755
|
-
output_file=output_file,
|
|
756
|
-
uploads_root=uploads_root_path,
|
|
757
|
-
skip_mmd=skip_mmd,
|
|
758
|
-
skip_timeline=skip_timeline,
|
|
759
|
-
)
|
|
760
|
-
|
|
761
774
|
def after_run(
|
|
762
775
|
self,
|
|
763
776
|
results: list[dict[str, Any]],
|
|
@@ -787,6 +800,7 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol):
|
|
|
787
800
|
self._after_run(
|
|
788
801
|
results=results,
|
|
789
802
|
output_file=output_file,
|
|
803
|
+
waldiez_file=WaldiezBaseRunner._waldiez_file,
|
|
790
804
|
uploads_root=uploads_root,
|
|
791
805
|
temp_dir=temp_dir,
|
|
792
806
|
skip_mmd=skip_mmd,
|
|
@@ -826,39 +840,23 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol):
|
|
|
826
840
|
temp_dir=temp_dir,
|
|
827
841
|
skip_mmd=skip_mmd,
|
|
828
842
|
skip_timeline=skip_timeline,
|
|
843
|
+
waldiez_file=WaldiezBaseRunner._waldiez_file,
|
|
829
844
|
)
|
|
830
845
|
|
|
831
|
-
def stop(self) -> None:
|
|
832
|
-
"""Stop the runner if it is running."""
|
|
833
|
-
if not self.is_running():
|
|
834
|
-
return
|
|
835
|
-
try:
|
|
836
|
-
self._stop()
|
|
837
|
-
finally:
|
|
838
|
-
WaldiezBaseRunner._running = False
|
|
839
|
-
|
|
840
|
-
async def a_stop(self) -> None:
|
|
841
|
-
"""Asynchronously stop the runner if it is running."""
|
|
842
|
-
if not self.is_running():
|
|
843
|
-
return
|
|
844
|
-
try:
|
|
845
|
-
await self._a_stop()
|
|
846
|
-
finally:
|
|
847
|
-
WaldiezBaseRunner._running = False
|
|
848
|
-
|
|
849
|
-
# ===================================================================
|
|
850
|
-
# PROPERTIES AND CONTEXT MANAGERS
|
|
851
|
-
# ===================================================================
|
|
852
|
-
|
|
853
846
|
@property
|
|
854
847
|
def waldiez(self) -> Waldiez:
|
|
855
848
|
"""Get the Waldiez instance."""
|
|
856
849
|
return self._waldiez
|
|
857
850
|
|
|
851
|
+
@property
|
|
852
|
+
def waldiez_file(self) -> Path:
|
|
853
|
+
"""Get the path to the waldiez file."""
|
|
854
|
+
return self._waldiez_file
|
|
855
|
+
|
|
858
856
|
@property
|
|
859
857
|
def is_async(self) -> bool:
|
|
860
858
|
"""Check if the workflow is async."""
|
|
861
|
-
return self.
|
|
859
|
+
return self._waldiez.is_async
|
|
862
860
|
|
|
863
861
|
@property
|
|
864
862
|
def running(self) -> bool:
|
|
@@ -890,11 +888,6 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol):
|
|
|
890
888
|
"""Get the uploads root path for the runner."""
|
|
891
889
|
return WaldiezBaseRunner._uploads_root
|
|
892
890
|
|
|
893
|
-
@property
|
|
894
|
-
def skip_patch_io(self) -> bool:
|
|
895
|
-
"""Check if the runner is skipping patching IO."""
|
|
896
|
-
return WaldiezBaseRunner._skip_patch_io
|
|
897
|
-
|
|
898
891
|
@classmethod
|
|
899
892
|
def load(
|
|
900
893
|
cls,
|
|
@@ -952,6 +945,31 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol):
|
|
|
952
945
|
dot_env=dot_env,
|
|
953
946
|
)
|
|
954
947
|
|
|
948
|
+
def stop(self) -> None:
|
|
949
|
+
"""Stop the workflow execution.
|
|
950
|
+
|
|
951
|
+
This method sets the stop flag that will be checked by the event
|
|
952
|
+
handlers and other parts of the workflow execution to gracefully
|
|
953
|
+
terminate the workflow execution.
|
|
954
|
+
Note: Stopping will occur at the "next" AutoGen event, not immediately.
|
|
955
|
+
"""
|
|
956
|
+
self.log.info("Stop requested - setting stop flag")
|
|
957
|
+
self._stop_requested.set()
|
|
958
|
+
|
|
959
|
+
def is_stop_requested(self) -> bool:
|
|
960
|
+
"""Check if a stop has been requested.
|
|
961
|
+
|
|
962
|
+
Returns
|
|
963
|
+
-------
|
|
964
|
+
bool
|
|
965
|
+
True if stop has been requested, False otherwise.
|
|
966
|
+
"""
|
|
967
|
+
return self._stop_requested.is_set()
|
|
968
|
+
|
|
969
|
+
def set_stop_requested(self) -> None:
|
|
970
|
+
"""Set the stop requested flag."""
|
|
971
|
+
self._stop_requested.set()
|
|
972
|
+
|
|
955
973
|
def __enter__(self) -> Self:
|
|
956
974
|
"""Enter the context manager."""
|
|
957
975
|
return self
|
|
@@ -968,7 +986,7 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol):
|
|
|
968
986
|
) -> None:
|
|
969
987
|
"""Exit the context manager."""
|
|
970
988
|
if self.is_running():
|
|
971
|
-
self.
|
|
989
|
+
self._stop_requested.set()
|
|
972
990
|
|
|
973
991
|
async def __aexit__(
|
|
974
992
|
self,
|
|
@@ -978,4 +996,4 @@ class WaldiezBaseRunner(WaldiezRunnerProtocol):
|
|
|
978
996
|
) -> None:
|
|
979
997
|
"""Exit the context manager asynchronously."""
|
|
980
998
|
if self.is_running():
|
|
981
|
-
|
|
999
|
+
self._stop_requested.set()
|