waldiez 0.3.11__py3-none-any.whl → 0.4.0__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 (66) hide show
  1. waldiez/_version.py +1 -1
  2. waldiez/cli.py +1 -3
  3. waldiez/exporting/agent/agent_exporter.py +26 -15
  4. waldiez/exporting/agent/utils/__init__.py +2 -4
  5. waldiez/exporting/agent/utils/captain_agent.py +250 -0
  6. waldiez/exporting/agent/utils/swarm_agent.py +12 -7
  7. waldiez/exporting/base/utils/comments.py +1 -0
  8. waldiez/exporting/chats/utils/swarm.py +1 -1
  9. waldiez/exporting/flow/flow_exporter.py +5 -6
  10. waldiez/exporting/flow/utils/__init__.py +3 -6
  11. waldiez/exporting/flow/utils/def_main.py +5 -4
  12. waldiez/exporting/flow/utils/flow_content.py +38 -0
  13. waldiez/exporting/flow/utils/importing_utils.py +64 -29
  14. waldiez/exporting/skills/skills_exporter.py +13 -6
  15. waldiez/exporting/skills/utils.py +92 -6
  16. waldiez/models/__init__.py +6 -0
  17. waldiez/models/agents/__init__.py +14 -0
  18. waldiez/models/agents/agent/__init__.py +2 -1
  19. waldiez/models/agents/agent/agent.py +71 -11
  20. waldiez/models/agents/agent/agent_type.py +11 -0
  21. waldiez/models/agents/agents.py +11 -1
  22. waldiez/models/agents/captain_agent/__init__.py +15 -0
  23. waldiez/models/agents/captain_agent/captain_agent.py +45 -0
  24. waldiez/models/agents/captain_agent/captain_agent_data.py +62 -0
  25. waldiez/models/agents/captain_agent/captain_agent_lib_entry.py +38 -0
  26. waldiez/models/agents/extra_requirements.py +88 -0
  27. waldiez/models/agents/group_manager/speakers.py +3 -0
  28. waldiez/models/agents/rag_user/retrieve_config.py +3 -0
  29. waldiez/models/agents/reasoning/reasoning_agent_reason_config.py +1 -0
  30. waldiez/models/agents/swarm_agent/after_work.py +13 -11
  31. waldiez/models/agents/swarm_agent/on_condition.py +3 -2
  32. waldiez/models/agents/swarm_agent/on_condition_available.py +1 -0
  33. waldiez/models/agents/swarm_agent/swarm_agent_data.py +3 -3
  34. waldiez/models/agents/swarm_agent/update_system_message.py +1 -0
  35. waldiez/models/chat/chat_message.py +1 -0
  36. waldiez/models/chat/chat_summary.py +1 -0
  37. waldiez/models/common/__init__.py +4 -0
  38. waldiez/models/common/ag2_version.py +30 -0
  39. waldiez/models/common/base.py +1 -1
  40. waldiez/models/common/date_utils.py +2 -0
  41. waldiez/models/common/dict_utils.py +2 -0
  42. waldiez/models/common/method_utils.py +98 -0
  43. waldiez/models/flow/__init__.py +2 -0
  44. waldiez/models/flow/utils.py +61 -1
  45. waldiez/models/model/__init__.py +2 -0
  46. waldiez/models/model/extra_requirements.py +57 -0
  47. waldiez/models/model/model.py +5 -2
  48. waldiez/models/model/model_data.py +3 -1
  49. waldiez/models/skill/__init__.py +4 -0
  50. waldiez/models/skill/extra_requirements.py +39 -0
  51. waldiez/models/skill/skill.py +157 -13
  52. waldiez/models/skill/skill_data.py +14 -0
  53. waldiez/models/skill/skill_type.py +8 -0
  54. waldiez/models/waldiez.py +47 -76
  55. waldiez/runner.py +19 -7
  56. waldiez/running/environment.py +30 -1
  57. waldiez/running/running.py +0 -6
  58. waldiez/utils/pysqlite3_checker.py +18 -5
  59. {waldiez-0.3.11.dist-info → waldiez-0.4.0.dist-info}/METADATA +42 -30
  60. {waldiez-0.3.11.dist-info → waldiez-0.4.0.dist-info}/RECORD +64 -55
  61. waldiez/exporting/agent/utils/agent_class_name.py +0 -36
  62. waldiez/exporting/agent/utils/agent_imports.py +0 -55
  63. {waldiez-0.3.11.dist-info → waldiez-0.4.0.dist-info}/WHEEL +0 -0
  64. {waldiez-0.3.11.dist-info → waldiez-0.4.0.dist-info}/entry_points.txt +0 -0
  65. {waldiez-0.3.11.dist-info → waldiez-0.4.0.dist-info}/licenses/LICENSE +0 -0
  66. {waldiez-0.3.11.dist-info → waldiez-0.4.0.dist-info}/licenses/NOTICE.md +0 -0
