waldiez 0.2.1__py3-none-any.whl → 0.3.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 (138) hide show
  1. waldiez/__init__.py +2 -0
  2. waldiez/__main__.py +2 -0
  3. waldiez/_version.py +3 -1
  4. waldiez/cli.py +13 -3
  5. waldiez/cli_extras.py +25 -27
  6. waldiez/conflict_checker.py +4 -3
  7. waldiez/exporter.py +28 -105
  8. waldiez/exporting/__init__.py +8 -9
  9. waldiez/exporting/agent/__init__.py +7 -0
  10. waldiez/exporting/agent/agent_exporter.py +279 -0
  11. waldiez/exporting/agent/utils/__init__.py +23 -0
  12. waldiez/exporting/agent/utils/agent_class_name.py +34 -0
  13. waldiez/exporting/agent/utils/agent_imports.py +50 -0
  14. waldiez/exporting/{agents → agent/utils}/code_execution.py +9 -11
  15. waldiez/exporting/{agents → agent/utils}/group_manager.py +47 -35
  16. waldiez/exporting/{agents → agent/utils}/rag_user/__init__.py +2 -0
  17. waldiez/exporting/{agents → agent/utils}/rag_user/chroma_utils.py +22 -17
  18. waldiez/exporting/{agents → agent/utils}/rag_user/mongo_utils.py +14 -10
  19. waldiez/exporting/{agents → agent/utils}/rag_user/pgvector_utils.py +12 -8
  20. waldiez/exporting/{agents → agent/utils}/rag_user/qdrant_utils.py +11 -8
  21. waldiez/exporting/{agents → agent/utils}/rag_user/rag_user.py +78 -55
  22. waldiez/exporting/{agents → agent/utils}/rag_user/vector_db.py +10 -8
  23. waldiez/exporting/agent/utils/swarm_agent.py +463 -0
  24. waldiez/exporting/{agents → agent/utils}/teachability.py +10 -6
  25. waldiez/exporting/{agents → agent/utils}/termination_message.py +7 -8
  26. waldiez/exporting/base/__init__.py +25 -0
  27. waldiez/exporting/base/agent_position.py +75 -0
  28. waldiez/exporting/base/base_exporter.py +118 -0
  29. waldiez/exporting/base/export_position.py +48 -0
  30. waldiez/exporting/base/import_position.py +23 -0
  31. waldiez/exporting/base/mixin.py +134 -0
  32. waldiez/exporting/base/utils/__init__.py +18 -0
  33. waldiez/exporting/{utils → base/utils}/comments.py +12 -55
  34. waldiez/exporting/{utils → base/utils}/naming.py +14 -4
  35. waldiez/exporting/base/utils/path_check.py +68 -0
  36. waldiez/exporting/{utils/object_string.py → base/utils/to_string.py} +21 -20
  37. waldiez/exporting/chats/__init__.py +5 -12
  38. waldiez/exporting/chats/chats_exporter.py +240 -0
  39. waldiez/exporting/chats/utils/__init__.py +15 -0
  40. waldiez/exporting/chats/utils/common.py +81 -0
  41. waldiez/exporting/chats/{nested.py → utils/nested.py} +125 -86
  42. waldiez/exporting/chats/utils/sequential.py +244 -0
  43. waldiez/exporting/chats/utils/single_chat.py +313 -0
  44. waldiez/exporting/chats/utils/swarm.py +207 -0
  45. waldiez/exporting/flow/__init__.py +5 -3
  46. waldiez/exporting/flow/flow_exporter.py +503 -0
  47. waldiez/exporting/flow/utils/__init__.py +47 -0
  48. waldiez/exporting/flow/utils/agent_utils.py +204 -0
  49. waldiez/exporting/flow/utils/chat_utils.py +71 -0
  50. waldiez/exporting/flow/utils/def_main.py +62 -0
  51. waldiez/exporting/flow/utils/flow_content.py +112 -0
  52. waldiez/exporting/flow/utils/flow_names.py +115 -0
  53. waldiez/exporting/flow/utils/importing_utils.py +179 -0
  54. waldiez/exporting/{utils → flow/utils}/logging_utils.py +34 -31
  55. waldiez/exporting/models/__init__.py +7 -242
  56. waldiez/exporting/models/models_exporter.py +192 -0
  57. waldiez/exporting/models/utils.py +166 -0
  58. waldiez/exporting/skills/__init__.py +7 -161
  59. waldiez/exporting/skills/skills_exporter.py +169 -0
  60. waldiez/exporting/skills/utils.py +281 -0
  61. waldiez/models/__init__.py +25 -7
  62. waldiez/models/agents/__init__.py +70 -0
  63. waldiez/models/agents/agent/__init__.py +11 -1
  64. waldiez/models/agents/agent/agent.py +9 -4
  65. waldiez/models/agents/agent/agent_data.py +3 -1
  66. waldiez/models/agents/agent/code_execution.py +2 -0
  67. waldiez/models/agents/agent/linked_skill.py +2 -0
  68. waldiez/models/agents/agent/nested_chat.py +2 -0
  69. waldiez/models/agents/agent/teachability.py +2 -0
  70. waldiez/models/agents/agent/termination_message.py +49 -13
  71. waldiez/models/agents/agents.py +15 -3
  72. waldiez/models/agents/assistant/__init__.py +2 -0
  73. waldiez/models/agents/assistant/assistant.py +2 -0
  74. waldiez/models/agents/assistant/assistant_data.py +2 -0
  75. waldiez/models/agents/group_manager/__init__.py +9 -1
  76. waldiez/models/agents/group_manager/group_manager.py +2 -0
  77. waldiez/models/agents/group_manager/group_manager_data.py +2 -0
  78. waldiez/models/agents/group_manager/speakers.py +49 -13
  79. waldiez/models/agents/rag_user/__init__.py +21 -4
  80. waldiez/models/agents/rag_user/rag_user.py +3 -1
  81. waldiez/models/agents/rag_user/rag_user_data.py +2 -0
  82. waldiez/models/agents/rag_user/retrieve_config.py +268 -17
  83. waldiez/models/agents/rag_user/vector_db_config.py +5 -3
  84. waldiez/models/agents/swarm_agent/__init__.py +49 -0
  85. waldiez/models/agents/swarm_agent/after_work.py +178 -0
  86. waldiez/models/agents/swarm_agent/on_condition.py +103 -0
  87. waldiez/models/agents/swarm_agent/on_condition_available.py +140 -0
  88. waldiez/models/agents/swarm_agent/on_condition_target.py +40 -0
  89. waldiez/models/agents/swarm_agent/swarm_agent.py +107 -0
  90. waldiez/models/agents/swarm_agent/swarm_agent_data.py +125 -0
  91. waldiez/models/agents/swarm_agent/update_system_message.py +144 -0
  92. waldiez/models/agents/user_proxy/__init__.py +2 -0
  93. waldiez/models/agents/user_proxy/user_proxy.py +2 -0
  94. waldiez/models/agents/user_proxy/user_proxy_data.py +2 -0
  95. waldiez/models/chat/__init__.py +21 -3
  96. waldiez/models/chat/chat.py +241 -7
  97. waldiez/models/chat/chat_data.py +192 -48
  98. waldiez/models/chat/chat_message.py +153 -144
  99. waldiez/models/chat/chat_nested.py +33 -53
  100. waldiez/models/chat/chat_summary.py +2 -0
  101. waldiez/models/common/__init__.py +6 -6
  102. waldiez/models/common/base.py +4 -1
  103. waldiez/models/common/method_utils.py +163 -83
  104. waldiez/models/flow/__init__.py +2 -0
  105. waldiez/models/flow/flow.py +176 -40
  106. waldiez/models/flow/flow_data.py +63 -2
  107. waldiez/models/flow/utils.py +172 -0
  108. waldiez/models/model/__init__.py +2 -0
  109. waldiez/models/model/model.py +25 -6
  110. waldiez/models/model/model_data.py +3 -1
  111. waldiez/models/skill/__init__.py +4 -1
  112. waldiez/models/skill/skill.py +30 -2
  113. waldiez/models/skill/skill_data.py +2 -0
  114. waldiez/models/waldiez.py +28 -4
  115. waldiez/runner.py +142 -228
  116. waldiez/running/__init__.py +33 -0
  117. waldiez/running/environment.py +83 -0
  118. waldiez/running/gen_seq_diagram.py +185 -0
  119. waldiez/running/running.py +300 -0
  120. {waldiez-0.2.1.dist-info → waldiez-0.3.0.dist-info}/METADATA +36 -30
  121. waldiez-0.3.0.dist-info/RECORD +125 -0
  122. waldiez-0.3.0.dist-info/licenses/LICENSE +201 -0
  123. waldiez/exporting/agents/__init__.py +0 -5
  124. waldiez/exporting/agents/agent.py +0 -236
  125. waldiez/exporting/agents/agent_skills.py +0 -67
  126. waldiez/exporting/agents/llm_config.py +0 -53
  127. waldiez/exporting/chats/chats.py +0 -46
  128. waldiez/exporting/chats/helpers.py +0 -420
  129. waldiez/exporting/flow/def_main.py +0 -32
  130. waldiez/exporting/flow/flow.py +0 -189
  131. waldiez/exporting/utils/__init__.py +0 -36
  132. waldiez/exporting/utils/importing.py +0 -265
  133. waldiez/exporting/utils/method_utils.py +0 -35
  134. waldiez/exporting/utils/path_check.py +0 -51
  135. waldiez-0.2.1.dist-info/RECORD +0 -92
  136. waldiez-0.2.1.dist-info/licenses/LICENSE +0 -21
  137. {waldiez-0.2.1.dist-info → waldiez-0.3.0.dist-info}/WHEEL +0 -0
  138. {waldiez-0.2.1.dist-info → waldiez-0.3.0.dist-info}/entry_points.txt +0 -0
