waldiez 0.6.0__py3-none-any.whl → 0.6.1__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 (188) hide show
  1. waldiez/__init__.py +1 -1
  2. waldiez/_version.py +1 -1
  3. waldiez/cli.py +18 -7
  4. waldiez/cli_extras/jupyter.py +3 -0
  5. waldiez/cli_extras/runner.py +3 -1
  6. waldiez/cli_extras/studio.py +3 -1
  7. waldiez/exporter.py +9 -3
  8. waldiez/exporting/agent/exporter.py +9 -10
  9. waldiez/exporting/agent/extras/captain_agent_extras.py +6 -6
  10. waldiez/exporting/agent/extras/doc_agent_extras.py +6 -6
  11. waldiez/exporting/agent/extras/group_manager_agent_extas.py +34 -23
  12. waldiez/exporting/agent/extras/group_member_extras.py +6 -5
  13. waldiez/exporting/agent/extras/handoffs/after_work.py +1 -1
  14. waldiez/exporting/agent/extras/handoffs/available.py +1 -1
  15. waldiez/exporting/agent/extras/handoffs/condition.py +3 -2
  16. waldiez/exporting/agent/extras/handoffs/handoff.py +1 -1
  17. waldiez/exporting/agent/extras/handoffs/target.py +6 -4
  18. waldiez/exporting/agent/extras/rag/chroma_extras.py +27 -19
  19. waldiez/exporting/agent/extras/rag/mongo_extras.py +8 -8
  20. waldiez/exporting/agent/extras/rag/pgvector_extras.py +5 -5
  21. waldiez/exporting/agent/extras/rag/qdrant_extras.py +5 -4
  22. waldiez/exporting/agent/extras/rag/vector_db_extras.py +1 -1
  23. waldiez/exporting/agent/extras/rag_user_proxy_agent_extras.py +5 -7
  24. waldiez/exporting/agent/extras/reasoning_agent_extras.py +3 -5
  25. waldiez/exporting/chats/exporter.py +4 -4
  26. waldiez/exporting/chats/processor.py +1 -2
  27. waldiez/exporting/chats/utils/common.py +89 -48
  28. waldiez/exporting/chats/utils/group.py +9 -9
  29. waldiez/exporting/chats/utils/nested.py +7 -7
  30. waldiez/exporting/chats/utils/sequential.py +1 -1
  31. waldiez/exporting/chats/utils/single.py +2 -2
  32. waldiez/exporting/core/content.py +7 -7
  33. waldiez/exporting/core/context.py +5 -3
  34. waldiez/exporting/core/exporter.py +5 -3
  35. waldiez/exporting/core/exporters.py +2 -2
  36. waldiez/exporting/core/extras/agent_extras/captain_extras.py +2 -2
  37. waldiez/exporting/core/extras/agent_extras/group_manager_extras.py +2 -2
  38. waldiez/exporting/core/extras/agent_extras/rag_user_extras.py +2 -2
  39. waldiez/exporting/core/extras/agent_extras/standard_extras.py +3 -8
  40. waldiez/exporting/core/extras/base.py +7 -5
  41. waldiez/exporting/core/extras/flow_extras.py +4 -5
  42. waldiez/exporting/core/extras/model_extras.py +2 -2
  43. waldiez/exporting/core/extras/path_resolver.py +1 -2
  44. waldiez/exporting/core/extras/serializer.py +2 -2
  45. waldiez/exporting/core/protocols.py +6 -5
  46. waldiez/exporting/core/result.py +25 -28
  47. waldiez/exporting/core/types.py +10 -10
  48. waldiez/exporting/core/utils/llm_config.py +2 -2
  49. waldiez/exporting/core/validation.py +10 -11
  50. waldiez/exporting/flow/execution_generator.py +98 -10
  51. waldiez/exporting/flow/exporter.py +2 -2
  52. waldiez/exporting/flow/factory.py +2 -2
  53. waldiez/exporting/flow/file_generator.py +4 -2
  54. waldiez/exporting/flow/merger.py +5 -3
  55. waldiez/exporting/flow/orchestrator.py +72 -2
  56. waldiez/exporting/flow/utils/common.py +5 -5
  57. waldiez/exporting/flow/utils/importing.py +6 -7
  58. waldiez/exporting/flow/utils/linting.py +25 -9
  59. waldiez/exporting/flow/utils/logging.py +2 -2
  60. waldiez/exporting/models/exporter.py +8 -8
  61. waldiez/exporting/models/processor.py +5 -5
  62. waldiez/exporting/tools/exporter.py +2 -2
  63. waldiez/exporting/tools/processor.py +7 -4
  64. waldiez/io/__init__.py +8 -4
  65. waldiez/io/_ws.py +10 -6
  66. waldiez/io/models/constants.py +10 -10
  67. waldiez/io/models/content/audio.py +1 -0
  68. waldiez/io/models/content/base.py +20 -18
  69. waldiez/io/models/content/file.py +1 -0
  70. waldiez/io/models/content/image.py +1 -0
  71. waldiez/io/models/content/text.py +1 -0
  72. waldiez/io/models/content/video.py +1 -0
  73. waldiez/io/models/user_input.py +10 -5
  74. waldiez/io/models/user_response.py +17 -16
  75. waldiez/io/mqtt.py +18 -31
  76. waldiez/io/redis.py +18 -22
  77. waldiez/io/structured.py +52 -53
  78. waldiez/io/utils.py +3 -0
  79. waldiez/io/ws.py +5 -1
  80. waldiez/logger.py +16 -3
  81. waldiez/models/agents/__init__.py +3 -0
  82. waldiez/models/agents/agent/agent.py +23 -16
  83. waldiez/models/agents/agent/agent_data.py +25 -22
  84. waldiez/models/agents/agent/code_execution.py +9 -11
  85. waldiez/models/agents/agent/termination_message.py +10 -12
  86. waldiez/models/agents/agent/update_system_message.py +2 -4
  87. waldiez/models/agents/agents.py +8 -8
  88. waldiez/models/agents/assistant/assistant.py +6 -3
  89. waldiez/models/agents/assistant/assistant_data.py +2 -2
  90. waldiez/models/agents/captain/captain_agent.py +7 -4
  91. waldiez/models/agents/captain/captain_agent_data.py +5 -7
  92. waldiez/models/agents/doc_agent/doc_agent.py +7 -4
  93. waldiez/models/agents/doc_agent/doc_agent_data.py +9 -10
  94. waldiez/models/agents/doc_agent/rag_query_engine.py +10 -12
  95. waldiez/models/agents/extra_requirements.py +3 -3
  96. waldiez/models/agents/group_manager/group_manager.py +12 -7
  97. waldiez/models/agents/group_manager/group_manager_data.py +13 -12
  98. waldiez/models/agents/group_manager/speakers.py +17 -19
  99. waldiez/models/agents/rag_user_proxy/rag_user_proxy.py +7 -4
  100. waldiez/models/agents/rag_user_proxy/rag_user_proxy_data.py +4 -1
  101. waldiez/models/agents/rag_user_proxy/retrieve_config.py +69 -63
  102. waldiez/models/agents/rag_user_proxy/vector_db_config.py +19 -19
  103. waldiez/models/agents/reasoning/reasoning_agent.py +7 -4
  104. waldiez/models/agents/reasoning/reasoning_agent_data.py +3 -2
  105. waldiez/models/agents/reasoning/reasoning_agent_reason_config.py +8 -8
  106. waldiez/models/agents/user_proxy/user_proxy.py +6 -3
  107. waldiez/models/agents/user_proxy/user_proxy_data.py +1 -1
  108. waldiez/models/chat/chat.py +27 -20
  109. waldiez/models/chat/chat_data.py +22 -19
  110. waldiez/models/chat/chat_message.py +9 -9
  111. waldiez/models/chat/chat_nested.py +9 -9
  112. waldiez/models/chat/chat_summary.py +6 -6
  113. waldiez/models/common/__init__.py +2 -0
  114. waldiez/models/common/ag2_version.py +2 -0
  115. waldiez/models/common/dict_utils.py +8 -6
  116. waldiez/models/common/handoff.py +18 -17
  117. waldiez/models/common/method_utils.py +7 -7
  118. waldiez/models/common/naming.py +49 -0
  119. waldiez/models/flow/flow.py +11 -6
  120. waldiez/models/flow/flow_data.py +23 -17
  121. waldiez/models/flow/info.py +3 -3
  122. waldiez/models/flow/naming.py +2 -1
  123. waldiez/models/model/_aws.py +11 -13
  124. waldiez/models/model/_llm.py +5 -0
  125. waldiez/models/model/_price.py +2 -4
  126. waldiez/models/model/extra_requirements.py +1 -3
  127. waldiez/models/model/model.py +2 -2
  128. waldiez/models/model/model_data.py +21 -21
  129. waldiez/models/tool/extra_requirements.py +2 -4
  130. waldiez/models/tool/predefined/_duckduckgo.py +1 -0
  131. waldiez/models/tool/predefined/_email.py +1 -0
  132. waldiez/models/tool/predefined/_google.py +1 -0
  133. waldiez/models/tool/predefined/_perplexity.py +1 -0
  134. waldiez/models/tool/predefined/_searxng.py +1 -0
  135. waldiez/models/tool/predefined/_tavily.py +1 -0
  136. waldiez/models/tool/predefined/_wikipedia.py +1 -0
  137. waldiez/models/tool/predefined/_youtube.py +1 -0
  138. waldiez/models/tool/tool.py +8 -5
  139. waldiez/models/tool/tool_data.py +2 -2
  140. waldiez/models/waldiez.py +152 -4
  141. waldiez/runner.py +11 -5
  142. waldiez/running/async_utils.py +192 -0
  143. waldiez/running/base_runner.py +117 -264
  144. waldiez/running/dir_utils.py +52 -0
  145. waldiez/running/environment.py +10 -44
  146. waldiez/running/events_mixin.py +252 -0
  147. waldiez/running/exceptions.py +20 -0
  148. waldiez/running/gen_seq_diagram.py +18 -15
  149. waldiez/running/io_utils.py +216 -0
  150. waldiez/running/protocol.py +11 -5
  151. waldiez/running/requirements_mixin.py +65 -0
  152. waldiez/running/results_mixin.py +926 -0
  153. waldiez/running/standard_runner.py +22 -25
  154. waldiez/running/step_by_step/breakpoints_mixin.py +192 -60
  155. waldiez/running/step_by_step/command_handler.py +3 -0
  156. waldiez/running/step_by_step/events_processor.py +194 -14
  157. waldiez/running/step_by_step/step_by_step_models.py +110 -43
  158. waldiez/running/step_by_step/step_by_step_runner.py +107 -57
  159. waldiez/running/subprocess_runner/__base__.py +9 -1
  160. waldiez/running/subprocess_runner/_async_runner.py +5 -3
  161. waldiez/running/subprocess_runner/_sync_runner.py +6 -2
  162. waldiez/running/subprocess_runner/runner.py +39 -23
  163. waldiez/running/timeline_processor.py +1 -1
  164. waldiez/utils/__init__.py +2 -0
  165. waldiez/utils/conflict_checker.py +4 -4
  166. waldiez/utils/python_manager.py +415 -0
  167. waldiez/ws/_file_handler.py +18 -18
  168. waldiez/ws/_mock.py +2 -1
  169. waldiez/ws/cli.py +36 -12
  170. waldiez/ws/client_manager.py +35 -27
  171. waldiez/ws/errors.py +3 -0
  172. waldiez/ws/models.py +43 -52
  173. waldiez/ws/reloader.py +12 -4
  174. waldiez/ws/server.py +85 -55
  175. waldiez/ws/session_manager.py +8 -9
  176. waldiez/ws/session_stats.py +1 -1
  177. waldiez/ws/utils.py +4 -1
  178. {waldiez-0.6.0.dist-info → waldiez-0.6.1.dist-info}/METADATA +82 -93
  179. waldiez-0.6.1.dist-info/RECORD +254 -0
  180. waldiez/running/post_run.py +0 -186
  181. waldiez/running/pre_run.py +0 -281
  182. waldiez/running/run_results.py +0 -14
  183. waldiez/running/utils.py +0 -625
  184. waldiez-0.6.0.dist-info/RECORD +0 -251
  185. {waldiez-0.6.0.dist-info → waldiez-0.6.1.dist-info}/WHEEL +0 -0
  186. {waldiez-0.6.0.dist-info → waldiez-0.6.1.dist-info}/entry_points.txt +0 -0
  187. {waldiez-0.6.0.dist-info → waldiez-0.6.1.dist-info}/licenses/LICENSE +0 -0
  188. {waldiez-0.6.0.dist-info → waldiez-0.6.1.dist-info}/licenses/NOTICE.md +0 -0
