waldiez 0.5.10__py3-none-any.whl → 0.6.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of waldiez might be problematic. Click here for more details.

Files changed (192) hide show
  1. waldiez/__init__.py +1 -1
  2. waldiez/_version.py +1 -1
  3. waldiez/cli.py +19 -7
  4. waldiez/cli_extras/jupyter.py +3 -0
  5. waldiez/cli_extras/runner.py +3 -1
  6. waldiez/cli_extras/studio.py +3 -1
  7. waldiez/exporter.py +9 -3
  8. waldiez/exporting/agent/exporter.py +15 -16
  9. waldiez/exporting/agent/extras/captain_agent_extras.py +6 -6
  10. waldiez/exporting/agent/extras/doc_agent_extras.py +6 -6
  11. waldiez/exporting/agent/extras/group_manager_agent_extas.py +40 -24
  12. waldiez/exporting/agent/extras/group_member_extras.py +6 -5
  13. waldiez/exporting/agent/extras/handoffs/after_work.py +2 -1
  14. waldiez/exporting/agent/extras/handoffs/available.py +2 -1
  15. waldiez/exporting/agent/extras/handoffs/condition.py +3 -2
  16. waldiez/exporting/agent/extras/handoffs/handoff.py +2 -1
  17. waldiez/exporting/agent/extras/handoffs/target.py +7 -4
  18. waldiez/exporting/agent/extras/rag/chroma_extras.py +27 -19
  19. waldiez/exporting/agent/extras/rag/mongo_extras.py +8 -8
  20. waldiez/exporting/agent/extras/rag/pgvector_extras.py +5 -5
  21. waldiez/exporting/agent/extras/rag/qdrant_extras.py +5 -4
  22. waldiez/exporting/agent/extras/rag/vector_db_extras.py +1 -1
  23. waldiez/exporting/agent/extras/rag_user_proxy_agent_extras.py +5 -7
  24. waldiez/exporting/agent/extras/reasoning_agent_extras.py +3 -5
  25. waldiez/exporting/agent/termination.py +1 -0
  26. waldiez/exporting/chats/exporter.py +4 -4
  27. waldiez/exporting/chats/processor.py +1 -2
  28. waldiez/exporting/chats/utils/common.py +89 -48
  29. waldiez/exporting/chats/utils/group.py +9 -9
  30. waldiez/exporting/chats/utils/nested.py +7 -7
  31. waldiez/exporting/chats/utils/sequential.py +1 -1
  32. waldiez/exporting/chats/utils/single.py +2 -2
  33. waldiez/exporting/core/constants.py +3 -1
  34. waldiez/exporting/core/content.py +7 -7
  35. waldiez/exporting/core/context.py +5 -3
  36. waldiez/exporting/core/exporter.py +5 -3
  37. waldiez/exporting/core/exporters.py +2 -2
  38. waldiez/exporting/core/extras/agent_extras/captain_extras.py +2 -2
  39. waldiez/exporting/core/extras/agent_extras/group_manager_extras.py +2 -2
  40. waldiez/exporting/core/extras/agent_extras/rag_user_extras.py +2 -2
  41. waldiez/exporting/core/extras/agent_extras/standard_extras.py +3 -8
  42. waldiez/exporting/core/extras/base.py +7 -5
  43. waldiez/exporting/core/extras/flow_extras.py +4 -5
  44. waldiez/exporting/core/extras/model_extras.py +2 -2
  45. waldiez/exporting/core/extras/path_resolver.py +1 -2
  46. waldiez/exporting/core/extras/serializer.py +13 -11
  47. waldiez/exporting/core/protocols.py +6 -5
  48. waldiez/exporting/core/result.py +25 -28
  49. waldiez/exporting/core/types.py +11 -10
  50. waldiez/exporting/core/utils/llm_config.py +4 -4
  51. waldiez/exporting/core/validation.py +10 -11
  52. waldiez/exporting/flow/execution_generator.py +99 -10
  53. waldiez/exporting/flow/exporter.py +2 -2
  54. waldiez/exporting/flow/factory.py +2 -2
  55. waldiez/exporting/flow/file_generator.py +4 -2
  56. waldiez/exporting/flow/merger.py +5 -3
  57. waldiez/exporting/flow/orchestrator.py +72 -2
  58. waldiez/exporting/flow/utils/common.py +6 -6
  59. waldiez/exporting/flow/utils/importing.py +7 -8
  60. waldiez/exporting/flow/utils/linting.py +25 -9
  61. waldiez/exporting/flow/utils/logging.py +5 -77
  62. waldiez/exporting/models/exporter.py +8 -8
  63. waldiez/exporting/models/processor.py +5 -5
  64. waldiez/exporting/tools/exporter.py +2 -2
  65. waldiez/exporting/tools/processor.py +7 -4
  66. waldiez/io/__init__.py +11 -5
  67. waldiez/io/_ws.py +12 -6
  68. waldiez/io/models/constants.py +10 -10
  69. waldiez/io/models/content/audio.py +1 -0
  70. waldiez/io/models/content/base.py +20 -18
  71. waldiez/io/models/content/file.py +1 -0
  72. waldiez/io/models/content/image.py +1 -0
  73. waldiez/io/models/content/text.py +1 -0
  74. waldiez/io/models/content/video.py +1 -0
  75. waldiez/io/models/user_input.py +10 -5
  76. waldiez/io/models/user_response.py +17 -16
  77. waldiez/io/mqtt.py +18 -31
  78. waldiez/io/redis.py +18 -22
  79. waldiez/io/structured.py +122 -70
  80. waldiez/io/utils.py +19 -10
  81. waldiez/io/ws.py +7 -3
  82. waldiez/logger.py +16 -3
  83. waldiez/models/agents/__init__.py +3 -0
  84. waldiez/models/agents/agent/agent.py +25 -17
  85. waldiez/models/agents/agent/agent_data.py +25 -22
  86. waldiez/models/agents/agent/code_execution.py +9 -11
  87. waldiez/models/agents/agent/termination_message.py +10 -12
  88. waldiez/models/agents/agent/update_system_message.py +2 -4
  89. waldiez/models/agents/agents.py +8 -8
  90. waldiez/models/agents/assistant/assistant.py +6 -3
  91. waldiez/models/agents/assistant/assistant_data.py +2 -2
  92. waldiez/models/agents/captain/captain_agent.py +7 -4
  93. waldiez/models/agents/captain/captain_agent_data.py +5 -7
  94. waldiez/models/agents/doc_agent/doc_agent.py +7 -4
  95. waldiez/models/agents/doc_agent/doc_agent_data.py +9 -10
  96. waldiez/models/agents/doc_agent/rag_query_engine.py +10 -12
  97. waldiez/models/agents/extra_requirements.py +3 -3
  98. waldiez/models/agents/group_manager/group_manager.py +12 -7
  99. waldiez/models/agents/group_manager/group_manager_data.py +13 -12
  100. waldiez/models/agents/group_manager/speakers.py +17 -19
  101. waldiez/models/agents/rag_user_proxy/rag_user_proxy.py +7 -4
  102. waldiez/models/agents/rag_user_proxy/rag_user_proxy_data.py +4 -1
  103. waldiez/models/agents/rag_user_proxy/retrieve_config.py +69 -63
  104. waldiez/models/agents/rag_user_proxy/vector_db_config.py +19 -19
  105. waldiez/models/agents/reasoning/reasoning_agent.py +7 -4
  106. waldiez/models/agents/reasoning/reasoning_agent_data.py +3 -2
  107. waldiez/models/agents/reasoning/reasoning_agent_reason_config.py +8 -8
  108. waldiez/models/agents/user_proxy/user_proxy.py +6 -3
  109. waldiez/models/agents/user_proxy/user_proxy_data.py +1 -1
  110. waldiez/models/chat/chat.py +28 -20
  111. waldiez/models/chat/chat_data.py +22 -21
  112. waldiez/models/chat/chat_message.py +9 -9
  113. waldiez/models/chat/chat_nested.py +9 -9
  114. waldiez/models/chat/chat_summary.py +6 -6
  115. waldiez/models/common/__init__.py +2 -0
  116. waldiez/models/common/ag2_version.py +2 -0
  117. waldiez/models/common/base.py +2 -0
  118. waldiez/models/common/dict_utils.py +8 -6
  119. waldiez/models/common/handoff.py +20 -17
  120. waldiez/models/common/method_utils.py +9 -7
  121. waldiez/models/common/naming.py +49 -0
  122. waldiez/models/flow/flow.py +11 -6
  123. waldiez/models/flow/flow_data.py +23 -17
  124. waldiez/models/flow/info.py +3 -3
  125. waldiez/models/flow/naming.py +2 -1
  126. waldiez/models/model/_aws.py +11 -13
  127. waldiez/models/model/_llm.py +8 -0
  128. waldiez/models/model/_price.py +2 -4
  129. waldiez/models/model/extra_requirements.py +1 -3
  130. waldiez/models/model/model.py +2 -2
  131. waldiez/models/model/model_data.py +21 -21
  132. waldiez/models/tool/extra_requirements.py +2 -4
  133. waldiez/models/tool/predefined/_duckduckgo.py +1 -0
  134. waldiez/models/tool/predefined/_email.py +4 -0
  135. waldiez/models/tool/predefined/_google.py +1 -0
  136. waldiez/models/tool/predefined/_perplexity.py +2 -1
  137. waldiez/models/tool/predefined/_searxng.py +2 -1
  138. waldiez/models/tool/predefined/_tavily.py +1 -0
  139. waldiez/models/tool/predefined/_wikipedia.py +2 -1
  140. waldiez/models/tool/predefined/_youtube.py +1 -0
  141. waldiez/models/tool/tool.py +8 -5
  142. waldiez/models/tool/tool_data.py +2 -2
  143. waldiez/models/waldiez.py +152 -4
  144. waldiez/runner.py +11 -5
  145. waldiez/running/async_utils.py +192 -0
  146. waldiez/running/base_runner.py +155 -241
  147. waldiez/running/dir_utils.py +52 -0
  148. waldiez/running/environment.py +10 -44
  149. waldiez/running/events_mixin.py +252 -0
  150. waldiez/running/exceptions.py +20 -0
  151. waldiez/running/gen_seq_diagram.py +18 -15
  152. waldiez/running/io_utils.py +216 -0
  153. waldiez/running/protocol.py +11 -5
  154. waldiez/running/requirements_mixin.py +65 -0
  155. waldiez/running/results_mixin.py +926 -0
  156. waldiez/running/standard_runner.py +24 -27
  157. waldiez/running/step_by_step/breakpoints_mixin.py +503 -47
  158. waldiez/running/step_by_step/command_handler.py +154 -0
  159. waldiez/running/step_by_step/events_processor.py +379 -0
  160. waldiez/running/step_by_step/step_by_step_models.py +425 -41
  161. waldiez/running/step_by_step/step_by_step_runner.py +437 -382
  162. waldiez/running/subprocess_runner/__base__.py +13 -8
  163. waldiez/running/subprocess_runner/_async_runner.py +6 -4
  164. waldiez/running/subprocess_runner/_sync_runner.py +11 -6
  165. waldiez/running/subprocess_runner/runner.py +48 -23
  166. waldiez/running/timeline_processor.py +1 -1
  167. waldiez/utils/__init__.py +2 -0
  168. waldiez/utils/conflict_checker.py +4 -4
  169. waldiez/utils/python_manager.py +415 -0
  170. waldiez/ws/__init__.py +8 -7
  171. waldiez/ws/_file_handler.py +18 -20
  172. waldiez/ws/_mock.py +75 -0
  173. waldiez/ws/cli.py +58 -10
  174. waldiez/ws/client_manager.py +77 -53
  175. waldiez/ws/errors.py +3 -0
  176. waldiez/ws/models.py +61 -53
  177. waldiez/ws/reloader.py +33 -4
  178. waldiez/ws/server.py +121 -52
  179. waldiez/ws/session_manager.py +8 -9
  180. waldiez/ws/session_stats.py +1 -1
  181. waldiez/ws/utils.py +33 -5
  182. {waldiez-0.5.10.dist-info → waldiez-0.6.1.dist-info}/METADATA +107 -109
  183. waldiez-0.6.1.dist-info/RECORD +254 -0
  184. waldiez/running/post_run.py +0 -180
  185. waldiez/running/pre_run.py +0 -159
  186. waldiez/running/run_results.py +0 -14
  187. waldiez/running/utils.py +0 -511
  188. waldiez-0.5.10.dist-info/RECORD +0 -248
  189. {waldiez-0.5.10.dist-info → waldiez-0.6.1.dist-info}/WHEEL +0 -0
  190. {waldiez-0.5.10.dist-info → waldiez-0.6.1.dist-info}/entry_points.txt +0 -0
  191. {waldiez-0.5.10.dist-info → waldiez-0.6.1.dist-info}/licenses/LICENSE +0 -0
  192. {waldiez-0.5.10.dist-info → waldiez-0.6.1.dist-info}/licenses/NOTICE.md +0 -0
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
- # pylint: disable=too-many-locals
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, Optional, Set
17
+ from typing import Annotated, Any
12
18
 
