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

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

Potentially problematic release.


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

Files changed (62) hide show
  1. waldiez/_version.py +1 -1
  2. waldiez/cli.py +1 -0
  3. waldiez/exporting/agent/exporter.py +6 -6
  4. waldiez/exporting/agent/extras/group_manager_agent_extas.py +6 -1
  5. waldiez/exporting/agent/extras/handoffs/after_work.py +1 -0
  6. waldiez/exporting/agent/extras/handoffs/available.py +1 -0
  7. waldiez/exporting/agent/extras/handoffs/handoff.py +1 -0
  8. waldiez/exporting/agent/extras/handoffs/target.py +1 -0
  9. waldiez/exporting/agent/termination.py +1 -0
  10. waldiez/exporting/core/constants.py +3 -1
  11. waldiez/exporting/core/extras/serializer.py +12 -10
  12. waldiez/exporting/core/types.py +1 -0
  13. waldiez/exporting/core/utils/llm_config.py +2 -2
  14. waldiez/exporting/flow/execution_generator.py +1 -0
  15. waldiez/exporting/flow/utils/common.py +1 -1
  16. waldiez/exporting/flow/utils/importing.py +1 -1
  17. waldiez/exporting/flow/utils/logging.py +3 -75
  18. waldiez/io/__init__.py +3 -1
  19. waldiez/io/_ws.py +2 -0
  20. waldiez/io/structured.py +81 -28
  21. waldiez/io/utils.py +16 -10
  22. waldiez/io/ws.py +2 -2
  23. waldiez/models/agents/agent/agent.py +2 -1
  24. waldiez/models/chat/chat.py +1 -0
  25. waldiez/models/chat/chat_data.py +0 -2
  26. waldiez/models/common/base.py +2 -0
  27. waldiez/models/common/handoff.py +2 -0
  28. waldiez/models/common/method_utils.py +2 -0
  29. waldiez/models/model/_llm.py +3 -0
  30. waldiez/models/tool/predefined/_email.py +3 -0
  31. waldiez/models/tool/predefined/_perplexity.py +1 -1
  32. waldiez/models/tool/predefined/_searxng.py +1 -1
  33. waldiez/models/tool/predefined/_wikipedia.py +1 -1
  34. waldiez/running/base_runner.py +81 -20
  35. waldiez/running/post_run.py +6 -0
  36. waldiez/running/pre_run.py +167 -45
  37. waldiez/running/standard_runner.py +5 -5
  38. waldiez/running/step_by_step/breakpoints_mixin.py +368 -44
  39. waldiez/running/step_by_step/command_handler.py +151 -0
  40. waldiez/running/step_by_step/events_processor.py +199 -0
  41. waldiez/running/step_by_step/step_by_step_models.py +358 -41
  42. waldiez/running/step_by_step/step_by_step_runner.py +358 -353
  43. waldiez/running/subprocess_runner/__base__.py +4 -7
  44. waldiez/running/subprocess_runner/_async_runner.py +1 -1
  45. waldiez/running/subprocess_runner/_sync_runner.py +5 -4
  46. waldiez/running/subprocess_runner/runner.py +9 -0
  47. waldiez/running/utils.py +116 -2
  48. waldiez/ws/__init__.py +8 -7
  49. waldiez/ws/_file_handler.py +0 -2
  50. waldiez/ws/_mock.py +74 -0
  51. waldiez/ws/cli.py +27 -3
  52. waldiez/ws/client_manager.py +45 -29
  53. waldiez/ws/models.py +18 -1
  54. waldiez/ws/reloader.py +23 -2
  55. waldiez/ws/server.py +47 -8
  56. waldiez/ws/utils.py +29 -4
  57. {waldiez-0.5.10.dist-info → waldiez-0.6.0.dist-info}/METADATA +53 -44
  58. {waldiez-0.5.10.dist-info → waldiez-0.6.0.dist-info}/RECORD +62 -59
  59. {waldiez-0.5.10.dist-info → waldiez-0.6.0.dist-info}/WHEEL +0 -0
  60. {waldiez-0.5.10.dist-info → waldiez-0.6.0.dist-info}/entry_points.txt +0 -0
  61. {waldiez-0.5.10.dist-info → waldiez-0.6.0.dist-info}/licenses/LICENSE +0 -0
  62. {waldiez-0.5.10.dist-info → waldiez-0.6.0.dist-info}/licenses/NOTICE.md +0 -0
