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.
- waldiez/__init__.py +1 -1
- waldiez/_version.py +1 -1
- waldiez/cli.py +19 -7
- waldiez/cli_extras/jupyter.py +3 -0
- waldiez/cli_extras/runner.py +3 -1
- waldiez/cli_extras/studio.py +3 -1
- waldiez/exporter.py +9 -3
- waldiez/exporting/agent/exporter.py +15 -16
- waldiez/exporting/agent/extras/captain_agent_extras.py +6 -6
- waldiez/exporting/agent/extras/doc_agent_extras.py +6 -6
- waldiez/exporting/agent/extras/group_manager_agent_extas.py +40 -24
- waldiez/exporting/agent/extras/group_member_extras.py +6 -5
- waldiez/exporting/agent/extras/handoffs/after_work.py +2 -1
- waldiez/exporting/agent/extras/handoffs/available.py +2 -1
- waldiez/exporting/agent/extras/handoffs/condition.py +3 -2
- waldiez/exporting/agent/extras/handoffs/handoff.py +2 -1
- waldiez/exporting/agent/extras/handoffs/target.py +7 -4
- waldiez/exporting/agent/extras/rag/chroma_extras.py +27 -19
- waldiez/exporting/agent/extras/rag/mongo_extras.py +8 -8
- waldiez/exporting/agent/extras/rag/pgvector_extras.py +5 -5
- waldiez/exporting/agent/extras/rag/qdrant_extras.py +5 -4
- waldiez/exporting/agent/extras/rag/vector_db_extras.py +1 -1
- waldiez/exporting/agent/extras/rag_user_proxy_agent_extras.py +5 -7
- waldiez/exporting/agent/extras/reasoning_agent_extras.py +3 -5
- waldiez/exporting/agent/termination.py +1 -0
- waldiez/exporting/chats/exporter.py +4 -4
- waldiez/exporting/chats/processor.py +1 -2
- waldiez/exporting/chats/utils/common.py +89 -48
- waldiez/exporting/chats/utils/group.py +9 -9
- waldiez/exporting/chats/utils/nested.py +7 -7
- waldiez/exporting/chats/utils/sequential.py +1 -1
- waldiez/exporting/chats/utils/single.py +2 -2
- waldiez/exporting/core/constants.py +3 -1
- waldiez/exporting/core/content.py +7 -7
- waldiez/exporting/core/context.py +5 -3
- waldiez/exporting/core/exporter.py +5 -3
- waldiez/exporting/core/exporters.py +2 -2
- waldiez/exporting/core/extras/agent_extras/captain_extras.py +2 -2
- waldiez/exporting/core/extras/agent_extras/group_manager_extras.py +2 -2
- waldiez/exporting/core/extras/agent_extras/rag_user_extras.py +2 -2
- waldiez/exporting/core/extras/agent_extras/standard_extras.py +3 -8
- waldiez/exporting/core/extras/base.py +7 -5
- waldiez/exporting/core/extras/flow_extras.py +4 -5
- waldiez/exporting/core/extras/model_extras.py +2 -2
- waldiez/exporting/core/extras/path_resolver.py +1 -2
- waldiez/exporting/core/extras/serializer.py +13 -11
- waldiez/exporting/core/protocols.py +6 -5
- waldiez/exporting/core/result.py +25 -28
- waldiez/exporting/core/types.py +11 -10
- waldiez/exporting/core/utils/llm_config.py +4 -4
- waldiez/exporting/core/validation.py +10 -11
- waldiez/exporting/flow/execution_generator.py +99 -10
- waldiez/exporting/flow/exporter.py +2 -2
- waldiez/exporting/flow/factory.py +2 -2
- waldiez/exporting/flow/file_generator.py +4 -2
- waldiez/exporting/flow/merger.py +5 -3
- waldiez/exporting/flow/orchestrator.py +72 -2
- waldiez/exporting/flow/utils/common.py +6 -6
- waldiez/exporting/flow/utils/importing.py +7 -8
- waldiez/exporting/flow/utils/linting.py +25 -9
- waldiez/exporting/flow/utils/logging.py +5 -77
- waldiez/exporting/models/exporter.py +8 -8
- waldiez/exporting/models/processor.py +5 -5
- waldiez/exporting/tools/exporter.py +2 -2
- waldiez/exporting/tools/processor.py +7 -4
- waldiez/io/__init__.py +11 -5
- waldiez/io/_ws.py +12 -6
- waldiez/io/models/constants.py +10 -10
- waldiez/io/models/content/audio.py +1 -0
- waldiez/io/models/content/base.py +20 -18
- waldiez/io/models/content/file.py +1 -0
- waldiez/io/models/content/image.py +1 -0
- waldiez/io/models/content/text.py +1 -0
- waldiez/io/models/content/video.py +1 -0
- waldiez/io/models/user_input.py +10 -5
- waldiez/io/models/user_response.py +17 -16
- waldiez/io/mqtt.py +18 -31
- waldiez/io/redis.py +18 -22
- waldiez/io/structured.py +122 -70
- waldiez/io/utils.py +19 -10
- waldiez/io/ws.py +7 -3
- waldiez/logger.py +16 -3
- waldiez/models/agents/__init__.py +3 -0
- waldiez/models/agents/agent/agent.py +25 -17
- waldiez/models/agents/agent/agent_data.py +25 -22
- waldiez/models/agents/agent/code_execution.py +9 -11
- waldiez/models/agents/agent/termination_message.py +10 -12
- waldiez/models/agents/agent/update_system_message.py +2 -4
- waldiez/models/agents/agents.py +8 -8
- waldiez/models/agents/assistant/assistant.py +6 -3
- waldiez/models/agents/assistant/assistant_data.py +2 -2
- waldiez/models/agents/captain/captain_agent.py +7 -4
- waldiez/models/agents/captain/captain_agent_data.py +5 -7
- waldiez/models/agents/doc_agent/doc_agent.py +7 -4
- waldiez/models/agents/doc_agent/doc_agent_data.py +9 -10
- waldiez/models/agents/doc_agent/rag_query_engine.py +10 -12
- waldiez/models/agents/extra_requirements.py +3 -3
- waldiez/models/agents/group_manager/group_manager.py +12 -7
- waldiez/models/agents/group_manager/group_manager_data.py +13 -12
- waldiez/models/agents/group_manager/speakers.py +17 -19
- waldiez/models/agents/rag_user_proxy/rag_user_proxy.py +7 -4
- waldiez/models/agents/rag_user_proxy/rag_user_proxy_data.py +4 -1
- waldiez/models/agents/rag_user_proxy/retrieve_config.py +69 -63
- waldiez/models/agents/rag_user_proxy/vector_db_config.py +19 -19
- waldiez/models/agents/reasoning/reasoning_agent.py +7 -4
- waldiez/models/agents/reasoning/reasoning_agent_data.py +3 -2
- waldiez/models/agents/reasoning/reasoning_agent_reason_config.py +8 -8
- waldiez/models/agents/user_proxy/user_proxy.py +6 -3
- waldiez/models/agents/user_proxy/user_proxy_data.py +1 -1
- waldiez/models/chat/chat.py +28 -20
- waldiez/models/chat/chat_data.py +22 -21
- waldiez/models/chat/chat_message.py +9 -9
- waldiez/models/chat/chat_nested.py +9 -9
- waldiez/models/chat/chat_summary.py +6 -6
- waldiez/models/common/__init__.py +2 -0
- waldiez/models/common/ag2_version.py +2 -0
- waldiez/models/common/base.py +2 -0
- waldiez/models/common/dict_utils.py +8 -6
- waldiez/models/common/handoff.py +20 -17
- waldiez/models/common/method_utils.py +9 -7
- waldiez/models/common/naming.py +49 -0
- waldiez/models/flow/flow.py +11 -6
- waldiez/models/flow/flow_data.py +23 -17
- waldiez/models/flow/info.py +3 -3
- waldiez/models/flow/naming.py +2 -1
- waldiez/models/model/_aws.py +11 -13
- waldiez/models/model/_llm.py +8 -0
- waldiez/models/model/_price.py +2 -4
- waldiez/models/model/extra_requirements.py +1 -3
- waldiez/models/model/model.py +2 -2
- waldiez/models/model/model_data.py +21 -21
- waldiez/models/tool/extra_requirements.py +2 -4
- waldiez/models/tool/predefined/_duckduckgo.py +1 -0
- waldiez/models/tool/predefined/_email.py +4 -0
- waldiez/models/tool/predefined/_google.py +1 -0
- waldiez/models/tool/predefined/_perplexity.py +2 -1
- waldiez/models/tool/predefined/_searxng.py +2 -1
- waldiez/models/tool/predefined/_tavily.py +1 -0
- waldiez/models/tool/predefined/_wikipedia.py +2 -1
- waldiez/models/tool/predefined/_youtube.py +1 -0
- waldiez/models/tool/tool.py +8 -5
- waldiez/models/tool/tool_data.py +2 -2
- waldiez/models/waldiez.py +152 -4
- waldiez/runner.py +11 -5
- waldiez/running/async_utils.py +192 -0
- waldiez/running/base_runner.py +155 -241
- waldiez/running/dir_utils.py +52 -0
- waldiez/running/environment.py +10 -44
- waldiez/running/events_mixin.py +252 -0
- waldiez/running/exceptions.py +20 -0
- waldiez/running/gen_seq_diagram.py +18 -15
- waldiez/running/io_utils.py +216 -0
- waldiez/running/protocol.py +11 -5
- waldiez/running/requirements_mixin.py +65 -0
- waldiez/running/results_mixin.py +926 -0
- waldiez/running/standard_runner.py +24 -27
- waldiez/running/step_by_step/breakpoints_mixin.py +503 -47
- waldiez/running/step_by_step/command_handler.py +154 -0
- waldiez/running/step_by_step/events_processor.py +379 -0
- waldiez/running/step_by_step/step_by_step_models.py +425 -41
- waldiez/running/step_by_step/step_by_step_runner.py +437 -382
- waldiez/running/subprocess_runner/__base__.py +13 -8
- waldiez/running/subprocess_runner/_async_runner.py +6 -4
- waldiez/running/subprocess_runner/_sync_runner.py +11 -6
- waldiez/running/subprocess_runner/runner.py +48 -23
- waldiez/running/timeline_processor.py +1 -1
- waldiez/utils/__init__.py +2 -0
- waldiez/utils/conflict_checker.py +4 -4
- waldiez/utils/python_manager.py +415 -0
- waldiez/ws/__init__.py +8 -7
- waldiez/ws/_file_handler.py +18 -20
- waldiez/ws/_mock.py +75 -0
- waldiez/ws/cli.py +58 -10
- waldiez/ws/client_manager.py +77 -53
- waldiez/ws/errors.py +3 -0
- waldiez/ws/models.py +61 -53
- waldiez/ws/reloader.py +33 -4
- waldiez/ws/server.py +121 -52
- waldiez/ws/session_manager.py +8 -9
- waldiez/ws/session_stats.py +1 -1
- waldiez/ws/utils.py +33 -5
- {waldiez-0.5.10.dist-info → waldiez-0.6.1.dist-info}/METADATA +107 -109
- waldiez-0.6.1.dist-info/RECORD +254 -0
- waldiez/running/post_run.py +0 -180
- waldiez/running/pre_run.py +0 -159
- waldiez/running/run_results.py +0 -14
- waldiez/running/utils.py +0 -511
- waldiez-0.5.10.dist-info/RECORD +0 -248
- {waldiez-0.5.10.dist-info → waldiez-0.6.1.dist-info}/WHEEL +0 -0
- {waldiez-0.5.10.dist-info → waldiez-0.6.1.dist-info}/entry_points.txt +0 -0
- {waldiez-0.5.10.dist-info → waldiez-0.6.1.dist-info}/licenses/LICENSE +0 -0
- {waldiez-0.5.10.dist-info → waldiez-0.6.1.dist-info}/licenses/NOTICE.md +0 -0
waldiez/ws/cli.py
CHANGED
|
@@ -1,20 +1,46 @@
|
|
|
1
1
|
# SPDX-License-Identifier: Apache-2.0.
|
|
2
2
|
# Copyright (c) 2024 - 2025 Waldiez and contributors.
|
|
3
|
-
|
|
3
|
+
|
|
4
|
+
# pylint: disable=too-many-locals,unused-import,invalid-name
|
|
5
|
+
# pyright: reportUnusedImport=false,reportConstantRedefinition=false
|
|
6
|
+
# pyright: reportUnusedParameter=false, reportCallInDefaultInitializer=false
|
|
7
|
+
|
|
4
8
|
"""CLI interface for Waldiez WebSocket server."""
|
|
5
9
|
|
|
6
10
|
import asyncio
|
|
7
11
|
import logging
|
|
12
|
+
import os
|
|
8
13
|
import re
|
|
9
14
|
import sys
|
|
15
|
+
import traceback
|
|
10
16
|
from pathlib import Path
|
|
11
|
-
from typing import Annotated, Any
|
|
17
|
+
from typing import Annotated, Any
|
|
12
18
|
|
|
13
19
|
import typer
|
|
14
20
|
|
|
15
|
-
|
|
21
|
+
HAS_WATCHDOG = False
|
|
22
|
+
try:
|
|
23
|
+
from .reloader import FileWatcher # noqa: F401
|
|
24
|
+
|
|
25
|
+
HAS_WATCHDOG = True
|
|
26
|
+
except ImportError:
|
|
27
|
+
pass
|
|
28
|
+
|
|
29
|
+
HAS_WEBSOCKETS = False
|
|
30
|
+
try:
|
|
31
|
+
from .server import run_server
|
|
32
|
+
|
|
33
|
+
HAS_WEBSOCKETS = True
|
|
34
|
+
except ImportError:
|
|
35
|
+
# pylint: disable=missing-param-doc,missing-raises-doc
|
|
36
|
+
# noinspection PyUnusedLocal
|
|
37
|
+
async def run_server(*args: Any, **kwargs: Any) -> None: # type: ignore
|
|
38
|
+
"""No WebSocket server available."""
|
|
39
|
+
raise NotImplementedError("WebSocket server is not available.")
|
|
40
|
+
|
|
16
41
|
|
|
17
42
|
DEFAULT_WORKSPACE_DIR = Path.cwd()
|
|
43
|
+
DEFAULT_WS_PORT = 8765
|
|
18
44
|
|
|
19
45
|
|
|
20
46
|
def setup_logging(verbose: bool = False) -> None:
|
|
@@ -50,12 +76,27 @@ app = typer.Typer(
|
|
|
50
76
|
)
|
|
51
77
|
|
|
52
78
|
|
|
79
|
+
def _get_ws_port() -> int:
|
|
80
|
+
"""Get the default ws server port to use."""
|
|
81
|
+
vite_ws_config = os.environ.get("VITE_DEV_WS_URL", "")
|
|
82
|
+
if not vite_ws_config:
|
|
83
|
+
return DEFAULT_WS_PORT
|
|
84
|
+
# VITE_DEV_WS_URL=ws://localhost:8765
|
|
85
|
+
server_parts = vite_ws_config.rsplit(":", maxsplit=1)
|
|
86
|
+
if not server_parts:
|
|
87
|
+
return DEFAULT_WS_PORT
|
|
88
|
+
try:
|
|
89
|
+
return int(server_parts[-1])
|
|
90
|
+
except Exception: # pylint: disable=broad-exception-caught
|
|
91
|
+
return DEFAULT_WS_PORT
|
|
92
|
+
|
|
93
|
+
|
|
53
94
|
@app.command()
|
|
54
95
|
def serve(
|
|
55
96
|
host: Annotated[
|
|
56
97
|
str, typer.Option(help="Server host address")
|
|
57
98
|
] = "localhost",
|
|
58
|
-
port: Annotated[int, typer.Option(help="Server port")] =
|
|
99
|
+
port: Annotated[int, typer.Option(help="Server port")] = _get_ws_port(),
|
|
59
100
|
max_clients: Annotated[
|
|
60
101
|
int,
|
|
61
102
|
typer.Option(
|
|
@@ -63,7 +104,7 @@ def serve(
|
|
|
63
104
|
),
|
|
64
105
|
] = 1,
|
|
65
106
|
allowed_origins: Annotated[
|
|
66
|
-
|
|
107
|
+
list[str] | None,
|
|
67
108
|
typer.Option(
|
|
68
109
|
"--allowed-origin",
|
|
69
110
|
help=(
|
|
@@ -84,7 +125,7 @@ def serve(
|
|
|
84
125
|
),
|
|
85
126
|
] = False,
|
|
86
127
|
watch_dir: Annotated[
|
|
87
|
-
|
|
128
|
+
list[Path] | None,
|
|
88
129
|
typer.Option(
|
|
89
130
|
"--watch-dir",
|
|
90
131
|
help=(
|
|
@@ -154,7 +195,7 @@ def serve(
|
|
|
154
195
|
logger = logging.getLogger(__name__)
|
|
155
196
|
|
|
156
197
|
# Convert watch directories to set
|
|
157
|
-
watch_dirs:
|
|
198
|
+
watch_dirs: set[Path] | None = None
|
|
158
199
|
if watch_dir:
|
|
159
200
|
watch_dirs = set(watch_dir)
|
|
160
201
|
|
|
@@ -176,7 +217,13 @@ def serve(
|
|
|
176
217
|
"ping_timeout": ping_timeout,
|
|
177
218
|
"max_size": max_size,
|
|
178
219
|
}
|
|
179
|
-
|
|
220
|
+
if not HAS_WATCHDOG and auto_reload:
|
|
221
|
+
msg = (
|
|
222
|
+
"Auto-reload requires the 'watchdog' package. "
|
|
223
|
+
"Please install it with: pip install watchdog"
|
|
224
|
+
)
|
|
225
|
+
typer.echo(msg)
|
|
226
|
+
auto_reload = False
|
|
180
227
|
logger.info("Starting Waldiez WebSocket server...")
|
|
181
228
|
logger.info("Configuration:")
|
|
182
229
|
logger.info(" Host: %s", host)
|
|
@@ -202,8 +249,9 @@ def serve(
|
|
|
202
249
|
)
|
|
203
250
|
except KeyboardInterrupt:
|
|
204
251
|
logger.info("Server stopped by user")
|
|
205
|
-
except Exception
|
|
206
|
-
|
|
252
|
+
except Exception: # pylint: disable=broad-exception-caught
|
|
253
|
+
tb = traceback.format_exc()
|
|
254
|
+
logger.error("Server error: %s", tb)
|
|
207
255
|
sys.exit(1)
|
|
208
256
|
|
|
209
257
|
|
waldiez/ws/client_manager.py
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# SPDX-License-Identifier: Apache-2.0.
|
|
2
2
|
# Copyright (c) 2024 - 2025 Waldiez and contributors.
|
|
3
|
-
# pylint: disable=too-many-try-statements,broad-exception-caught
|
|
4
|
-
# pylint: disable=too-complex,too-many-return-statements
|
|
3
|
+
# pylint: disable=too-many-try-statements,broad-exception-caught,line-too-long
|
|
4
|
+
# pylint: disable=too-complex,too-many-return-statements,import-error
|
|
5
5
|
# pyright: reportUnknownMemberType=false,reportAttributeAccessIssue=false
|
|
6
6
|
# pyright: reportUnknownVariableType=false,reportUnknownArgumentType=false
|
|
7
|
-
# pyright: reportAssignmentType=false
|
|
7
|
+
# pyright: reportAssignmentType=false,reportUnknownParameterType=false
|
|
8
|
+
# pyright: reportArgumentType=false
|
|
8
9
|
# flake8: noqa: C901
|
|
9
10
|
"""WebSocket client manager: bridges WS <-> subprocess runner."""
|
|
10
11
|
|
|
@@ -15,7 +16,11 @@ import time
|
|
|
15
16
|
from pathlib import Path
|
|
16
17
|
from typing import Any, Callable, Literal
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
try:
|
|
20
|
+
import websockets # type: ignore[unused-ignore, unused-import, import-not-found, import-untyped] # noqa
|
|
21
|
+
except ImportError: # pragma: no cover
|
|
22
|
+
from ._mock import websockets # type: ignore[no-redef,unused-ignore]
|
|
23
|
+
|
|
19
24
|
|
|
20
25
|
from waldiez.models import Waldiez
|
|
21
26
|
from waldiez.running.subprocess_runner.runner import WaldiezSubprocessRunner
|
|
@@ -141,7 +146,10 @@ class ClientManager:
|
|
|
141
146
|
data = json.loads(json.dumps(payload, default=str))
|
|
142
147
|
await self.websocket.send(json.dumps(data))
|
|
143
148
|
return True
|
|
144
|
-
except (
|
|
149
|
+
except (
|
|
150
|
+
websockets.ConnectionClosed,
|
|
151
|
+
ConnectionResetError,
|
|
152
|
+
) as e:
|
|
145
153
|
self.logger.info("Client %s disconnected: %s", self.client_id, e)
|
|
146
154
|
await self.cleanup()
|
|
147
155
|
return False
|
|
@@ -273,8 +281,8 @@ class ClientManager:
|
|
|
273
281
|
server_status = await self.session_manager.get_status()
|
|
274
282
|
wf_status = None
|
|
275
283
|
if msg.session_id:
|
|
276
|
-
|
|
277
|
-
wf_status =
|
|
284
|
+
session = await self.session_manager.get_session(msg.session_id)
|
|
285
|
+
wf_status = session.status if session else None
|
|
278
286
|
return StatusResponse.ok(
|
|
279
287
|
server_status=server_status,
|
|
280
288
|
workflow_status=wf_status,
|
|
@@ -282,7 +290,7 @@ class ClientManager:
|
|
|
282
290
|
).model_dump(mode="json")
|
|
283
291
|
|
|
284
292
|
if isinstance(msg, SaveFlowRequest):
|
|
285
|
-
return FileRequestHandler.
|
|
293
|
+
return FileRequestHandler.handle_save_request(
|
|
286
294
|
msg=msg,
|
|
287
295
|
workspace_dir=self.workspace_dir,
|
|
288
296
|
client_id=self.client_id,
|
|
@@ -290,7 +298,7 @@ class ClientManager:
|
|
|
290
298
|
)
|
|
291
299
|
|
|
292
300
|
if isinstance(msg, ConvertWorkflowRequest):
|
|
293
|
-
return FileRequestHandler.
|
|
301
|
+
return FileRequestHandler.handle_convert_request(
|
|
294
302
|
msg=msg,
|
|
295
303
|
client_id=self.client_id,
|
|
296
304
|
workspace_dir=self.workspace_dir,
|
|
@@ -299,11 +307,11 @@ class ClientManager:
|
|
|
299
307
|
|
|
300
308
|
# Start workflow (STANDARD)
|
|
301
309
|
if isinstance(msg, RunWorkflowRequest):
|
|
302
|
-
return await self.
|
|
310
|
+
return await self._handle_run(msg)
|
|
303
311
|
|
|
304
312
|
# Start workflow (STEP-BY-STEP / DEBUG)
|
|
305
313
|
if isinstance(msg, StepRunWorkflowRequest):
|
|
306
|
-
return await self.
|
|
314
|
+
return await self._handle_step_run(msg)
|
|
307
315
|
|
|
308
316
|
# Step controls
|
|
309
317
|
if isinstance(msg, StepControlRequest):
|
|
@@ -318,24 +326,21 @@ class ClientManager:
|
|
|
318
326
|
return await self._handle_user_input(msg)
|
|
319
327
|
|
|
320
328
|
# Stop workflow
|
|
321
|
-
if hasattr(msg, "type") and msg.type == "
|
|
322
|
-
return await self.
|
|
329
|
+
if hasattr(msg, "type") and msg.type == "stop":
|
|
330
|
+
return await self.handle_stop(msg)
|
|
323
331
|
|
|
324
332
|
# Unknown
|
|
325
333
|
return self._error_to_response(
|
|
326
334
|
UnsupportedActionError(getattr(msg, "type", "unknown"))
|
|
327
335
|
)
|
|
328
336
|
|
|
329
|
-
async def
|
|
330
|
-
self, msg: RunWorkflowRequest
|
|
331
|
-
) -> dict[str, Any]:
|
|
337
|
+
async def _handle_run(self, msg: RunWorkflowRequest) -> dict[str, Any]:
|
|
332
338
|
try:
|
|
333
|
-
data_dict = json.loads(msg.
|
|
339
|
+
data_dict = json.loads(msg.data)
|
|
334
340
|
waldiez = Waldiez.from_dict(data_dict)
|
|
335
341
|
except Exception as e:
|
|
336
342
|
return RunWorkflowResponse.fail(
|
|
337
343
|
error=f"Invalid flow_data: {e}",
|
|
338
|
-
execution_mode=msg.execution_mode,
|
|
339
344
|
session_id="",
|
|
340
345
|
).model_dump(mode="json")
|
|
341
346
|
# structured path preferred
|
|
@@ -354,20 +359,19 @@ class ClientManager:
|
|
|
354
359
|
asyncio.create_task(self._run_runner(session_id, runner))
|
|
355
360
|
|
|
356
361
|
return RunWorkflowResponse.ok(
|
|
357
|
-
session_id=session_id,
|
|
362
|
+
session_id=session_id, mode=ExecutionMode.STANDARD
|
|
358
363
|
).model_dump(mode="json")
|
|
359
364
|
|
|
360
|
-
async def
|
|
365
|
+
async def _handle_step_run(
|
|
361
366
|
self, msg: StepRunWorkflowRequest
|
|
362
367
|
) -> dict[str, Any]:
|
|
363
368
|
try:
|
|
364
|
-
data_dict = json.loads(msg.
|
|
369
|
+
data_dict = json.loads(msg.data)
|
|
365
370
|
waldiez = Waldiez.from_dict(data_dict)
|
|
366
371
|
except Exception as e:
|
|
367
372
|
return StepRunWorkflowResponse.fail(
|
|
368
373
|
error=f"Invalid flow_data: {e}",
|
|
369
374
|
session_id="",
|
|
370
|
-
auto_continue=msg.auto_continue,
|
|
371
375
|
breakpoints=msg.breakpoints,
|
|
372
376
|
).model_dump(mode="json")
|
|
373
377
|
session_id = self._next_session_id()
|
|
@@ -376,6 +380,7 @@ class ClientManager:
|
|
|
376
380
|
on_output=self._mk_on_output(session_id),
|
|
377
381
|
on_input_request=self._mk_on_input_request(session_id),
|
|
378
382
|
mode="debug", # step-by-step via CLI
|
|
383
|
+
breakpoints=msg.breakpoints,
|
|
379
384
|
)
|
|
380
385
|
|
|
381
386
|
await self._create_session_for_runner(
|
|
@@ -383,13 +388,10 @@ class ClientManager:
|
|
|
383
388
|
ExecutionMode.STEP_BY_STEP,
|
|
384
389
|
session_id=session_id,
|
|
385
390
|
)
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
if sess:
|
|
390
|
-
sess.state.metadata.update(
|
|
391
|
+
session = await self.session_manager.get_session(session_id)
|
|
392
|
+
if session:
|
|
393
|
+
session.state.metadata.update(
|
|
391
394
|
{
|
|
392
|
-
"auto_continue": msg.auto_continue,
|
|
393
395
|
"breakpoints": list(msg.breakpoints),
|
|
394
396
|
}
|
|
395
397
|
)
|
|
@@ -398,7 +400,6 @@ class ClientManager:
|
|
|
398
400
|
|
|
399
401
|
return StepRunWorkflowResponse.ok(
|
|
400
402
|
session_id=session_id,
|
|
401
|
-
auto_continue=msg.auto_continue,
|
|
402
403
|
breakpoints=list(msg.breakpoints),
|
|
403
404
|
).model_dump(mode="json")
|
|
404
405
|
|
|
@@ -411,7 +412,7 @@ class ClientManager:
|
|
|
411
412
|
await self.session_manager.create_session(
|
|
412
413
|
session_id=session_id,
|
|
413
414
|
client_id=self.client_id,
|
|
414
|
-
|
|
415
|
+
mode=mode,
|
|
415
416
|
runner=runner,
|
|
416
417
|
temp_file=None,
|
|
417
418
|
metadata={},
|
|
@@ -474,17 +475,20 @@ class ClientManager:
|
|
|
474
475
|
result="",
|
|
475
476
|
session_id=msg.session_id,
|
|
476
477
|
).model_dump(mode="json")
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
478
|
+
code: str | None
|
|
479
|
+
if msg.action in {"", "c", "s", "r", "q", "i", "h", "st"}:
|
|
480
|
+
code = msg.action if msg.action else "c"
|
|
481
|
+
else:
|
|
482
|
+
code = {
|
|
483
|
+
"": "c",
|
|
484
|
+
"continue": "c",
|
|
485
|
+
"step": "s",
|
|
486
|
+
"run": "r",
|
|
487
|
+
"quit": "q",
|
|
488
|
+
"info": "i",
|
|
489
|
+
"help": "h",
|
|
490
|
+
"stats": "st",
|
|
491
|
+
}.get(msg.action)
|
|
488
492
|
|
|
489
493
|
if not code:
|
|
490
494
|
return StepControlResponse.fail(
|
|
@@ -552,7 +556,19 @@ class ClientManager:
|
|
|
552
556
|
runner.provide_user_input(msg.data)
|
|
553
557
|
return {"type": "ok", "success": True}
|
|
554
558
|
|
|
555
|
-
async def
|
|
559
|
+
async def handle_stop(self, msg: Any) -> dict[str, Any]:
|
|
560
|
+
"""Handle stop request.
|
|
561
|
+
|
|
562
|
+
Parameters
|
|
563
|
+
----------
|
|
564
|
+
msg : Any
|
|
565
|
+
The stop request.
|
|
566
|
+
|
|
567
|
+
Returns
|
|
568
|
+
-------
|
|
569
|
+
dict[str, Any]
|
|
570
|
+
The processing result to respond with.
|
|
571
|
+
"""
|
|
556
572
|
session_id = getattr(msg, "session_id", "")
|
|
557
573
|
runner = self._runners.get(session_id)
|
|
558
574
|
if not runner:
|
|
@@ -566,7 +582,7 @@ class ClientManager:
|
|
|
566
582
|
session_id, WorkflowStatus.STOPPING
|
|
567
583
|
)
|
|
568
584
|
return {
|
|
569
|
-
"type": "
|
|
585
|
+
"type": "stop_response",
|
|
570
586
|
"session_id": session_id,
|
|
571
587
|
"success": True,
|
|
572
588
|
"forced": getattr(msg, "force", False),
|
|
@@ -596,6 +612,7 @@ class ClientManager:
|
|
|
596
612
|
- anything else
|
|
597
613
|
-> fallback stdout SubprocessOutputNotification
|
|
598
614
|
"""
|
|
615
|
+
# self.logger.debug("Handling runner output: %s", data)
|
|
599
616
|
try:
|
|
600
617
|
msg_type = str(data.get("type", "")).lower()
|
|
601
618
|
session_id_raw = data.get("session_id")
|
|
@@ -606,7 +623,9 @@ class ClientManager:
|
|
|
606
623
|
)
|
|
607
624
|
|
|
608
625
|
if msg_type in ("input_request", "debug_input_request"):
|
|
609
|
-
await self._handle_runner_input_request(
|
|
626
|
+
await self._handle_runner_input_request(
|
|
627
|
+
session_id, data, is_debug=msg_type == "debug_input_request"
|
|
628
|
+
)
|
|
610
629
|
return
|
|
611
630
|
|
|
612
631
|
if msg_type.startswith("debug_"):
|
|
@@ -646,7 +665,10 @@ class ClientManager:
|
|
|
646
665
|
)
|
|
647
666
|
|
|
648
667
|
async def _handle_runner_input_request(
|
|
649
|
-
self,
|
|
668
|
+
self,
|
|
669
|
+
session_id: str,
|
|
670
|
+
data: dict[str, Any],
|
|
671
|
+
is_debug: bool,
|
|
650
672
|
) -> None:
|
|
651
673
|
"""Handle an input request from the runner."""
|
|
652
674
|
request_id = str(data.get("request_id", ""))
|
|
@@ -657,15 +679,17 @@ class ClientManager:
|
|
|
657
679
|
await self.session_manager.update_session_status(
|
|
658
680
|
session_id, WorkflowStatus.INPUT_WAITING
|
|
659
681
|
)
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
timeout=float(data.get("timeout", 120.0)),
|
|
667
|
-
)
|
|
682
|
+
notification = UserInputRequestNotification(
|
|
683
|
+
session_id=session_id,
|
|
684
|
+
request_id=request_id,
|
|
685
|
+
prompt=prompt,
|
|
686
|
+
password=bool(data.get("password", False)),
|
|
687
|
+
timeout=float(data.get("timeout", 120.0)),
|
|
668
688
|
)
|
|
689
|
+
msg_dump = notification.model_dump(mode="json", fallback=str)
|
|
690
|
+
if is_debug:
|
|
691
|
+
msg_dump["type"] = "debug_input_request"
|
|
692
|
+
await self.send_message(msg_dump)
|
|
669
693
|
|
|
670
694
|
# pylint: disable=line-too-long
|
|
671
695
|
async def _handle_runner_debug(
|
waldiez/ws/errors.py
CHANGED
waldiez/ws/models.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# SPDX-License-Identifier: Apache-2.0.
|
|
2
2
|
# Copyright (c) 2024 - 2025 Waldiez and contributors.
|
|
3
|
-
# pylint: disable=broad-exception-caught,no-member
|
|
4
|
-
# pyright: reportUnknownVariableType=false
|
|
3
|
+
# pylint: disable=broad-exception-caught,no-member,invalid-name
|
|
4
|
+
# pyright: reportUnknownVariableType=false,reportAny=false
|
|
5
|
+
# pyright: reportDeprecated=false, reportUnannotatedClassAttribute=false
|
|
5
6
|
"""Session management models for WebSocket workflow execution."""
|
|
6
7
|
|
|
7
8
|
import json
|
|
@@ -119,42 +120,51 @@ class BaseNotification(BaseWaldiezMessage):
|
|
|
119
120
|
class SaveFlowRequest(BaseRequest):
|
|
120
121
|
"""Request to save a workflow."""
|
|
121
122
|
|
|
122
|
-
type: Literal["
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
123
|
+
type: Literal["save"] = "save"
|
|
124
|
+
data: str # JSON string of workflow
|
|
125
|
+
path: str | None = None
|
|
126
|
+
force: bool = False
|
|
126
127
|
|
|
127
128
|
|
|
128
129
|
class RunWorkflowRequest(BaseRequest):
|
|
129
130
|
"""Request to run a workflow in standard mode."""
|
|
130
131
|
|
|
131
|
-
type: Literal["
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
structured_io: bool = True
|
|
135
|
-
uploads_root: str | None = None
|
|
136
|
-
dot_env_path: str | None = None
|
|
137
|
-
output_path: str | None = None
|
|
132
|
+
type: Literal["run"] = "run"
|
|
133
|
+
data: str # JSON string of workflow
|
|
134
|
+
path: str | None = None
|
|
138
135
|
|
|
139
136
|
|
|
140
137
|
class StepRunWorkflowRequest(BaseRequest):
|
|
141
138
|
"""Request to run a workflow in step-by-step mode."""
|
|
142
139
|
|
|
143
|
-
type: Literal["
|
|
144
|
-
|
|
145
|
-
auto_continue: bool = False
|
|
140
|
+
type: Literal["step_run"] = "step_run"
|
|
141
|
+
data: str # JSON string of workflow
|
|
146
142
|
breakpoints: list[str] = Field(default_factory=list)
|
|
147
|
-
|
|
148
|
-
uploads_root: str | None = None
|
|
149
|
-
dot_env_path: str | None = None
|
|
150
|
-
output_path: str | None = None
|
|
143
|
+
path: str | None = None
|
|
151
144
|
|
|
152
145
|
|
|
153
146
|
class StepControlRequest(BaseRequest):
|
|
154
147
|
"""Request to control step-by-step execution."""
|
|
155
148
|
|
|
156
149
|
type: Literal["step_control"] = "step_control"
|
|
157
|
-
action: Literal[
|
|
150
|
+
action: Literal[
|
|
151
|
+
"continue",
|
|
152
|
+
"step",
|
|
153
|
+
"run",
|
|
154
|
+
"quit",
|
|
155
|
+
"info",
|
|
156
|
+
"help",
|
|
157
|
+
"stats",
|
|
158
|
+
"",
|
|
159
|
+
"c",
|
|
160
|
+
"s",
|
|
161
|
+
"r",
|
|
162
|
+
"q",
|
|
163
|
+
"i",
|
|
164
|
+
"h",
|
|
165
|
+
"?",
|
|
166
|
+
"st",
|
|
167
|
+
]
|
|
158
168
|
session_id: str
|
|
159
169
|
|
|
160
170
|
|
|
@@ -190,7 +200,7 @@ class UserInputResponse(BaseRequest):
|
|
|
190
200
|
class StopWorkflowRequest(BaseRequest):
|
|
191
201
|
"""Request to stop workflow execution."""
|
|
192
202
|
|
|
193
|
-
type: Literal["
|
|
203
|
+
type: Literal["stop"] = "stop"
|
|
194
204
|
session_id: str
|
|
195
205
|
force: bool = False
|
|
196
206
|
|
|
@@ -199,7 +209,7 @@ class StopWorkflowResponse(BaseResponse):
|
|
|
199
209
|
"""Response to stop workflow request."""
|
|
200
210
|
|
|
201
211
|
session_id: str
|
|
202
|
-
type: Literal["
|
|
212
|
+
type: Literal["stop_response"] = "stop_response"
|
|
203
213
|
error: str | None = None
|
|
204
214
|
forced: bool = False
|
|
205
215
|
|
|
@@ -207,10 +217,10 @@ class StopWorkflowResponse(BaseResponse):
|
|
|
207
217
|
class ConvertWorkflowRequest(BaseRequest):
|
|
208
218
|
"""Request to convert workflow to different format."""
|
|
209
219
|
|
|
210
|
-
type: Literal["
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
220
|
+
type: Literal["convert"] = "convert"
|
|
221
|
+
data: str
|
|
222
|
+
format: Literal["py", "ipynb"]
|
|
223
|
+
path: str | None = None
|
|
214
224
|
|
|
215
225
|
|
|
216
226
|
class UploadFileRequest(BaseRequest):
|
|
@@ -245,26 +255,24 @@ class GetStatusRequest(BaseRequest):
|
|
|
245
255
|
class SaveFlowResponse(BaseResponse):
|
|
246
256
|
"""Response to save flow request."""
|
|
247
257
|
|
|
248
|
-
type: Literal["
|
|
249
|
-
|
|
258
|
+
type: Literal["save_response"] = "save_response"
|
|
259
|
+
path: str | None = None
|
|
250
260
|
error: str | None = None
|
|
251
261
|
|
|
252
262
|
|
|
253
263
|
class RunWorkflowResponse(BaseResponse):
|
|
254
264
|
"""Response to run workflow request."""
|
|
255
265
|
|
|
256
|
-
type: Literal["
|
|
266
|
+
type: Literal["run_response"] = "run_response"
|
|
257
267
|
session_id: str
|
|
258
|
-
execution_mode: ExecutionMode
|
|
259
268
|
error: str | None = None
|
|
260
269
|
|
|
261
270
|
|
|
262
271
|
class StepRunWorkflowResponse(BaseResponse):
|
|
263
272
|
"""Response to step run workflow request."""
|
|
264
273
|
|
|
265
|
-
type: Literal["
|
|
274
|
+
type: Literal["step_run_response"] = "step_run_response"
|
|
266
275
|
session_id: str
|
|
267
|
-
auto_continue: bool
|
|
268
276
|
breakpoints: list[str]
|
|
269
277
|
error: str | None = None
|
|
270
278
|
|
|
@@ -291,9 +299,9 @@ class BreakpointResponse(BaseResponse):
|
|
|
291
299
|
class ConvertWorkflowResponse(BaseResponse):
|
|
292
300
|
"""Response to convert workflow request."""
|
|
293
301
|
|
|
294
|
-
type: Literal["
|
|
295
|
-
|
|
296
|
-
|
|
302
|
+
type: Literal["convert_response"] = "convert_response"
|
|
303
|
+
format: str
|
|
304
|
+
path: str | None = None
|
|
297
305
|
error: str | None = None
|
|
298
306
|
|
|
299
307
|
|
|
@@ -301,8 +309,8 @@ class UploadFileResponse(BaseResponse):
|
|
|
301
309
|
"""Response to file upload."""
|
|
302
310
|
|
|
303
311
|
type: Literal["upload_file_response"] = "upload_file_response"
|
|
304
|
-
|
|
305
|
-
|
|
312
|
+
path: str | None = None
|
|
313
|
+
size: int | None = None
|
|
306
314
|
error: str | None = None
|
|
307
315
|
|
|
308
316
|
|
|
@@ -344,7 +352,7 @@ class WorkflowStatusNotification(BaseNotification):
|
|
|
344
352
|
type: Literal["workflow_status"] = "workflow_status"
|
|
345
353
|
session_id: str
|
|
346
354
|
status: WorkflowStatus
|
|
347
|
-
|
|
355
|
+
mode: ExecutionMode
|
|
348
356
|
details: str | None = None
|
|
349
357
|
|
|
350
358
|
@classmethod
|
|
@@ -376,7 +384,7 @@ class WorkflowStatusNotification(BaseNotification):
|
|
|
376
384
|
return cls(
|
|
377
385
|
session_id=session_id,
|
|
378
386
|
status=status,
|
|
379
|
-
|
|
387
|
+
mode=mode,
|
|
380
388
|
details=details,
|
|
381
389
|
)
|
|
382
390
|
|
|
@@ -721,14 +729,14 @@ def create_session_id() -> str:
|
|
|
721
729
|
# ========================================
|
|
722
730
|
|
|
723
731
|
CLIENT_MESSAGE_TYPES = {
|
|
724
|
-
"
|
|
725
|
-
"
|
|
726
|
-
"
|
|
732
|
+
"save",
|
|
733
|
+
"run",
|
|
734
|
+
"step_run",
|
|
727
735
|
"step_control",
|
|
728
736
|
"breakpoint_control",
|
|
729
737
|
"user_input",
|
|
730
|
-
"
|
|
731
|
-
"
|
|
738
|
+
"stop",
|
|
739
|
+
"convert",
|
|
732
740
|
"upload_file",
|
|
733
741
|
"ping",
|
|
734
742
|
"get_status",
|
|
@@ -736,12 +744,12 @@ CLIENT_MESSAGE_TYPES = {
|
|
|
736
744
|
|
|
737
745
|
SERVER_MESSAGE_TYPES = {
|
|
738
746
|
# Responses
|
|
739
|
-
"
|
|
740
|
-
"
|
|
741
|
-
"
|
|
747
|
+
"save_response",
|
|
748
|
+
"run_response",
|
|
749
|
+
"step_run_response",
|
|
742
750
|
"step_control_response",
|
|
743
751
|
"breakpoint_response",
|
|
744
|
-
"
|
|
752
|
+
"convert_response",
|
|
745
753
|
"upload_file_response",
|
|
746
754
|
"pong",
|
|
747
755
|
"status_response",
|
|
@@ -769,7 +777,7 @@ class SessionState(BaseModel):
|
|
|
769
777
|
session_id: str
|
|
770
778
|
client_id: str
|
|
771
779
|
status: WorkflowStatus
|
|
772
|
-
|
|
780
|
+
mode: ExecutionMode
|
|
773
781
|
start_time: int = Field(default_factory=time.monotonic_ns)
|
|
774
782
|
end_time: int | None = None
|
|
775
783
|
metadata: dict[str, Any] = Field(default_factory=dict)
|
|
@@ -828,7 +836,7 @@ class SessionState(BaseModel):
|
|
|
828
836
|
"session_id": self.session_id,
|
|
829
837
|
"client_id": self.client_id,
|
|
830
838
|
"status": self.status.value,
|
|
831
|
-
"
|
|
839
|
+
"mode": self.mode.value,
|
|
832
840
|
"duration_seconds": self.duration,
|
|
833
841
|
"start_time": self.start_time,
|
|
834
842
|
"end_time": self.end_time,
|
|
@@ -898,9 +906,9 @@ class WorkflowSession:
|
|
|
898
906
|
return self._state.status
|
|
899
907
|
|
|
900
908
|
@property
|
|
901
|
-
def
|
|
909
|
+
def mode(self) -> ExecutionMode:
|
|
902
910
|
"""Get execution mode."""
|
|
903
|
-
return self._state.
|
|
911
|
+
return self._state.mode
|
|
904
912
|
|
|
905
913
|
@property
|
|
906
914
|
def temp_file(self) -> Path | None:
|