@@ -0,0 +1,57 @@
1
+ # SPDX-License-Identifier: Apache-2.0.
2
+ # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
+ """Waldiez model extra requirements."""
4
+
5
+ from typing import Iterator, Set
6
+
7
+ from .model import WaldiezModel
8
+
9
+
10
+ def get_models_extra_requirements(
11
+ models: Iterator[WaldiezModel], autogen_version: str
12
+ ) -> Set[str]:
13
+ """Get the models extra requirements.
14
+
15
+ Parameters
16
+ ----------
17
+ models : List[WaldiezModel]
18
+ The models.
19
+ autogen_version : str
20
+ The autogen version.
21
+
22
+ Returns
23
+ -------
24
+ List[str]
25
+ The models extra requirements.
26
+ """
27
+ model_requirements: Set[str] = set()
28
+ # ref: https://github.com/ag2ai/ag2/blob/main/pyproject.toml
29
+ models_with_additional_requirements = [
30
+ "together",
31
+ "gemini",
32
+ "mistral",
33
+ "groq",
34
+ "anthropic",
35
+ "cohere",
36
+ "bedrock", # we might add this later
37
+ ]
38
+ for model in models:
39
+ for requirement in model.requirements:
40
+ model_requirements.add(requirement)
41
+ if model.data.api_type == "google":
42
+ model_requirements.add(f"pyautogen[gemini]=={autogen_version}")
43
+ continue
44
+ if model.data.api_type in models_with_additional_requirements:
45
+ model_requirements.add(
46
+ f"pyautogen[{model.data.api_type}]=={autogen_version}"
47
+ )
48
+ return model_requirements
49
+
50
+
51
+ # for bedrock, there are some additional params needed
52
+ # (both here and on the ui part):
53
+ # aws_region (mandatory)
54
+ # aws_access_key (or environment variable: AWS_ACCESS_KEY)
55
+ # aws_secret_key (or environment variable: AWS_SECRET_KEY)
56
+ # aws_session_token (or environment variable: AWS_SESSION_TOKEN)
57
+ # aws_profile_name
@@ -19,6 +19,7 @@ DEFAULT_BASE_URLS: Dict[WaldiezModelAPIType, str] = {
19
19
  "groq": "https://api.groq.com/openai/v1",
20
20
  "together": "https://api.together.xyz/v1",
21
21
  "nim": "https://integrate.api.nvidia.com/v1",
22
+ "cohere": "https://api.cohere.com",
22
23
  }
23
24
 
24
25
 
