waldiez 0.4.7__py3-none-any.whl → 0.4.8__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 (244) hide show
  1. waldiez/__init__.py +5 -5
  2. waldiez/_version.py +1 -1
  3. waldiez/cli.py +112 -73
  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} +16 -15
  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 +419 -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 +114 -49
  175. waldiez/running/__init__.py +1 -1
  176. waldiez/running/environment.py +49 -68
  177. waldiez/running/gen_seq_diagram.py +16 -14
  178. waldiez/running/running.py +53 -34
  179. waldiez/utils/__init__.py +0 -4
  180. waldiez/utils/cli_extras/jupyter.py +5 -3
  181. waldiez/utils/cli_extras/runner.py +6 -4
  182. waldiez/utils/cli_extras/studio.py +6 -4
  183. waldiez/utils/conflict_checker.py +15 -9
  184. waldiez/utils/flaml_warnings.py +5 -5
  185. {waldiez-0.4.7.dist-info → waldiez-0.4.8.dist-info}/METADATA +235 -91
  186. waldiez-0.4.8.dist-info/RECORD +200 -0
  187. waldiez/exporting/agent/agent_exporter.py +0 -297
  188. waldiez/exporting/agent/utils/__init__.py +0 -23
  189. waldiez/exporting/agent/utils/captain_agent.py +0 -263
  190. waldiez/exporting/agent/utils/code_execution.py +0 -65
  191. waldiez/exporting/agent/utils/group_manager.py +0 -220
  192. waldiez/exporting/agent/utils/rag_user/__init__.py +0 -7
  193. waldiez/exporting/agent/utils/rag_user/rag_user.py +0 -209
  194. waldiez/exporting/agent/utils/reasoning.py +0 -36
  195. waldiez/exporting/agent/utils/swarm_agent.py +0 -469
  196. waldiez/exporting/agent/utils/teachability.py +0 -41
  197. waldiez/exporting/agent/utils/termination_message.py +0 -44
  198. waldiez/exporting/base/__init__.py +0 -25
  199. waldiez/exporting/base/agent_position.py +0 -75
  200. waldiez/exporting/base/base_exporter.py +0 -118
  201. waldiez/exporting/base/export_position.py +0 -48
  202. waldiez/exporting/base/import_position.py +0 -23
  203. waldiez/exporting/base/mixin.py +0 -137
  204. waldiez/exporting/base/utils/__init__.py +0 -18
  205. waldiez/exporting/base/utils/comments.py +0 -96
  206. waldiez/exporting/base/utils/path_check.py +0 -68
  207. waldiez/exporting/base/utils/to_string.py +0 -84
  208. waldiez/exporting/chats/chats_exporter.py +0 -240
  209. waldiez/exporting/chats/utils/swarm.py +0 -210
  210. waldiez/exporting/flow/flow_exporter.py +0 -528
  211. waldiez/exporting/flow/utils/agent_utils.py +0 -204
  212. waldiez/exporting/flow/utils/chat_utils.py +0 -71
  213. waldiez/exporting/flow/utils/def_main.py +0 -77
  214. waldiez/exporting/flow/utils/flow_content.py +0 -202
  215. waldiez/exporting/flow/utils/flow_names.py +0 -116
  216. waldiez/exporting/flow/utils/importing_utils.py +0 -227
  217. waldiez/exporting/models/models_exporter.py +0 -199
  218. waldiez/exporting/models/utils.py +0 -174
  219. waldiez/exporting/skills/__init__.py +0 -9
  220. waldiez/exporting/skills/skills_exporter.py +0 -176
  221. waldiez/exporting/skills/utils.py +0 -369
  222. waldiez/models/agents/agent/teachability.py +0 -70
  223. waldiez/models/agents/rag_user/rag_user.py +0 -60
  224. waldiez/models/agents/swarm_agent/__init__.py +0 -50
  225. waldiez/models/agents/swarm_agent/after_work.py +0 -179
  226. waldiez/models/agents/swarm_agent/on_condition.py +0 -105
  227. waldiez/models/agents/swarm_agent/on_condition_available.py +0 -142
  228. waldiez/models/agents/swarm_agent/on_condition_target.py +0 -40
  229. waldiez/models/agents/swarm_agent/swarm_agent.py +0 -107
  230. waldiez/models/agents/swarm_agent/swarm_agent_data.py +0 -124
  231. waldiez/models/flow/utils.py +0 -232
  232. waldiez/models/skill/__init__.py +0 -16
  233. waldiez/models/skill/extra_requirements.py +0 -36
  234. waldiez/models/skill/skill_data.py +0 -53
  235. waldiez/models/skill/skill_type.py +0 -8
  236. waldiez/utils/pysqlite3_checker.py +0 -308
  237. waldiez/utils/rdps_checker.py +0 -122
  238. waldiez-0.4.7.dist-info/RECORD +0 -149
  239. /waldiez/models/agents/{captain_agent → captain}/__init__.py +0 -0
  240. /waldiez/models/agents/{captain_agent → captain}/captain_agent_lib_entry.py +0 -0
  241. {waldiez-0.4.7.dist-info → waldiez-0.4.8.dist-info}/WHEEL +0 -0
  242. {waldiez-0.4.7.dist-info → waldiez-0.4.8.dist-info}/entry_points.txt +0 -0
  243. {waldiez-0.4.7.dist-info → waldiez-0.4.8.dist-info}/licenses/LICENSE +0 -0
  244. {waldiez-0.4.7.dist-info → waldiez-0.4.8.dist-info}/licenses/NOTICE.md +0 -0
