waldiez 0.4.6__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.6.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.6.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.6.dist-info → waldiez-0.4.8.dist-info}/WHEEL +0 -0
  242. {waldiez-0.4.6.dist-info → waldiez-0.4.8.dist-info}/entry_points.txt +0 -0
  243. {waldiez-0.4.6.dist-info → waldiez-0.4.8.dist-info}/licenses/LICENSE +0 -0
  244. {waldiez-0.4.6.dist-info → waldiez-0.4.8.dist-info}/licenses/NOTICE.md +0 -0
@@ -0,0 +1,705 @@
1
+ # SPDX-License-Identifier: Apache-2.0.
2
+ # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
+
4
+ """Export result containers and related classes."""
5
+
6
+ from dataclasses import dataclass, field
7
+ from typing import Any, Optional, Union
8
+
9
+ from .constants import (
10
+ DEFAULT_EXPORT_POSITION,
11
+ DEFAULT_IMPORT_POSITION,
12
+ )
13
+ from .content import (
14
+ PositionedContent,
15
+ )
16
+ from .enums import (
17
+ AgentPosition,
18
+ ContentOrder,
19
+ ExportPosition,
20
+ ImportPosition,
21
+ )
22
+ from .types import (
23
+ EnvironmentVariable,
24
+ ImportStatement,
25
+ InstanceArgument,
26
+ )
27
+ from .validation import ValidationResult
28
+
29
+
30
+ @dataclass
31
+ class ExportResult:
32
+ """Complete export result with all components."""
33
+
34
+ main_content: Optional[str] = None
35
+ imports: set[ImportStatement] = field(
36
+ default_factory=set[ImportStatement],
37
+ )
38
+ positioned_content: list[PositionedContent] = field(
39
+ default_factory=list[PositionedContent],
40
+ )
41
+ instance_arguments: list[InstanceArgument] = field(
42
+ default_factory=list[InstanceArgument],
43
+ )
44
+ environment_variables: list[EnvironmentVariable] = field(
45
+ default_factory=list[EnvironmentVariable]
46
+ )
47
+ validation_result: Optional[ValidationResult] = None
48
+ metadata: dict[str, Any] = field(default_factory=dict[str, Any])
49
+
50
+ def add_import(
51
+ self, statement: str, position: ImportPosition = DEFAULT_IMPORT_POSITION
52
+ ) -> None:
53
+ """Add an import statement.
54
+
55
+ Parameters
56
+ ----------
57
+ statement : str
58
+ The import statement to add.
59
+ position : ImportPosition, optional
60
+ The position of the import, by default THIRD_PARTY
61
+ """
62
+ if statement and statement.strip():
63
+ self.imports.add(
64
+ ImportStatement(
65
+ statement=statement.strip(),
66
+ position=position,
67
+ )
68
+ )
69
+
70
+ def add_imports(
71
+ self,
72
+ statements: Union[
73
+ set[str],
74
+ list[str],
75
+ set[ImportStatement],
76
+ list[ImportStatement],
77
+ ],
78
+ position: ImportPosition = DEFAULT_IMPORT_POSITION,
79
+ ) -> None:
80
+ """Add multiple import statements.
81
+
82
+ Parameters
83
+ ----------
84
+ statements : Union[
85
+ set[str],
86
+ list[str]],
87
+ set[ImportStatement],
88
+ list[ImportStatement]
89
+ ]
90
+ The import statements to add.
91
+ position : ImportPosition, optional
92
+ The position of the imports, by default THIRD_PARTY
93
+ """
94
+ for statement in statements:
95
+ if isinstance(statement, ImportStatement):
96
+ # If it's already an ImportStatement, use it directly
97
+ self.add_import(statement.statement, statement.position)
98
+ else:
99
+ # Otherwise, treat it as a string
100
+ self.add_import(statement, position)
101
+ # self.add_import(statement, position)
102
+
103
+ def add_instance_argument(
104
+ self,
105
+ name: str,
106
+ value: Any,
107
+ instance_id: str,
108
+ tabs: int = 0,
109
+ comment: Optional[str] = None,
110
+ ) -> None:
111
+ """Add an instance argument.
112
+
113
+ Parameters
114
+ ----------
115
+ name : str
116
+ The name of the argument.
117
+ value : Any
118
+ The value of the argument.
119
+ instance_id : str
120
+ The ID of the instance this argument belongs to.
121
+ tabs : int, optional
122
+ Number of tabs for indentation, by default 0
123
+ comment : Optional[str], optional
124
+ Optional comment for the argument, by default None
125
+ """
126
+ if name and value is not None:
127
+ arg = InstanceArgument(
128
+ instance_id=instance_id,
129
+ name=name,
130
+ value=value,
131
+ comment=comment,
132
+ tabs=tabs,
133
+ )
134
+ # Avoid duplicates based on name
135
+ for existing in self.instance_arguments:
136
+ if existing.name == name:
137
+ # Update existing
138
+ existing.value = value
139
+ existing.comment = comment
140
+ return
141
+ self.instance_arguments.append(arg)
142
+
143
+ def add_instance_arguments(
144
+ self,
145
+ arguments: Union[list[InstanceArgument], set[InstanceArgument]],
146
+ ) -> None:
147
+ """Add multiple instance arguments.
148
+
149
+ Parameters
150
+ ----------
151
+ arguments : Union[list[InstanceArgument], set[InstanceArgument]]
152
+ The instance arguments to add.
153
+ """
154
+ for arg in arguments:
155
+ self.add_instance_argument(
156
+ instance_id=arg.instance_id,
157
+ name=arg.name,
158
+ value=arg.value,
159
+ comment=arg.comment,
160
+ )
161
+
162
+ def merge(
163
+ self,
164
+ other: "ExportResult",
165
+ position: ExportPosition = DEFAULT_EXPORT_POSITION,
166
+ ) -> None:
167
+ """Merge another ExportResult into this one.
168
+
169
+ Parameters
170
+ ----------
171
+ other : ExportResult
172
+ The other result to merge.
173
+ position : ExportPosition, optional
174
+ The position for the merged content, by default AGENTS
175
+ """
176
+ if other.main_content:
177
+ self.main_content = (
178
+ (self.main_content or "") + "\n" + other.main_content
179
+ ).strip()
180
+
181
+ self.imports.update(other.imports)
182
+
183
+ self.positioned_content.extend(
184
+ PositionedContent(
185
+ content=c.content,
186
+ position=position,
187
+ order=c.order,
188
+ agent_id=c.agent_id,
189
+ agent_position=c.agent_position,
190
+ **c.metadata,
191
+ )
192
+ for c in other.positioned_content
193
+ )
194
+
195
+ self.environment_variables.extend(other.environment_variables)
196
+
197
+ if other.validation_result:
198
+ if self.validation_result:
199
+ self.validation_result.merge(other.validation_result)
200
+ else:
201
+ self.validation_result = other.validation_result
202
+
203
+ # Merge instance arguments
204
+ for arg in other.instance_arguments:
205
+ self.add_instance_argument(
206
+ name=arg.name,
207
+ value=arg.value,
208
+ instance_id=arg.instance_id,
209
+ comment=arg.comment,
210
+ )
211
+
212
+ self.metadata.update(other.metadata)
213
+
214
+ def add_content(
215
+ self,
216
+ content: str,
217
+ position: ExportPosition = DEFAULT_EXPORT_POSITION,
218
+ order: Union[ContentOrder, int] = ContentOrder.MAIN_CONTENT,
219
+ skip_strip: bool = False,
220
+ agent_id: Optional[str] = None,
221
+ agent_position: Optional[AgentPosition] = None,
222
+ metadata: Optional[dict[str, Any]] = None,
223
+ ) -> None:
224
+ """Add positioned content.
225
+
226
+ Parameters
227
+ ----------
228
+ content : str
229
+ The content to add.
230
+ position : ExportPosition, optional
231
+ The position of the content, by default AGENTS
232
+ order : int, optional
233
+ The order within the position, by default 0
234
+ skip_strip : bool, optional
235
+ Whether to skip stripping whitespace from content, by default False
236
+ agent_id : Optional[str], optional
237
+ The agent ID if positioned relative to an agent, by default None
238
+ agent_position : Optional[AgentPosition], optional
239
+ The position relative to the agent, by default None
240
+ metadata : Optional[dict[str, Any]], optional
241
+ Additional metadata for the content, by default None
242
+ """
243
+ order_value = order.value if isinstance(order, ContentOrder) else order
244
+ if content and content.strip():
245
+ positioned = PositionedContent(
246
+ content=content.strip() if not skip_strip else content,
247
+ position=position,
248
+ order=order_value,
249
+ agent_id=agent_id,
250
+ agent_position=agent_position,
251
+ **(metadata or {}),
252
+ )
253
+ if positioned not in self.positioned_content:
254
+ self.positioned_content.append(positioned)
255
+
256
+ def add_env_var(
257
+ self,
258
+ name: str,
259
+ value: str,
260
+ description: Optional[str] = None,
261
+ required: bool = True,
262
+ ) -> None:
263
+ """Add environment variable.
264
+
265
+ Parameters
266
+ ----------
267
+ name : str
268
+ The name of the environment variable.
269
+ value : str
270
+ The value of the environment variable.
271
+ description : Optional[str], optional
272
+ Description of the variable, by default None
273
+ required : bool, optional
274
+ Whether the variable is required, by default True
275
+ """
276
+ if name and value:
277
+ env_var = EnvironmentVariable(
278
+ name=name,
279
+ value=value,
280
+ description=description,
281
+ required=required,
282
+ )
283
+ # Avoid duplicates based on name
284
+ for existing in self.environment_variables:
285
+ if existing.name == name:
286
+ # Update existing
287
+ existing.value = value
288
+ existing.description = description
289
+ existing.required = required
290
+ return
291
+ self.environment_variables.append(env_var)
292
+
293
+ def get_sorted_imports(self) -> list[ImportStatement]:
294
+ """Get imports sorted by position and statement.
295
+
296
+ Returns
297
+ -------
298
+ list[ImportStatement]
299
+ Sorted list of import statements.
300
+ """
301
+ return sorted(self.imports)
302
+
303
+ def get_imports_by_position(
304
+ self, position: ImportPosition
305
+ ) -> list[ImportStatement]:
306
+ """Get imports filtered by position.
307
+
308
+ Parameters
309
+ ----------
310
+ position : ImportPosition
311
+ The position to filter by.
312
+
313
+ Returns
314
+ -------
315
+ list[ImportStatement]
316
+ list of imports for the specified position.
317
+ """
318
+ return [
319
+ imp for imp in self.get_sorted_imports() if imp.position == position
320
+ ]
321
+
322
+ def get_content_by_position(
323
+ self,
324
+ position: ExportPosition,
325
+ skip_agent_arguments: bool = True,
326
+ ) -> list[PositionedContent]:
327
+ """Get all content for a specific position.
328
+
329
+ Parameters
330
+ ----------
331
+ position : ExportPosition
332
+ The position to filter by.
333
+ skip_agent_arguments : bool, optional
334
+ Whether to skip content positioned as agent arguments,
335
+ by default True
336
+
337
+ Returns
338
+ -------
339
+ list[PositionedContent]
340
+ Sorted list of content for the specified position.
341
+ """
342
+ if not skip_agent_arguments:
343
+ content = [
344
+ c for c in self.positioned_content if c.position == position
345
+ ]
346
+ else:
347
+ content = [
348
+ c
349
+ for c in self.positioned_content
350
+ if c.position == position
351
+ and (c.agent_position != AgentPosition.AS_ARGUMENT)
352
+ ]
353
+ return sorted(content)
354
+
355
+ def get_agent_content(
356
+ self, agent_id: str, agent_position: Optional[AgentPosition] = None
357
+ ) -> list[PositionedContent]:
358
+ """Get content positioned relative to a specific agent.
359
+
360
+ Parameters
361
+ ----------
362
+ agent_id : str
363
+ The ID of the agent.
364
+ agent_position : Optional[AgentPosition], optional
365
+ Filter by specific agent position, by default None (all positions)
366
+
367
+ Returns
368
+ -------
369
+ list[PositionedContent]
370
+ Sorted list of content for the specified agent.
371
+ """
372
+ content = [
373
+ c
374
+ for c in self.positioned_content
375
+ if c.agent_id == agent_id
376
+ and (agent_position is None or c.agent_position == agent_position)
377
+ ]
378
+ return sorted(content)
379
+
380
+ def get_all_content_sorted(self) -> list[PositionedContent]:
381
+ """Get all positioned content sorted by position and order.
382
+
383
+ Returns
384
+ -------
385
+ list[PositionedContent]
386
+ All positioned content sorted.
387
+ """
388
+ return sorted(self.positioned_content)
389
+
390
+ def merge_with(self, other: "ExportResult") -> None:
391
+ """Merge another ExportResult into this one.
392
+
393
+ Parameters
394
+ ----------
395
+ other : ExportResult
396
+ The other result to merge.
397
+ """
398
+ # Merge imports (set automatically handles duplicates)
399
+ self.imports.update(other.imports)
400
+
401
+ # Merge positioned content
402
+ self.positioned_content.extend(other.positioned_content)
403
+
404
+ # Merge environment variables (avoid duplicates by name)
405
+ for env_var in other.environment_variables:
406
+ self.add_env_var(
407
+ env_var.name,
408
+ env_var.value,
409
+ env_var.description,
410
+ env_var.required,
411
+ )
412
+
413
+ # Merge metadata
414
+ self.metadata.update(other.metadata)
415
+
416
+ # Handle validation results
417
+ if other.validation_result:
418
+ if self.validation_result:
419
+ # Merge validation results
420
+ self.validation_result.errors.extend(
421
+ other.validation_result.errors
422
+ )
423
+ self.validation_result.warnings.extend(
424
+ other.validation_result.warnings
425
+ )
426
+ self.validation_result.is_valid = (
427
+ self.validation_result.is_valid
428
+ and other.validation_result.is_valid
429
+ )
430
+ else:
431
+ self.validation_result = other.validation_result
432
+
433
+ def has_content(self) -> bool:
434
+ """Check if there's any meaningful content.
435
+
436
+ Returns
437
+ -------
438
+ bool
439
+ True if there's any content, imports, or environment variables.
440
+ """
441
+ return bool(
442
+ self.main_content
443
+ or self.imports
444
+ or self.positioned_content
445
+ or self.environment_variables
446
+ )
447
+
448
+ def has_errors(self) -> bool:
449
+ """Check if there are validation errors.
450
+
451
+ Returns
452
+ -------
453
+ bool
454
+ True if there are validation errors.
455
+ """
456
+ return (
457
+ self.validation_result is not None
458
+ and self.validation_result.has_errors()
459
+ )
460
+
461
+ def has_warnings(self) -> bool:
462
+ """Check if there are validation warnings.
463
+
464
+ Returns
465
+ -------
466
+ bool
467
+ True if there are validation warnings.
468
+ """
469
+ return (
470
+ self.validation_result is not None
471
+ and self.validation_result.has_warnings()
472
+ )
473
+
474
+ def get_statistics(self) -> dict[str, int]:
475
+ """Get statistics about the export result.
476
+
477
+ Returns
478
+ -------
479
+ dict[str, int]
480
+ dictionary with statistics about the export.
481
+ """
482
+ return {
483
+ "total_imports": len(self.imports),
484
+ "builtin_imports": len(
485
+ self.get_imports_by_position(ImportPosition.BUILTINS)
486
+ ),
487
+ "third_party_imports": len(
488
+ self.get_imports_by_position(ImportPosition.THIRD_PARTY)
489
+ ),
490
+ "local_imports": len(
491
+ self.get_imports_by_position(ImportPosition.LOCAL)
492
+ ),
493
+ "positioned_content_items": len(self.positioned_content),
494
+ "environment_variables": len(self.environment_variables),
495
+ "validation_errors": (
496
+ len(self.validation_result.errors)
497
+ if self.validation_result
498
+ else 0
499
+ ),
500
+ "validation_warnings": (
501
+ len(self.validation_result.warnings)
502
+ if self.validation_result
503
+ else 0
504
+ ),
505
+ }
506
+
507
+ def clear(self) -> None:
508
+ """Clear all content from the result."""
509
+ self.main_content = None
510
+ self.imports.clear()
511
+ self.positioned_content.clear()
512
+ self.environment_variables.clear()
513
+ self.validation_result = None
514
+ self.metadata.clear()
515
+
516
+
517
+ @dataclass
518
+ class ExportResultBuilder:
519
+ """Builder pattern for constructing ExportResult objects."""
520
+
521
+ _result: ExportResult = field(default_factory=ExportResult)
522
+
523
+ def with_main_content(self, content: str) -> "ExportResultBuilder":
524
+ """Set the main content.
525
+
526
+ Parameters
527
+ ----------
528
+ content : str
529
+ The main content to set.
530
+
531
+ Returns
532
+ -------
533
+ ExportResultBuilder
534
+ Self for method chaining.
535
+ """
536
+ self._result.main_content = content
537
+ return self
538
+
539
+ def with_import(
540
+ self, statement: str, position: ImportPosition = DEFAULT_IMPORT_POSITION
541
+ ) -> "ExportResultBuilder":
542
+ """Add an import statement.
543
+
544
+ Parameters
545
+ ----------
546
+ statement : str
547
+ The import statement.
548
+ position : ImportPosition, optional
549
+ The import position, by default THIRD_PARTY
550
+
551
+ Returns
552
+ -------
553
+ ExportResultBuilder
554
+ Self for method chaining.
555
+ """
556
+ self._result.add_import(statement, position)
557
+ return self
558
+
559
+ def with_content(
560
+ self,
561
+ content: str,
562
+ position: ExportPosition = DEFAULT_EXPORT_POSITION,
563
+ order: ContentOrder = ContentOrder.MAIN_CONTENT,
564
+ agent_id: Optional[str] = None,
565
+ agent_position: Optional[AgentPosition] = None,
566
+ ) -> "ExportResultBuilder":
567
+ """Add positioned content.
568
+
569
+ Parameters
570
+ ----------
571
+ content : str
572
+ The content to add.
573
+ position : ExportPosition, optional
574
+ The content position, by default AGENTS
575
+ order : int, optional
576
+ The order within position, by default 0
577
+ agent_id : Optional[str], optional
578
+ Agent ID for agent-relative positioning, by default None
579
+ agent_position : Optional[AgentPosition], optional
580
+ Position relative to agent, by default None
581
+
582
+ Returns
583
+ -------
584
+ ExportResultBuilder
585
+ Self for method chaining.
586
+ """
587
+ self._result.add_content(
588
+ content=content,
589
+ position=position,
590
+ order=order,
591
+ agent_id=agent_id,
592
+ agent_position=agent_position,
593
+ )
594
+ return self
595
+
596
+ def with_env_var(
597
+ self, name: str, value: str, description: Optional[str] = None
598
+ ) -> "ExportResultBuilder":
599
+ """Add environment variable.
600
+
601
+ Parameters
602
+ ----------
603
+ name : str
604
+ Variable name.
605
+ value : str
606
+ Variable value.
607
+ description : Optional[str], optional
608
+ Variable description, by default None
609
+
610
+ Returns
611
+ -------
612
+ ExportResultBuilder
613
+ Self for method chaining.
614
+ """
615
+ self._result.add_env_var(name, value, description)
616
+ return self
617
+
618
+ def with_metadata(self, key: str, value: Any) -> "ExportResultBuilder":
619
+ """Add metadata.
620
+
621
+ Parameters
622
+ ----------
623
+ key : str
624
+ Metadata key.
625
+ value : Any
626
+ Metadata value.
627
+
628
+ Returns
629
+ -------
630
+ ExportResultBuilder
631
+ Self for method chaining.
632
+ """
633
+ self._result.metadata[key] = value
634
+ return self
635
+
636
+ def build(self) -> ExportResult:
637
+ """Build the final ExportResult.
638
+
639
+ Returns
640
+ -------
641
+ ExportResult
642
+ The constructed ExportResult.
643
+ """
644
+ return self._result
645
+
646
+
647
+ # Utility functions for common operations
648
+ def merge_export_results(*results: ExportResult) -> ExportResult:
649
+ """Merge multiple ExportResult objects into one.
650
+
651
+ Parameters
652
+ ----------
653
+ *results : ExportResult
654
+ Variable number of ExportResult objects to merge.
655
+
656
+ Returns
657
+ -------
658
+ ExportResult
659
+ A new ExportResult containing all merged content.
660
+ """
661
+ if not results:
662
+ return ExportResult()
663
+
664
+ merged = ExportResult()
665
+ for result in results:
666
+ merged.merge_with(result)
667
+
668
+ return merged
669
+
670
+
671
+ def create_empty_result() -> ExportResult:
672
+ """Create an empty ExportResult.
673
+
674
+ Returns
675
+ -------
676
+ ExportResult
677
+ An empty ExportResult instance.
678
+ """
679
+ return ExportResult()
680
+
681
+
682
+ def create_result_with_content(
683
+ main_content: str,
684
+ imports: Optional[list[str]] = None,
685
+ ) -> ExportResult:
686
+ """Create an ExportResult with basic content.
687
+
688
+ Parameters
689
+ ----------
690
+ main_content : str
691
+ The main content.
692
+ imports : Optional[list[str]], optional
693
+ list of import statements, by default None
694
+
695
+ Returns
696
+ -------
697
+ ExportResult
698
+ The created ExportResult.
699
+ """
700
+ result = ExportResult(main_content=main_content)
701
+
702
+ if imports:
703
+ result.add_imports(imports)
704
+
705
+ return result