@@ -63,7 +64,7 @@ class WaldiezModel(WaldiezBase):
63
64
  description: Annotated[
64
65
  str,
65
66
  Field(
66
- ...,
67
+ "Model's Description",
67
68
  title="Description",
68
69
  description="The description of the model.",
69
70
  ),
@@ -118,6 +119,7 @@ class WaldiezModel(WaldiezBase):
118
119
  - groq: 'GROQ_API_KEY',
119
120
  - together: 'TOGETHER_API_KEY',
120
121
  - nim: 'NIM_API_KEY',
122
+ - cohere: 'COHERE_API_KEY',
121
123
  - other: 'OPENAI_API_KEY'
122
124
  """
123
125
  env_key = "OPENAI_API_KEY"
@@ -142,6 +144,7 @@ class WaldiezModel(WaldiezBase):
142
144
  - groq: 'GROQ_API_KEY',
143
145
  - together: 'TOGETHER_API_KEY',
144
146
  - nim: 'NIM_API_KEY',
147
+ - cohere: 'COHERE_API_KEY',
145
148
  - other: 'OPENAI_API_KEY'
146
149
  """
147
150
  if self.data.api_key and self.data.api_key != "REPLACE_ME":
@@ -222,7 +225,7 @@ def set_default_base_url(
222
225
  Dict[str, Any]
223
226
  The llm config dictionary with the default base url set.
224
227
  """
225
- if api_type in ("openai", "other", "azure"):
228
+ if api_type in ("openai", "other", "azure", "cohere"):
226
229
  return llm_config
227
230
  if "base_url" not in llm_config or not llm_config["base_url"]:
228
231
  dict_copy = llm_config.copy()
@@ -20,8 +20,10 @@ WaldiezModelAPIType = Literal[
20
20
  "groq",
21
21
  "together",
22
22
  "nim",
23
+ "cohere",
23
24
  "other",
24
25
  ]
26
+ """Possible API types for the model."""
25
27
 
26
28
 
27
29
  class WaldiezModelPrice(WaldiezBase):
@@ -53,7 +55,7 @@ class WaldiezModelData(WaldiezBase):
53
55
  The base url of the model, by default None.
54
56
  api_key : Optional[str]
55
57
  The api key to use with the model, by default None.
56
- api_type : Literal["openai","azure","deepseek","google","anthropic","mistral","groq","together","nim","other"]
58
+ api_type : WaldiezModelAPIType
57
59
  The api type of the model.
58
60
  api_version : Optional[str]
59
61
  The api version of the model, by default None.
@@ -2,11 +2,15 @@
2
2
  # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
3
  """Waldiez Skill related models."""
4
4
 
5
+ from .extra_requirements import get_skills_extra_requirements
5
6
  from .skill import SHARED_SKILL_NAME, WaldiezSkill
6
7
  from .skill_data import WaldiezSkillData
8
+ from .skill_type import WaldiezSkillType
7
9
 
8
10
  __all__ = [
9
11
  "SHARED_SKILL_NAME",
10
12
  "WaldiezSkill",
11
13
  "WaldiezSkillData",
14
+ "WaldiezSkillType",
15
+ "get_skills_extra_requirements",
12
16
  ]
@@ -0,0 +1,39 @@
1
+ # SPDX-License-Identifier: Apache-2.0.
2
+ # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
+ """Waldiez skill extra requirements."""
4
+
5
+ from typing import Iterator, Set
6
+
7
+ from .skill import WaldiezSkill
8
+
9
+
10
+ def get_skills_extra_requirements(
11
+ skills: Iterator[WaldiezSkill],
12
+ autogen_version: str,
13
+ ) -> Set[str]:
14
+ """Get the skills extra requirements.
15
+
16
+ Parameters
17
+ ----------
18
+ skills : List[WaldiezSkill]
19
+ The skills.
20
+ autogen_version : str
21
+ The ag2 version.
22
+ Returns
23
+ -------
24
+ List[str]
25
+ The skills extra requirements.
26
+ """
27
+ skill_requirements: Set[str] = set()
28
+ for skill in skills:
29
+ if skill.skill_type == "langchain":
30
+ skill_requirements.add(
31
+ f"pyautogen[interop-langchain]=={autogen_version}"
32
+ )
33
+ if skill.skill_type == "crewai":
34
+ skill_requirements.add(
35
+ f"pyautogen[interop-crewai]=={autogen_version}"
36
+ )
37
+ for requirement in skill.requirements:
38
+ skill_requirements.add(requirement)
39
+ return skill_requirements
@@ -2,13 +2,23 @@
2
2
  # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
3
  """Waldiez Skill model."""
4
4
 
5
- from typing import Dict, List
5
+ import json
6
+ import re
7
+ from pathlib import Path
8
+ from typing import Any, Dict, List, Tuple, Union
6
9
 
7
10
  from pydantic import Field, model_validator
8
11
  from typing_extensions import Annotated, Literal, Self
9
12
 
10
- from ..common import WaldiezBase, get_function, now, parse_code_string
13
+ from ..common import (
14
+ WaldiezBase,
15
+ gather_code_imports,
16
+ get_function,
17
+ now,
18
+ parse_code_string,
19
+ )
11
20
  from .skill_data import WaldiezSkillData
21
+ from .skill_type import WaldiezSkillType
12
22
 
13
23
  SHARED_SKILL_NAME = "waldiez_shared"
14
24
 
@@ -97,6 +107,66 @@ class WaldiezSkill(WaldiezBase):
97
107
  ),
98
108
  ]
99
109
 
110
+ @staticmethod
111
+ def load(data_or_path: Union[str, Path, Dict[str, Any]]) -> "WaldiezSkill":
112
+ """Load a skill from a read-only file.
113
+
114
+ Parameters
115
+ ----------
116
+ data_or_path : Union[str, Path, Dict[str, Any]]
117
+ The path to the read-only file or the loaded data.
118
+
119
+ Returns
120
+ -------
121
+ WaldiezSkill
122
+ The skill.
123
+
124
+ Raises
125
+ ------
126
+ FileNotFoundError
127
+ If the file is not found.
128
+ ValueError
129
+ If the JSON is invalid or the data is invalid.
130
+ """
131
+ if isinstance(data_or_path, dict):
132
+ return WaldiezSkill.model_validate(data_or_path)
133
+ if not isinstance(data_or_path, Path):
134
+ data_or_path = Path(data_or_path)
135
+ resolved = data_or_path.resolve()
136
+ if not resolved.is_file():
137
+ raise FileNotFoundError(f"File not found: {resolved}")
138
+ with resolved.open("r", encoding="utf-8") as file:
139
+ data_string = file.read()
140
+ try:
141
+ data_dict = json.loads(data_string)
142
+ except BaseException as exc: # pylint: disable=broad-except
143
+ raise ValueError(f"Invalid WaldiezSkill/JSON: {exc}") from exc
144
+ return WaldiezSkill.model_validate(data_dict)
145
+
146
+ @property
147
+ def skill_type(self) -> WaldiezSkillType:
148
+ """Get the skill type.
149
+
150
+ Returns
151
+ -------
152
+ WaldiezSkillType
153
+ The type of the skill:
154
+ [shared, custom, langchain, crewai].
155
+ """
156
+ return self.data.skill_type
157
+
158
+ _skill_imports: Tuple[List[str], List[str]] = ([], [])
159
+
160
+ def get_imports(self) -> Tuple[List[str], List[str]]:
161
+ """Get the skill imports.
162
+
163
+ Returns
164
+ -------
165
+ Tuple[List[str], List[str]]
166
+ The builtin and external imports.
167
+ """
168
+ return self._skill_imports
169
+
100
170
  @property
101
171
  def is_shared(self) -> bool:
102
172
  """Check if the skill is shared.
@@ -106,7 +176,18 @@ class WaldiezSkill(WaldiezBase):
106
176
  bool
107
177
  True if the skill is shared, False otherwise.
108
178
  """
109
- return self.name == SHARED_SKILL_NAME
179
+ return self.skill_type == "shared" or self.name == SHARED_SKILL_NAME
180
+
181
+ @property
182
+ def is_interop(self) -> bool:
183
+ """Check if the skill is interoperability.
184
+
185
+ Returns
186
+ -------
187
+ bool
188
+ True if the skill is interoperability, False otherwise.
189
+ """
190
+ return self.skill_type in ("langchain", "crewai")
110
191
 
111
192
  def get_content(self) -> str:
112
193
  """Get the content of the skill.
@@ -116,11 +197,61 @@ class WaldiezSkill(WaldiezBase):
116
197
  str
117
198
  The content of the skill.
118
199
  """
119
- if self.name == SHARED_SKILL_NAME:
120
- # the whole content (globals)
200
+ if self.is_shared or self.is_interop:
121
201
  return self.data.content
202
+ # if custom, only the function content
122
203
  return get_function(self.data.content, self.name)
123
204
 
205
+ def _validate_interop_skill(self) -> None:
206
+ """Validate the interoperability skill.
207
+
208
+ Raises
209
+ ------
210
+ ValueError
211
+ If the skill name is not in the content.
212
+ """
213
+ if self.is_interop:
214
+ # we expect sth like:
215
+ # with single or double quotes for type={skill_type}
216
+ # {skill_name} = *.convert_tool(..., type="{skill_type}", ...)
217
+ if f"{self.name} = " not in self.data.content:
218
+ raise ValueError(
219
+ f"The skill name '{self.name}' is not in the content."
220
+ )
221
+ # we don't want the conversion to ag2 tool (we do it internally)
222
+ # or the skill registration (we do it after having the agent names)
223
+ # so no" .convert_tool(... type="...")
224
+ # or .register_for_llm(...), .register_for_execution(...)
225
+ to_exclude = [
226
+ r".convert_tool\(.+?type=",
227
+ rf"{self.name}.register_for_llm\(",
228
+ rf"{self.name}.register_for_execution\(",
229
+ ]
230
+ for exclude in to_exclude:
231
+ if re.search(exclude, self.data.content):
232
+ raise ValueError(
233
+ f"Invalid skill content: '{exclude}' is not allowed."
234
+ )
235
+
236
+ def _validate_custom_skill(self) -> None:
237
+ """Validate a custom skill.
238
+
239
+ Raises
240
+ ------
241
+ ValueError
242
+ If the skill name is not in the content.
243
+ If the skill content is invalid.
244
+ """
245
+ search = f"def {self.name}("
246
+ if self.skill_type == "custom" and not self.is_shared:
247
+ if search not in self.data.content:
248
+ raise ValueError(
249
+ f"The skill name '{self.name}' is not in the content."
250
+ )
251
+ error, tree = parse_code_string(self.data.content)
252
+ if error is not None or tree is None:
253
+ raise ValueError(f"Invalid skill content: {error}")
254
+
124
255
  @model_validator(mode="after")
125
256
  def validate_data(self) -> Self:
126
257
  """Validate the data.
@@ -136,14 +267,27 @@ class WaldiezSkill(WaldiezBase):
136
267
  If the skill name is not in the content.
137
268
  If the skill content is invalid.
138
269
  """
139
- search = f"def {self.name}("
140
- if self.name != SHARED_SKILL_NAME and search not in self.data.content:
141
- raise ValueError(
142
- f"The skill name '{self.name}' is not in the content."
143
- )
144
- error, tree = parse_code_string(self.data.content)
145
- if error is not None or tree is None:
146
- raise ValueError(f"Invalid skill content: {error}")
270
+ self._validate_custom_skill()
271
+ self._validate_interop_skill()
272
+ self._skill_imports = gather_code_imports(
273
+ self.data.content, self.is_interop
274
+ )
275
+ # remove the imports from the content
276
+ # we 'll place them at the top of the file
277
+ all_imports = self._skill_imports[0] + self._skill_imports[1]
278
+ code_lines = self.data.content.splitlines()
279
+ valid_lines = [
280
+ line
281
+ for line in code_lines
282
+ if not any(line.startswith(imp) for imp in all_imports)
283
+ ]
284
+ # remove empty lines at the beginning and end
285
+ # of the content
286
+ while valid_lines and not valid_lines[0].strip():
287
+ valid_lines.pop(0)
288
+ while valid_lines and not valid_lines[-1].strip():
289
+ valid_lines.pop()
290
+ self.data.content = "\n".join(valid_lines)
147
291
  return self
148
292
 
149
293
  @property
@@ -8,6 +8,7 @@ from pydantic import Field
8
8
  from typing_extensions import Annotated
9
9
 
10
10
  from ..common import WaldiezBase
11
+ from .skill_type import WaldiezSkillType
11
12
 
12
13
 
13
14
  class WaldiezSkillData(WaldiezBase):
@@ -15,12 +16,25 @@ class WaldiezSkillData(WaldiezBase):
15
16
 
16
17
  Attributes
17
18
  ----------
19
+ skill_type : WaldiezSkillType
20
+ The type of the skill: shared, custom, langchain, crewai.
18
21
  content : str
19
22
  The content (source code) of the skill.
20
23
  secrets : Dict[str, str]
21
24
  The secrets (environment variables) of the skill.
22
25
  """
23
26
 
27
+ skill_type: Annotated[
28
+ WaldiezSkillType,
29
+ Field(
30
+ "custom",
31
+ alias="skillType",
32
+ title="Skill Type",
33
+ description=(
34
+ "The type of the skill: shared, custom, langchain, crewai."
35
+ ),
36
+ ),
37
+ ] = "custom"
24
38
  content: Annotated[
25
39
  str,
26
40
  Field(
@@ -0,0 +1,8 @@
1
+ # SPDX-License-Identifier: Apache-2.0.
2
+ # Copyright (c) 2024 - 2025 Waldiez and contributors.
3
+ """Waldiez Skill types."""
4
+
5
+ from typing_extensions import Literal
6
+
7
+ WaldiezSkillType = Literal["shared", "custom", "langchain", "crewai"]
8
+ """Possible types of a Waldiez Skill."""
waldiez/models/waldiez.py CHANGED
@@ -8,17 +8,16 @@ definitions and their optional additional skills to be used.
8
8
  """
9
9
 
10
10
  import json
11
- import warnings
12
11
  from dataclasses import dataclass
13
- from functools import cache
14
12
  from pathlib import Path
15
13
  from typing import Any, Dict, Iterator, List, Optional, Tuple, Union
16
14
 
17
- from .agents import WaldiezAgent
15
+ from .agents import WaldiezAgent, get_retrievechat_extra_requirements
18
16
  from .chat import WaldiezChat
19
- from .flow import WaldiezFlow
20
- from .model import WaldiezModel
21
- from .skill import WaldiezSkill
17
+ from .common import get_autogen_version
18
+ from .flow import WaldiezFlow, get_flow_data
19
+ from .model import WaldiezModel, get_models_extra_requirements
20
+ from .skill import WaldiezSkill, get_skills_extra_requirements
22
21
 
23
22
 
24
23
  @dataclass(frozen=True, slots=True)
@@ -62,7 +61,7 @@ class Waldiez:
62
61
  Waldiez
63
62
  The Waldiez.
64
63
  """
65
- flow = _get_flow(
64
+ flow = get_flow_data(
66
65
  data,
67
66
  flow_id=flow_id,
68
67
  name=name,
@@ -153,6 +152,11 @@ class Waldiez:
153
152
  """Check if the flow has multimodal agents."""
154
153
  return any(agent.data.is_multimodal for agent in self.agents)
155
154
 
155
+ @property
156
+ def has_captain_agents(self) -> bool:
157
+ """Check if the flow has captain agents."""
158
+ return any(agent.agent_type == "captain" for agent in self.agents)
159
+
156
160
  @property
157
161
  def chats(self) -> List[Tuple[WaldiezChat, WaldiezAgent, WaldiezAgent]]:
158
162
  """Get the chats."""
@@ -224,8 +228,9 @@ class Waldiez:
224
228
  @property
225
229
  def requirements(self) -> List[str]:
226
230
  """Get the flow requirements."""
227
- autogen_version = _get_autogen_version()
231
+ autogen_version = get_autogen_version()
228
232
  requirements_list = filter(
233
+ # we use the fixed "pyautogen=={autogen_version}" below
229
234
  lambda requirement: not (
230
235
  requirement.startswith("pyautogen")
231
236
  or requirement.startswith("ag2")
@@ -234,30 +239,43 @@ class Waldiez:
234
239
  self.flow.requirements,
235
240
  )
236
241
  requirements = set(requirements_list)
242
+ requirements.add(f"pyautogen=={autogen_version}")
237
243
  if self.has_rag_agents:
238
- requirements.add(f"pyautogen[retrievechat]=={autogen_version}")
239
- else:
240
- requirements.add(f"pyautogen=={autogen_version}")
244
+ rag_extras = get_retrievechat_extra_requirements(self.agents)
245
+ requirements.update(rag_extras)
241
246
  if self.has_multimodal_agents:
242
247
  requirements.add(f"pyautogen[lmm]=={autogen_version}")
243
- # ref: https://github.com/ag2ai/ag2/blob/main/pyproject.toml
244
- models_with_additional_requirements = [
245
- "together",
246
- "gemini",
247
- "mistral",
248
- "groq",
249
- "anthropic",
250
- "cohere",
251
- "bedrock",
252
- ]
253
- for model in self.models:
254
- if model.data.api_type == "google":
255
- requirements.add(f"pyautogen[gemini]=={autogen_version}")
256
- continue
257
- if model.data.api_type in models_with_additional_requirements:
258
- requirements.add(
259
- f"pyautogen[{model.data.api_type}]=={autogen_version}"
260
- )
248
+ if self.has_captain_agents:
249
+ # pysqlite3-binary might not get installed on windows
250
+ captain_extras = [
251
+ "chromadb",
252
+ "sentence-transformers",
253
+ "huggingface-hub",
254
+ # tools:
255
+ "pillow",
256
+ "markdownify",
257
+ "arxiv",
258
+ "pymupdf",
259
+ "wikipedia-api",
260
+ "easyocr",
261
+ "python-pptx",
262
+ "openai-whisper",
263
+ "pandas",
264
+ "scipy",
265
+ ]
266
+ requirements.update(captain_extras)
267
+ requirements.update(
268
+ get_models_extra_requirements(
269
+ self.models,
270
+ autogen_version=autogen_version,
271
+ )
272
+ )
273
+ requirements.update(
274
+ get_skills_extra_requirements(
275
+ self.skills,
276
+ autogen_version=autogen_version,
277
+ )
278
+ )
261
279
  return sorted(list(requirements))
262
280
 
263
281
  def get_flow_env_vars(self) -> List[Tuple[str, str]]:
@@ -307,50 +325,3 @@ class Waldiez:
307
325
  The swarm agents and the user agent.
308
326
  """
309
327
  return self.flow.get_swarm_chat_members(initial_agent)
310
-
311
-
312
- def _get_flow(
313
- data: Dict[str, Any],
314
- flow_id: Optional[str] = None,
315
- name: Optional[str] = None,
316
- description: Optional[str] = None,
317
- tags: Optional[List[str]] = None,
318
- requirements: Optional[List[str]] = None,
319
- ) -> Dict[str, Any]:
320
- """Get the flow."""
321
- item_type = data.get("type", "flow")
322
- if item_type != "flow":
323
- # empty flow (from exported model/skill ?)
324
- raise ValueError(f"Invalid flow type: {item_type}")
325
- from_args = {
326
- "id": flow_id,
327
- "name": name,
328
- "description": description,
329
- "tags": tags,
330
- "requirements": requirements,
331
- }
332
- for key, value in from_args.items():
333
- if value:
334
- data[key] = value
335
- if "name" not in data:
336
- data["name"] = "Waldiez Flow"
337
- if "description" not in data:
338
- data["description"] = "Waldiez Flow description"
339
- if "tags" not in data:
340
- data["tags"] = []
341
- if "requirements" not in data:
342
- data["requirements"] = []
343
- return data
344
-
345
-
346
- @cache
347
- def _get_autogen_version() -> str:
348
- """Get the autogen version."""
349
- # pylint: disable=import-outside-toplevel
350
- with warnings.catch_warnings():
351
- warnings.simplefilter("ignore")
352
- try:
353
- from autogen.version import __version__ as ag2 # type: ignore
354
- except ImportError as error: # pragma: no cover
355
- raise ValueError("pyautogen is not installed.") from error
356
- return ag2
waldiez/runner.py CHANGED
@@ -14,7 +14,7 @@ import sys
14
14
  import tempfile
15
15
  from pathlib import Path
16
16
  from types import TracebackType
17
- from typing import TYPE_CHECKING, Dict, List, Optional, Type, Union
17
+ from typing import TYPE_CHECKING, Dict, List, Optional, Set, Type, Union
18
18
 
19
19
  from .exporter import WaldiezExporter
20
20
  from .models.waldiez import Waldiez
@@ -30,6 +30,7 @@ from .running import (
30
30
  reset_env_vars,
31
31
  set_env_vars,
32
32
  )
33
+ from .utils import check_pysqlite3
33
34
 
34
35
  if TYPE_CHECKING:
35
36
  from autogen import ChatResult # type: ignore
@@ -135,13 +136,26 @@ class WaldiezRunner:
135
136
  """Get the running status."""
136
137
  return self._running
137
138
 
139
+ def gather_requirements(self) -> Set[str]:
140
+ """Gather extra requirements to install before running the flow.
141
+
142
+ Returns
143
+ -------
144
+ Set[str]
145
+ The extra requirements.
146
+ """
147
+ extra_requirements = set(
148
+ req for req in self.waldiez.requirements if req not in sys.modules
149
+ )
150
+ if self.waldiez.has_captain_agents:
151
+ check_pysqlite3()
152
+ return extra_requirements
153
+
138
154
  def install_requirements(self) -> None:
139
155
  """Install the requirements for the flow."""
140
156
  self._called_install_requirements = True
141
157
  printer = get_printer()
142
- extra_requirements = set(
143
- req for req in self.waldiez.requirements if req not in sys.modules
144
- )
158
+ extra_requirements = self.gather_requirements()
145
159
  if extra_requirements:
146
160
  install_requirements(extra_requirements, printer)
147
161
  refresh_environment()
@@ -150,9 +164,7 @@ class WaldiezRunner:
150
164
  """Install the requirements for the flow asynchronously."""
151
165
  self._called_install_requirements = True
152
166
  printer = get_printer()
153
- extra_requirements = set(
154
- req for req in self.waldiez.requirements if req not in sys.modules
155
- )
167
+ extra_requirements = self.gather_requirements()
156
168
  if extra_requirements:
157
169
  await a_install_requirements(extra_requirements, printer)
158
170
  refresh_environment()