13
19
  import typer
14
20
 
15
- from .server import run_server
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")] = 8765,
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
- Optional[list[str]],
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
- Optional[list[Path]],
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: Optional[Set[Path]] = None
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 as e: # pylint: disable=broad-exception-caught
206
- logger.error("Server error: %s", e)
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
 
@@ -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
- import websockets
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 (websockets.ConnectionClosed, ConnectionResetError) as e:
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
- sess = await self.session_manager.get_session(msg.session_id)
277
- wf_status = sess.status if sess else None
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.handle_save_flow_request(
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.handle_convert_workflow_request(
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._handle_run_workflow(msg)
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._handle_step_run_workflow(msg)
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 == "stop_workflow":
322
- return await self._handle_stop_workflow(msg)
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 _handle_run_workflow(
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.flow_data)
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, execution_mode=ExecutionMode.STANDARD
362
+ session_id=session_id, mode=ExecutionMode.STANDARD
358
363
  ).model_dump(mode="json")
359
364
 
360
- async def _handle_step_run_workflow(
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.flow_data)
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
- # Persist desired flags (optional)
388
- sess = await self.session_manager.get_session(session_id)
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
- execution_mode=mode,
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
- code = {
479
- "": "c",
480
- "continue": "c",
481
- "step": "s",
482
- "run": "r",
483
- "quit": "q",
484
- "info": "i",
485
- "help": "h",
486
- "stats": "st",
487
- }.get(msg.action)
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 _handle_stop_workflow(self, msg: Any) -> dict[str, Any]:
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": "stop_workflow_response",
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(session_id, data)
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, session_id: str, data: dict[str, Any]
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
- await self.send_message(
661
- UserInputRequestNotification(
662
- session_id=session_id,
663
- request_id=request_id,
664
- prompt=prompt,
665
- password=bool(data.get("password", False)),
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
@@ -1,6 +1,9 @@
1
1
  # SPDX-License-Identifier: Apache-2.0.
2
2
  # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
+
3
4
  # pylint: disable=unused-argument
5
+ # pyright: reportUnusedParameter=false
6
+
4
7
  """Error handling and exceptions for Waldiez WebSocket server."""
5
8
 
6
9
  import logging
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["save_flow"] = "save_flow"
123
- flow_data: str # JSON string of workflow
124
- filename: str | None = None
125
- force_overwrite: bool = False
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["run_workflow"] = "run_workflow"
132
- flow_data: str # JSON string of workflow
133
- execution_mode: ExecutionMode = ExecutionMode.STANDARD
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["step_run_workflow"] = "step_run_workflow"
144
- flow_data: str # JSON string of workflow
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
- structured_io: bool = True
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["continue", "step", "run", "quit", "info", "help", "stats"]
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["stop_workflow"] = "stop_workflow"
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["stop_workflow_response"] = "stop_workflow_response"
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["convert_workflow"] = "convert_workflow"
211
- flow_data: str
212
- target_format: Literal["py", "ipynb"]
213
- output_path: str | None = None
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["save_flow_response"] = "save_flow_response"
249
- file_path: str | None = None
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["run_workflow_response"] = "run_workflow_response"
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["step_run_workflow_response"] = "step_run_workflow_response"
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["convert_workflow_response"] = "convert_workflow_response"
295
- target_format: str
296
- output_path: str | None = None
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
- file_path: str | None = None
305
- file_size: int | None = None
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
- execution_mode: ExecutionMode
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
- execution_mode=mode,
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
- "save_flow",
725
- "run_workflow",
726
- "step_run_workflow",
732
+ "save",
733
+ "run",
734
+ "step_run",
727
735
  "step_control",
728
736
  "breakpoint_control",
729
737
  "user_input",
730
- "stop_workflow",
731
- "convert_workflow",
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
- "save_flow_response",
740
- "run_workflow_response",
741
- "step_run_workflow_response",
747
+ "save_response",
748
+ "run_response",
749
+ "step_run_response",
742
750
  "step_control_response",
743
751
  "breakpoint_response",
744
- "convert_workflow_response",
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
- execution_mode: ExecutionMode
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
- "execution_mode": self.execution_mode.value,
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 execution_mode(self) -> ExecutionMode:
909
+ def mode(self) -> ExecutionMode:
902
910
  """Get execution mode."""
903
- return self._state.execution_mode
911
+ return self._state.mode
904
912
 
905
913
  @property
906
914
  def temp_file(self) -> Path | None: