waldiez 0.6.0__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 +18 -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 +9 -10
- 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 +34 -23
- waldiez/exporting/agent/extras/group_member_extras.py +6 -5
- waldiez/exporting/agent/extras/handoffs/after_work.py +1 -1
- waldiez/exporting/agent/extras/handoffs/available.py +1 -1
- waldiez/exporting/agent/extras/handoffs/condition.py +3 -2
- waldiez/exporting/agent/extras/handoffs/handoff.py +1 -1
- waldiez/exporting/agent/extras/handoffs/target.py +6 -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/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/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 +2 -2
- waldiez/exporting/core/protocols.py +6 -5
- waldiez/exporting/core/result.py +25 -28
- waldiez/exporting/core/types.py +10 -10
- waldiez/exporting/core/utils/llm_config.py +2 -2
- waldiez/exporting/core/validation.py +10 -11
- waldiez/exporting/flow/execution_generator.py +98 -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 +5 -5
- waldiez/exporting/flow/utils/importing.py +6 -7
- waldiez/exporting/flow/utils/linting.py +25 -9
- waldiez/exporting/flow/utils/logging.py +2 -2
- 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 +8 -4
- waldiez/io/_ws.py +10 -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 +52 -53
- waldiez/io/utils.py +3 -0
- waldiez/io/ws.py +5 -1
- waldiez/logger.py +16 -3
- waldiez/models/agents/__init__.py +3 -0
- waldiez/models/agents/agent/agent.py +23 -16
- 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 +27 -20
- waldiez/models/chat/chat_data.py +22 -19
- 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/dict_utils.py +8 -6
- waldiez/models/common/handoff.py +18 -17
- waldiez/models/common/method_utils.py +7 -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 +5 -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 +1 -0
- waldiez/models/tool/predefined/_google.py +1 -0
- waldiez/models/tool/predefined/_perplexity.py +1 -0
- waldiez/models/tool/predefined/_searxng.py +1 -0
- waldiez/models/tool/predefined/_tavily.py +1 -0
- waldiez/models/tool/predefined/_wikipedia.py +1 -0
- 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 +117 -264
- 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 +22 -25
- waldiez/running/step_by_step/breakpoints_mixin.py +192 -60
- waldiez/running/step_by_step/command_handler.py +3 -0
- waldiez/running/step_by_step/events_processor.py +194 -14
- waldiez/running/step_by_step/step_by_step_models.py +110 -43
- waldiez/running/step_by_step/step_by_step_runner.py +107 -57
- waldiez/running/subprocess_runner/__base__.py +9 -1
- waldiez/running/subprocess_runner/_async_runner.py +5 -3
- waldiez/running/subprocess_runner/_sync_runner.py +6 -2
- waldiez/running/subprocess_runner/runner.py +39 -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/_file_handler.py +18 -18
- waldiez/ws/_mock.py +2 -1
- waldiez/ws/cli.py +36 -12
- waldiez/ws/client_manager.py +35 -27
- waldiez/ws/errors.py +3 -0
- waldiez/ws/models.py +43 -52
- waldiez/ws/reloader.py +12 -4
- waldiez/ws/server.py +85 -55
- waldiez/ws/session_manager.py +8 -9
- waldiez/ws/session_stats.py +1 -1
- waldiez/ws/utils.py +4 -1
- {waldiez-0.6.0.dist-info → waldiez-0.6.1.dist-info}/METADATA +82 -93
- waldiez-0.6.1.dist-info/RECORD +254 -0
- waldiez/running/post_run.py +0 -186
- waldiez/running/pre_run.py +0 -281
- waldiez/running/run_results.py +0 -14
- waldiez/running/utils.py +0 -625
- waldiez-0.6.0.dist-info/RECORD +0 -251
- {waldiez-0.6.0.dist-info → waldiez-0.6.1.dist-info}/WHEEL +0 -0
- {waldiez-0.6.0.dist-info → waldiez-0.6.1.dist-info}/entry_points.txt +0 -0
- {waldiez-0.6.0.dist-info → waldiez-0.6.1.dist-info}/licenses/LICENSE +0 -0
- {waldiez-0.6.0.dist-info → waldiez-0.6.1.dist-info}/licenses/NOTICE.md +0 -0
waldiez/ws/cli.py
CHANGED
|
@@ -1,22 +1,28 @@
|
|
|
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
|
|
16
22
|
try:
|
|
17
|
-
from .reloader import FileWatcher #
|
|
23
|
+
from .reloader import FileWatcher # noqa: F401
|
|
18
24
|
|
|
19
|
-
HAS_WATCHDOG = True
|
|
25
|
+
HAS_WATCHDOG = True
|
|
20
26
|
except ImportError:
|
|
21
27
|
pass
|
|
22
28
|
|
|
@@ -24,7 +30,7 @@ HAS_WEBSOCKETS = False
|
|
|
24
30
|
try:
|
|
25
31
|
from .server import run_server
|
|
26
32
|
|
|
27
|
-
HAS_WEBSOCKETS = True
|
|
33
|
+
HAS_WEBSOCKETS = True
|
|
28
34
|
except ImportError:
|
|
29
35
|
# pylint: disable=missing-param-doc,missing-raises-doc
|
|
30
36
|
# noinspection PyUnusedLocal
|
|
@@ -34,6 +40,7 @@ except ImportError:
|
|
|
34
40
|
|
|
35
41
|
|
|
36
42
|
DEFAULT_WORKSPACE_DIR = Path.cwd()
|
|
43
|
+
DEFAULT_WS_PORT = 8765
|
|
37
44
|
|
|
38
45
|
|
|
39
46
|
def setup_logging(verbose: bool = False) -> None:
|
|
@@ -69,12 +76,27 @@ app = typer.Typer(
|
|
|
69
76
|
)
|
|
70
77
|
|
|
71
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
|
+
|
|
72
94
|
@app.command()
|
|
73
95
|
def serve(
|
|
74
96
|
host: Annotated[
|
|
75
97
|
str, typer.Option(help="Server host address")
|
|
76
98
|
] = "localhost",
|
|
77
|
-
port: Annotated[int, typer.Option(help="Server port")] =
|
|
99
|
+
port: Annotated[int, typer.Option(help="Server port")] = _get_ws_port(),
|
|
78
100
|
max_clients: Annotated[
|
|
79
101
|
int,
|
|
80
102
|
typer.Option(
|
|
@@ -82,7 +104,7 @@ def serve(
|
|
|
82
104
|
),
|
|
83
105
|
] = 1,
|
|
84
106
|
allowed_origins: Annotated[
|
|
85
|
-
|
|
107
|
+
list[str] | None,
|
|
86
108
|
typer.Option(
|
|
87
109
|
"--allowed-origin",
|
|
88
110
|
help=(
|
|
@@ -103,7 +125,7 @@ def serve(
|
|
|
103
125
|
),
|
|
104
126
|
] = False,
|
|
105
127
|
watch_dir: Annotated[
|
|
106
|
-
|
|
128
|
+
list[Path] | None,
|
|
107
129
|
typer.Option(
|
|
108
130
|
"--watch-dir",
|
|
109
131
|
help=(
|
|
@@ -173,7 +195,7 @@ def serve(
|
|
|
173
195
|
logger = logging.getLogger(__name__)
|
|
174
196
|
|
|
175
197
|
# Convert watch directories to set
|
|
176
|
-
watch_dirs:
|
|
198
|
+
watch_dirs: set[Path] | None = None
|
|
177
199
|
if watch_dir:
|
|
178
200
|
watch_dirs = set(watch_dir)
|
|
179
201
|
|
|
@@ -196,10 +218,11 @@ def serve(
|
|
|
196
218
|
"max_size": max_size,
|
|
197
219
|
}
|
|
198
220
|
if not HAS_WATCHDOG and auto_reload:
|
|
199
|
-
|
|
221
|
+
msg = (
|
|
200
222
|
"Auto-reload requires the 'watchdog' package. "
|
|
201
223
|
"Please install it with: pip install watchdog"
|
|
202
224
|
)
|
|
225
|
+
typer.echo(msg)
|
|
203
226
|
auto_reload = False
|
|
204
227
|
logger.info("Starting Waldiez WebSocket server...")
|
|
205
228
|
logger.info("Configuration:")
|
|
@@ -226,8 +249,9 @@ def serve(
|
|
|
226
249
|
)
|
|
227
250
|
except KeyboardInterrupt:
|
|
228
251
|
logger.info("Server stopped by user")
|
|
229
|
-
except Exception
|
|
230
|
-
|
|
252
|
+
except Exception: # pylint: disable=broad-exception-caught
|
|
253
|
+
tb = traceback.format_exc()
|
|
254
|
+
logger.error("Server error: %s", tb)
|
|
231
255
|
sys.exit(1)
|
|
232
256
|
|
|
233
257
|
|
waldiez/ws/client_manager.py
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
# pyright: reportUnknownMemberType=false,reportAttributeAccessIssue=false
|
|
6
6
|
# pyright: reportUnknownVariableType=false,reportUnknownArgumentType=false
|
|
7
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
|
|
|
@@ -71,7 +72,7 @@ class ClientManager:
|
|
|
71
72
|
|
|
72
73
|
def __init__(
|
|
73
74
|
self,
|
|
74
|
-
websocket: websockets.ServerConnection,
|
|
75
|
+
websocket: websockets.ServerConnection,
|
|
75
76
|
client_id: str,
|
|
76
77
|
session_manager: SessionManager,
|
|
77
78
|
workspace_dir: Path = CWD,
|
|
@@ -148,7 +149,7 @@ class ClientManager:
|
|
|
148
149
|
except (
|
|
149
150
|
websockets.ConnectionClosed,
|
|
150
151
|
ConnectionResetError,
|
|
151
|
-
) as e:
|
|
152
|
+
) as e:
|
|
152
153
|
self.logger.info("Client %s disconnected: %s", self.client_id, e)
|
|
153
154
|
await self.cleanup()
|
|
154
155
|
return False
|
|
@@ -280,8 +281,8 @@ class ClientManager:
|
|
|
280
281
|
server_status = await self.session_manager.get_status()
|
|
281
282
|
wf_status = None
|
|
282
283
|
if msg.session_id:
|
|
283
|
-
|
|
284
|
-
wf_status =
|
|
284
|
+
session = await self.session_manager.get_session(msg.session_id)
|
|
285
|
+
wf_status = session.status if session else None
|
|
285
286
|
return StatusResponse.ok(
|
|
286
287
|
server_status=server_status,
|
|
287
288
|
workflow_status=wf_status,
|
|
@@ -289,7 +290,7 @@ class ClientManager:
|
|
|
289
290
|
).model_dump(mode="json")
|
|
290
291
|
|
|
291
292
|
if isinstance(msg, SaveFlowRequest):
|
|
292
|
-
return FileRequestHandler.
|
|
293
|
+
return FileRequestHandler.handle_save_request(
|
|
293
294
|
msg=msg,
|
|
294
295
|
workspace_dir=self.workspace_dir,
|
|
295
296
|
client_id=self.client_id,
|
|
@@ -297,7 +298,7 @@ class ClientManager:
|
|
|
297
298
|
)
|
|
298
299
|
|
|
299
300
|
if isinstance(msg, ConvertWorkflowRequest):
|
|
300
|
-
return FileRequestHandler.
|
|
301
|
+
return FileRequestHandler.handle_convert_request(
|
|
301
302
|
msg=msg,
|
|
302
303
|
client_id=self.client_id,
|
|
303
304
|
workspace_dir=self.workspace_dir,
|
|
@@ -306,11 +307,11 @@ class ClientManager:
|
|
|
306
307
|
|
|
307
308
|
# Start workflow (STANDARD)
|
|
308
309
|
if isinstance(msg, RunWorkflowRequest):
|
|
309
|
-
return await self.
|
|
310
|
+
return await self._handle_run(msg)
|
|
310
311
|
|
|
311
312
|
# Start workflow (STEP-BY-STEP / DEBUG)
|
|
312
313
|
if isinstance(msg, StepRunWorkflowRequest):
|
|
313
|
-
return await self.
|
|
314
|
+
return await self._handle_step_run(msg)
|
|
314
315
|
|
|
315
316
|
# Step controls
|
|
316
317
|
if isinstance(msg, StepControlRequest):
|
|
@@ -325,24 +326,21 @@ class ClientManager:
|
|
|
325
326
|
return await self._handle_user_input(msg)
|
|
326
327
|
|
|
327
328
|
# Stop workflow
|
|
328
|
-
if hasattr(msg, "type") and msg.type == "
|
|
329
|
-
return await self.
|
|
329
|
+
if hasattr(msg, "type") and msg.type == "stop":
|
|
330
|
+
return await self.handle_stop(msg)
|
|
330
331
|
|
|
331
332
|
# Unknown
|
|
332
333
|
return self._error_to_response(
|
|
333
334
|
UnsupportedActionError(getattr(msg, "type", "unknown"))
|
|
334
335
|
)
|
|
335
336
|
|
|
336
|
-
async def
|
|
337
|
-
self, msg: RunWorkflowRequest
|
|
338
|
-
) -> dict[str, Any]:
|
|
337
|
+
async def _handle_run(self, msg: RunWorkflowRequest) -> dict[str, Any]:
|
|
339
338
|
try:
|
|
340
|
-
data_dict = json.loads(msg.
|
|
339
|
+
data_dict = json.loads(msg.data)
|
|
341
340
|
waldiez = Waldiez.from_dict(data_dict)
|
|
342
341
|
except Exception as e:
|
|
343
342
|
return RunWorkflowResponse.fail(
|
|
344
343
|
error=f"Invalid flow_data: {e}",
|
|
345
|
-
execution_mode=msg.execution_mode,
|
|
346
344
|
session_id="",
|
|
347
345
|
).model_dump(mode="json")
|
|
348
346
|
# structured path preferred
|
|
@@ -361,20 +359,19 @@ class ClientManager:
|
|
|
361
359
|
asyncio.create_task(self._run_runner(session_id, runner))
|
|
362
360
|
|
|
363
361
|
return RunWorkflowResponse.ok(
|
|
364
|
-
session_id=session_id,
|
|
362
|
+
session_id=session_id, mode=ExecutionMode.STANDARD
|
|
365
363
|
).model_dump(mode="json")
|
|
366
364
|
|
|
367
|
-
async def
|
|
365
|
+
async def _handle_step_run(
|
|
368
366
|
self, msg: StepRunWorkflowRequest
|
|
369
367
|
) -> dict[str, Any]:
|
|
370
368
|
try:
|
|
371
|
-
data_dict = json.loads(msg.
|
|
369
|
+
data_dict = json.loads(msg.data)
|
|
372
370
|
waldiez = Waldiez.from_dict(data_dict)
|
|
373
371
|
except Exception as e:
|
|
374
372
|
return StepRunWorkflowResponse.fail(
|
|
375
373
|
error=f"Invalid flow_data: {e}",
|
|
376
374
|
session_id="",
|
|
377
|
-
auto_continue=msg.auto_continue,
|
|
378
375
|
breakpoints=msg.breakpoints,
|
|
379
376
|
).model_dump(mode="json")
|
|
380
377
|
session_id = self._next_session_id()
|
|
@@ -383,6 +380,7 @@ class ClientManager:
|
|
|
383
380
|
on_output=self._mk_on_output(session_id),
|
|
384
381
|
on_input_request=self._mk_on_input_request(session_id),
|
|
385
382
|
mode="debug", # step-by-step via CLI
|
|
383
|
+
breakpoints=msg.breakpoints,
|
|
386
384
|
)
|
|
387
385
|
|
|
388
386
|
await self._create_session_for_runner(
|
|
@@ -390,11 +388,10 @@ class ClientManager:
|
|
|
390
388
|
ExecutionMode.STEP_BY_STEP,
|
|
391
389
|
session_id=session_id,
|
|
392
390
|
)
|
|
393
|
-
|
|
394
|
-
if
|
|
395
|
-
|
|
391
|
+
session = await self.session_manager.get_session(session_id)
|
|
392
|
+
if session:
|
|
393
|
+
session.state.metadata.update(
|
|
396
394
|
{
|
|
397
|
-
"auto_continue": msg.auto_continue,
|
|
398
395
|
"breakpoints": list(msg.breakpoints),
|
|
399
396
|
}
|
|
400
397
|
)
|
|
@@ -403,7 +400,6 @@ class ClientManager:
|
|
|
403
400
|
|
|
404
401
|
return StepRunWorkflowResponse.ok(
|
|
405
402
|
session_id=session_id,
|
|
406
|
-
auto_continue=msg.auto_continue,
|
|
407
403
|
breakpoints=list(msg.breakpoints),
|
|
408
404
|
).model_dump(mode="json")
|
|
409
405
|
|
|
@@ -416,7 +412,7 @@ class ClientManager:
|
|
|
416
412
|
await self.session_manager.create_session(
|
|
417
413
|
session_id=session_id,
|
|
418
414
|
client_id=self.client_id,
|
|
419
|
-
|
|
415
|
+
mode=mode,
|
|
420
416
|
runner=runner,
|
|
421
417
|
temp_file=None,
|
|
422
418
|
metadata={},
|
|
@@ -560,7 +556,19 @@ class ClientManager:
|
|
|
560
556
|
runner.provide_user_input(msg.data)
|
|
561
557
|
return {"type": "ok", "success": True}
|
|
562
558
|
|
|
563
|
-
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
|
+
"""
|
|
564
572
|
session_id = getattr(msg, "session_id", "")
|
|
565
573
|
runner = self._runners.get(session_id)
|
|
566
574
|
if not runner:
|
|
@@ -574,7 +582,7 @@ class ClientManager:
|
|
|
574
582
|
session_id, WorkflowStatus.STOPPING
|
|
575
583
|
)
|
|
576
584
|
return {
|
|
577
|
-
"type": "
|
|
585
|
+
"type": "stop_response",
|
|
578
586
|
"session_id": session_id,
|
|
579
587
|
"success": True,
|
|
580
588
|
"forced": getattr(msg, "force", False),
|
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,35 +120,27 @@ 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):
|
|
@@ -207,7 +200,7 @@ class UserInputResponse(BaseRequest):
|
|
|
207
200
|
class StopWorkflowRequest(BaseRequest):
|
|
208
201
|
"""Request to stop workflow execution."""
|
|
209
202
|
|
|
210
|
-
type: Literal["
|
|
203
|
+
type: Literal["stop"] = "stop"
|
|
211
204
|
session_id: str
|
|
212
205
|
force: bool = False
|
|
213
206
|
|
|
@@ -216,7 +209,7 @@ class StopWorkflowResponse(BaseResponse):
|
|
|
216
209
|
"""Response to stop workflow request."""
|
|
217
210
|
|
|
218
211
|
session_id: str
|
|
219
|
-
type: Literal["
|
|
212
|
+
type: Literal["stop_response"] = "stop_response"
|
|
220
213
|
error: str | None = None
|
|
221
214
|
forced: bool = False
|
|
222
215
|
|
|
@@ -224,10 +217,10 @@ class StopWorkflowResponse(BaseResponse):
|
|
|
224
217
|
class ConvertWorkflowRequest(BaseRequest):
|
|
225
218
|
"""Request to convert workflow to different format."""
|
|
226
219
|
|
|
227
|
-
type: Literal["
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
220
|
+
type: Literal["convert"] = "convert"
|
|
221
|
+
data: str
|
|
222
|
+
format: Literal["py", "ipynb"]
|
|
223
|
+
path: str | None = None
|
|
231
224
|
|
|
232
225
|
|
|
233
226
|
class UploadFileRequest(BaseRequest):
|
|
@@ -262,26 +255,24 @@ class GetStatusRequest(BaseRequest):
|
|
|
262
255
|
class SaveFlowResponse(BaseResponse):
|
|
263
256
|
"""Response to save flow request."""
|
|
264
257
|
|
|
265
|
-
type: Literal["
|
|
266
|
-
|
|
258
|
+
type: Literal["save_response"] = "save_response"
|
|
259
|
+
path: str | None = None
|
|
267
260
|
error: str | None = None
|
|
268
261
|
|
|
269
262
|
|
|
270
263
|
class RunWorkflowResponse(BaseResponse):
|
|
271
264
|
"""Response to run workflow request."""
|
|
272
265
|
|
|
273
|
-
type: Literal["
|
|
266
|
+
type: Literal["run_response"] = "run_response"
|
|
274
267
|
session_id: str
|
|
275
|
-
execution_mode: ExecutionMode
|
|
276
268
|
error: str | None = None
|
|
277
269
|
|
|
278
270
|
|
|
279
271
|
class StepRunWorkflowResponse(BaseResponse):
|
|
280
272
|
"""Response to step run workflow request."""
|
|
281
273
|
|
|
282
|
-
type: Literal["
|
|
274
|
+
type: Literal["step_run_response"] = "step_run_response"
|
|
283
275
|
session_id: str
|
|
284
|
-
auto_continue: bool
|
|
285
276
|
breakpoints: list[str]
|
|
286
277
|
error: str | None = None
|
|
287
278
|
|
|
@@ -308,9 +299,9 @@ class BreakpointResponse(BaseResponse):
|
|
|
308
299
|
class ConvertWorkflowResponse(BaseResponse):
|
|
309
300
|
"""Response to convert workflow request."""
|
|
310
301
|
|
|
311
|
-
type: Literal["
|
|
312
|
-
|
|
313
|
-
|
|
302
|
+
type: Literal["convert_response"] = "convert_response"
|
|
303
|
+
format: str
|
|
304
|
+
path: str | None = None
|
|
314
305
|
error: str | None = None
|
|
315
306
|
|
|
316
307
|
|
|
@@ -318,8 +309,8 @@ class UploadFileResponse(BaseResponse):
|
|
|
318
309
|
"""Response to file upload."""
|
|
319
310
|
|
|
320
311
|
type: Literal["upload_file_response"] = "upload_file_response"
|
|
321
|
-
|
|
322
|
-
|
|
312
|
+
path: str | None = None
|
|
313
|
+
size: int | None = None
|
|
323
314
|
error: str | None = None
|
|
324
315
|
|
|
325
316
|
|
|
@@ -361,7 +352,7 @@ class WorkflowStatusNotification(BaseNotification):
|
|
|
361
352
|
type: Literal["workflow_status"] = "workflow_status"
|
|
362
353
|
session_id: str
|
|
363
354
|
status: WorkflowStatus
|
|
364
|
-
|
|
355
|
+
mode: ExecutionMode
|
|
365
356
|
details: str | None = None
|
|
366
357
|
|
|
367
358
|
@classmethod
|
|
@@ -393,7 +384,7 @@ class WorkflowStatusNotification(BaseNotification):
|
|
|
393
384
|
return cls(
|
|
394
385
|
session_id=session_id,
|
|
395
386
|
status=status,
|
|
396
|
-
|
|
387
|
+
mode=mode,
|
|
397
388
|
details=details,
|
|
398
389
|
)
|
|
399
390
|
|
|
@@ -738,14 +729,14 @@ def create_session_id() -> str:
|
|
|
738
729
|
# ========================================
|
|
739
730
|
|
|
740
731
|
CLIENT_MESSAGE_TYPES = {
|
|
741
|
-
"
|
|
742
|
-
"
|
|
743
|
-
"
|
|
732
|
+
"save",
|
|
733
|
+
"run",
|
|
734
|
+
"step_run",
|
|
744
735
|
"step_control",
|
|
745
736
|
"breakpoint_control",
|
|
746
737
|
"user_input",
|
|
747
|
-
"
|
|
748
|
-
"
|
|
738
|
+
"stop",
|
|
739
|
+
"convert",
|
|
749
740
|
"upload_file",
|
|
750
741
|
"ping",
|
|
751
742
|
"get_status",
|
|
@@ -753,12 +744,12 @@ CLIENT_MESSAGE_TYPES = {
|
|
|
753
744
|
|
|
754
745
|
SERVER_MESSAGE_TYPES = {
|
|
755
746
|
# Responses
|
|
756
|
-
"
|
|
757
|
-
"
|
|
758
|
-
"
|
|
747
|
+
"save_response",
|
|
748
|
+
"run_response",
|
|
749
|
+
"step_run_response",
|
|
759
750
|
"step_control_response",
|
|
760
751
|
"breakpoint_response",
|
|
761
|
-
"
|
|
752
|
+
"convert_response",
|
|
762
753
|
"upload_file_response",
|
|
763
754
|
"pong",
|
|
764
755
|
"status_response",
|
|
@@ -786,7 +777,7 @@ class SessionState(BaseModel):
|
|
|
786
777
|
session_id: str
|
|
787
778
|
client_id: str
|
|
788
779
|
status: WorkflowStatus
|
|
789
|
-
|
|
780
|
+
mode: ExecutionMode
|
|
790
781
|
start_time: int = Field(default_factory=time.monotonic_ns)
|
|
791
782
|
end_time: int | None = None
|
|
792
783
|
metadata: dict[str, Any] = Field(default_factory=dict)
|
|
@@ -845,7 +836,7 @@ class SessionState(BaseModel):
|
|
|
845
836
|
"session_id": self.session_id,
|
|
846
837
|
"client_id": self.client_id,
|
|
847
838
|
"status": self.status.value,
|
|
848
|
-
"
|
|
839
|
+
"mode": self.mode.value,
|
|
849
840
|
"duration_seconds": self.duration,
|
|
850
841
|
"start_time": self.start_time,
|
|
851
842
|
"end_time": self.end_time,
|
|
@@ -915,9 +906,9 @@ class WorkflowSession:
|
|
|
915
906
|
return self._state.status
|
|
916
907
|
|
|
917
908
|
@property
|
|
918
|
-
def
|
|
909
|
+
def mode(self) -> ExecutionMode:
|
|
919
910
|
"""Get execution mode."""
|
|
920
|
-
return self._state.
|
|
911
|
+
return self._state.mode
|
|
921
912
|
|
|
922
913
|
@property
|
|
923
914
|
def temp_file(self) -> Path | None:
|
waldiez/ws/reloader.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# SPDX-License-Identifier: Apache-2.0.
|
|
2
2
|
# Copyright (c) 2024 - 2025 Waldiez and contributors.
|
|
3
|
-
# pylint: disable=line-too-long
|
|
3
|
+
# pylint: disable=line-too-long,invalid-name
|
|
4
4
|
# pyright: reportUnknownVariableType=false,reportConstantRedefinition=false
|
|
5
5
|
# pyright: reportUntypedBaseClass=false,reportUnknownMemberType=false
|
|
6
6
|
# pyright: reportUnknownParameterType=false,reportUnknownArgumentType=false
|
|
@@ -14,7 +14,9 @@ import threading
|
|
|
14
14
|
import time
|
|
15
15
|
from pathlib import Path
|
|
16
16
|
from types import TracebackType
|
|
17
|
-
from typing import Any, Callable
|
|
17
|
+
from typing import Any, Callable, final
|
|
18
|
+
|
|
19
|
+
from typing_extensions import override
|
|
18
20
|
|
|
19
21
|
HAS_WATCHDOG = False
|
|
20
22
|
try:
|
|
@@ -28,16 +30,18 @@ try:
|
|
|
28
30
|
|
|
29
31
|
HAS_WATCHDOG = True
|
|
30
32
|
except ImportError as exc:
|
|
31
|
-
|
|
33
|
+
_msg = ( # pylint: disable=invalid-name
|
|
32
34
|
"The 'watchdog' package is required for auto-reload functionality. "
|
|
33
35
|
"Please install it using 'pip install watchdog'."
|
|
34
|
-
)
|
|
36
|
+
)
|
|
37
|
+
raise ImportError(_msg) from exc
|
|
35
38
|
|
|
36
39
|
logger = logging.getLogger(__name__)
|
|
37
40
|
fsevents_logger = logging.getLogger("fsevents")
|
|
38
41
|
fsevents_logger.setLevel(logging.WARNING) # Reduce noise from fsevents
|
|
39
42
|
|
|
40
43
|
|
|
44
|
+
@final
|
|
41
45
|
class ReloadHandler(FileSystemEventHandler):
|
|
42
46
|
"""Handler for file system events that triggers server reload."""
|
|
43
47
|
|
|
@@ -134,6 +138,7 @@ class ReloadHandler(FileSystemEventHandler):
|
|
|
134
138
|
else str(event.src_path)
|
|
135
139
|
)
|
|
136
140
|
|
|
141
|
+
@override
|
|
137
142
|
def on_modified(self, event: FileSystemEvent) -> None:
|
|
138
143
|
"""Handle file modification events.
|
|
139
144
|
|
|
@@ -150,6 +155,7 @@ class ReloadHandler(FileSystemEventHandler):
|
|
|
150
155
|
logger.info("File changed: %s", src_path)
|
|
151
156
|
self._schedule_restart()
|
|
152
157
|
|
|
158
|
+
@override
|
|
153
159
|
def on_created(self, event: FileSystemEvent) -> None:
|
|
154
160
|
"""Handle file creation events.
|
|
155
161
|
|
|
@@ -163,6 +169,7 @@ class ReloadHandler(FileSystemEventHandler):
|
|
|
163
169
|
logger.info("File created: %s", src_path)
|
|
164
170
|
self._schedule_restart()
|
|
165
171
|
|
|
172
|
+
@override
|
|
166
173
|
def on_deleted(self, event: FileSystemEvent) -> None:
|
|
167
174
|
"""Handle file deletion events.
|
|
168
175
|
|
|
@@ -235,6 +242,7 @@ class ReloadHandler(FileSystemEventHandler):
|
|
|
235
242
|
os._exit(1) # nosec
|
|
236
243
|
|
|
237
244
|
|
|
245
|
+
@final
|
|
238
246
|
class FileWatcher:
|
|
239
247
|
"""File watcher with auto-reload functionality."""
|
|
240
248
|
|