@@ -3,7 +3,6 @@
3
3
  """Validation types and results for Waldiez exporting core."""
4
4
 
5
5
  from dataclasses import dataclass, field
6
- from typing import Optional
7
6
 
8
7
 
9
8
  # Validation Types
@@ -13,8 +12,8 @@ class ValidationError:
13
12
 
14
13
  message: str
15
14
  severity: str = "error" # error, warning, info
16
- location: Optional[str] = None
17
- suggestion: Optional[str] = None
15
+ location: str | None = None
16
+ suggestion: str | None = None
18
17
 
19
18
 
20
19
  @dataclass
@@ -23,17 +22,17 @@ class ValidationResult:
23
22
 
24
23
  is_valid: bool
25
24
  errors: list[ValidationError] = field(
26
- default_factory=list[ValidationError],
25
+ default_factory=list,
27
26
  )
28
27
  warnings: list[ValidationError] = field(
29
- default_factory=list[ValidationError],
28
+ default_factory=list,
30
29
  )
31
30
 
32
31
  def add_error(
33
32
  self,
34
33
  message: str,
35
- location: Optional[str] = None,
36
- suggestion: Optional[str] = None,
34
+ location: str | None = None,
35
+ suggestion: str | None = None,
37
36
  ) -> None:
38
37
  """Add a validation error.
39
38
 
@@ -41,9 +40,9 @@ class ValidationResult:
41
40
  ----------
42
41
  message : str
43
42
  The error message to add.
44
- location : Optional[str], optional
43
+ location : str | None, optional
45
44
  The location in the code where the error occurred, by default None
46
- suggestion : Optional[str], optional
45
+ suggestion : str | None, optional
47
46
  A suggestion for fixing the error, by default None
48
47
  """