waldiez/models/waldiez.py CHANGED
@@ -1,4 +1,6 @@
1
- """Waldiez data class.
1
+ # SPDX-License-Identifier: Apache-2.0.
2
+ # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
+ """Waldiez
2
4
 
3
5
  A Waldiez class contains all the information that is needed to generate
4
6
  and run an autogen workflow. It has the model/LLM configurations, the agent
@@ -204,6 +206,11 @@ class Waldiez:
204
206
  """Get the flow tags."""
205
207
  return self.flow.tags
206
208
 
209
+ @property
210
+ def is_async(self) -> bool:
211
+ """Check if the flow is asynchronous."""
212
+ return self.flow.is_async
213
+
207
214
  @property
208
215
  def requirements(self) -> List[str]:
209
216
  """Get the flow requirements."""
@@ -236,9 +243,9 @@ class Waldiez:
236
243
  for model in self.models:
237
244
  if model.data.api_type in models_with_additional_requirements:
238
245
  requirements.add(
239
- f"pyautogen[{model.data.api_type}]==" f"{autogen_version}"
246
+ f"pyautogen[{model.data.api_type}]=={autogen_version}"
240
247
  )
241
- return list(requirements)
248
+ return sorted(list(requirements))
242
249
 
243
250
  def get_flow_env_vars(self) -> List[Tuple[str, str]]:
244
251
  """Get the flow environment variables.
@@ -255,7 +262,7 @@ class Waldiez:
255
262
  return env_vars
256
263
 
257
264
  def get_group_chat_members(self, agent: WaldiezAgent) -> List[WaldiezAgent]:
258
- """Get the chat members that connect to a group chat manger agent.
265
+ """Get the chat members that connect to a group chat manager agent.
259
266
 
260
267
  Parameters
261
268
  ----------
@@ -271,6 +278,23 @@ class Waldiez:
271
278
  return []
272
279
  return self.flow.get_group_chat_members(agent.id)
273
280
 
281
+ def get_swarm_members(
282
+ self, initial_agent: WaldiezAgent
283
+ ) -> Tuple[List[WaldiezAgent], Optional[WaldiezAgent]]:
284
+ """Get the chat members that connect to a swarm agent.
285
+
286
+ Parameters
287
+ ----------
288
+ initial_agent : WaldiezAgent
289
+ The initial agent.
290
+
291
+ Returns
292
+ -------
293
+ Tuple[List[WaldiezAgent], Optional[WaldiezAgent]]
294
+ The swarm agents and the user agent.
295
+ """
296
+ return self.flow.get_swarm_chat_members(initial_agent)
297
+
274
298
 
275
299
  def _get_flow(
276
300
  data: Dict[str, Any],
waldiez/runner.py CHANGED
@@ -1,6 +1,6 @@
1
- """Waldiez Flow runner.
2
-
3
- Run a waldiez flow.
1
+ # SPDX-License-Identifier: Apache-2.0.
2
+ # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
+ """Run a waldiez flow.
4
4
  The flow is first converted to an autogen flow with agents, chats and skills.
5
5
  We then chown to temporary directory, call the flow's `main()` and
6
6
  return the results. Before running the flow, any additional environment
@@ -9,60 +9,34 @@ variables specified in the waldiez file are set.
9
9
 
10
10
  # pylint: disable=import-outside-toplevel,reimported
11
11
 
12
- import datetime
13
12
  import importlib.util
14
- import io
15
- import os
16
- import shutil
17
- import site
18
- import subprocess # nosemgrep # nosec
19
13
  import sys
20
14
  import tempfile
21
- import warnings
22
- from contextlib import contextmanager
23
15
  from pathlib import Path
24
16
  from types import TracebackType
25
- from typing import (
26
- TYPE_CHECKING,
27
- Callable,
28
- Dict,
29
- Iterator,
30
- List,
31
- Optional,
32
- Tuple,
33
- Type,
34
- Union,
35
- )
17
+ from typing import TYPE_CHECKING, List, Optional, Type, Union
18
+
19
+ from asyncer import syncify
36
20
 
37
21
  from .exporter import WaldiezExporter
38
22
  from .models.waldiez import Waldiez
23
+ from .running import (
24
+ a_chdir,
25
+ a_install_requirements,
26
+ after_run,
27
+ before_run,
28
+ chdir,
29
+ get_printer,
30
+ install_requirements,
31
+ refresh_environment,
32
+ reset_env_vars,
33
+ set_env_vars,
34
+ )
39
35
 
40
36
  if TYPE_CHECKING:
41
37
  from autogen import ChatResult # type: ignore
42
38
 
43
39
 
44
- @contextmanager
45
- def _chdir(to: Union[str, Path]) -> Iterator[None]:
46
- """Change the current working directory in a context.
47
-
48
- Parameters
49
- ----------
50
- to : Union[str, Path]
51
- The directory to change to.
52
-
53
- Yields
54
- ------
55
- Iterator[None]
56
- The context manager.
57
- """
58
- old_cwd = str(os.getcwd())
59
- os.chdir(to)
60
- try:
61
- yield
62
- finally:
63
- os.chdir(old_cwd)
64
-
65
-
66
40
  class WaldiezRunner:
67
41
  """Waldiez runner class."""
68
42
 
@@ -127,6 +101,12 @@ class WaldiezRunner:
127
101
  """Enter the context manager."""
128
102
  return self
129
103
 
104
+ async def __aenter__(
105
+ self,
106
+ ) -> "WaldiezRunner":
107
+ """Enter the context manager asynchronously."""
108
+ return self
109
+
130
110
  def __exit__(
131
111
  self,
132
112
  exc_type: Type[BaseException],
@@ -137,6 +117,16 @@ class WaldiezRunner:
137
117
  if self._running:
138
118
  self._running = False
139
119
 
120
+ async def __aexit__(
121
+ self,
122
+ exc_type: Type[BaseException],
123
+ exc_value: BaseException,
124
+ traceback: TracebackType,
125
+ ) -> None:
126
+ """Exit the context manager asynchronously."""
127
+ if self._running:
128
+ self._running = False
129
+
140
130
  @property
141
131
  def waldiez(self) -> Waldiez:
142
132
  """Get the Waldiez instance."""
@@ -155,78 +145,25 @@ class WaldiezRunner:
155
145
  req for req in self.waldiez.requirements if req not in sys.modules
156
146
  )
157
147
  if extra_requirements:
158
- requirements_string = ", ".join(extra_requirements)
159
- printer(f"Installing requirements: {requirements_string}")
160
- pip_install = [sys.executable, "-m", "pip", "install"]
161
- if not in_virtualenv():
162
- pip_install.append("--user")
163
- pip_install.extend(extra_requirements)
164
- with subprocess.Popen(
165
- pip_install,
166
- stdout=subprocess.PIPE,
167
- stderr=subprocess.PIPE,
168
- ) as proc:
169
- if proc.stdout:
170
- for line in io.TextIOWrapper(proc.stdout, encoding="utf-8"):
171
- printer(line.strip())
172
- if proc.stderr:
173
- for line in io.TextIOWrapper(proc.stderr, encoding="utf-8"):
174
- printer(line.strip())
148
+ install_requirements(extra_requirements, printer)
175
149
  refresh_environment()
176
150
 
177
- @staticmethod
178
- def _after_run(
179
- temp_dir: Path,
180
- output_path: Optional[Union[str, Path]],
181
- printer: Callable[..., None],
182
- ) -> None:
183
- if output_path:
184
- destination_dir = Path(output_path).parent
185
- destination_dir = (
186
- destination_dir
187
- / "waldiez_out"
188
- / datetime.datetime.now().strftime("%Y%m%d%H%M%S")
189
- )
190
- destination_dir.mkdir(parents=True, exist_ok=True)
191
- # copy the contents of the temp dir to the destination dir
192
- printer(f"Copying the results to {destination_dir}")
193
- for item in temp_dir.iterdir():
194
- # skip cache files
195
- if (
196
- item.name.startswith("__pycache__")
197
- or item.name.endswith(".pyc")
198
- or item == ".cache"
199
- ):
200
- continue
201
- if item.is_file():
202
- shutil.copy(item, destination_dir)
203
- else:
204
- shutil.copytree(item, destination_dir / item.name)
205
- shutil.rmtree(temp_dir)
206
-
207
- def _set_env_vars(self) -> Dict[str, str]:
208
- """Set environment variables and return the old ones (if any)."""
209
- old_vars: Dict[str, str] = {}
210
- for var_key, var_value in self.waldiez.get_flow_env_vars():
211
- if var_key:
212
- current = os.environ.get(var_key, "")
213
- old_vars[var_key] = current
214
- os.environ[var_key] = var_value
215
- return old_vars
216
-
217
- @staticmethod
218
- def _reset_env_vars(old_vars: Dict[str, str]) -> None:
219
- """Reset the environment variables."""
220
- for var_key, var_value in old_vars.items():
221
- if not var_value:
222
- os.environ.pop(var_key, "")
223
- else:
224
- os.environ[var_key] = var_value
151
+ async def a_install_requirements(self) -> None:
152
+ """Install the requirements for the flow asynchronously."""
153
+ self._called_install_requirements = True
154
+ printer = get_printer()
155
+ extra_requirements = set(
156
+ req for req in self.waldiez.requirements if req not in sys.modules
157
+ )
158
+ if extra_requirements:
159
+ await a_install_requirements(extra_requirements, printer)
160
+ refresh_environment()
225
161
 
226
- def _do_run(
162
+ def _run(
227
163
  self,
228
164
  output_path: Optional[Union[str, Path]],
229
165
  uploads_root: Optional[Union[str, Path]],
166
+ skip_mmd: bool = False,
230
167
  ) -> Union["ChatResult", List["ChatResult"]]:
231
168
  """Run the Waldiez workflow.