@@ -0,0 +1,233 @@
1
+ # SPDX-License-Identifier: Apache-2.0.
2
+ # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
+ """Models exporter module."""
4
+
5
+ from pathlib import Path
6
+ from typing import Any, Optional, Union
7
+
8
+ from waldiez.models import WaldiezAgent, WaldiezModel
9
+
10
+ from ..core import (
11
+ AgentPosition,
12
+ ContentOrder,
13
+ DefaultSerializer,
14
+ Exporter,
15
+ ExporterContext,
16
+ ExportPosition,
17
+ ModelExtras,
18
+ get_comment,
19
+ )
20
+ from .processor import ModelProcessor
21
+
22
+
23
+ class ModelsExporter(Exporter[ModelExtras]):
24
+ """Mdels exporter with structured extras."""
25
+
26
+ def __init__(
27
+ self,
28
+ flow_name: str,
29
+ agents: list[WaldiezAgent],
30
+ agent_names: dict[str, str],
31
+ models: list[WaldiezModel],
32
+ model_names: dict[str, str],
33
+ for_notebook: bool = False,
34
+ cache_seed: Optional[int] = None,
35
+ output_dir: Optional[Union[str, Path]] = None,
36
+ context: Optional[ExporterContext] = None,
37
+ **kwargs: Any,
38
+ ):
39
+ """Initialize the models exporter."""
40
+ super().__init__(context, **kwargs)
41
+
42
+ self.flow_name = flow_name
43
+ self.agents = agents
44
+ self.agent_names = agent_names
45
+ self.models = models
46
+ self.model_names = model_names
47
+ self.for_notebook = for_notebook
48
+ self.cache_seed = cache_seed
49
+ self.output_dir = Path(output_dir) if output_dir else None
50
+
51
+ # Initialize extras with processed model content
52
+ self._extras = self._create_model_extras()
53
+
54
+ @property
55
+ def extras(self) -> ModelExtras:
56
+ """Get the model extras."""
57
+ return self._extras
58
+
59
+ def _create_model_extras(self) -> ModelExtras:
60
+ """Create and populate model extras."""
61
+ extras = ModelExtras("models")
62
+
63
+ # Process models to generate LLM configs
64
+ model_processor = ModelProcessor(
65
+ flow_name=self.flow_name,
66
+ models=self.models,
67
+ model_names=self.model_names,
68
+ serializer=(
69
+ self.context.serializer
70
+ if self.context.serializer
71
+ else DefaultSerializer()
72
+ ),
73
+ output_dir=self.output_dir,
74
+ )
75
+
76
+ llm_configs_content = model_processor.process()
77
+
78
+ # Set LLM config content
79
+ if llm_configs_content:
80
+ extras.set_llm_config({"content": llm_configs_content})
81
+ self.add_content(
82
+ llm_configs_content,
83
+ ExportPosition.MODELS,
84
+ order=ContentOrder.MAIN_CONTENT,
85
+ )
86
+ # Add environment variables for API keys
87
+ for model in self.models:
88
+ if model.api_key:
89
+ self.add_env_var(
90
+ model.api_key_env_key,
91
+ model.api_key,
92
+ f"API key for {self.model_names[model.id]} model",
93
+ )
94
+
95
+ return extras
96
+
97
+ def generate_main_content(self) -> Optional[str]:
98
+ """Generate the main models content (LLM configs).
99
+
100
+ Returns
101
+ -------
102
+ Optional[str]
103
+ The main content string, or None if no content is available.
104
+ """
105
+ # handled in extras._contribute_specific_content(...)
106
+ # also here for direct access
107
+ if self.extras.has_specific_content():
108
+ return self.extras.get_content()
109
+ return None
110
+
111
+ def _add_additional_content(self) -> None:
112
+ """Add model related additional content."""
113
+ if self.output_dir is not None:
114
+ # Add API key loader script if output directory is set
115
+ api_key_loader_script = self.get_api_key_loader_script()
116
+ self.add_content(
117
+ api_key_loader_script,
118
+ ExportPosition.IMPORTS,
119
+ order=ContentOrder.LATE_CLEANUP,
120
+ skip_strip=True,
121
+ )
122
+ for agent in self.agents:
123
+ llm_config_arg = self.get_agent_llm_config_arg(agent)
124
+ if llm_config_arg:
125
+ # Position as agent argument
126
+ self.add_content(
127
+ llm_config_arg,
128
+ ExportPosition.AGENTS,
129
+ order=ContentOrder.MAIN_CONTENT,
130
+ agent_id=agent.id,
131
+ agent_position=AgentPosition.AS_ARGUMENT,
132
+ )
133
+
134
+ def get_agent_llm_config_arg(self, agent: WaldiezAgent) -> str:
135
+ """Get LLM config argument for agent.
136
+
137
+ Parameters
138
+ ----------
139
+ agent : WaldiezAgent
140
+ The agent for which to get the LLM config argument.
141
+
142
+ Returns
143
+ -------
144
+ str
145
+ The LLM config argument string for the agent,
146
+ or "llm_config=False" if no models are configured.
147
+ """
148
+ if not agent.data.model_ids:
149
+ return " llm_config=False, # pyright: ignore\n"
150
+
151
+ # Get model configs for this agent
152
+ model_configs: list[str] = []
153
+ for model_id in agent.data.model_ids:
154
+ model_name = self.model_names.get(model_id)
155
+ if model_name:
156
+ model_configs.append(f"{model_name}_llm_config")
157
+ tab = " " * 4
158
+ if not model_configs:
159
+ return f"{tab}llm_config=False, # pyright: ignore\n"
160
+
161
+ config_list = f",\n{tab}{tab}{tab}".join(model_configs)
162
+ llm_config = f"""{tab}llm_config=autogen.LLMConfig(
163
+ config_list=[
164
+ {config_list},
165
+ ]"""
166
+ #
167
+ # Add cache seed if provided
168
+ if self.cache_seed is not None:
169
+ llm_config += f",\n{tab}{tab}cache_seed={self.cache_seed},\n"
170
+ else:
171
+ llm_config += f",\n{tab}{tab}cache_seed=None,\n"
172
+ llm_config += f"{tab}),\n"
173
+
174
+ return llm_config
175
+
176
+ def get_api_key_loader_script(self) -> str:
177
+ """Get the api key loader script.
178
+
179
+ Returns
180
+ -------
181
+ str
182
+ The api key loader script.
183
+ """
184
+ comment = get_comment(
185
+ "Load model API keys",
186
+ for_notebook=self.config.for_notebook,
187
+ )
188
+ loader_script = f'''{comment}# NOTE:
189
+ # This section assumes that a file named "{self.flow_name}_api_keys"
190
+ # exists in the same directory as this file.
191
+ # This file contains the API keys for the models used in this flow.
192
+ # It should be .gitignored and not shared publicly.
193
+ # If this file is not present, you can either create it manually
194
+ # or change the way API keys are loaded in the flow.
195
+
196
+
197
+ def load_api_key_module(flow_name: str) -> ModuleType:
198
+ """Load the api key module.
199
+
200
+ Parameters
201
+ ----------
202
+ flow_name : str
203
+ The flow name.
204
+
205
+ Returns
206
+ -------
207
+ ModuleType
208
+ The api keys loading module.
209
+ """
210
+ module_name = f"{{flow_name}}_api_keys"
211
+ if module_name in sys.modules:
212
+ return importlib.reload(sys.modules[module_name])
213
+ return importlib.import_module(module_name)
214
+
215
+ __MODELS_MODULE__ = load_api_key_module("{self.flow_name}")
216
+
217
+
218
+ def get_{self.flow_name}_model_api_key(model_name: str) -> str:
219
+ """Get the model api key.
220
+ Parameters
221
+ ----------
222
+ model_name : str
223
+ The model name.
224
+
225
+ Returns
226
+ -------
227
+ str
228
+ The model api key.
229
+ """
230
+ return __MODELS_MODULE__.get_{self.flow_name}_model_api_key(model_name)
231
+
232
+ '''
233
+ return loader_script
@@ -0,0 +1,66 @@
1
+ # SPDX-License-Identifier: Apache-2.0.
2
+ # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
+ """Factory function for creating a ModelsExporter instance."""
4
+
5
+ from pathlib import Path
6
+ from typing import Optional, Union
7
+
8
+ from waldiez.models import WaldiezAgent, WaldiezModel
9
+
10
+ from ..core import ExporterContext, get_default_exporter_context
11
+ from .exporter import ModelsExporter
12
+
13
+
14
+ def create_models_exporter(
15
+ # Factory function for models exporter creation
16
+ flow_name: str,
17
+ agents: list[WaldiezAgent],
18
+ agent_names: dict[str, str],
19
+ models: list[WaldiezModel],
20
+ model_names: dict[str, str],
21
+ for_notebook: bool = False,
22
+ cache_seed: Optional[int] = None,
23
+ output_dir: Optional[Union[str, Path]] = None,
24
+ context: Optional[ExporterContext] = None,
25
+ ) -> ModelsExporter:
26
+ """Create a models exporter.
27
+
28
+ Parameters
29
+ ----------
30
+ flow_name : str
31
+ The name of the flow.
32
+ agents : list[WaldiezAgent]
33
+ The agents that use models.
34
+ agent_names : dict[str, str]
35
+ Mapping of agent IDs to names.
36
+ models : list[WaldiezModel]
37
+ The models to export.
38
+ model_names : dict[str, str]
39
+ Mapping of model IDs to names.
40
+ for_notebook : bool, optional
41
+ Whether the export is for a notebook, by default False
42
+ cache_seed : Optional[int], optional
43
+ The cache seed if any, by default None
44
+ output_dir : Optional[Union[str, Path]], optional
45
+ Output directory for generated files, by default None
46
+ context : Optional[ExporterContext], optional
47
+ Exporter context with dependencies, by default None
48
+
49
+ Returns
50
+ -------
51
+ ModelsExporter
52
+ The created models exporter.
53
+ """
54
+ if context is None:
55
+ context = get_default_exporter_context()
56
+ return ModelsExporter(
57
+ flow_name=flow_name,
58
+ agents=agents,
59
+ agent_names=agent_names,
60
+ models=models,
61
+ model_names=model_names,
62
+ for_notebook=for_notebook,
63
+ cache_seed=cache_seed,
64
+ output_dir=output_dir,
65
+ context=context,
66
+ )
@@ -0,0 +1,139 @@
1
+ # SPDX-License-Identifier: Apache-2.0.
2
+ # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
+ # pylint: disable=too-few-public-methods,unused-argument
4
+ """Model exporting utilities for Waldiez."""
5
+
6
+ from dataclasses import dataclass
7
+ from pathlib import Path
8
+ from typing import Optional
9
+
10
+ from waldiez.models import WaldiezModel
11
+
12
+ from ..core.constants import FILE_HEADER
13
+ from ..core.errors import ExporterContentError
14
+ from ..core.extras.serializer import DefaultSerializer
15
+ from ..core.protocols import Serializer
16
+
17
+
18
+ @dataclass
19
+ class ModelProcessingResult:
20
+ """Result from processing models."""
21
+
22
+ llm_configs_content: str = ""
23
+ api_keys_file: Optional[Path] = None
24
+ needs_api_key_loader: bool = False
25
+
26
+
27
+ class ModelProcessor:
28
+ """Model processor for generating LLM configs and API key loading."""
29
+
30
+ def __init__(
31
+ self,
32
+ flow_name: str,
33
+ models: list[WaldiezModel],
34
+ model_names: dict[str, str],
35
+ serializer: Optional[Serializer] = None,
36
+ output_dir: Optional[Path] = None,
37
+ ):
38
+ self.flow_name = flow_name
39
+ self.models = models
40
+ self.model_names = model_names
41
+ self.serializer = serializer or DefaultSerializer()
42
+ self.output_dir = output_dir
43
+
44
+ def process(self) -> str:
45
+ """Process the flow models.
46
+
47
+ Returns
48
+ -------
49
+ str
50
+ The string representation of all models' LLM configs.
51
+ """
52
+ content = ""
53
+ for model in self.models:
54
+ model_name = self.model_names[model.id]
55
+ model_config = model.get_llm_config()
56
+
57
+ # Remove api_key if present
58
+ api_key = model_config.pop("api_key", None)
59
+ model_dict_str = self.serializer.serialize(model_config, tabs=0)
60
+ # and use the getter function to get it when needed
61
+ if api_key: # pragma: no branch
62
+ extra_arg = (
63
+ f'get_{self.flow_name}_model_api_key("{model_name}")'
64
+ )
65
+ # remove the \n}, from the end of the dict string
66
+ model_dict_str = model_dict_str.rstrip("\n},")
67
+ model_dict_str += f',\n "api_key": {extra_arg}\n}}'
68
+ content += (
69
+ f"\n{model_name}_llm_config: dict[str, Any] = "
70
+ f"{model_dict_str}\n"
71
+ )
72
+
73
+ # Write API keys file if output directory provided
74
+ if self.output_dir:
75
+ self.output_dir = Path(self.output_dir)
76
+ self.output_dir.mkdir(parents=True, exist_ok=True)
77
+ self._write_api_keys()
78
+
79
+ return content
80
+
81
+ def _write_api_keys(self) -> None:
82
+ """Write API keys file."""
83
+ flow_name_upper = self.flow_name.upper()
84
+ api_keys_content = f'''{FILE_HEADER}
85
+ # flake8: noqa: E501
86
+ # pylint: disable=line-too-long
87
+ """API keys for the {self.flow_name} models."""
88
+
89
+ import os
90
+
91
+ __{flow_name_upper}_MODEL_API_KEYS__ = {{'''
92
+
93
+ for model in self.models:
94
+ model_name = self.model_names[model.id]
95
+ key_env = model.api_key_env_key
96
+ api_keys_content += (
97
+ "\n" + f' "{model_name}": '
98
+ f'{{"key": "{model.api_key}", "env_key": "{key_env}"}},'
99
+ )
100
+
101
+ api_keys_content += "\n}\n"
102
+ api_keys_content += f'''
103
+
104
+ def get_{self.flow_name}_model_api_key(model_name: str) -> str:
105
+ """Get the api key for the model.
106
+
107
+ Parameters
108
+ ----------
109
+ model_name : str
110
+ The name of the model.
111
+
112
+ Returns
113
+ -------
114
+ str
115
+ The api key for the model.
116
+ """
117
+ entry = __{flow_name_upper}_MODEL_API_KEYS__.get(model_name, {{}})
118
+ if not entry:
119
+ return ""
120
+ env_key = entry.get("env_key", "")
121
+ if env_key:
122
+ from_env = os.environ.get(env_key, "")
123
+ if from_env:
124
+ return from_env
125
+ return entry.get("key", "")
126
+ '''
127
+
128
+ # Write the file
129
+ file_name = f"{self.flow_name}_api_keys.py"
130
+ output_path = (
131
+ self.output_dir / file_name if self.output_dir else Path(file_name)
132
+ )
133
+ try:
134
+ with open(output_path, "w", encoding="utf-8", newline="\n") as f:
135
+ f.write(api_keys_content)
136
+ except Exception as e: # pragma: no cover
137
+ raise ExporterContentError(
138
+ f"Failed to write API keys file: {e}"
139
+ ) from e
@@ -0,0 +1,11 @@
1
+ # SPDX-License-Identifier: Apache-2.0.
2
+ # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
+ """Export tool."""
4
+
5
+ from .exporter import ToolsExporter
6
+ from .factory import create_tools_exporter
7
+
8
+ __all__ = [
9
+ "ToolsExporter",
10
+ "create_tools_exporter",
11
+ ]
@@ -0,0 +1,207 @@
1
+ # SPDX-License-Identifier: Apache-2.0.
2
+ # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
+
4
+ """Tools exporter."""
5
+
6
+ from pathlib import Path
7
+ from typing import Any, Optional, Union
8
+
9
+ from waldiez.models import WaldiezAgent, WaldiezTool
10
+
11
+ from ..core import (
12
+ ContentOrder,
13
+ Exporter,
14
+ ExporterContext,
15
+ ExportPosition,
16
+ ImportPosition,
17
+ ImportStatement,
18
+ ToolExtras,
19
+ get_comment,
20
+ )
21
+ from .processor import ToolProcessor
22
+ from .registration import ToolRegistrationProcessor
23
+
24
+
25
+ class ToolsExporter(Exporter[ToolExtras]):
26
+ """Rools exporter with structured extras."""
27
+
28
+ def __init__(
29
+ self,
30
+ flow_name: str,
31
+ agents: list[WaldiezAgent],
32
+ agent_names: dict[str, str],
33
+ tools: list[WaldiezTool],
34
+ tool_names: dict[str, str],
35
+ output_dir: Optional[Union[str, Path]] = None,
36
+ context: Optional[ExporterContext] = None,
37
+ **kwargs: Any,
38
+ ):
39
+ """Initialize the tools exporter.
40
+
41
+ Parameters
42
+ ----------
43
+ flow_name : str
44
+ The name of the flow.
45
+ agents : list[WaldiezAgent]
46
+ The agents that use tools.
47
+ agent_names : dict[str, str]
48
+ Mapping of agent IDs to names.
49
+ tools : list[WaldiezTool]
50
+ The tools to export.
51
+ tool_names : dict[str, str]
52
+ Mapping of tool IDs to names.
53
+ output_dir : Optional[Union[str, Path]], optional
54
+ Output directory for generated files, by default None
55
+ context : Optional[ExporterContext], optional
56
+ Exporter context with dependencies, by default None
57
+ **kwargs
58
+ Additional keyword arguments.
59
+ """
60
+ super().__init__(context, **kwargs)
61
+
62
+ self.flow_name = flow_name
63
+ self.agents = agents
64
+ self.agent_names = agent_names
65
+ self.tools = tools
66
+ self.tool_names = tool_names
67
+ self.output_dir = Path(output_dir) if output_dir else None
68
+
69
+ # Initialize extras with processed tool content
70
+ self._extras = self._create_tool_extras()
71
+
72
+ @property
73
+ def extras(self) -> ToolExtras:
74
+ """Get the tool extras."""
75
+ return self._extras
76
+
77
+ def _create_tool_extras(self) -> ToolExtras:
78
+ """Create and populate tool extras."""
79
+ extras = ToolExtras("tools")
80
+
81
+ # Process tools to generate content
82
+ tool_processor = ToolProcessor(
83
+ flow_name=self.flow_name,
84
+ tools=self.tools,
85
+ tool_names=self.tool_names,
86
+ output_dir=self.output_dir,
87
+ )
88
+
89
+ tool_result = tool_processor.process()
90
+
91
+ # Add tool content
92
+ if tool_result.content:
93
+ extras.add_function_content(tool_result.content)
94
+
95
+ # Add imports
96
+ for import_stmt in tool_result.builtin_imports:
97
+ extras.add_import(
98
+ ImportStatement(import_stmt, ImportPosition.BUILTINS)
99
+ )
100
+ # Override import position for builtins
101
+ self.add_import(import_stmt, ImportPosition.BUILTINS)
102
+
103
+ for import_stmt in tool_result.third_party_imports:
104
+ extras.add_import(
105
+ ImportStatement(import_stmt, ImportPosition.THIRD_PARTY)
106
+ )
107
+
108
+ # Add environment variables
109
+ for environment_variable in tool_result.environment_variables:
110
+ self.add_env_var(
111
+ name=environment_variable.name,
112
+ value=environment_variable.value,
113
+ description=environment_variable.description,
114
+ required=environment_variable.required,
115
+ )
116
+
117
+ # Process tool registrations
118
+
119
+ registration_processor = ToolRegistrationProcessor(
120
+ agents=self.agents,
121
+ agent_names=self.agent_names,
122
+ tools=self.tools,
123
+ tool_names=self.tool_names,
124
+ )
125
+
126
+ registration_content = registration_processor.process()
127
+ if registration_content:
128
+ extras.add_registration_content(registration_content)
129
+
130
+ if self.output_dir is not None:
131
+ # add the tool secrets loader script
132
+ for tool in self.tools:
133
+ if tool.secrets:
134
+ tool_name = self.tool_names.get(tool.id, tool.id)
135
+ tool_secrets_loader_script = (
136
+ self.get_tool_secrets_loader_script(tool_name=tool_name)
137
+ )
138
+ self.add_content(
139
+ tool_secrets_loader_script,
140
+ ExportPosition.TOOLS,
141
+ order=ContentOrder.EARLY_SETUP,
142
+ skip_strip=True,
143
+ )
144
+ return extras
145
+
146
+ def generate_main_content(self) -> Optional[str]:
147
+ """Generate the main tools content."""
148
+ # handled as positioned content
149
+ return None
150
+
151
+ def _add_default_imports(self) -> None:
152
+ """Add default imports for tools."""
153
+ # Tools might need register_function import
154
+ if any(agent.data.tools for agent in self.agents): # pragma: no branch
155
+ self.add_import("from autogen import register_function")
156
+
157
+ # Add interop import if needed
158
+ if any(tool.is_interop for tool in self.tools):
159
+ self.add_import("from autogen.interop import Interoperability")
160
+
161
+ def get_tool_secrets_loader_script(self, tool_name: str) -> str:
162
+ """Get the tool secrets loader script.
163
+
164
+ Parameters
165
+ ----------
166
+ tool_name : str
167
+ The name of the tool for which to generate the loader script.
168
+
169
+ Returns
170
+ -------
171
+ str
172
+ The tool secrets loader script.
173
+ """
174
+ comment = get_comment(
175
+ "Load tool secrets module if needed",
176
+ for_notebook=self.config.for_notebook,
177
+ )
178
+ loader_script = f'''{comment}# NOTE:
179
+ # This section assumes that a file named "{self.flow_name}_{tool_name}_secrets"
180
+ # exists in the same directory as this file.
181
+ # This file contains the secrets for the tool used in this flow.
182
+ # It should be .gitignored and not shared publicly.
183
+ # If this file is not present, you can either create it manually
184
+ # or change the way secrets are loaded in the flow.
185
+
186
+
187
+ def load_tool_secrets_module(flow_name: str, tool_name: str) -> ModuleType:
188
+ """Load the tool secrets module for the given flow name and tool name.
189
+
190
+ Parameters
191
+ ----------
192
+ flow_name : str
193
+ The flow name.
194
+
195
+ Returns
196
+ -------
197
+ ModuleType
198
+ The loaded module.
199
+ """
200
+ module_name = f"{{flow_name}}_{{tool_name}}_secrets"
201
+ if module_name in sys.modules:
202
+ return importlib.reload(sys.modules[module_name])
203
+ return importlib.import_module(module_name)
204
+
205
+ load_tool_secrets_module("{self.flow_name}", "{tool_name}")
206
+ '''
207
+ return loader_script