waldiez 0.5.10__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 (192) hide show
  1. waldiez/__init__.py +1 -1
  2. waldiez/_version.py +1 -1
  3. waldiez/cli.py +19 -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 +15 -16
  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 +40 -24
  12. waldiez/exporting/agent/extras/group_member_extras.py +6 -5
  13. waldiez/exporting/agent/extras/handoffs/after_work.py +2 -1
  14. waldiez/exporting/agent/extras/handoffs/available.py +2 -1
  15. waldiez/exporting/agent/extras/handoffs/condition.py +3 -2
  16. waldiez/exporting/agent/extras/handoffs/handoff.py +2 -1
  17. waldiez/exporting/agent/extras/handoffs/target.py +7 -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/agent/termination.py +1 -0
  26. waldiez/exporting/chats/exporter.py +4 -4
  27. waldiez/exporting/chats/processor.py +1 -2
  28. waldiez/exporting/chats/utils/common.py +89 -48
  29. waldiez/exporting/chats/utils/group.py +9 -9
  30. waldiez/exporting/chats/utils/nested.py +7 -7
  31. waldiez/exporting/chats/utils/sequential.py +1 -1
  32. waldiez/exporting/chats/utils/single.py +2 -2
  33. waldiez/exporting/core/constants.py +3 -1
  34. waldiez/exporting/core/content.py +7 -7
  35. waldiez/exporting/core/context.py +5 -3
  36. waldiez/exporting/core/exporter.py +5 -3
  37. waldiez/exporting/core/exporters.py +2 -2
  38. waldiez/exporting/core/extras/agent_extras/captain_extras.py +2 -2
  39. waldiez/exporting/core/extras/agent_extras/group_manager_extras.py +2 -2
  40. waldiez/exporting/core/extras/agent_extras/rag_user_extras.py +2 -2
  41. waldiez/exporting/core/extras/agent_extras/standard_extras.py +3 -8
  42. waldiez/exporting/core/extras/base.py +7 -5
  43. waldiez/exporting/core/extras/flow_extras.py +4 -5
  44. waldiez/exporting/core/extras/model_extras.py +2 -2
  45. waldiez/exporting/core/extras/path_resolver.py +1 -2
  46. waldiez/exporting/core/extras/serializer.py +13 -11
  47. waldiez/exporting/core/protocols.py +6 -5
  48. waldiez/exporting/core/result.py +25 -28
  49. waldiez/exporting/core/types.py +11 -10
  50. waldiez/exporting/core/utils/llm_config.py +4 -4
  51. waldiez/exporting/core/validation.py +10 -11
  52. waldiez/exporting/flow/execution_generator.py +99 -10
  53. waldiez/exporting/flow/exporter.py +2 -2
  54. waldiez/exporting/flow/factory.py +2 -2
  55. waldiez/exporting/flow/file_generator.py +4 -2
  56. waldiez/exporting/flow/merger.py +5 -3
  57. waldiez/exporting/flow/orchestrator.py +72 -2
  58. waldiez/exporting/flow/utils/common.py +6 -6
  59. waldiez/exporting/flow/utils/importing.py +7 -8
  60. waldiez/exporting/flow/utils/linting.py +25 -9
  61. waldiez/exporting/flow/utils/logging.py +5 -77
  62. waldiez/exporting/models/exporter.py +8 -8
  63. waldiez/exporting/models/processor.py +5 -5
  64. waldiez/exporting/tools/exporter.py +2 -2
  65. waldiez/exporting/tools/processor.py +7 -4
  66. waldiez/io/__init__.py +11 -5
  67. waldiez/io/_ws.py +12 -6
  68. waldiez/io/models/constants.py +10 -10
  69. waldiez/io/models/content/audio.py +1 -0
  70. waldiez/io/models/content/base.py +20 -18
  71. waldiez/io/models/content/file.py +1 -0
  72. waldiez/io/models/content/image.py +1 -0
  73. waldiez/io/models/content/text.py +1 -0
  74. waldiez/io/models/content/video.py +1 -0
  75. waldiez/io/models/user_input.py +10 -5
  76. waldiez/io/models/user_response.py +17 -16
  77. waldiez/io/mqtt.py +18 -31
  78. waldiez/io/redis.py +18 -22
  79. waldiez/io/structured.py +122 -70
  80. waldiez/io/utils.py +19 -10
  81. waldiez/io/ws.py +7 -3
  82. waldiez/logger.py +16 -3
  83. waldiez/models/agents/__init__.py +3 -0
  84. waldiez/models/agents/agent/agent.py +25 -17
  85. waldiez/models/agents/agent/agent_data.py +25 -22
  86. waldiez/models/agents/agent/code_execution.py +9 -11
  87. waldiez/models/agents/agent/termination_message.py +10 -12
  88. waldiez/models/agents/agent/update_system_message.py +2 -4
  89. waldiez/models/agents/agents.py +8 -8
  90. waldiez/models/agents/assistant/assistant.py +6 -3
  91. waldiez/models/agents/assistant/assistant_data.py +2 -2
  92. waldiez/models/agents/captain/captain_agent.py +7 -4
  93. waldiez/models/agents/captain/captain_agent_data.py +5 -7
  94. waldiez/models/agents/doc_agent/doc_agent.py +7 -4
  95. waldiez/models/agents/doc_agent/doc_agent_data.py +9 -10
  96. waldiez/models/agents/doc_agent/rag_query_engine.py +10 -12
  97. waldiez/models/agents/extra_requirements.py +3 -3
  98. waldiez/models/agents/group_manager/group_manager.py +12 -7
  99. waldiez/models/agents/group_manager/group_manager_data.py +13 -12
  100. waldiez/models/agents/group_manager/speakers.py +17 -19
  101. waldiez/models/agents/rag_user_proxy/rag_user_proxy.py +7 -4
  102. waldiez/models/agents/rag_user_proxy/rag_user_proxy_data.py +4 -1
  103. waldiez/models/agents/rag_user_proxy/retrieve_config.py +69 -63
  104. waldiez/models/agents/rag_user_proxy/vector_db_config.py +19 -19
  105. waldiez/models/agents/reasoning/reasoning_agent.py +7 -4
  106. waldiez/models/agents/reasoning/reasoning_agent_data.py +3 -2
  107. waldiez/models/agents/reasoning/reasoning_agent_reason_config.py +8 -8
  108. waldiez/models/agents/user_proxy/user_proxy.py +6 -3
  109. waldiez/models/agents/user_proxy/user_proxy_data.py +1 -1
  110. waldiez/models/chat/chat.py +28 -20
  111. waldiez/models/chat/chat_data.py +22 -21
  112. waldiez/models/chat/chat_message.py +9 -9
  113. waldiez/models/chat/chat_nested.py +9 -9
  114. waldiez/models/chat/chat_summary.py +6 -6
  115. waldiez/models/common/__init__.py +2 -0
  116. waldiez/models/common/ag2_version.py +2 -0
  117. waldiez/models/common/base.py +2 -0
  118. waldiez/models/common/dict_utils.py +8 -6
  119. waldiez/models/common/handoff.py +20 -17
  120. waldiez/models/common/method_utils.py +9 -7
  121. waldiez/models/common/naming.py +49 -0
  122. waldiez/models/flow/flow.py +11 -6
  123. waldiez/models/flow/flow_data.py +23 -17
  124. waldiez/models/flow/info.py +3 -3
  125. waldiez/models/flow/naming.py +2 -1
  126. waldiez/models/model/_aws.py +11 -13
  127. waldiez/models/model/_llm.py +8 -0
  128. waldiez/models/model/_price.py +2 -4
  129. waldiez/models/model/extra_requirements.py +1 -3
  130. waldiez/models/model/model.py +2 -2
  131. waldiez/models/model/model_data.py +21 -21
  132. waldiez/models/tool/extra_requirements.py +2 -4
  133. waldiez/models/tool/predefined/_duckduckgo.py +1 -0
  134. waldiez/models/tool/predefined/_email.py +4 -0
  135. waldiez/models/tool/predefined/_google.py +1 -0
  136. waldiez/models/tool/predefined/_perplexity.py +2 -1
  137. waldiez/models/tool/predefined/_searxng.py +2 -1
  138. waldiez/models/tool/predefined/_tavily.py +1 -0
  139. waldiez/models/tool/predefined/_wikipedia.py +2 -1
  140. waldiez/models/tool/predefined/_youtube.py +1 -0
  141. waldiez/models/tool/tool.py +8 -5
  142. waldiez/models/tool/tool_data.py +2 -2
  143. waldiez/models/waldiez.py +152 -4
  144. waldiez/runner.py +11 -5
  145. waldiez/running/async_utils.py +192 -0
  146. waldiez/running/base_runner.py +155 -241
  147. waldiez/running/dir_utils.py +52 -0
  148. waldiez/running/environment.py +10 -44
  149. waldiez/running/events_mixin.py +252 -0
  150. waldiez/running/exceptions.py +20 -0
  151. waldiez/running/gen_seq_diagram.py +18 -15
  152. waldiez/running/io_utils.py +216 -0
  153. waldiez/running/protocol.py +11 -5
  154. waldiez/running/requirements_mixin.py +65 -0
  155. waldiez/running/results_mixin.py +926 -0
  156. waldiez/running/standard_runner.py +24 -27
  157. waldiez/running/step_by_step/breakpoints_mixin.py +503 -47
  158. waldiez/running/step_by_step/command_handler.py +154 -0
  159. waldiez/running/step_by_step/events_processor.py +379 -0
  160. waldiez/running/step_by_step/step_by_step_models.py +425 -41
  161. waldiez/running/step_by_step/step_by_step_runner.py +437 -382
  162. waldiez/running/subprocess_runner/__base__.py +13 -8
  163. waldiez/running/subprocess_runner/_async_runner.py +6 -4
  164. waldiez/running/subprocess_runner/_sync_runner.py +11 -6
  165. waldiez/running/subprocess_runner/runner.py +48 -23
  166. waldiez/running/timeline_processor.py +1 -1
  167. waldiez/utils/__init__.py +2 -0
  168. waldiez/utils/conflict_checker.py +4 -4
  169. waldiez/utils/python_manager.py +415 -0
  170. waldiez/ws/__init__.py +8 -7
  171. waldiez/ws/_file_handler.py +18 -20
  172. waldiez/ws/_mock.py +75 -0
  173. waldiez/ws/cli.py +58 -10
  174. waldiez/ws/client_manager.py +77 -53
  175. waldiez/ws/errors.py +3 -0
  176. waldiez/ws/models.py +61 -53
  177. waldiez/ws/reloader.py +33 -4
  178. waldiez/ws/server.py +121 -52
  179. waldiez/ws/session_manager.py +8 -9
  180. waldiez/ws/session_stats.py +1 -1
  181. waldiez/ws/utils.py +33 -5
  182. {waldiez-0.5.10.dist-info → waldiez-0.6.1.dist-info}/METADATA +107 -109
  183. waldiez-0.6.1.dist-info/RECORD +254 -0
  184. waldiez/running/post_run.py +0 -180
  185. waldiez/running/pre_run.py +0 -159
  186. waldiez/running/run_results.py +0 -14
  187. waldiez/running/utils.py +0 -511
  188. waldiez-0.5.10.dist-info/RECORD +0 -248
  189. {waldiez-0.5.10.dist-info → waldiez-0.6.1.dist-info}/WHEEL +0 -0
  190. {waldiez-0.5.10.dist-info → waldiez-0.6.1.dist-info}/entry_points.txt +0 -0
  191. {waldiez-0.5.10.dist-info → waldiez-0.6.1.dist-info}/licenses/LICENSE +0 -0
  192. {waldiez-0.5.10.dist-info → waldiez-0.6.1.dist-info}/licenses/NOTICE.md +0 -0
