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
@@ -3,10 +3,10 @@
3
3
  # flake8: noqa: E501
4
4
  """Waldiez Model Data."""
5
5
 
6
- from typing import Any, Optional
6
+ from typing import Annotated, Any, Literal
7
7
 
8
8
  from pydantic import Field, model_validator
9
- from typing_extensions import Annotated, Literal, Self
9
+ from typing_extensions import Self
10
10
 
11
11
  from ..common import WaldiezBase, update_dict
12
12
  from ._aws import WaldiezModelAWS
@@ -59,23 +59,23 @@ class WaldiezModelData(WaldiezBase):
59
59
  """
60
60
 
61
61
  base_url: Annotated[
62
- Optional[str],
62
+ str | None,
63
63
  Field(
64
64
  default=None,
65
65
  title="Base URL",
66
66
  description="The base url of the model",
67
67
  alias="baseUrl",
68
68
  ),
69
- ] = None
69
+ ]
70
70
  api_key: Annotated[
71
- Optional[str],
71
+ str | None,
72
72
  Field(
73
73
  default=None,
74
74
  alias="apiKey",
75
75
  title="API Key",
76
76
  description="The api key to use with the model",
77
77
  ),
78
- ] = None
78
+ ]
79
79
  api_type: Annotated[
80
80
  WaldiezModelAPIType,
81
81
  Field(
@@ -84,52 +84,52 @@ class WaldiezModelData(WaldiezBase):
84
84
  title="API Type",
85
85
  description="The api type of the model",
86
86
  ),
87
- ] = "other"
87
+ ]
88
88
  api_version: Annotated[
89
- Optional[str],
89
+ str | None,
90
90
  Field(
91
91
  default=None,
92
92
  alias="apiVersion",
93
93
  title="API Version",
94
94
  description="The api version of the model",
95
95
  ),
96
- ] = None
96
+ ]
97
97
  temperature: Annotated[
98
- Optional[float],
98
+ float | None,
99
99
  Field(
100
100
  default=None,
101
101
  alias="temperature",
102
102
  title="Temperature",
103
103
  description="The temperature of the model",
104
104
  ),
105
- ] = None
105
+ ]
106
106
  top_p: Annotated[
107
- Optional[float],
107
+ float | None,
108
108
  Field(
109
109
  default=None,
110
110
  alias="topP",
111
111
  title="Top P",
112
112
  description="The top p of the model",
113
113
  ),
114
- ] = None
114
+ ]
115
115
  max_tokens: Annotated[
116
- Optional[int],
116
+ int | None,
117
117
  Field(
118
118
  default=None,
119
119
  alias="maxTokens",
120
120
  title="Max Tokens",
121
121
  description="The max tokens of the model",
122
122
  ),
123
- ] = None
123
+ ]
124
124
  aws: Annotated[
125
- Optional[WaldiezModelAWS],
125
+ WaldiezModelAWS | None,
126
126
  Field(
127
127
  default=None,
128
128
  alias="aws",
129
129
  title="AWS",
130
130
  description="The AWS related parameters",
131
131
  ),
132
- ] = None
132
+ ]
133
133
  extras: Annotated[
134
134
  dict[str, Any],
135
135
  Field(
@@ -138,7 +138,7 @@ class WaldiezModelData(WaldiezBase):
138
138
  title="Extras",
139
139
  description="Any extra attributes to include in the LLM Config",
140
140
  ),
141
- ] = {}
141
+ ]
142
142
  default_headers: Annotated[
143
143
  dict[str, str],
144
144
  Field(
@@ -147,13 +147,13 @@ class WaldiezModelData(WaldiezBase):
147
147
  title="Default Headers",
148
148
  description="The default headers of the model",
149
149
  ),
150
- ] = {}
150
+ ]
151
151
  price: Annotated[
152
- Optional[WaldiezModelPrice],
152
+ WaldiezModelPrice | None,
153
153
  Field(
154
154
  default=None, title="Price", description="The price of the model"
155
155
  ),
156
- ] = None
156
+ ]
157
157
 
158
158
  @model_validator(mode="after")
159
159
  def validate_model_data(self) -> Self:
@@ -2,8 +2,6 @@
2
2
  # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
3
  """Waldiez tool extra requirements."""
4
4
 
5
- from typing import Set
6
-
7
5
  from .predefined import get_predefined_tool_requirements
8
6
  from .tool import WaldiezTool
9
7
 
@@ -11,7 +9,7 @@ from .tool import WaldiezTool
11
9
  def get_tools_extra_requirements(
12
10
  tools: list[WaldiezTool],
13
11
  autogen_version: str,
14
- ) -> Set[str]:
12
+ ) -> set[str]:
15
13
  """Get the tools extra requirements.