49
48
  self.errors.append(
@@ -51,14 +50,14 @@ class ValidationResult:
51
50
  )
52
51
  self.is_valid = False
53
52
 
54
- def add_warning(self, message: str, location: Optional[str] = None) -> None:
53
+ def add_warning(self, message: str, location: str | None = None) -> None:
55
54
  """Add a validation warning.
56
55
 
57
56
  Parameters
58
57
  ----------
59
58
  message : str
60
59
  The warning message to add.
61
- location : Optional[str], optional
60
+ location : str | None, optional
62
61
  The location in the code where the warning occurred, by default None
63
62
  """
64
63
  self.warnings.append(ValidationError(message, "warning", location))
@@ -67,6 +67,79 @@ class ExecutionGenerator:
67
67
  + "\n"
68
68
  )
69
69
 
70
+ @staticmethod
71
+ def generate_store_results(is_async: bool) -> str:
72
+ """Generate the part that writes the results to results.json.
73
+
74
+ Parameters
75
+ ----------
76
+ is_async : bool
77
+ Whether the flow is async or not.
78
+
79
+ Returns
80
+ -------
81
+ str
82
+ The part that generates the code to store the results.
83
+ """
84
+ content: str = "async " if is_async else ""
85
+ tab = " "
86
+ content += (
87
+ "def store_results(result_dicts: list[dict[str, Any]]) -> None:\n"
88
+ )
89
+ content += f'{tab}"""Store the results to results.json.\n'
90
+ content += f"{tab}Parameters\n"
91
+ content += f"{tab}----------\n"
92
+ content += f"{tab}result_dicts : list[dict[str, Any]]\n"
93
+ content += f"{tab}{tab}The list of the results.\n"
94
+ content += f'{tab}"""\n'
95
+ if is_async:
96
+ content += f'{tab}async with aiofiles.open("results.json", "w", encoding="utf-8", newline="\\n") as file:\n'
97
+ content += f"{tab}{tab}await file.write(json.dumps({{'results': result_dicts}}, indent=4, ensure_ascii=False))\n"
98
+ else:
99
+ content += f'{tab}with open("results.json", "w", encoding="utf-8", newline="\\n") as file:\n'
100
+ content += f"{tab}{tab}file.write(json.dumps({{'results': result_dicts}}, indent=4, ensure_ascii=False))\n"
101
+ return content
102
+
103
+ @staticmethod
104
+ def generate_store_error(is_async: bool) -> str:
105
+ """Generate the part that writes an error to error.json.
106
+
107
+ Parameters
108
+ ----------
109
+ is_async : bool
110
+ Whether the flow is async or not.
111
+
112
+ Returns
113
+ -------
114
+ str
115
+ The content for writing the error to file.
116
+ """
117
+ content = "\nasync " if is_async else "\n"
118
+ content += '''def store_error(exc: BaseException | None = None) -> None:
119
+ """Store the error in error.json.
120
+
121
+ Parameters
122
+ ----------
123
+ exc : BaseException | None
124
+ The exception we got if any.
125
+ """
126
+ reason = "Event handler stopped processing" if not exc else traceback.format_exc()
127
+ try:'''
128
+ if is_async:
129
+ content += """
130
+ async with aiofiles.open("error.json", "w", encoding="utf-8", newline="\\n") as file:
131
+ await file.write(json.dumps({"error": reason}))"""
132
+ else:
133
+ content += """
134
+ with open("error.json", "w", encoding="utf-8", newline="\\n") as file:
135
+ file.write(json.dumps({"error": reason}))"""
136
+ content += """
137
+ except BaseException: # pylint: disable=broad-exception-caught
138
+ pass
139
+ """
140
+
141
+ return content
142
+
70
143
  @staticmethod