232
169
 
@@ -236,12 +173,16 @@ class WaldiezRunner:
236
173
  The output path.
237
174
  uploads_root : Optional[Union[str, Path]]
238
175
  The runtime uploads root.
239
-
176
+ skip_mmd : bool
177
+ Whether to skip the Mermaid diagram generation.
240
178
  Returns
241
179
  -------
242
180
  Union[ChatResult, List[ChatResult]]
243
181
  The result(s) of the chat(s).
244
182
  """
183
+ temp_dir = Path(tempfile.mkdtemp())
184
+ file_name = before_run(output_path, uploads_root)
185
+ module_name = file_name.replace(".py", "")
245
186
  if not self._called_install_requirements:
246
187
  self.install_requirements()
247
188
  else:
@@ -253,22 +194,52 @@ class WaldiezRunner:
253
194
  "you might need to restart the kernel."
254
195
  )
255
196
  results: Union["ChatResult", List["ChatResult"]] = []
256
- if not uploads_root:
257
- uploads_root = Path(tempfile.mkdtemp())
258
- else:
259
- uploads_root = Path(uploads_root)
260
- if not uploads_root.exists():
261
- uploads_root.mkdir(parents=True)
262
- temp_dir = Path(tempfile.mkdtemp())
263
- file_name = "flow.py" if not output_path else Path(output_path).name
264
- if file_name.endswith((".json", ".waldiez")):
265
- file_name = file_name.replace(".json", ".py").replace(
266
- ".waldiez", ".py"
197
+ with chdir(to=temp_dir):
198
+ self._exporter.export(Path(file_name))
199
+ spec = importlib.util.spec_from_file_location(
200
+ module_name, temp_dir / file_name
267
201
  )
268
- if not file_name.endswith(".py"):
269
- file_name += ".py"
202
+ if not spec or not spec.loader:
203
+ raise ImportError("Could not import the flow")
204
+ sys.path.insert(0, str(temp_dir))
205
+ old_vars = set_env_vars(self.waldiez.get_flow_env_vars())
206
+ module = importlib.util.module_from_spec(spec)
207
+ spec.loader.exec_module(module)
208
+ printer("<Waldiez> - Starting workflow...")
209
+ results = module.main()
210
+ sys.path.pop(0)
211
+ reset_env_vars(old_vars)
212
+ after_run(
213
+ temp_dir=temp_dir,
214
+ output_path=output_path,
215
+ printer=printer,
216
+ flow_name=self.waldiez.name,
217
+ skip_mmd=skip_mmd,
218
+ )
219
+ return results
220
+
221
+ async def _a_run(
222
+ self,
223
+ output_path: Optional[Union[str, Path]],
224
+ uploads_root: Optional[Union[str, Path]],
225
+ skip_mmd: bool = False,
226
+ ) -> Union["ChatResult", List["ChatResult"]]:
227
+ """Run the Waldiez workflow asynchronously."""
228
+ temp_dir = Path(tempfile.mkdtemp())
229
+ file_name = before_run(output_path, uploads_root)
270
230
  module_name = file_name.replace(".py", "")
271
- with _chdir(to=temp_dir):
231
+ if not self._called_install_requirements:
232
+ await self.a_install_requirements()
233
+ else:
234
+ refresh_environment()
235
+ printer = get_printer()
236
+ printer(
237
+ "Requirements installed.\n"
238
+ "NOTE: If new packages were added and you are using Jupyter, "
239
+ "you might need to restart the kernel."
240
+ )
241
+ results: Union["ChatResult", List["ChatResult"]] = []
242
+ async with a_chdir(to=temp_dir):
272
243
  self._exporter.export(Path(file_name))
273
244
  spec = importlib.util.spec_from_file_location(
274
245
  module_name, temp_dir / file_name
@@ -276,20 +247,27 @@ class WaldiezRunner:
276
247
  if not spec or not spec.loader:
277
248
  raise ImportError("Could not import the flow")
278
249
  sys.path.insert(0, str(temp_dir))
279
- old_vars = self._set_env_vars()
250
+ old_vars = set_env_vars(self.waldiez.get_flow_env_vars())
280
251
  module = importlib.util.module_from_spec(spec)
281
252
  spec.loader.exec_module(module)
282
253
  printer("<Waldiez> - Starting workflow...")
283
- results = module.main()
254
+ results = await module.main()
284
255
  sys.path.pop(0)
285
- self._reset_env_vars(old_vars)
286
- self._after_run(temp_dir, output_path, printer)
256
+ reset_env_vars(old_vars)
257
+ after_run(
258
+ temp_dir=temp_dir,
259
+ output_path=output_path,
260
+ printer=printer,
261
+ flow_name=self.waldiez.name,
262
+ skip_mmd=skip_mmd,
263
+ )
287
264
  return results
288
265
 
289
266
  def run(
290
267
  self,
291
268
  output_path: Optional[Union[str, Path]] = None,
292
269
  uploads_root: Optional[Union[str, Path]] = None,
270
+ skip_mmd: bool = False,
293
271
  ) -> Union["ChatResult", List["ChatResult"]]:
294
272
  """Run the Waldiez workflow.