16
14
 
17
15
  Parameters
@@ -26,7 +24,7 @@ def get_tools_extra_requirements(
26
24
  list[str]
27
25
  The tools extra requirements.
28
26
  """
29
- tool_requirements: Set[str] = set()
27
+ tool_requirements: set[str] = set()
30
28
  for tool in tools:
31
29
  if tool.tool_type == "langchain":
32
30
  tool_requirements.add(f"ag2[interop-langchain]=={autogen_version}")
@@ -115,6 +115,7 @@ def {self.name}(query: str, num_results: int = 5) -> list[dict[str, Any]]:
115
115
  return content
116
116
 
117
117
 
118
+ # pylint: disable=invalid-name
118
119
  DuckDuckGoSearchTool = DuckDuckGoSearchToolImpl()
119
120
  DuckDuckGoSearchConfig = PredefinedToolConfig(
120
121
  name=DuckDuckGoSearchTool.name,
@@ -11,6 +11,7 @@ from ._config import PredefinedToolConfig
11
11
  from .protocol import PredefinedTool
12
12
 
13
13
 
14
+ # noinspection PyBroadException,TryExceptPass
14
15
  class SendEmailToolImpl(PredefinedTool):
15
16
  """Predefined tool for sending emails."""
16
17
 
@@ -139,6 +140,7 @@ class SendEmailToolImpl(PredefinedTool):
139
140
  A list of validation error messages, if any.
140
141
  """
141
142
  updated = dict(self._kwargs)
143
+ # noinspection DuplicatedCode
142
144
  for key, value in kwargs.items():
143
145
  if key in self.kwarg_types:
144
146
  typ = self.kwarg_types[key]
@@ -161,6 +163,7 @@ class SendEmailToolImpl(PredefinedTool):
161
163
  ) -> dict[str, Any]:
162
164
  """Get effective keyword arguments."""
163
165
  effective = dict(self._kwargs)
166
+ # noinspection DuplicatedCode
164
167
  if runtime_kwargs:
165
168
  # cast only known keys, same rules as validate_kwargs
166
169
  for k, v in runtime_kwargs.items():