71
144
  def generate_main_function(
72
145
  content: str,
@@ -108,12 +181,12 @@ class ExecutionGenerator:
108
181
  flow_content += f"{comment}\n"
109
182
  if is_async:
110
183
  flow_content += "async "
111
- on_event_arg = "on_event: Optional[Callable[[BaseEvent], bool]] = None"
184
+ on_event_arg = "on_event: Callable[[BaseEvent, list[ConversableAgent]], bool] | None = None"
112
185
  if is_async:
113
186
  on_event_arg = (
114
- "on_event: Optional["
115
- "Callable[[BaseEvent], Coroutine[None, None, bool]]"
116
- "] = None"
187
+ "on_event: "
188
+ "Callable[[BaseEvent, list[ConversableAgent]], Coroutine[None, None, bool]]"
189
+ " | None = None"
117
190
  )
118
191
  return_type_hint = "list[dict[str, Any]]"
119
192
  flow_content += f"def main({on_event_arg}) -> {return_type_hint}:\n"
@@ -127,24 +200,39 @@ class ExecutionGenerator:
127
200
  if cache_seed is not None:
128
201
  # noinspection SqlDialectInspection
129
202
  flow_content += (
130
- f" with Cache.disk(cache_seed={cache_seed}"
131
- ") as cache: # pyright: ignore\n"
203
+ f" with Cache.disk(cache_seed={cache_seed}) as cache:\n"
132
204
  )
133
205
  space = f"{space} "
134
206
  flow_content += f"{content}" + "\n"
135
207
  if not skip_logging:
136
- if is_async:
137
- flow_content += f"{space}await stop_logging()"
138
- else:
139
- flow_content += f"{space}stop_logging()"
208
+ flow_content += ExecutionGenerator._get_stop_logging_call(
209
+ space, is_async
210
+ )
140
211
  flow_content += "\n"
141
212
  if after_run:
142
213
  flow_content += after_run + "\n"
143
214
  if cache_seed is not None:
144
215
  space = space[4:]
216
+ flow_content += ExecutionGenerator._get_store_results_call(
217
+ space, is_async
218
+ )
145
219
  flow_content += f"{space}return result_dicts\n"
146
220
  return flow_content
147
221
 
222
+ @staticmethod
223
+ def _get_stop_logging_call(space: str, is_async: bool) -> str:
224
+ """Get stop logging call."""
225
+ if is_async:
226
+ return f"{space}await stop_logging()"
227
+ return f"{space}stop_logging()"
228
+
229
+ @staticmethod
230
+ def _get_store_results_call(space: str, is_async: bool) -> str:
231
+ """Get store results call."""
232
+ if is_async:
233
+ return f"{space}await store_results(result_dicts)\n"
234
+ return f"{space}store_results(result_dicts)\n"
235
+
148
236
  @staticmethod
149
237
  def generate_call_main_function(is_async: bool, for_notebook: bool) -> str:
150
238
  """Generate the call_main function for the flow script.
@@ -3,7 +3,7 @@
3
3
  """Flow exporter."""
4
4
 
5
5
  from pathlib import Path
6
- from typing import Any, Optional
6
+ from typing import Any
7
7
 
8
8
  from waldiez.models import Waldiez
9
9
 
@@ -21,7 +21,7 @@ class FlowExporter(Exporter[FlowExtras]):
21
21
  waldiez: Waldiez,
22
22
  output_dir: Path | None,
23
23
  for_notebook: bool,
24
- context: Optional[ExporterContext] = None,
24
+ context: ExporterContext | None = None,
25
25
  **kwargs: Any,
26
26
  ) -> None:
27
27
  """Initialize the chats exporter.
@@ -3,7 +3,7 @@
3
3
  """Factory function for creating a FlowExporter instance."""
4
4
 
5
5
  from pathlib import Path
6
- from typing import Any, Optional
6
+ from typing import Any
7
7
 
8
8
  from waldiez.logger import WaldiezLogger
9
9
  from waldiez.models import Waldiez
@@ -22,7 +22,7 @@ def create_flow_exporter(
22
22
  output_dir: Path | None,
23
23
  uploads_root: Path | None,
24
24
  for_notebook: bool,
25
- context: Optional[ExporterContext] = None,
25
+ context: ExporterContext | None = None,
26
26
  **kwargs: Any,
27
27
  ) -> FlowExporter:
28
28
  """Create a flow exporter.
@@ -43,7 +43,7 @@ class FileGenerator(ContentGenerator):
43
43
  after_run: str,
44
44
  skip_logging: bool,
45
45
  **kwargs: Any,
46
- ) -> str: # pyright: ignore
46
+ ) -> str:
47
47
  """Generate content based on provided parameters.
48
48
 
49
49
  Parameters
@@ -172,7 +172,9 @@ class FileGenerator(ContentGenerator):
172
172
  )
