waldiez 0.4.7__py3-none-any.whl → 0.4.9__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 (248) hide show
  1. waldiez/__init__.py +5 -5
  2. waldiez/_version.py +1 -1
  3. waldiez/cli.py +97 -102
  4. waldiez/exporter.py +61 -19
  5. waldiez/exporting/__init__.py +25 -6
  6. waldiez/exporting/agent/__init__.py +7 -3
  7. waldiez/exporting/agent/code_execution.py +114 -0
  8. waldiez/exporting/agent/exporter.py +354 -0
  9. waldiez/exporting/agent/extras/__init__.py +15 -0
  10. waldiez/exporting/agent/extras/captain_agent_extras.py +315 -0
  11. waldiez/exporting/agent/extras/group/target.py +178 -0
  12. waldiez/exporting/agent/extras/group_manager_agent_extas.py +500 -0
  13. waldiez/exporting/agent/extras/group_member_extras.py +181 -0
  14. waldiez/exporting/agent/extras/handoffs/__init__.py +19 -0
  15. waldiez/exporting/agent/extras/handoffs/after_work.py +78 -0
  16. waldiez/exporting/agent/extras/handoffs/available.py +74 -0
  17. waldiez/exporting/agent/extras/handoffs/condition.py +158 -0
  18. waldiez/exporting/agent/extras/handoffs/handoff.py +171 -0
  19. waldiez/exporting/agent/extras/handoffs/target.py +189 -0
  20. waldiez/exporting/agent/extras/rag/__init__.py +10 -0
  21. waldiez/exporting/agent/{utils/rag_user/chroma_utils.py → extras/rag/chroma_extras.py} +37 -24
  22. waldiez/exporting/agent/{utils/rag_user/mongo_utils.py → extras/rag/mongo_extras.py} +10 -10
  23. waldiez/exporting/agent/{utils/rag_user/pgvector_utils.py → extras/rag/pgvector_extras.py} +13 -13
  24. waldiez/exporting/agent/{utils/rag_user/qdrant_utils.py → extras/rag/qdrant_extras.py} +13 -13
  25. waldiez/exporting/agent/{utils/rag_user/vector_db.py → extras/rag/vector_db_extras.py} +59 -46
  26. waldiez/exporting/agent/extras/rag_user_proxy_agent_extras.py +245 -0
  27. waldiez/exporting/agent/extras/reasoning_agent_extras.py +88 -0
  28. waldiez/exporting/agent/factory.py +95 -0
  29. waldiez/exporting/agent/processor.py +150 -0
  30. waldiez/exporting/agent/system_message.py +36 -0
  31. waldiez/exporting/agent/termination.py +50 -0
  32. waldiez/exporting/chats/__init__.py +7 -3
  33. waldiez/exporting/chats/exporter.py +97 -0
  34. waldiez/exporting/chats/factory.py +65 -0
  35. waldiez/exporting/chats/processor.py +226 -0
  36. waldiez/exporting/chats/utils/__init__.py +6 -5
  37. waldiez/exporting/chats/utils/common.py +11 -45
  38. waldiez/exporting/chats/utils/group.py +55 -0
  39. waldiez/exporting/chats/utils/nested.py +37 -52
  40. waldiez/exporting/chats/utils/sequential.py +72 -61
  41. waldiez/exporting/chats/utils/{single_chat.py → single.py} +48 -50
  42. waldiez/exporting/core/__init__.py +196 -0
  43. waldiez/exporting/core/constants.py +17 -0
  44. waldiez/exporting/core/content.py +69 -0
  45. waldiez/exporting/core/context.py +244 -0
  46. waldiez/exporting/core/enums.py +89 -0
  47. waldiez/exporting/core/errors.py +19 -0
  48. waldiez/exporting/core/exporter.py +390 -0
  49. waldiez/exporting/core/exporters.py +67 -0
  50. waldiez/exporting/core/extras/__init__.py +39 -0
  51. waldiez/exporting/core/extras/agent_extras/__init__.py +27 -0
  52. waldiez/exporting/core/extras/agent_extras/captain_extras.py +57 -0
  53. waldiez/exporting/core/extras/agent_extras/group_manager_extras.py +102 -0
  54. waldiez/exporting/core/extras/agent_extras/rag_user_extras.py +53 -0
  55. waldiez/exporting/core/extras/agent_extras/reasoning_extras.py +68 -0
  56. waldiez/exporting/core/extras/agent_extras/standard_extras.py +263 -0
  57. waldiez/exporting/core/extras/base.py +241 -0
  58. waldiez/exporting/core/extras/chat_extras.py +118 -0
  59. waldiez/exporting/core/extras/flow_extras.py +70 -0
  60. waldiez/exporting/core/extras/model_extras.py +73 -0
  61. waldiez/exporting/core/extras/path_resolver.py +93 -0
  62. waldiez/exporting/core/extras/serializer.py +138 -0
  63. waldiez/exporting/core/extras/tool_extras.py +82 -0
  64. waldiez/exporting/core/protocols.py +259 -0
  65. waldiez/exporting/core/result.py +705 -0
  66. waldiez/exporting/core/types.py +329 -0
  67. waldiez/exporting/core/utils/__init__.py +11 -0
  68. waldiez/exporting/core/utils/comment.py +33 -0
  69. waldiez/exporting/core/utils/llm_config.py +117 -0
  70. waldiez/exporting/core/validation.py +96 -0
  71. waldiez/exporting/flow/__init__.py +6 -2
  72. waldiez/exporting/flow/execution_generator.py +193 -0
  73. waldiez/exporting/flow/exporter.py +107 -0
  74. waldiez/exporting/flow/factory.py +94 -0
  75. waldiez/exporting/flow/file_generator.py +214 -0
  76. waldiez/exporting/flow/merger.py +387 -0
  77. waldiez/exporting/flow/orchestrator.py +411 -0
  78. waldiez/exporting/flow/utils/__init__.py +9 -36
  79. waldiez/exporting/flow/utils/common.py +206 -0
  80. waldiez/exporting/flow/utils/importing.py +373 -0
  81. waldiez/exporting/flow/utils/linting.py +200 -0
  82. waldiez/exporting/flow/utils/{logging_utils.py → logging.py} +23 -9
  83. waldiez/exporting/models/__init__.py +3 -1
  84. waldiez/exporting/models/exporter.py +233 -0
  85. waldiez/exporting/models/factory.py +66 -0
  86. waldiez/exporting/models/processor.py +139 -0
  87. waldiez/exporting/tools/__init__.py +11 -0
  88. waldiez/exporting/tools/exporter.py +207 -0
  89. waldiez/exporting/tools/factory.py +57 -0
  90. waldiez/exporting/tools/processor.py +248 -0
  91. waldiez/exporting/tools/registration.py +133 -0
  92. waldiez/io/__init__.py +128 -0
  93. waldiez/io/_ws.py +199 -0
  94. waldiez/io/models/__init__.py +60 -0
  95. waldiez/io/models/base.py +66 -0
  96. waldiez/io/models/constants.py +78 -0
  97. waldiez/io/models/content/__init__.py +23 -0
  98. waldiez/io/models/content/audio.py +43 -0
  99. waldiez/io/models/content/base.py +45 -0
  100. waldiez/io/models/content/file.py +43 -0
  101. waldiez/io/models/content/image.py +96 -0
  102. waldiez/io/models/content/text.py +37 -0
  103. waldiez/io/models/content/video.py +43 -0
  104. waldiez/io/models/user_input.py +269 -0
  105. waldiez/io/models/user_response.py +215 -0
  106. waldiez/io/mqtt.py +681 -0
  107. waldiez/io/redis.py +782 -0
  108. waldiez/io/structured.py +439 -0
  109. waldiez/io/utils.py +184 -0
  110. waldiez/io/ws.py +298 -0
  111. waldiez/logger.py +481 -0
  112. waldiez/models/__init__.py +108 -51
  113. waldiez/models/agents/__init__.py +34 -70
  114. waldiez/models/agents/agent/__init__.py +10 -4
  115. waldiez/models/agents/agent/agent.py +466 -65
  116. waldiez/models/agents/agent/agent_data.py +119 -47
  117. waldiez/models/agents/agent/agent_type.py +13 -2
  118. waldiez/models/agents/agent/code_execution.py +12 -12
  119. waldiez/models/agents/agent/human_input_mode.py +8 -0
  120. waldiez/models/agents/agent/{linked_skill.py → linked_tool.py} +7 -7
  121. waldiez/models/agents/agent/nested_chat.py +35 -7
  122. waldiez/models/agents/agent/termination_message.py +30 -22
  123. waldiez/models/agents/{swarm_agent → agent}/update_system_message.py +22 -22
  124. waldiez/models/agents/agents.py +58 -63
  125. waldiez/models/agents/assistant/assistant.py +4 -4
  126. waldiez/models/agents/assistant/assistant_data.py +13 -1
  127. waldiez/models/agents/{captain_agent → captain}/captain_agent.py +5 -5
  128. waldiez/models/agents/{captain_agent → captain}/captain_agent_data.py +5 -5
  129. waldiez/models/agents/extra_requirements.py +11 -16
  130. waldiez/models/agents/group_manager/group_manager.py +103 -13
  131. waldiez/models/agents/group_manager/group_manager_data.py +36 -14
  132. waldiez/models/agents/group_manager/speakers.py +77 -24
  133. waldiez/models/agents/{rag_user → rag_user_proxy}/__init__.py +16 -16
  134. waldiez/models/agents/rag_user_proxy/rag_user_proxy.py +64 -0
  135. waldiez/models/agents/{rag_user/rag_user_data.py → rag_user_proxy/rag_user_proxy_data.py} +6 -5
  136. waldiez/models/agents/{rag_user → rag_user_proxy}/retrieve_config.py +182 -114
  137. waldiez/models/agents/{rag_user → rag_user_proxy}/vector_db_config.py +13 -13
  138. waldiez/models/agents/reasoning/reasoning_agent.py +6 -6
  139. waldiez/models/agents/reasoning/reasoning_agent_data.py +110 -63
  140. waldiez/models/agents/reasoning/reasoning_agent_reason_config.py +38 -10
  141. waldiez/models/agents/user_proxy/user_proxy.py +11 -7
  142. waldiez/models/agents/user_proxy/user_proxy_data.py +2 -2
  143. waldiez/models/chat/__init__.py +2 -1
  144. waldiez/models/chat/chat.py +166 -87
  145. waldiez/models/chat/chat_data.py +99 -136
  146. waldiez/models/chat/chat_message.py +33 -23
  147. waldiez/models/chat/chat_nested.py +31 -30
  148. waldiez/models/chat/chat_summary.py +10 -8
  149. waldiez/models/common/__init__.py +52 -2
  150. waldiez/models/common/ag2_version.py +1 -1
  151. waldiez/models/common/base.py +38 -7
  152. waldiez/models/common/dict_utils.py +42 -17
  153. waldiez/models/common/handoff.py +459 -0
  154. waldiez/models/common/id_generator.py +19 -0
  155. waldiez/models/common/method_utils.py +130 -68
  156. waldiez/{exporting/base/utils → models/common}/naming.py +38 -61
  157. waldiez/models/common/waldiez_version.py +37 -0
  158. waldiez/models/flow/__init__.py +9 -2
  159. waldiez/models/flow/connection.py +18 -0
  160. waldiez/models/flow/flow.py +311 -215
  161. waldiez/models/flow/flow_data.py +207 -40
  162. waldiez/models/flow/info.py +85 -0
  163. waldiez/models/flow/naming.py +131 -0
  164. waldiez/models/model/__init__.py +7 -1
  165. waldiez/models/model/extra_requirements.py +3 -12
  166. waldiez/models/model/model.py +76 -21
  167. waldiez/models/model/model_data.py +108 -20
  168. waldiez/models/tool/__init__.py +16 -0
  169. waldiez/models/tool/extra_requirements.py +36 -0
  170. waldiez/models/{skill/skill.py → tool/tool.py} +88 -88
  171. waldiez/models/tool/tool_data.py +51 -0
  172. waldiez/models/tool/tool_type.py +8 -0
  173. waldiez/models/waldiez.py +97 -80
  174. waldiez/runner.py +115 -61
  175. waldiez/running/__init__.py +13 -7
  176. waldiez/running/environment.py +49 -68
  177. waldiez/running/gen_seq_diagram.py +16 -14
  178. waldiez/running/post_run.py +119 -0
  179. waldiez/running/pre_run.py +149 -0
  180. waldiez/running/util.py +134 -0
  181. waldiez/utils/__init__.py +2 -4
  182. waldiez/utils/cli_extras/jupyter.py +5 -3
  183. waldiez/utils/cli_extras/runner.py +6 -4
  184. waldiez/utils/cli_extras/studio.py +6 -4
  185. waldiez/utils/conflict_checker.py +15 -9
  186. waldiez/utils/flaml_warnings.py +5 -5
  187. waldiez/utils/version.py +47 -0
  188. {waldiez-0.4.7.dist-info → waldiez-0.4.9.dist-info}/METADATA +235 -91
  189. waldiez-0.4.9.dist-info/RECORD +203 -0
  190. waldiez/exporting/agent/agent_exporter.py +0 -297
  191. waldiez/exporting/agent/utils/__init__.py +0 -23
  192. waldiez/exporting/agent/utils/captain_agent.py +0 -263
  193. waldiez/exporting/agent/utils/code_execution.py +0 -65
  194. waldiez/exporting/agent/utils/group_manager.py +0 -220
  195. waldiez/exporting/agent/utils/rag_user/__init__.py +0 -7
  196. waldiez/exporting/agent/utils/rag_user/rag_user.py +0 -209
  197. waldiez/exporting/agent/utils/reasoning.py +0 -36
  198. waldiez/exporting/agent/utils/swarm_agent.py +0 -469
  199. waldiez/exporting/agent/utils/teachability.py +0 -41
  200. waldiez/exporting/agent/utils/termination_message.py +0 -44
  201. waldiez/exporting/base/__init__.py +0 -25
  202. waldiez/exporting/base/agent_position.py +0 -75
  203. waldiez/exporting/base/base_exporter.py +0 -118
  204. waldiez/exporting/base/export_position.py +0 -48
  205. waldiez/exporting/base/import_position.py +0 -23
  206. waldiez/exporting/base/mixin.py +0 -137
  207. waldiez/exporting/base/utils/__init__.py +0 -18
  208. waldiez/exporting/base/utils/comments.py +0 -96
  209. waldiez/exporting/base/utils/path_check.py +0 -68
  210. waldiez/exporting/base/utils/to_string.py +0 -84
  211. waldiez/exporting/chats/chats_exporter.py +0 -240
  212. waldiez/exporting/chats/utils/swarm.py +0 -210
  213. waldiez/exporting/flow/flow_exporter.py +0 -528
  214. waldiez/exporting/flow/utils/agent_utils.py +0 -204
  215. waldiez/exporting/flow/utils/chat_utils.py +0 -71
  216. waldiez/exporting/flow/utils/def_main.py +0 -77
  217. waldiez/exporting/flow/utils/flow_content.py +0 -202
  218. waldiez/exporting/flow/utils/flow_names.py +0 -116
  219. waldiez/exporting/flow/utils/importing_utils.py +0 -227
  220. waldiez/exporting/models/models_exporter.py +0 -199
  221. waldiez/exporting/models/utils.py +0 -174
  222. waldiez/exporting/skills/__init__.py +0 -9
  223. waldiez/exporting/skills/skills_exporter.py +0 -176
  224. waldiez/exporting/skills/utils.py +0 -369
  225. waldiez/models/agents/agent/teachability.py +0 -70
  226. waldiez/models/agents/rag_user/rag_user.py +0 -60
  227. waldiez/models/agents/swarm_agent/__init__.py +0 -50
  228. waldiez/models/agents/swarm_agent/after_work.py +0 -179
  229. waldiez/models/agents/swarm_agent/on_condition.py +0 -105
  230. waldiez/models/agents/swarm_agent/on_condition_available.py +0 -142
  231. waldiez/models/agents/swarm_agent/on_condition_target.py +0 -40
  232. waldiez/models/agents/swarm_agent/swarm_agent.py +0 -107
  233. waldiez/models/agents/swarm_agent/swarm_agent_data.py +0 -124
  234. waldiez/models/flow/utils.py +0 -232
  235. waldiez/models/skill/__init__.py +0 -16
  236. waldiez/models/skill/extra_requirements.py +0 -36
  237. waldiez/models/skill/skill_data.py +0 -53
  238. waldiez/models/skill/skill_type.py +0 -8
  239. waldiez/running/running.py +0 -369
  240. waldiez/utils/pysqlite3_checker.py +0 -308
  241. waldiez/utils/rdps_checker.py +0 -122
  242. waldiez-0.4.7.dist-info/RECORD +0 -149
  243. /waldiez/models/agents/{captain_agent → captain}/__init__.py +0 -0
  244. /waldiez/models/agents/{captain_agent → captain}/captain_agent_lib_entry.py +0 -0
  245. {waldiez-0.4.7.dist-info → waldiez-0.4.9.dist-info}/WHEEL +0 -0
  246. {waldiez-0.4.7.dist-info → waldiez-0.4.9.dist-info}/entry_points.txt +0 -0
  247. {waldiez-0.4.7.dist-info → waldiez-0.4.9.dist-info}/licenses/LICENSE +0 -0
  248. {waldiez-0.4.7.dist-info → waldiez-0.4.9.dist-info}/licenses/NOTICE.md +0 -0