@@ -462,6 +465,7 @@ def {self.name}(
462
465
  return base + exported
463
466
 
464
467
 
468
+ # pylint: disable=invalid-name
465
469
  SendEmailTool = SendEmailToolImpl()
466
470
  SendEmailConfig = PredefinedToolConfig(
467
471
  name=SendEmailTool.name,
@@ -173,6 +173,7 @@ def {self.name}(
173
173
  return content
174
174
 
175
175
 
176
+ # pylint: disable=invalid-name
176
177
  GoogleSearchTool = GoogleSearchToolImpl()
177
178
  GoogleSearchConfig = PredefinedToolConfig(
178
179
  name=GoogleSearchTool.name,
@@ -94,7 +94,7 @@ class PerplexitySearchToolImpl(PredefinedTool):
94
94
  if key in kwargs: # pragma: no branch
95
95
  type_of = self.kwarg_types.get(key, str)
96
96
  # pylint: disable=broad-exception-caught
97
- # noinspection PyBroadException
97
+ # noinspection PyBroadException,TryExceptPass
98
98
  try:
99
99
  casted = type_of(value)
100
100
  if key in self.kwargs: # pragma: no branch
@@ -156,6 +156,7 @@ def {self.name}(
156
156
  return content
157
157
 
158
158
 
159
+ # pylint: disable=invalid-name
159
160
  PerplexitySearchTool = PerplexitySearchToolImpl()
160
161
  PerplexitySearchConfig = PredefinedToolConfig(
161
162
  name=PerplexitySearchTool.name,
@@ -86,7 +86,7 @@ class SearxNGSearchToolImpl(PredefinedTool):
86
86
  if key in kwargs: # pragma: no branch
87
87
  type_of = self.kwarg_types.get(key, str)
88
88
  # pylint: disable=broad-exception-caught
89
- # noinspection PyBroadException
89
+ # noinspection PyBroadException,TryExceptPass
90
90
  try:
91
91
  casted = type_of(value)
92
92
  if key in self.kwargs: # pragma: no branch
@@ -146,6 +146,7 @@ def {self.name}(
146
146
  return content
147
147
 
148
148
 
149
+ # pylint: disable=invalid-name
149
150
  SearxNGSearchTool = SearxNGSearchToolImpl()
150
151
  SearxNGSearchConfig = PredefinedToolConfig(
151
152
  name=SearxNGSearchTool.name,
@@ -146,6 +146,7 @@ def {self.name}(
146
146
  return content
147
147
 
148
148
 
149
+ # pylint: disable=invalid-name
149
150
  TavilySearchTool = TavilySearchToolImpl()
150
151
  TavilySearchConfig = PredefinedToolConfig(
151
152
  name=TavilySearchTool.name,
@@ -91,7 +91,7 @@ class WikipediaSearchToolImpl(PredefinedTool):
91
91
  if key in kwargs:
92
92
  type_of = self.kwargs_types.get(key, str)
93
93
  # pylint: disable=broad-exception-caught
94
- # noinspection PyBroadException
94
+ # noinspection PyBroadException,TryExceptPass
95
95
  try:
96
96
  casted = type_of(value)
97
97
  if key in self.kwargs:
@@ -145,6 +145,7 @@ def {self.name}(query: str, language: str = "en", top_k: int = 3, verbose: bool
145
145
  return content
146
146
 
147
147
 
148
+ # pylint: disable=invalid-name
148
149
  WikipediaSearchTool = WikipediaSearchToolImpl()
149
150
  WikipediaSearchConfig = PredefinedToolConfig(
150
151
  name=WikipediaSearchTool.name,
@@ -138,6 +138,7 @@ def {self.name}(
138
138
  return content
139
139
 
140
140
 
141
+ # pylint: disable=invalid-name
141
142
  YouTubeSearchTool = YouTubeSearchToolImpl()
142
143
  YouTubeSearchConfig = PredefinedToolConfig(
143
144
  name=YouTubeSearchTool.name,
@@ -81,7 +81,7 @@ class WaldiezTool(WaldiezBase):
81
81
  description="The tags of the tool.",
82
82
  default_factory=list,
83
83
  ),
84
- ] = []
84
+ ]
85
85
  requirements: Annotated[
86
86
  list[str],
87
87
  Field(
@@ -89,7 +89,7 @@ class WaldiezTool(WaldiezBase):
89
89
  description="The requirements of the tool.",
90
90
  default_factory=list,
91
91
  ),
92
- ] = []
92
+ ]
93
93
  data: Annotated[
94
94
  WaldiezToolData,
95
95
  Field(..., title="Data", description="The data of the tool."),
@@ -324,22 +324,25 @@ class WaldiezTool(WaldiezBase):
324
324
  config = get_predefined_tool_config(self.name)
325
325
  if not config:
326
326
  available_tools = list_predefined_tools()
327
- raise ValueError(
327
+ msg = (
328
328
  f"Unknown predefined tool: {self.name}. "
329
329
  f"Available tools: {available_tools}"
330
330
  )
331
+ raise ValueError(msg)
331
332
  missing_secrets = config.validate_secrets(self.data.secrets)
332
333
  if missing_secrets:
333
- raise ValueError(
334
+ msg = (
334
335
  f"Missing required secrets for {self.name}: "
335
336
  f"{missing_secrets}"
336
337
  )
338
+ raise ValueError(msg)
337
339
  invalid_kwargs = config.validate_kwargs(self.data.kwargs)
338
340
  if invalid_kwargs:
339
- raise ValueError(
341
+ msg = (
340
342
  f"Invalid keyword arguments for {self.name}: "
341
343
  f"{invalid_kwargs}"
342
344
  )
345
+ raise ValueError(msg)
343
346
  # Update tool metadata from predefined config
344
347
  if not self.description:
345
348
  self.description = config.description
@@ -34,7 +34,7 @@ class WaldiezToolData(WaldiezBase):
34
34
  "The type of the tool: shared, custom, langchain, crewai."
35
35
  ),
36
36
  ),
37
- ] = "custom"
37
+ ]
38
38
  content: Annotated[
39
39
  str,
40
40
  Field(
@@ -60,7 +60,7 @@ class WaldiezToolData(WaldiezBase):
60
60
  "Keyword arguments for the tool, used for initialization."
61
61
  ),
62
62
  ),
63
- ] = {}
63
+ ]
64
64
 
65
65
  _raw_content: str = ""
66
66
 
waldiez/models/waldiez.py CHANGED
@@ -8,10 +8,15 @@ and run an autogen workflow. It has the model/LLM configurations, the agent
8
8
  definitions and their optional additional tools to be used.
9
9
  """
10
10
 
11
+ import asyncio
11
12
  import json
13
+ import tempfile
14
+ from collections.abc import Iterator
12
15
  from dataclasses import dataclass
13
16
  from pathlib import Path
14
- from typing import Any, Iterator
17
+ from typing import Any
18
+
19
+ import aiofiles
15
20
 
16
21
  from .agents import (
17
22
  WaldiezAgent,
@@ -19,7 +24,7 @@ from .agents import (
19
24
  get_captain_agent_extra_requirements,
20
25
  get_retrievechat_extra_requirements,
21
26
  )
22
- from .common import get_autogen_version
27
+ from .common import get_autogen_version, safe_filename
23
28
  from .flow import (
24
29
  WaldiezAgentConnection,
25
30
  WaldiezFlow,
@@ -92,7 +97,7 @@ class Waldiez:
92
97
  requirements=requirements,
93
98
  )
94
99
  validated = WaldiezFlow.model_validate(flow)
95
- return cls(flow=validated) # pyright: ignore
100
+ return cls(flow=validated)
96
101
 
97
102
  @classmethod
98
103
  def load(
@@ -131,7 +136,7 @@ class Waldiez:
131
136
  data: dict[str, Any] = {}
132
137
  if not Path(waldiez_file).exists():
133
138
  raise ValueError(f"File not found: {waldiez_file}")
134
- with open(waldiez_file, "r", encoding="utf-8") as file:
139
+ with open(waldiez_file, "r", encoding="utf-8", newline="\n") as file:
135
140
  try:
136
141
  data = json.load(file)
137
142
  except json.decoder.JSONDecodeError as error:
@@ -144,6 +149,59 @@ class Waldiez:
144
149
  requirements=requirements,
145
150
  )
146
151
 
152
+ @classmethod
153
+ async def a_load(
154
+ cls,
155
+ waldiez_file: str | Path,
156
+ name: str | None = None,
157
+ description: str | None = None,
158
+ tags: list[str] | None = None,
159
+ requirements: list[str] | None = None,
160
+ ) -> "Waldiez":
161
+ """Load a Waldiez from a file.
162
+
163
+ Parameters
164
+ ----------
165
+ waldiez_file : Union[str, Path]
166
+ The Waldiez file.
167
+ name: str | None, optional
168
+ The name, by default None (retrieved from data).
169
+ description : str | None, optional
170
+ The description, by default None (retrieved from data).
171
+ tags: list[str] | None, optional
172
+ The tags, by default None (retrieved from data).
173
+ requirements: list[str] | None, optional
174
+ The requirements, by default None (retrieved from data).
175
+
176
+ Returns
177
+ -------
178
+ Waldiez
179
+ The Waldiez.
180
+
181
+ Raises
182
+ ------
183
+ ValueError
184
+ If the file is not found or invalid JSON.
185
+ """
186
+ data: dict[str, Any] = {}
187
+ if not Path(waldiez_file).exists():
188
+ raise ValueError(f"File not found: {waldiez_file}")
189
+ async with aiofiles.open(
190
+ waldiez_file, "r", encoding="utf-8", newline="\n"
191
+ ) as file:
192
+ try:
193
+ contents = await file.read()
194
+ data = json.loads(contents)
195
+ except json.decoder.JSONDecodeError as error:
196
+ raise ValueError(f"Invalid JSON: {waldiez_file}") from error
197
+ return cls.from_dict(
198
+ data,
199
+ name=name,
200
+ description=description,
201
+ tags=tags,
202
+ requirements=requirements,
203
+ )
204
+
147
205
  def model_dump_json(
148
206
  self, by_alias: bool = True, indent: int | None = None
149
207
  ) -> str:
@@ -200,6 +258,26 @@ class Waldiez:
200
258
  """Get the chats."""
201
259
  return self.flow.ordered_flow
202
260
 
261
+ @property
262
+ def is_group_pattern_based(
263
+ self,
264
+ ) -> bool:
265
+ """Check if the group manager should use pattern strategy.
266
+
267
+ Returns
268
+ -------
269
+ bool
270
+ True if pattern strategy should be used, False otherwise.
271
+ """
272
+ if not self.initial_chats:
273
+ return True
274
+
275
+ first_chat = self.initial_chats[0]["chat"]
276
+ return (
277
+ isinstance(first_chat.data.message, str)
278
+ or not first_chat.data.message.is_method()
279
+ )
280
+
203
281
  @property
204
282
  def agents(self) -> Iterator[WaldiezAgent]:
205
283
  """Get the agents.
@@ -367,3 +445,73 @@ class Waldiez:
367
445
  if not agent.is_group_manager:
368
446
  return []
369
447
  return self.flow.get_group_chat_members(agent.id)
448
+
449
+ def dump(self, to: str | Path | None = None) -> Path:
450
+ """Dump waldiez flow to a file.
451
+
452
+ Parameters
453
+ ----------
454
+ to : str | Path | None
455
+ Optional output path to determine the directory to save the flow to.
456
+
457
+ Returns
458
+ -------
459
+ Path
460
+ The path to the generated file.
461
+ """
462
+ file_path = Path(to) if to else None
463
+ if file_path:
464
+ file_name = file_path.name
465
+ if not file_name.endswith(".waldiez"):
466
+ file_path.with_suffix(".waldiez")
467
+
468
+ else:
469
+ full_name = self.name
470
+ file_name = safe_filename(full_name, "waldiez")
471
+ file_dir: Path
472
+ if file_path:
473
+ file_dir = file_path if file_path.is_dir() else file_path.parent
474
+ else:
475
+ file_dir = Path(tempfile.mkdtemp())
476
+ file_dir.mkdir(parents=True, exist_ok=True)
477
+ output_path = file_dir / file_name
478
+ with output_path.open(
479
+ "w", encoding="utf-8", errors="replace", newline="\n"
480
+ ) as f_open:
481
+ f_open.write(self.model_dump_json())
482
+ return output_path
483
+
484
+ async def a_dump(self, to: str | Path | None = None) -> Path:
485
+ """Dump waldiez flow to a file asynchronously.
486
+
487
+ Parameters
488
+ ----------
489
+ to : str | Path | None
490
+ Optional output path to determine the directory to save the flow to.
491
+
492
+ Returns
493
+ -------
494
+ Path
495
+ The path to the generated file.
496
+ """
497
+ file_path = Path(to) if to else None
498
+ if file_path:
499
+ file_name = file_path.name
500
+ if not file_name.endswith(".waldiez"):
501
+ file_path.with_suffix(".waldiez")
502
+ else:
503
+ full_name = self.name
504
+ file_name = safe_filename(full_name, "waldiez")
505
+ file_dir: Path
506
+ if file_path:
507
+ file_dir = file_path if file_path.is_dir() else file_path.parent
508
+ else:
509
+ tmp_dir = await asyncio.to_thread(tempfile.mkdtemp)
510
+ file_dir = Path(tmp_dir)
511
+ file_dir.mkdir(parents=True, exist_ok=True)
512
+ output_path = file_dir / file_name
513
+ async with aiofiles.open(
514
+ output_path, "w", encoding="utf-8", errors="replace", newline="\n"
515
+ ) as f_open:
516
+ await f_open.write(self.model_dump_json())
517
+ return output_path
waldiez/runner.py CHANGED
@@ -20,7 +20,7 @@ during the flow execution.
20
20
  from pathlib import Path
21
21
  from typing import Any
22
22
 
23
- from typing_extensions import Literal
23
+ from typing_extensions import Literal, override
24
24
 
25
25
  from .models.waldiez import Waldiez
26
26
  from .running import (
@@ -81,12 +81,12 @@ def create_runner(
81
81
  f"Unknown runner mode '{mode}'. Available: {available}"
82
82
  )
83
83
 
84
- runner_class = runners[mode]
84
+ runner_cls = runners[mode]
85
85
  if mode == "subprocess":
86
86
  subprocess_mode = kwargs.pop("subprocess_mode", "run")
87
87
  if subprocess_mode not in ["run", "debug"]:
88
88
  subprocess_mode = "run"
89
- return runner_class(
89
+ return runner_cls(
90
90
  waldiez=waldiez,
91
91
  output_path=output_path,
92
92
  uploads_root=uploads_root,
@@ -95,7 +95,9 @@ def create_runner(
95
95
  mode=subprocess_mode,
96
96
  **kwargs,
97
97
  )
98
- return runner_class(
98
+ if mode != "debug" and "breakpoints" in kwargs: # pragma: no cover
99
+ kwargs.pop("breakpoints", None)
100
+ return runner_cls(
99
101
  waldiez=waldiez,
100
102
  output_path=output_path,
101
103
  uploads_root=uploads_root,
@@ -111,7 +113,7 @@ class WaldiezRunner(WaldiezBaseRunner):
111
113
 
112
114
  # pylint: disable=super-init-not-called
113
115
  # noinspection PyMissingConstructor
114
- def __init__(
116
+ def __init__( # pyright: ignore[reportMissingSuperCall]
115
117
  self,
116
118
  waldiez: Waldiez,
117
119
  mode: Literal["standard", "debug"] = "standard",
@@ -150,6 +152,7 @@ class WaldiezRunner(WaldiezBaseRunner):
150
152
  **kwargs,
151
153
  )
152
154
 
155
+ @override
153
156
  def __repr__(self) -> str: # pragma: no cover
154
157
  """Get the string representation of the runner.
155
158
 
@@ -179,6 +182,7 @@ class WaldiezRunner(WaldiezBaseRunner):
179
182
  f"{type(self).__name__} has no attribute '{name}'"
180
183
  ) # pragma: no cover
181
184
 
185
+ @override
182
186
  def _run(
183
187
  self,
184
188
  temp_dir: Path,
@@ -197,6 +201,7 @@ class WaldiezRunner(WaldiezBaseRunner):
197
201
  **kwargs,
198
202
  )
199
203
 
204
+ @override
200
205
  async def _a_run(
201
206
  self,
202
207
  temp_dir: Path,
@@ -217,6 +222,7 @@ class WaldiezRunner(WaldiezBaseRunner):
217
222
  **kwargs,
218
223
  )
219
224
 
225
+ @override
220
226
  @classmethod
221
227
  def load(
222
228
  cls,