295
273
 
@@ -299,6 +277,8 @@ class WaldiezRunner:
299
277
  The output path, by default None.
300
278
  uploads_root : Optional[Union[str, Path]], optional
301
279
  The uploads root, to get user-uploaded files, by default None.
280
+ skip_mmd : bool, optional
281
+ Whether to skip the Mermaid diagram generation, by default False.
302
282
 
303
283
  Returns
304
284
  -------
@@ -310,114 +290,48 @@ class WaldiezRunner:
310
290
  RuntimeError
311
291
  If the workflow is already running.
312
292
  """
293
+ if self.waldiez.is_async:
294
+ return syncify(self.a_run, raise_sync_error=False)(
295
+ output_path, uploads_root
296
+ )
313
297
  if self._running is True:
314
298
  raise RuntimeError("Workflow already running")
315
299
  self._running = True
316
300
  file_path = output_path or self._file_path
317
301
  try:
318
- return self._do_run(file_path, uploads_root)
302
+ return self._run(file_path, uploads_root, skip_mmd)
319
303
  finally:
320
304
  self._running = False
321
305
 
306
+ async def a_run(
307
+ self,
308
+ output_path: Optional[Union[str, Path]] = None,
309
+ uploads_root: Optional[Union[str, Path]] = None,
310
+ ) -> Union["ChatResult", List["ChatResult"]]:
311
+ """Run the Waldiez workflow asynchronously.
322
312
 