@@ -5,7 +5,7 @@
5
5
  import json
6
6
  import re
7
7
  from pathlib import Path
8
- from typing import Any, Dict, Set, Union
8
+ from typing import Any, Set, Union
9
9
 
10
10
  import pandas as pd
11
11
 
@@ -38,7 +38,7 @@ def escape_mermaid_text(text: str) -> str:
38
38
  return output
39
39
 
40
40
 
41
- def get_json_state(json_state: Any) -> Dict[str, Any]:
41
+ def get_json_state(json_state: Any) -> dict[str, Any]:
42
42
  """Get the JSON state of the event.
43
43
 
44
44
  Parameters
@@ -52,7 +52,7 @@ def get_json_state(json_state: Any) -> Dict[str, Any]:
52
52
  The JSON state of the event.
53
53
  """
54
54
  if isinstance(json_state, dict):
55
- return json_state
55
+ return json_state # pyright: ignore
56
56
  if isinstance(json_state, str):
57
57
  try:
58
58
  return json.loads(json_state)
@@ -68,7 +68,7 @@ def process_events(df_events: pd.DataFrame) -> str:
68
68
  Parameters
69
69
  ----------
70
70
  df_events : pd.DataFrame
71
- The DataFrame containing the events data.
71
+ The DataFrame containing the events' data.
72
72
 