173
173
  execution_gen = ExecutionGenerator()
174
174
  chat_contents = "\n".join(chat.content for chat in chats_content)
175
- main = execution_gen.generate_main_function(
175
+ before_main = execution_gen.generate_store_error(is_async) + "\n\n"
176
+ before_main += execution_gen.generate_store_results(is_async) + "\n\n"
177
+ main = before_main + execution_gen.generate_main_function(
176
178
  content=chat_contents,
177
179
  is_async=is_async,
178
180
  for_notebook=for_notebook,
@@ -27,7 +27,7 @@ class MergeStatistics:
27
27
  total_content_items: int = 0
28
28
  total_env_vars: int = 0
29
29
  deduplicated_env_vars: int = 0
30
- conflicts_found: list[str] = field(default_factory=list[str])
30
+ conflicts_found: list[str] = field(default_factory=list)
31
31
 
32
32
 
33
33
  class ContentMerger:
@@ -156,10 +156,11 @@ class ContentMerger:
156
156
  elif imp.position.value == existing.position.value:
157
157
  # Same position - check for conflicts
158
158
  if imp.metadata != existing.metadata:
159
- conflicts.append(
159
+ conflict = (
160
160
  f"Import '{key}' has conflicting metadata: "
161
161
  f"{existing.metadata} vs {imp.metadata}"
162
162
  )
163
+ conflicts.append(conflict)
163
164
  # Keep existing (first wins for same priority)
164
165
 
165
166
  if conflicts:
@@ -292,10 +293,11 @@ class ContentMerger:
292
293
 
293
294
  # Check for value conflicts
294
295
  if existing.value != env_var.value:
295
- conflicts.append(
296
+ conflict = (
296
297
  f"Environment variable '{key}' has conflicting "
297
298
  f"values: '{existing.value}' vs '{env_var.value}'"
298
299
  )
300
+ conflicts.append(conflict)
299
301
  # Keep first occurrence
300
302
  # (tools/models take precedence over agents)
301
303
 
@@ -4,7 +4,7 @@
4
4
 
5
5
  from typing import Any, Callable
6
6
 
7
- from waldiez.models import Waldiez, WaldiezAgent
7
+ from waldiez.models import Waldiez, WaldiezAgent, WaldiezGroupManager
8
8
 
9
9
  from ..agent import AgentExporter, create_agent_exporter
10
10
  from ..chats import ChatsExporter, create_chats_exporter
@@ -167,7 +167,7 @@ class ExportOrchestrator:
167
167
  tags=self.waldiez.tags,
168
168
  for_notebook=self.config.for_notebook,
169
169
  ),
170
- position=ExportPosition.TOP, # befoe everything
170
+ position=ExportPosition.TOP, # before everything
171
171
  order=ContentOrder.EARLY_SETUP,
172
172
  )
173
173
  merged_result.add_content(
@@ -224,6 +224,12 @@ class ExportOrchestrator:
224
224
  position=ExportPosition.IMPORTS, # imports section
225
225
  order=ContentOrder.EARLY_SETUP, # top position
226
226
  )
227
+ known_agents_string = self._get_the_known_agents_string()
228
+ merged_result.add_content(
229
+ known_agents_string,
230
+ position=ExportPosition.AGENTS,
231
+ order=ContentOrder.POST_CONTENT.value + 100,
232
+ )
227
233
  return merged_result
228
234
 
229
235
  def get_after_run_content(self) -> str:
@@ -240,6 +246,70 @@ class ExportOrchestrator:
240
246
  tabs=1,
241
247
  )
242
248
 
249
+ def _get_the_known_agents_string(self) -> str:
250
+ """Get the _get_known_agents method."""
251
+ content: str = """
252
+
253
+ def _check_for_extra_agents(agent: ConversableAgent) -> list[ConversableAgent]:
254
+ _extra_agents: list[ConversableAgent] = []
255
+ _agent_cls_name = agent.__class__.__name__
256
+ if _agent_cls_name == "CaptainAgent":
257
+ _assistant_agent = getattr(agent, "assistant", None)
258
+ if _assistant_agent and _assistant_agent not in _extra_agents:
259
+ _extra_agents.append(_assistant_agent)
260
+ _executor_agent = getattr(agent, "executor", None)
261
+ if _executor_agent and _executor_agent not in _extra_agents:
262
+ _extra_agents.append(_executor_agent)
263
+ return _extra_agents
264
+
265
+
266
+ def _check_for_group_members(agent: ConversableAgent) -> list[ConversableAgent]:
267
+ _extra_agents: list[ConversableAgent] = []
268
+ _group_chat = getattr(agent, "_groupchat", None)
269
+ if _group_chat:
270
+ _chat_agents = getattr(_group_chat, "agents", [])
271
+ if isinstance(_chat_agents, list):
272
+ for _group_member in _chat_agents:
273
+ if _group_member not in _extra_agents:
274
+ _extra_agents.append(_group_member)
275
+ _manager = getattr(agent, "_group_manager", None)
276
+ if _manager:
277
+ if _manager not in _extra_agents:
278
+ _extra_agents.append(_manager)
279
+ for _group_member in _check_for_group_members(_manager):
280
+ if _group_member not in _extra_agents:
281
+ _extra_agents.append(_group_member)
282
+ return _extra_agents
283
+
284
+
285
+ def _get_known_agents() -> list[ConversableAgent]:
286
+ _known_agents: list[ConversableAgent] = []"""
287
+ # group_manager: WaldiezGroupManager | None = None
288
+ for agent in self.waldiez.agents:
289
+ if (
290
+ isinstance(agent, WaldiezGroupManager)
291
+ and self.waldiez.is_group_pattern_based
292
+ ):
293
+ # not defined in agents
294
+ # we'll get it from "._group_manager"
295
+ # if found in the rest of the agents.
296
+ continue
297
+ agent_name = self.agent_names.get(agent.id)
298
+ if agent_name:
299
+ content += f"""
300
+ if {agent_name} not in _known_agents:
301
+ _known_agents.append({agent_name})
302
+ _known_agents.append({agent_name})
303
+ for _group_member in _check_for_group_members({agent_name}):
304
+ if _group_member not in _known_agents:
305
+ _known_agents.append(_group_member)
306
+ for _extra_agent in _check_for_extra_agents({agent_name}):
307
+ if _extra_agent not in _known_agents:
308
+ _known_agents.append(_extra_agent)
309
+ """
310
+ content += " return _known_agents\n\n"
311
+ return content
312
+
243
313
  def _get_tools_exporter(self) -> ToolsExporter:
244
314
  """Get or create tools exporter."""
245
315
  if self._tools_exporter is None:
@@ -51,7 +51,7 @@ def generate_header(
51
51
  requirements=requirements,
52
52
  tags=tags,
53
53
  )
54
- return _get_ipynb_heeader(
54
+ return _get_ipynb_header(
55
55
  name=name,
56
56
  description=description,
57
57
  requirements=requirements,
@@ -59,7 +59,7 @@ def generate_header(
59
59
  )
60
60
 
61
61
 
62
- def _get_ipynb_heeader(
62
+ def _get_ipynb_header(
63
63
  name: str,
64
64
  description: str,
65
65
  requirements: list[str],
@@ -73,7 +73,7 @@ def _get_ipynb_heeader(
73
73
  content += f"### Tags: {tags_str}\n\n"
74
74
  content += f"####{GENERATED_WITH}\n\n"
75
75
  content += "#### Requirements\n\n# %%\n"
76
- content += "import sys # pyright: ignore\n"
76
+ content += "import sys\n"
77
77
  # fmt: off
78
78
  content += "# # " + f"!{{sys.executable}} -m pip install -q {requirements_str}" + "\n"
79
79
  # fmt: on
@@ -166,8 +166,8 @@ def get_after_run_content(
166
166
  {space}# save the tree to json
167
167
  {space}# pylint: disable=protected-access
168
168
  {space}try:
169
- {space}{tab}data = {agent_name}._root.to_dict() # pyright: ignore
170
- {space}{tab}with open("{agent_name}_reasoning_tree.json", "w", encoding="utf-8") as f:
169
+ {space}{tab}data = {agent_name}._root.to_dict() # pyright: ignore[reportPrivateUsage]
170
+ {space}{tab}with open("{agent_name}_reasoning_tree.json", "w", encoding="utf-8", newline="\\n") as f:
171
171
  {space}{tab}{tab}json.dump(data, f)
172
172
  {space}except BaseException:
173
173
  {space}{tab}pass
@@ -2,8 +2,6 @@
2
2
  # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
3
  """Get the standard imports for the flow exporter."""
4
4
 
5
- from typing import Optional
6
-
7
5
  from waldiez.exporting.core import ImportPosition
8
6
 
9
7
  BUILTIN_IMPORTS = [
@@ -14,6 +12,7 @@ BUILTIN_IMPORTS = [
14
12
  "import os",
15
13
  "import sqlite3",
16
14
  "import sys",
15
+ "import traceback",
17
16
  "from dataclasses import asdict",
18
17
  "from pprint import pprint",
19
18
  "from types import ModuleType",
@@ -239,7 +238,7 @@ def get_the_imports_string(
239
238
  final_string += (
240
239
  "# pylint: disable=broad-exception-caught\n"
241
240
  "try:\n"
242
- " nest_asyncio.apply() # pyright: ignore\n"
241
+ " nest_asyncio.apply()\n"
243
242
  "except BaseException:\n"
244
243
  " pass # maybe on uvloop?\n"
245
244
  )
@@ -269,10 +268,10 @@ def ensure_np_import(third_party_imports: list[str]) -> list[str]:
269
268
 
270
269
 
271
270
  def gather_imports(
272
- model_imports: Optional[list[tuple[str, ImportPosition]]] = None,
273
- tool_imports: Optional[list[tuple[str, ImportPosition]]] = None,
274
- chat_imports: Optional[list[tuple[str, ImportPosition]]] = None,
275
- agent_imports: Optional[list[tuple[str, ImportPosition]]] = None,
271
+ model_imports: list[tuple[str, ImportPosition]] | None = None,
272
+ tool_imports: list[tuple[str, ImportPosition]] | None = None,
273
+ chat_imports: list[tuple[str, ImportPosition]] | None = None,
274
+ agent_imports: list[tuple[str, ImportPosition]] | None = None,
276
275
  ) -> list[tuple[str, ImportPosition]]:
277
276
  """Gather all the imports.
278
277
 
@@ -37,32 +37,48 @@ def split_linter_comment(
37
37
 
38
38
 
39
39
  PYLINT_RULES = [
40
- "line-too-long",
41
- "unknown-option-value",
42
- "unused-argument",
43
- "unused-import",
44
- "unused-variable",
40
+ "broad-exception-caught",
41
+ "f-string-without-interpolation",
45
42
  "invalid-name",
46
43
  "import-error",
47
44
  "import-outside-toplevel",
48
45
  "inconsistent-quotes",
46
+ "line-too-long",
49
47
  "missing-function-docstring",
50
48
  "missing-param-doc",
51
49
  "missing-return-doc",
52
- "ungrouped-imports",
53
- "unnecessary-lambda-assignment",
50
+ "no-member",
51
+ "pointless-string-statement",
52
+ "too-complex",
54
53
  "too-many-arguments",
55
54
  "too-many-locals",
56
55
  "too-many-try-statements",
57
- "broad-exception-caught",
56
+ "ungrouped-imports",
57
+ "unnecessary-lambda-assignment",
58
+ "unknown-option-value",
59
+ "unused-argument",
60
+ "unused-import",
61
+ "unused-variable",
58
62
  ]
63
+ # including basedpyright specific (not pyright only) rules
59
64
  PYRIGHT_RULES = [
60
- "reportUnusedImport",
65
+ "reportArgumentType",
66
+ "reportAttributeAccessIssue",
67
+ "reportCallInDefaultInitializer",
68
+ "reportDeprecated",
69
+ "reportDuplicateImport",
61
70
  "reportMissingTypeStubs",
71
+ "reportOperatorIssue",
72
+ "reportOptionalMemberAccess",
73
+ "reportPossiblyUnboundVariable",
74
+ "reportUnreachable",
75
+ "reportUnusedImport",
62
76
  "reportUnknownArgumentType",
63
77
  "reportUnknownMemberType",
64
78
  "reportUnknownLambdaType",
65
79
  "reportUnnecessaryIsInstance",
80
+ "reportUnusedParameter",
81
+ "reportUnusedVariable",
66
82
  "reportUnknownVariableType",
67
83
  ]
68
84
 
@@ -126,7 +126,7 @@ def get_sync_sqlite_out() -> str:
126
126
  content += " csv_writer.writeheader()\n"
127
127
  content += " csv_writer.writerows(data)\n"
128
128
  content += ' json_file = csv_file.replace(".csv", ".json")\n'
129
- content += ' with open(json_file, "w", encoding="utf-8") as file:\n'
129
+ content += ' with open(json_file, "w", encoding="utf-8", newline="\\n") as file:\n'
130
130
  content += " json.dump(data, file, indent=4, ensure_ascii=False)\n"
131
131
  content += "\n"
132
132
  return content
@@ -172,7 +172,7 @@ def get_async_sqlite_out() -> str:
172
172
  content += " await csv_writer.writeheader()\n"
173
173
  content += " await csv_writer.writerows(data)\n"
174
174
  content += ' json_file = csv_file.replace(".csv", ".json")\n'
175
- content += ' async with aiofiles.open(json_file, "w", encoding="utf-8") as file:\n'
175
+ content += ' async with aiofiles.open(json_file, "w", encoding="utf-8", newline="\\n") as file:\n'
176
176
  content += " await file.write(json.dumps(data, indent=4, ensure_ascii=False))\n"
177
177
  content += "\n"
178
178
  # fmt: on
@@ -3,7 +3,7 @@
3
3
  """Models exporter module."""
4
4
 
5
5
  from pathlib import Path
6
- from typing import Any, Optional, Union
6
+ from typing import Any
7
7
 
8
8
  from waldiez.models import WaldiezAgent, WaldiezModel
9
9
 
@@ -31,9 +31,9 @@ class ModelsExporter(Exporter[ModelExtras]):
31
31
  models: list[WaldiezModel],
32
32
  model_names: dict[str, str],
33
33
  for_notebook: bool = False,
34
- cache_seed: Optional[int] = None,
35
- output_dir: Optional[Union[str, Path]] = None,
36
- context: Optional[ExporterContext] = None,
34
+ cache_seed: int | None = None,
35
+ output_dir: str | Path | None = None,
36
+ context: ExporterContext | None = None,
37
37
  **kwargs: Any,
38
38
  ):
39
39
  """Initialize the models exporter."""
@@ -94,7 +94,7 @@ class ModelsExporter(Exporter[ModelExtras]):
94
94
 
95
95
  return extras
96
96
 
97
- def generate_main_content(self) -> Optional[str]:
97
+ def generate_main_content(self) -> str | None:
98
98
  """Generate the main models content (LLM configs).
99
99
 
100
100
  Returns
@@ -146,7 +146,7 @@ class ModelsExporter(Exporter[ModelExtras]):
146
146
  or "llm_config=False" if no models are configured.
147
147
  """
148
148
  if not agent.data.model_ids:
149
- return " llm_config=False, # pyright: ignore\n"
149
+ return " llm_config=False,\n"
150
150
 
151
151
  # Get model configs for this agent
152
152
  model_configs: list[str] = []
@@ -154,9 +154,9 @@ class ModelsExporter(Exporter[ModelExtras]):
154
154
  model_name = self.model_names.get(model_id)
155
155
  if model_name:
156
156
  model_configs.append(f"{model_name}_llm_config")
157
- tab = " " * 4
157
+ tab: str = " " * 4
158
158
  if not model_configs:
159
- return f"{tab}llm_config=False, # pyright: ignore\n"
159
+ return f"{tab}llm_config=False,\n"
160
160
 
161
161
  config_list = f",\n{tab}{tab}{tab}".join(model_configs)
162
162
  llm_config = f"""{tab}llm_config=autogen.LLMConfig(
@@ -5,7 +5,6 @@
5
5
 
6
6
  from dataclasses import dataclass
7
7
  from pathlib import Path
8
- from typing import Optional
9
8
 
10
9
  from waldiez.models import WaldiezModel
11
10
 
@@ -20,7 +19,7 @@ class ModelProcessingResult:
20
19
  """Result from processing models."""
21
20
 
22
21
  llm_configs_content: str = ""
23
- api_keys_file: Optional[Path] = None
22
+ api_keys_file: Path | None = None
24
23
  needs_api_key_loader: bool = False
25
24
 
26
25
 
@@ -32,8 +31,8 @@ class ModelProcessor:
32
31
  flow_name: str,
33
32
  models: list[WaldiezModel],
34
33
  model_names: dict[str, str],
35
- serializer: Optional[Serializer] = None,
36
- output_dir: Optional[Path] = None,
34
+ serializer: Serializer | None = None,
35
+ output_dir: Path | None = None,
37
36
  ):
38
37
  self.flow_name = flow_name
39
38
  self.models = models
@@ -93,8 +92,9 @@ __{flow_name_upper}_MODEL_API_KEYS__ = {{'''
93
92
  for model in self.models:
94
93
  model_name = self.model_names[model.id]
95
94
  key_env = model.api_key_env_key
95
+ api_keys_content += "\n"
96
96
  api_keys_content += (
97
- "\n" + f' "{model_name}": '
97
+ f' "{model_name}": '
98
98
  f'{{"key": "{model.api_key}", "env_key": "{key_env}"}},'
99
99
  )
100
100
 
@@ -4,7 +4,7 @@
4
4
  """Tools exporter."""
5
5
 
6
6
  from pathlib import Path
7
- from typing import Any, Optional
7
+ from typing import Any
8
8
 
9
9
  from waldiez.models import WaldiezAgent, WaldiezTool
10
10
 
@@ -148,7 +148,7 @@ class ToolsExporter(Exporter[ToolExtras]):
148
148
  )
149
149
  return extras
150
150
 
151
- def generate_main_content(self) -> Optional[str]:
151
+ def generate_main_content(self) -> str | None:
152
152
  """Generate the main tools content."""
153
153
  # handled as positioned content
154
154
  return None