323
- def in_virtualenv() -> bool:
324
- """Check if we are inside a virtualenv.
325
-
326
- Returns
327
- -------
328
- bool
329
- True if inside a virtualenv, False otherwise.
330
- """
331
- return hasattr(sys, "real_prefix") or (
332
- hasattr(sys, "base_prefix") and sys.base_prefix != sys.prefix
333
- )
334
-
335
-
336
- def refresh_environment() -> None:
337
- """Refresh the environment."""
338
- # backup the default IOStream
339
- from autogen.io import IOStream # type: ignore
340
-
341
- default_io_stream = IOStream.get_default()
342
- site.main()
343
- # pylint: disable=import-outside-toplevel
344
- modules_to_reload = [
345
- mod for mod in sys.modules if mod.startswith("autogen")
346
- ]
347
- for mod in modules_to_reload:
348
- del sys.modules[mod]
349
- warnings.filterwarnings(
350
- "ignore", module="flaml", message="^.*flaml.automl is not available.*$"
351
- )
352
- import autogen
353
- from autogen.io import IOStream
354
-
355
- importlib.reload(autogen)
356
- # restore the default IOStream
357
- IOStream.set_global_default(default_io_stream)
358
-
359
-
360
- def get_printer() -> Callable[..., None]:
361
- """Get the printer function.
362
-
363
- Returns
364
- -------
365
- Callable[..., None]
366
- The printer function.
367
- """
368
- from autogen.io import IOStream
313
+ Parameters
314
+ ----------
315
+ output_path : Optional[Union[str, Path]], optional
316
+ The output path, by default None.
317
+ uploads_root : Optional[Union[str, Path]], optional
318
+ The uploads root, to get user-uploaded files, by default None.
369
319
 