@@ -5,7 +5,7 @@
5
5
 
6
6
  from dataclasses import dataclass, field, fields
7
7
  from pathlib import Path
8
- from typing import Any, Optional, TypeVar
8
+ from typing import Any, TypeVar
9
9
 
10
10
  from .enums import (
11
11
  ImportPosition,
@@ -25,7 +25,7 @@ class _NoExtrasType:
25
25
  return "<NoExtras>"
26
26
 
27
27
 
28
- NoExtras = _NoExtrasType()
28
+ NoExtras = _NoExtrasType() # pylint: disable=invalid-name
29
29
 
30
30
 
31
31
  # Core Data Structures
@@ -35,7 +35,7 @@ class ImportStatement:
35
35
 
36
36
  statement: str
37
37
  position: ImportPosition = ImportPosition.THIRD_PARTY
38
- metadata: Optional[dict[str, Any]] = None
38
+ metadata: dict[str, Any] | None = None
39
39
 
40
40
  def __hash__(self) -> int:
41
41
  """Hash based on the import statement.
@@ -89,7 +89,7 @@ class EnvironmentVariable:
89
89
 
90
90
  name: str
91
91
  value: str
92
- description: Optional[str] = None
92
+ description: str | None = None
93
93
  required: bool = True
94
94
 
95
95
  def __post_init__(self) -> None:
@@ -151,7 +151,7 @@ class InstanceArgument:
151
151
  with_new_line_if_empty: bool = False
152
152
  skip_if_empty_string: bool = True
153
153
  skip_trailing_comma: bool = False
154
- comment: Optional[str] = None
154
+ comment: str | None = None
155
155
 
156
156
  def has_content(self) -> bool:
157
157
  """Check if the instance argument has content.
@@ -266,13 +266,13 @@ class ExportConfig:
266
266
  description: str = (
267
267
  "Make AG2 Agents Collaborate: Drag, Drop, and Orchestrate with Waldiez"
268
268
  )
269
- requirements: list[str] = field(default_factory=list[str])
270
- tags: list[str] = field(default_factory=list[str])
269
+ requirements: list[str] = field(default_factory=list)
270
+ tags: list[str] = field(default_factory=list)
271
271
  output_extension: str = "py"
272
272
  is_async: bool = False
273
- output_directory: Optional[str | Path] = None
274
- uploads_root: Optional[Path] = None
275
- cache_seed: Optional[int] = None
273
+ output_directory: str | Path | None = None
274
+ uploads_root: Path | None = None
275
+ cache_seed: int | None = None
276
276
 
277
277
  @property
278
278
  def for_notebook(self) -> bool:
@@ -314,6 +314,7 @@ class ExportConfig:
314
314
  if for_notebook:
315
315
  output_extension = "ipynb"
316
316
  cache_seed = kwargs.pop("cache_seed", None)
317
+ # noinspection PyUnreachableCode
317
318
  if cache_seed is not None and not isinstance(cache_seed, int):
318
319
  cache_seed = None
319
320
  return cls(
@@ -64,7 +64,7 @@ def _get_agent_llm_config_arg_as_arg(
64
64
  tab = " " * tab_leng * tabs if tabs > 0 else ""
65
65
  # tab = " " * tabs if tabs > 0 else ""
66
66
  if not agent.data.model_ids:
67
- return f"{tab}llm_config=False, # pyright: ignore" + "\n"
67
+ return f"{tab}llm_config=False," + "\n"
68
68
  content = f"{tab}llm_config=autogen.LLMConfig(" + "\n"
69
69
  content += f"{tab} config_list=["
70
70
  got_at_least_one_model = False
@@ -77,7 +77,7 @@ def _get_agent_llm_config_arg_as_arg(
77
77
  content += "\n" + f"{tab} {model_name}_llm_config,"
78
78
  got_at_least_one_model = True
79
79
  if not got_at_least_one_model: # pragma: no cover
80
- return f"{tab}llm_config=False, # pyright: ignore" + "\n"
80
+ return f"{tab}llm_config=False," + "\n"
81
81
  content += "\n" + f"{tab} ]," + "\n"
82
82
  content += f"{tab} cache_seed={cache_seed}," + "\n"
83
83
  if temperature is not None:
@@ -97,7 +97,7 @@ def _get_agent_llm_config_arg_as_dict(
97
97
  ) -> str:
98
98
  tab = " " * tab_leng * tabs if tabs > 0 else ""
99
99
  if not agent.data.model_ids:
100
- return f'{tab}"llm_config": False' + "\n"
100
+ return f'{tab}"llm_config": False,' + "\n"
101
101
  content = f'{tab}"llm_config": autogen.LLMConfig(' + "\n"
102
102
  content += f"{tab} config_list=["
103
103
  got_at_least_one_model = False
@@ -110,7 +110,7 @@ def _get_agent_llm_config_arg_as_dict(
110
110
  content += "\n" + f"{tab} {model_name}_llm_config,"
111
111
  got_at_least_one_model = True
112
112
  if not got_at_least_one_model: # pragma: no cover
113
- return f'{tab}"llm_config": False' + "\n"
113
+ return f'{tab}"llm_config": False,' + "\n"
114
114
  content += "\n" + f"{tab} ]," + "\n"
115
115
  content += f"{tab} cache_seed={cache_seed}," + "\n"
116
116
  if temperature is not None:
@@ -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"
@@ -125,25 +198,41 @@ class ExecutionGenerator:
125
198
  flow_content += " result_dicts: list[dict[str, Any]] = []\n"
126
199
  space = " "
127
200
  if cache_seed is not None:
201
+ # noinspection SqlDialectInspection
128
202
  flow_content += (
129
- f" with Cache.disk(cache_seed={cache_seed}"
130
- ") as cache: # pyright: ignore\n"
203
+ f" with Cache.disk(cache_seed={cache_seed}) as cache:\n"
131
204
  )
132
205
  space = f"{space} "
133
206
  flow_content += f"{content}" + "\n"
134
207
  if not skip_logging:
135
- if is_async:
136
- flow_content += f"{space}await stop_logging()"
137
- else:
138
- flow_content += f"{space}stop_logging()"
208
+ flow_content += ExecutionGenerator._get_stop_logging_call(
209
+ space, is_async
210
+ )
139
211
  flow_content += "\n"
140
212
  if after_run:
141
213
  flow_content += after_run + "\n"
142
214
  if cache_seed is not None:
143
215
  space = space[4:]
216
+ flow_content += ExecutionGenerator._get_store_results_call(
217
+ space, is_async
218
+ )
144
219
  flow_content += f"{space}return result_dicts\n"
145
220
  return flow_content
146
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
+
147
236
  @staticmethod
148
237
  def generate_call_main_function(is_async: bool, for_notebook: bool) -> str:
149
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:
@@ -2,7 +2,7 @@
2
2
  # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
3
  # pylint: disable=line-too-long
4
4
  # flake8: noqa: E501
5
- """Common utils for the final generatio."""
5
+ """Common utils for the final flow generation."""
6
6
 
7
7
  from waldiez.models import Waldiez
8
8
 
@@ -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,17 +2,17 @@
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 = [
8
+ "import asyncio",
10
9
  "import csv",
11
10
  "import importlib",
12
11
  "import json",
13
12
  "import os",
14
13
  "import sqlite3",
15
14
  "import sys",
15
+ "import traceback",
16
16
  "from dataclasses import asdict",
17
17
  "from pprint import pprint",
18
18
  "from types import ModuleType",
@@ -208,7 +208,6 @@ def get_the_imports_string(
208
208
  final_string += "\n"
209
209
 
210
210
  if is_async:
211
- builtin_imports.insert(0, "import asyncio")
212
211
  final_string += (
213
212
  "\nimport aiofiles"
214
213
  "\nimport aiosqlite"
@@ -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