@@ -7,12 +7,20 @@ import io
7
7
  import os
8
8
  import subprocess
9
9
  import sys
10
+ import tempfile
11
+ from pathlib import Path
10
12
  from typing import Callable
11
13
 
12
14
  from waldiez.models import Waldiez
13
15
 
14
16
  from .environment import in_virtualenv, is_root
15
- from .utils import strip_ansi
17
+ from .utils import (
18
+ ensure_pip,
19
+ get_pip_install_location,
20
+ get_python_executable,
21
+ safe_filename,
22
+ strip_ansi,
23
+ )
16
24
 
17
25
 
18
26
  class RequirementsMixin:
@@ -75,20 +83,12 @@ def install_requirements(
75
83
  """
76
84
  requirements_string = ", ".join(extra_requirements)
77
85
  printer(f"Installing requirements: {requirements_string}")
78
- pip_install = [sys.executable, "-m", "pip", "install"]
79
- break_system_packages = ""
80
- if not in_virtualenv(): # it should # pragma: no cover
81
- # if not, let's try to install as user
82
- # not sure if --break-system-packages is safe,
83
- # but it might fail if we don't
84
- break_system_packages = os.environ.get("PIP_BREAK_SYSTEM_PACKAGES", "")
85
- os.environ["PIP_BREAK_SYSTEM_PACKAGES"] = "1"
86
- if not is_root():
87
- pip_install.append("--user")
88
- if upgrade: # pragma: no cover
89
- pip_install.append("--upgrade")
90
- pip_install.extend(extra_requirements)
91
- # pylint: disable=too-many-try-statements
86
+ ensure_pip()
87
+ pip_install, break_system_packages, install_location = _before_pip(
88
+ extra_requirements, upgrade
89
+ )
90
+
91
+ # pylint: disable=too-many-try-statements,broad-exception-caught
92
92
  try:
93
93
  with subprocess.Popen(
94
94
  pip_install,
@@ -97,17 +97,26 @@ def install_requirements(
97
97
  ) as proc:
98
98
  if proc.stdout: # pragma: no branch
99
99
  for line in io.TextIOWrapper(proc.stdout, encoding="utf-8"):
100
- printer(strip_ansi(line.strip()))
100
+ stripped_line = strip_ansi(line.strip())
101
+ if stripped_line: # Only print non-empty lines
102
+ printer(stripped_line)
101
103
  if proc.stderr: # pragma: no branch
102
104
  for line in io.TextIOWrapper(proc.stderr, encoding="utf-8"):
103
- printer(strip_ansi(line.strip()))
105
+ stripped_line = strip_ansi(line.strip())
106
+ if stripped_line: # Only print non-empty lines
107
+ printer(stripped_line)
108
+
109
+ # Wait for process to complete and check return code
110
+ return_code = proc.wait()
111
+ if return_code != 0:
112
+ printer(
113
+ f"Package installation failed with exit code {return_code}"
114
+ )
115
+
116
+ except Exception as e:
117
+ printer(f"Failed to install requirements: {e}")
104
118
  finally:
105
- if not in_virtualenv(): # pragma: no cover
106
- # restore the old env var
107
- if break_system_packages:
108
- os.environ["PIP_BREAK_SYSTEM_PACKAGES"] = break_system_packages
109
- else:
110
- del os.environ["PIP_BREAK_SYSTEM_PACKAGES"]
119
+ _after_pip(break_system_packages, install_location)
111
120
 
112
121
 
113
122
  async def a_install_requirements(
@@ -126,34 +135,147 @@ async def a_install_requirements(
126
135
  printer : Callable[..., None]
127
136
  The printer function to use, defaults to print.
128
137
  """
138
+ pip_install, break_system_packages, install_location = _before_pip(
139
+ extra_requirements, upgrade=upgrade
140
+ )
129
141
  requirements_string = ", ".join(extra_requirements)
130
142
  printer(f"Installing requirements: {requirements_string}")
131
- pip_install = [sys.executable, "-m", "pip", "install"]
132
- break_system_packages = ""
133
- if not in_virtualenv(): # pragma: no cover
134
- break_system_packages = os.environ.get("PIP_BREAK_SYSTEM_PACKAGES", "")
135
- os.environ["PIP_BREAK_SYSTEM_PACKAGES"] = "1"
136
- if not is_root():
137
- pip_install.extend(["--user"])
138
- if upgrade: # pragma: no cover
139
- pip_install.append("--upgrade")
140
- pip_install.extend(extra_requirements)
141
- # pylint: disable=too-many-try-statements
143
+ # pylint: disable=too-many-try-statements,broad-exception-caught
142
144
  try:
143
145
  proc = await asyncio.create_subprocess_exec(
144
146
  *pip_install,
145
147
  stdout=asyncio.subprocess.PIPE,
146
148
  stderr=asyncio.subprocess.PIPE,
147
149
  )
148
- if proc.stdout:
149
- async for line in proc.stdout:
150
- printer(strip_ansi(line.decode().strip()))
151
- if proc.stderr:
152
- async for line in proc.stderr:
153
- printer(strip_ansi(line.decode().strip()))
150
+
151
+ async def _pump_stream(stream: asyncio.StreamReader | None) -> None:
152
+ if not stream:
153
+ return
154
+ async for raw in stream:
155
+ text = strip_ansi(raw.decode().rstrip())
156
+ if text:
157
+ printer(text)
158
+
159
+ # Create tasks for concurrent execution
160
+ tasks: list[asyncio.Task[int | None]] = [
161
+ asyncio.create_task(_pump_stream(proc.stdout)),
162
+ asyncio.create_task(_pump_stream(proc.stderr)),
163
+ asyncio.create_task(proc.wait()),
164
+ ]
165
+ await asyncio.gather(*tasks, return_exceptions=True)
166
+ if proc.returncode != 0:
167
+ printer(
168
+ f"Package installation failed with exit code {proc.returncode}"
169
+ )
170
+
171
+ except Exception as e:
172
+ printer(f"Failed to install requirements: {e}")
154
173
  finally:
155
- if not in_virtualenv(): # pragma: no cover
156
- if break_system_packages:
157
- os.environ["PIP_BREAK_SYSTEM_PACKAGES"] = break_system_packages
158
- else:
159
- del os.environ["PIP_BREAK_SYSTEM_PACKAGES"]
174
+ _after_pip(break_system_packages, install_location)
175
+
176
+
177
+ def _before_pip(
178
+ packages: set[str],
179
+ upgrade: bool,
180
+ ) -> tuple[list[str], str, str | None]:
181
+ """Gather the pip command for installing requirements.
182
+
183
+ Parameters
184
+ ----------
185
+ packages : set[str]
186
+ The packages to install.
187
+ upgrade : bool
188
+ Whether to upgrade the packages.
189
+
190
+ Returns
191
+ -------
192
+ tuple[list[str], str, str | None]
193
+ The pip command, break_system_packages flag, and install location.
194
+ """
195
+ ensure_pip()
196
+ pip_install = [
197
+ get_python_executable(),
198
+ "-m",
199
+ "pip",
200
+ "install",
201
+ "--disable-pip-version-check",
202
+ "--no-input",
203
+ ]
204
+ install_location = get_pip_install_location()
205
+ break_system_packages = ""
206
+
207
+ if install_location:
208
+ pip_install += ["--target", install_location]
209
+ elif not in_virtualenv(): # it should # pragma: no cover
210
+ # if not, let's try to install as user
211
+ # not sure if --break-system-packages is safe,
212
+ # but it might fail if we don't
213
+ break_system_packages = os.environ.get("PIP_BREAK_SYSTEM_PACKAGES", "")
214
+ os.environ["PIP_BREAK_SYSTEM_PACKAGES"] = "1"
215
+ if not is_root():
216
+ pip_install.append("--user")
217
+
218
+ if upgrade: # pragma: no cover
219
+ pip_install.append("--upgrade")
220
+
221
+ pip_install.extend(packages)
222
+ return pip_install, break_system_packages, install_location
223
+
224
+
225
+ def _after_pip(
226
+ break_system_packages: str, install_location: str | None
227
+ ) -> None:
228
+ """Restore environment variables after pip installation.
229
+
230
+ Parameters
231
+ ----------
232
+ break_system_packages : str
233
+ The original value of PIP_BREAK_SYSTEM_PACKAGES.
234
+ install_location : str | None
235
+ The install location used (None if default).
236
+ """
237
+ if install_location is None and not in_virtualenv(): # pragma: no cover
238
+ # restore the old env var
239
+ if break_system_packages:
240
+ os.environ["PIP_BREAK_SYSTEM_PACKAGES"] = break_system_packages
241
+ else:
242
+ # Use pop to avoid KeyError if the key doesn't exist
243
+ os.environ.pop("PIP_BREAK_SYSTEM_PACKAGES", None)
244
+
245
+
246
+ def dump_waldiez(waldiez: Waldiez, output_path: str | Path | None) -> Path:
247
+ """Dump waldiez flow to a file.
248
+
249
+ Parameters
250
+ ----------
251
+ waldiez : Waldiez
252
+ The waldiez instance to dump.
253
+ output_path : str | Path | None
254
+ Optional output path to determine the directory to save the flow to.
255
+
256
+ Returns
257
+ -------
258
+ Path
259
+ The path to the generated file.
260
+ """
261
+ file_path = Path(output_path) if output_path else None
262
+ if file_path:
263
+ file_name = file_path.name
264
+ if not file_name.endswith(".waldiez"):
265
+ file_path.with_suffix(".waldiez")
266
+
267
+ else:
268
+ full_name = waldiez.name
269
+ file_name = safe_filename(full_name, "waldiez")
270
+ file_dir: Path
271
+ if file_path:
272
+ file_dir = file_path if file_path.is_dir() else file_path.parent
273
+ else:
274
+ file_dir = Path(tempfile.mkdtemp())
275
+ file_dir.mkdir(parents=True, exist_ok=True)
276
+ output_path = file_dir / file_name
277
+ with output_path.open(
278
+ "w", encoding="utf-8", errors="replace", newline="\n"
279
+ ) as f_open:
280
+ f_open.write(waldiez.model_dump_json())
281
+ return output_path
@@ -2,7 +2,7 @@
2
2
  # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
3
 
4
4
  # pyright: reportUnknownMemberType=false, reportAttributeAccessIssue=false
5
- # pylint: disable=duplicate-code
5
+ # pylint: disable=duplicate-code,too-few-public-methods
6
6
  """Run a waldiez flow.
7
7
 
8
8
  The flow is first converted to an autogen flow with agents, chats, and tools.
@@ -59,9 +59,8 @@ class WaldiezStandardRunner(WaldiezBaseRunner):
59
59
  self._event_count = 0
60
60
  self._processed_events = 0
61
61
 
62
- # pylint: disable=no-self-use
63
- # noinspection PyMethodMayBeStatic
64
- def print(self, *args: Any, **kwargs: Any) -> None:
62
+ @staticmethod
63
+ def print(*args: Any, **kwargs: Any) -> None:
65
64
  """Print.
66
65
 
67
66
  Parameters
@@ -71,7 +70,7 @@ class WaldiezStandardRunner(WaldiezBaseRunner):
71
70
  **kwargs : Any
72
71
  Keyword arguments to print.
73
72
  """
74
- WaldiezBaseRunner._print(*args, **kwargs)
73
+ WaldiezBaseRunner.print(*args, **kwargs)
75
74
 
76
75
  # pylint: disable=unused-argument
77
76
  def _run(
@@ -214,6 +213,7 @@ class WaldiezStandardRunner(WaldiezBaseRunner):
214
213
  "workflow event processing (async)"
215
214
  )
216
215
  return []
216
+ # noinspection DuplicatedCode
217
217
  if self.structured_io:
218
218
  stream = StructuredIOStream(
219
219
  uploads_root=uploads_root, is_async=True