370
- printer = IOStream.get_default().print
320
+ Returns
321
+ -------
322
+ Union[ChatResult, List[ChatResult]]
323
+ The result(s) of the chat(s).
371
324
 
372
- def safe_printer(*args: object, **kwargs: object) -> None:
325
+ Raises
326
+ ------
327
+ RuntimeError
328
+ If the workflow is already running.
329
+ """
330
+ if self._running is True:
331
+ raise RuntimeError("Workflow already running")
332
+ self._running = True
333
+ file_path = output_path or self._file_path
373
334
  try:
374
- printer(*args, **kwargs)
375
- except UnicodeEncodeError:
376
- # pylint: disable=too-many-try-statements
377
- try:
378
- msg, flush = get_what_to_print(*args, **kwargs)
379
- printer(msg, end="", flush=flush)
380
- except UnicodeEncodeError:
381
- sys.stdout = io.TextIOWrapper(
382
- sys.stdout.buffer, encoding="utf-8"
383
- )
384
- sys.stderr = io.TextIOWrapper(
385
- sys.stderr.buffer, encoding="utf-8"
386
- )
387
- try:
388
- printer(*args, **kwargs)
389
- except UnicodeEncodeError:
390
- sys.stderr.write(
391
- "Could not print the message due to encoding issues.\n"
392
- )
393
-
394
- return safe_printer
395
-
396
-
397
- def get_what_to_print(*args: object, **kwargs: object) -> Tuple[str, bool]:
398
- """Get what to print.
399
-
400
- Parameters
401
- ----------
402
- args : object
403
- The arguments.
404
- kwargs : object
405
- The keyword arguments.
406
-
407
- Returns
408
- -------
409
- Tuple[str, bool]
410
- The message and whether to flush.
411
- """
412
- sep = kwargs.get("sep", " ")
413
- if not isinstance(sep, str):
414
- sep = " "
415
- end = kwargs.get("end", "\n")
416
- if not isinstance(end, str):
417
- end = "\n"
418
- flush = kwargs.get("flush", False)
419
- if not isinstance(flush, bool):
420
- flush = False
421
- msg = sep.join(str(arg) for arg in args) + end
422
- utf8_msg = msg.encode("utf-8", errors="replace").decode("utf-8")
423
- return utf8_msg, flush
335
+ return await self._a_run(file_path, uploads_root)
336
+ finally:
337
+ self._running = False
@@ -0,0 +1,33 @@
1
+ # SPDX-License-Identifier: Apache-2.0.
2
+ # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
+ """Common utility functions."""
4
+
5
+ from .environment import (
6
+ in_virtualenv,
7
+ refresh_environment,
8
+ reset_env_vars,
9
+ set_env_vars,
10
+ )
11
+ from .running import (
12
+ a_chdir,
13
+ a_install_requirements,
14
+ after_run,
15
+ before_run,
16
+ chdir,
17
+ get_printer,
18
+ install_requirements,
19
+ )
20
+
21
+ __all__ = [
22
+ "a_chdir",
23
+ "a_install_requirements",
24
+ "after_run",
25
+ "before_run",
26
+ "chdir",
27
+ "get_printer",
28
+ "in_virtualenv",
29
+ "install_requirements",
30
+ "refresh_environment",
31
+ "reset_env_vars",
32
+ "set_env_vars",
33
+ ]
@@ -0,0 +1,83 @@
1
+ # SPDX-License-Identifier: Apache-2.0.
2
+ # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
+ # pylint: disable=import-outside-toplevel,reimported
4
+ """Environment related utilities."""
5
+
6
+ import importlib.util
7
+ import os
8
+ import site
9
+ import sys
10
+ import warnings
11
+ from typing import Dict, List, Tuple
12
+
13
+
14
+ def in_virtualenv() -> bool:
15
+ """Check if we are inside a virtualenv.
16
+
17
+ Returns
18
+ -------
19
+ bool
20
+ True if inside a virtualenv, False otherwise.
21
+ """
22
+ return hasattr(sys, "real_prefix") or (
23
+ hasattr(sys, "base_prefix") and sys.base_prefix != sys.prefix
24
+ )
25
+
26
+
27
+ def refresh_environment() -> None:
28
+ """Refresh the environment."""
29
+ # backup the default IOStream
30
+ from autogen.io import IOStream # type: ignore
31
+
32
+ default_io_stream = IOStream.get_default()
33
+ site.main()
34
+ # pylint: disable=import-outside-toplevel
35
+ modules_to_reload = [mod for mod in sys.modules if "autogen" in mod]
36
+ for mod in modules_to_reload:
37
+ del sys.modules[mod]
38
+ warnings.filterwarnings(
39
+ "ignore", module="flaml", message="^.*flaml.automl is not available.*$"
40
+ )
41
+ import autogen # type: ignore
42
+ from autogen.io import IOStream
43
+
44
+ importlib.reload(autogen)
45
+ # restore the default IOStream
46
+ IOStream.set_global_default(default_io_stream)
47
+
48
+
49
+ def set_env_vars(flow_env_vars: List[Tuple[str, str]]) -> Dict[str, str]:
50
+ """Set environment variables and return the old ones (if any).
51
+
52
+ Parameters
53
+ ----------
54
+ flow_env_vars : List[Tuple[str, str]]
55
+ The environment variables to set.
56
+
57
+ Returns
58
+ -------
59
+ Dict[str, str]
60
+ The old environment variables.
61
+ """
62
+ old_vars: Dict[str, str] = {}
63
+ for var_key, var_value in flow_env_vars:
64
+ if var_key:
65
+ current = os.environ.get(var_key, "")
66
+ old_vars[var_key] = current
67
+ os.environ[var_key] = var_value
68
+ return old_vars
69
+
70
+
71
+ def reset_env_vars(old_vars: Dict[str, str]) -> None:
72
+ """Reset the environment variables.
73
+
74
+ Parameters
75
+ ----------
76
+ old_vars : Dict[str, str]
77
+ The old environment variables.
78
+ """
79
+ for var_key, var_value in old_vars.items():
80
+ if not var_value:
81
+ os.environ.pop(var_key, "")
82
+ else:
83
+ os.environ[var_key] = var_value