73
73
  Returns
74
74
  -------
@@ -83,7 +83,7 @@ def process_events(df_events: pd.DataFrame) -> str:
83
83
  seq_text = SEQ_TXT
84
84
 
85
85
  # Loop through each event in the DataFrame
86
- for i in range(len(df_events["json_state"])):
86
+ for i in range(len(df_events["json_state"])): # pyright: ignore
87
87
  # Parse the JSON state of the event
88
88
  df_j = get_json_state(df_events["json_state"][i])
89
89
  # Skip events that are not relevant (e.g., replies or missing messages)
@@ -91,26 +91,28 @@ def process_events(df_events: pd.DataFrame) -> str:
91
91
  df_events["event_name"][i] != "reply_func_executed"
92
92
  ):
93
93
  sender = df_j["sender"]
94
- recipient = df_events["source_name"][i]
94
+ # noinspection PyTypeChecker
95
+ recipient = df_events["source_name"][i] # pyright: ignore
95
96
 
96
97
  # Extract message content if available
97
98
  if (
98
99
  isinstance(df_j["message"], dict)
99
100
  and "content" in df_j["message"]
100
101
  ):
101
- message = "Content: " + str(df_j["message"]["content"])
102
+ content = str(df_j["message"]["content"]) # pyright: ignore
103
+ message = "Content: " + content
102
104
  else:
103
- message = str(df_j["message"])
105
+ message = str(df_j["message"]) # pyright: ignore
104
106
 
105
107
  # Escape the message for Mermaid compatibility and
106
108
  # truncate long messages
107
109
  message = escape_mermaid_text(message)
108
110
 
109
111
  # Add sender and recipient to participants set
110
- participants.add(recipient)
112
+ participants.add(recipient) # pyright: ignore
111
113
  participants.add(sender)
112
114
 
113
- # Split message into main message and context
115
+ # Split into the main message and the context
114
116
  # if "Content" is present
115
117
  if "Content: " in message:
116
118
  message_parts = message.split("Content: ")
@@ -155,7 +157,7 @@ def generate_sequence_diagram(
155
157
  Parameters
156
158
  ----------
157
159
  file_path : Union[str, Path]
158
- The path to the JSON or CSV file containing the events data.
160
+ The path to the JSON or CSV file containing the events' data.
159
161
  output_path : Union[str, Path]
160
162
  The path to save the Mermaid diagram.
161
163
 
@@ -176,10 +178,10 @@ def generate_sequence_diagram(
176
178
  is_csv = file_path.suffix == ".csv"
177
179
  try:
178
180
  if is_csv:
179
- df_events = pd.read_csv(file_path)
181
+ df_events = pd.read_csv(file_path) # pyright: ignore
180
182
  else:
181
- df_events = pd.read_json(file_path)
182
- except pd.errors.EmptyDataError:
183
+ df_events = pd.read_json(file_path) # pyright: ignore
184
+ except pd.errors.EmptyDataError: # pragma: no cover
183
185
  return
184
186
 
185
187
  # Generate the Mermaid sequence diagram text
@@ -0,0 +1,119 @@
1
+ # SPDX-License-Identifier: Apache-2.0.
2
+ # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
+ """Utilities for running code."""
4
+
5
+ import datetime
6
+ import shutil
7
+ from pathlib import Path
8
+ from typing import Optional, Union
9
+
10
+ from .gen_seq_diagram import generate_sequence_diagram
11
+
12
+
13
+ def after_run(
14
+ temp_dir: Path,
15
+ output_path: Optional[Union[str, Path]],
16
+ flow_name: str,
17
+ skip_mmd: bool = False,
18
+ ) -> None:
19
+ """Actions to perform after running the flow.
20
+
21
+ Parameters
22
+ ----------
23
+ temp_dir : Path
24
+ The temporary directory.
25
+ output_path : Optional[Union[str, Path]]
26
+ The output path.
27
+ flow_name : str
28
+ The flow name.
29
+ skip_mmd : bool, optional
30
+ Whether to skip the mermaid sequence diagram generation,
31
+ by default, False
32
+ """
33
+ if isinstance(output_path, str):
34
+ output_path = Path(output_path)
35
+ mmd_dir = output_path.parent if output_path else Path.cwd()
36
+ if skip_mmd is False:
37
+ events_csv_path = temp_dir / "logs" / "events.csv"
38
+ if events_csv_path.exists():
39
+ print("Generating mermaid sequence diagram...")
40
+ mmd_path = temp_dir / f"{flow_name}.mmd"
41
+ generate_sequence_diagram(events_csv_path, mmd_path)
42
+ if (
43
+ not output_path
44
+ and mmd_path.exists()
45
+ and mmd_path != mmd_dir / f"{flow_name}.mmd"
46
+ ):
47
+ try:
48
+ shutil.copyfile(mmd_path, mmd_dir / f"{flow_name}.mmd")
49
+ except BaseException: # pylint: disable=broad-exception-caught
50
+ pass
51
+ if output_path:
52
+ destination_dir = output_path.parent
53
+ destination_dir = (
54
+ destination_dir
55
+ / "waldiez_out"
56
+ / datetime.datetime.now().strftime("%Y%m%d%H%M%S")
57
+ )
58
+ destination_dir.mkdir(parents=True, exist_ok=True)
59
+ # copy the contents of the temp dir to the destination dir
60
+ print(f"Copying the results to {destination_dir}")
61
+ copy_results(
62
+ temp_dir=temp_dir,
63
+ output_path=output_path,
64
+ output_dir=output_path.parent,
65
+ destination_dir=destination_dir,
66
+ )
67
+ shutil.rmtree(temp_dir)
68
+
69
+
70
+ def copy_results(
71
+ temp_dir: Path,
72
+ output_path: Path,
73
+ output_dir: Path,
74
+ destination_dir: Path,
75
+ ) -> None:
76
+ """Copy the results to the output directory.
77
+
78
+ Parameters
79
+ ----------
80
+ temp_dir : Path
81
+ The temporary directory.
82
+ output_path : Path
83
+ The output path.
84
+ output_dir : Path
85
+ The output directory.
86
+ destination_dir : Path
87
+ The destination directory.
88
+ """
89
+ temp_dir.mkdir(parents=True, exist_ok=True)
90
+ for item in temp_dir.iterdir():
91
+ # skip cache files
92
+ if (
93
+ item.name.startswith("__pycache__")
94
+ or item.name.endswith(".pyc")
95
+ or item.name.endswith(".pyo")
96
+ or item.name.endswith(".pyd")
97
+ or item.name == ".cache"
98
+ ):
99
+ continue
100
+ if item.is_file():
101
+ # let's also copy the "tree of thoughts" image
102
+ # to the output directory
103
+ if item.name.endswith("tree_of_thoughts.png") or item.name.endswith(
104
+ "reasoning_tree.json"
105
+ ):
106
+ shutil.copy(item, output_dir / item.name)
107
+ shutil.copy(item, destination_dir)
108
+ else:
109
+ shutil.copytree(item, destination_dir / item.name)
110
+ if output_path.is_file():
111
+ if output_path.suffix == ".waldiez":
112
+ output_path = output_path.with_suffix(".py")
113
+ if output_path.suffix == ".py":
114
+ src = temp_dir / output_path.name
115
+ if src.exists():
116
+ dst = destination_dir / output_path.name
117
+ if dst.exists():
118
+ dst.unlink()
119
+ shutil.copyfile(src, output_dir / output_path.name)
@@ -0,0 +1,149 @@
1
+ # SPDX-License-Identifier: Apache-2.0.
2
+ # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
+ """Actions to perform before running the flow."""
4
+
5
+ import asyncio
6
+ import io
7
+ import os
8
+ import subprocess
9
+ import sys
10
+ import tempfile
11
+ from pathlib import Path
12
+ from typing import Callable, Optional, Union
13
+
14
+ from .environment import in_virtualenv, is_root
15
+ from .util import strip_ansi
16
+
17
+
18
+ def before_run(
19
+ output_path: Optional[Union[str, Path]],
20
+ uploads_root: Optional[Union[str, Path]],
21
+ ) -> str:
22
+ """Actions to perform before running the flow.
23
+
24
+ Parameters
25
+ ----------
26
+ output_path : Optional[Union[str, Path]]
27
+ The output path.
28
+ uploads_root : Optional[Union[str, Path]]
29
+ The runtime uploads root.
30
+
31
+ Returns
32
+ -------
33
+ str
34
+ The file name.
35
+ """
36
+ if not uploads_root:
37
+ uploads_root = Path(tempfile.mkdtemp())
38
+ else:
39
+ uploads_root = Path(uploads_root)
40
+ if not uploads_root.exists():
41
+ uploads_root.mkdir(parents=True)
42
+ output_dir = Path.cwd()
43
+ if output_path and isinstance(output_path, str):
44
+ output_path = Path(output_path)
45
+ if output_path:
46
+ if output_path.is_dir():
47
+ output_dir = output_path
48
+ else:
49
+ output_dir = output_path.parent if output_path else Path.cwd()
50
+ if not output_dir.exists():
51
+ output_dir.mkdir(parents=True, exist_ok=True)
52
+ file_name = Path(output_path).name if output_path else "waldiez_flow.py"
53
+ if file_name.endswith((".json", ".waldiez")):
54
+ file_name = file_name.replace(".json", ".py").replace(".waldiez", ".py")
55
+ if not file_name.endswith(".py"):
56
+ file_name += ".py"
57
+ return file_name
58
+
59
+
60
+ def install_requirements(
61
+ extra_requirements: set[str],
62
+ printer: Callable[..., None] = print,
63
+ ) -> None:
64
+ """Install the requirements.
65
+
66
+ Parameters
67
+ ----------
68
+ extra_requirements : set[str]
69
+ The extra requirements.
70
+ printer : Callable[..., None]
71
+ The printer function to use, defaults to print.
72
+ """
73
+ requirements_string = ", ".join(extra_requirements)
74
+ printer(f"Installing requirements: {requirements_string}")
75
+ pip_install = [sys.executable, "-m", "pip", "install"]
76
+ break_system_packages = ""
77
+ if not in_virtualenv(): # it should
78
+ # if not, let's try to install as user
79
+ # not sure if --break-system-packages is safe,
80
+ # but it might fail if we don't
81
+ break_system_packages = os.environ.get("PIP_BREAK_SYSTEM_PACKAGES", "")
82
+ os.environ["PIP_BREAK_SYSTEM_PACKAGES"] = "1"
83
+ if not is_root():
84
+ pip_install.append("--user")
85
+ pip_install.extend(extra_requirements)
86
+ # pylint: disable=too-many-try-statements
87
+ try:
88
+ with subprocess.Popen(
89
+ pip_install,
90
+ stdout=subprocess.PIPE,
91
+ stderr=subprocess.PIPE,
92
+ ) as proc:
93
+ if proc.stdout:
94
+ for line in io.TextIOWrapper(proc.stdout, encoding="utf-8"):
95
+ printer(strip_ansi(line.strip()))
96
+ if proc.stderr:
97
+ for line in io.TextIOWrapper(proc.stderr, encoding="utf-8"):
98
+ printer(strip_ansi(line.strip()))
99
+ finally:
100
+ if not in_virtualenv():
101
+ # restore the old env var
102
+ if break_system_packages:
103
+ os.environ["PIP_BREAK_SYSTEM_PACKAGES"] = break_system_packages
104
+ else:
105
+ del os.environ["PIP_BREAK_SYSTEM_PACKAGES"]
106
+
107
+
108
+ async def a_install_requirements(
109
+ extra_requirements: set[str],
110
+ printer: Callable[..., None] = print,
111
+ ) -> None:
112
+ """Install the requirements asynchronously.
113
+
114
+ Parameters
115
+ ----------
116
+ extra_requirements : set[str]
117
+ The extra requirements.
118
+ printer : Callable[..., None]
119
+ The printer function to use, defaults to print.
120
+ """
121
+ requirements_string = ", ".join(extra_requirements)
122
+ printer(f"Installing requirements: {requirements_string}")
123
+ pip_install = [sys.executable, "-m", "pip", "install"]
124
+ break_system_packages = ""
125
+ if not in_virtualenv():
126
+ break_system_packages = os.environ.get("PIP_BREAK_SYSTEM_PACKAGES", "")
127
+ os.environ["PIP_BREAK_SYSTEM_PACKAGES"] = "1"
128
+ if not is_root():
129
+ pip_install.extend(["--user"])
130
+ pip_install.extend(extra_requirements)
131
+ # pylint: disable=too-many-try-statements
132
+ try:
133
+ proc = await asyncio.create_subprocess_exec(
134
+ *pip_install,
135
+ stdout=asyncio.subprocess.PIPE,
136
+ stderr=asyncio.subprocess.PIPE,
137
+ )
138
+ if proc.stdout:
139
+ async for line in proc.stdout:
140
+ printer(strip_ansi(line.decode().strip()))
141
+ if proc.stderr:
142
+ async for line in proc.stderr:
143
+ printer(strip_ansi(line.decode().strip()))
144
+ finally:
145
+ if not in_virtualenv():
146
+ if break_system_packages:
147
+ os.environ["PIP_BREAK_SYSTEM_PACKAGES"] = break_system_packages
148
+ else:
149
+ del os.environ["PIP_BREAK_SYSTEM_PACKAGES"]
@@ -0,0 +1,134 @@
1
+ # SPDX-License-Identifier: Apache-2.0.
2
+ # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
+ """Common utilities for the waldiez runner."""
4
+
5
+ import asyncio
6
+ import os
7
+ import re
8
+ import subprocess
9
+ import sys
10
+ from asyncio.subprocess import Process
11
+ from contextlib import asynccontextmanager, contextmanager
12
+ from dataclasses import dataclass
13
+ from pathlib import Path
14
+ from typing import AsyncIterator, Iterator, Union
15
+
16
+
17
+ @dataclass
18
+ class ProcessSetup:
19
+ """Container for subprocess setup data."""
20
+
21
+ temp_dir: Path
22
+ file_path: Path
23
+ old_vars: dict[str, str]
24
+ skip_mmd: bool
25
+
26
+
27
+ @contextmanager
28
+ def chdir(to: Union[str, Path]) -> Iterator[None]:
29
+ """Change the current working directory in a context.
30
+
31
+ Parameters
32
+ ----------
33
+ to : Union[str, Path]
34
+ The directory to change to.
35
+
36
+ Yields
37
+ ------
38
+ Iterator[None]
39
+ The context manager.
40
+ """
41
+ old_cwd = str(os.getcwd())
42
+ os.chdir(to)
43
+ try:
44
+ yield
45
+ finally:
46
+ os.chdir(old_cwd)
47
+
48
+
49
+ @asynccontextmanager
50
+ async def a_chdir(to: Union[str, Path]) -> AsyncIterator[None]:
51
+ """Asynchronously change the current working directory in a context.
52
+
53
+ Parameters
54
+ ----------
55
+ to : Union[str, Path]
56
+ The directory to change to.
57
+
58
+ Yields
59
+ ------
60
+ AsyncIterator[None]
61
+ The async context manager.
62
+ """
63
+ old_cwd = str(os.getcwd())
64
+ os.chdir(to)
65
+ try:
66
+ yield
67
+ finally:
68
+ os.chdir(old_cwd)
69
+
70
+
71
+ def strip_ansi(text: str) -> str:
72
+ """Remove ANSI escape sequences from text.
73
+
74
+ Parameters
75
+ ----------
76
+ text : str
77
+ The text to strip.
78
+
79
+ Returns
80
+ -------
81
+ str
82
+ The text without ANSI escape sequences.
83
+ """
84
+ ansi_pattern = re.compile(r"\x1b\[[0-9;]*m|\x1b\[.*?[@-~]")
85
+ return ansi_pattern.sub("", text)
86
+
87
+
88
+ def create_sync_subprocess(setup: ProcessSetup) -> subprocess.Popen[bytes]:
89
+ """Create a synchronous subprocess.
90
+
91
+ Parameters
92
+ ----------
93
+ setup : ProcessSetup
94
+ The setup data for the subprocess.
95
+
96
+ Returns
97
+ -------
98
+ subprocess.Popen[bytes]
99
+ The created subprocess.
100
+ """
101
+ return subprocess.Popen(
102
+ [sys.executable, "-u", str(setup.file_path)],
103
+ stdout=subprocess.PIPE,
104
+ stderr=subprocess.PIPE,
105
+ stdin=subprocess.PIPE,
106
+ # text=True,
107
+ # bufsize=1, # Line buffered for real-time output
108
+ # universal_newlines=True,
109
+ env={**os.environ},
110
+ )
111
+
112
+
113
+ async def create_async_subprocess(setup: ProcessSetup) -> Process:
114
+ """Create an asynchronous subprocess.
115
+
116
+ Parameters
117
+ ----------
118
+ setup : ProcessSetup
119
+ The setup data for the subprocess.
120
+
121
+ Returns
122
+ -------
123
+ Process
124
+ The created asynchronous subprocess.
125
+ """
126
+ return await asyncio.create_subprocess_exec(
127
+ sys.executable,
128
+ "-u",
129
+ str(setup.file_path),
130
+ # stdout=asyncio.subprocess.PIPE,
131
+ # stderr=asyncio.subprocess.PIPE,
132
+ # stdin=asyncio.subprocess.PIPE,
133
+ env={**os.environ},
134
+ )
waldiez/utils/__init__.py CHANGED
@@ -5,13 +5,11 @@
5
5
  from .cli_extras import add_cli_extras
6
6
  from .conflict_checker import check_conflicts
7
7
  from .flaml_warnings import check_flaml_warnings
8
- from .pysqlite3_checker import check_pysqlite3
9
- from .rdps_checker import check_rpds_py
8
+ from .version import get_waldiez_version
10
9
 
11
10
  __all__ = [
12
11
  "check_conflicts",
13
12
  "check_flaml_warnings",
14
13
  "add_cli_extras",
15
- "check_pysqlite3",
16
- "check_rpds_py",
14
+ "get_waldiez_version",
17
15
  ]
@@ -11,12 +11,14 @@ import typer
11
11
  from typer.models import CommandInfo
12
12
  import subprocess # nosemgrep # nosec
13
13
 
14
- HAVE_JUPYTER = False
14
+ _have_jupyter = False
15
15
 
16
+ # noinspection PyBroadException
17
+ # pylint: disable=broad-exception-caught
16
18
  try:
17
19
  import waldiez_jupyter # type: ignore[unused-ignore, unused-import, import-not-found, import-untyped] # noqa
18
20
 
19
- HAVE_JUPYTER = True
21
+ _have_jupyter = True
20
22
  except BaseException:
21
23
  pass
22
24
 
@@ -34,7 +36,7 @@ def add_jupyter_cli(app: typer.Typer) -> None:
34
36
  typer.Typer
35
37
  The app with the extra command added
36
38
  """
37
- if HAVE_JUPYTER:
39
+ if _have_jupyter:
38
40
  jupyter_app = get_jupyter_app()
39
41
  app.registered_commands.append(
40
42
  CommandInfo(name="lab", callback=jupyter_app)
@@ -10,15 +10,17 @@ from typing import Any, Callable
10
10
  import typer
11
11
  from typer.models import CommandInfo
12
12
 
13
- HAVE_RUNNER = False
13
+ _have_runner = False
14
14
  runner_app: Callable[..., Any] | None = None
15
15
 
16
+ # noinspection PyBroadException
17
+ # pylint: disable=broad-exception-caught
16
18
  try:
17
19
  from waldiez_runner.cli import run # type: ignore[unused-ignore, import-not-found, import-untyped]
18
20
 
19
- runner_app = run
21
+ runner_app = run # pyright: ignore
20
22
 
21
- HAVE_RUNNER = True
23
+ _have_runner = True
22
24
  except BaseException:
23
25
  pass
24
26
 
@@ -31,7 +33,7 @@ def add_runner_cli(app: typer.Typer) -> None:
31
33
  app : typer.Typer
32
34
  The Typer app to add the runner command to.
33
35
  """
34
- if HAVE_RUNNER:
36
+ if _have_runner:
35
37
  app.registered_commands.append(
36
38
  CommandInfo(name="serve", callback=runner_app)
37
39
  )
@@ -10,15 +10,17 @@ from typing import Any, Callable
10
10
  import typer
11
11
  from typer.models import CommandInfo
12
12
 
13
- HAVE_STUDIO = False
13
+ _have_studio = False
14
14
  studio_app: Callable[..., Any] | None = None
15
15
 
16
+ # noinspection PyBroadException
17
+ # pylint: disable=broad-exception-caught
16
18
  try:
17
19
  from waldiez_studio.cli import run # type: ignore[unused-ignore, import-not-found, import-untyped]
18
20
 
19
- studio_app = run
21
+ studio_app = run # pyright: ignore
20
22
 
21
- HAVE_STUDIO = True
23
+ _have_studio = True
22
24
  except BaseException:
23
25
  pass
24
26
 
@@ -31,7 +33,7 @@ def add_studio_cli(app: typer.Typer) -> None:
31
33
  app : typer.Typer
32
34
  The Typer app to add the studio command to.
33
35
  """
34
- if HAVE_STUDIO:
36
+ if _have_studio:
35
37
  app.registered_commands.append(
36
38
  CommandInfo(name="studio", callback=studio_app)
37
39
  )
@@ -1,17 +1,18 @@
1
1
  # SPDX-License-Identifier: Apache-2.0.
2
2
  # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
- # pylint: disable=line-too-long
4
- """Check for conflicts with 'autogen-agentchat' package."""
3
+ # pylint: disable=line-too-long, broad-exception-caught,invalid-name
4
+ # pylint: disable=too-many-try-statements
5
+ """Check for conflicts between Waldiez and other packages."""
5
6
 
6
7
  import sys
7
8
  from importlib.metadata import PackageNotFoundError, version
8
9
 
9
- __WALDIEZ_CHECKED_FOR_CONFLICTS = False
10
+ # Global variable to track if conflicts have been checked
11
+ __waldiez_checked_conflicts = False
10
12
 
11
13
 
12
14
  # fmt: off
13
- def _check_conflicts() -> None: # pragma: no cover
14
- """Check for conflicts with 'autogen-agentchat' package."""
15
+ def _check_autogen_agentchat() -> None: # pragma: no cover
15
16
  try:
16
17
  version("autogen-agentchat")
17
18
  print(
@@ -30,10 +31,15 @@ def _check_conflicts() -> None: # pragma: no cover
30
31
  # fmt: on
31
32
 
32
33
 
34
+ def _check_conflicts() -> None: # pragma: no cover
35
+ """Check for conflicts."""
36
+ _check_autogen_agentchat()
37
+
38
+
33
39
  def check_conflicts() -> None: # pragma: no cover
34
- """Check for conflicts with 'autogen-agentchat' package."""
40
+ """Check for conflicts."""
35
41
  # pylint: disable=global-statement
36
- global __WALDIEZ_CHECKED_FOR_CONFLICTS
37
- if __WALDIEZ_CHECKED_FOR_CONFLICTS is False:
42
+ global __waldiez_checked_conflicts
43
+ if __waldiez_checked_conflicts is False:
44
+ __waldiez_checked_conflicts = True
38
45
  _check_conflicts()
39
- __WALDIEZ_CHECKED_FOR_CONFLICTS = True
@@ -1,17 +1,17 @@
1
1
  # SPDX-License-Identifier: Apache-2.0.
2
2
  # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
- """Try to suppress the annoying flaml.automl not being available warning."""
3
+ """Try to suppress the warning about flaml.automl not being available."""
4
4
 
5
5
  import logging
6
6
 
7
- __WALDIEZ_CHECKED_FLAML_WARNINGS = False
7
+ __waldiez_checked_flaml_warnings = False # pylint: disable=invalid-name
8
8
 
9
9
 
10
10
  def check_flaml_warnings() -> None: # pragma: no cover
11
11
  """Check for flaml warnings once."""
12
12
  # pylint: disable=global-statement
13
- global __WALDIEZ_CHECKED_FLAML_WARNINGS
14
- if __WALDIEZ_CHECKED_FLAML_WARNINGS is False:
13
+ global __waldiez_checked_flaml_warnings
14
+ if __waldiez_checked_flaml_warnings is False:
15
15
  flam_logger = logging.getLogger("flaml")
16
16
  flam_logger.setLevel(logging.ERROR)
17
- __WALDIEZ_CHECKED_FLAML_WARNINGS = True
17
+ __waldiez_checked_flaml_warnings = True