waldiez 0.3.12__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.
- waldiez/_version.py +1 -1
- waldiez/cli.py +1 -3
- waldiez/exporting/agent/agent_exporter.py +5 -1
- waldiez/exporting/agent/utils/captain_agent.py +4 -8
- waldiez/exporting/agent/utils/swarm_agent.py +12 -7
- waldiez/exporting/base/utils/comments.py +1 -0
- waldiez/exporting/chats/utils/swarm.py +1 -1
- waldiez/exporting/flow/flow_exporter.py +2 -0
- waldiez/exporting/flow/utils/__init__.py +3 -6
- waldiez/exporting/flow/utils/flow_content.py +38 -0
- waldiez/exporting/flow/utils/importing_utils.py +64 -29
- waldiez/exporting/skills/skills_exporter.py +13 -6
- waldiez/exporting/skills/utils.py +92 -6
- waldiez/models/agents/agent/__init__.py +2 -1
- waldiez/models/agents/agent/agent.py +5 -8
- waldiez/models/agents/agent/agent_type.py +11 -0
- waldiez/models/agents/captain_agent/captain_agent.py +1 -1
- waldiez/models/agents/group_manager/speakers.py +3 -0
- waldiez/models/agents/rag_user/retrieve_config.py +3 -0
- waldiez/models/agents/reasoning/reasoning_agent_reason_config.py +1 -0
- waldiez/models/agents/swarm_agent/after_work.py +13 -11
- waldiez/models/agents/swarm_agent/on_condition.py +3 -2
- waldiez/models/agents/swarm_agent/on_condition_available.py +1 -0
- waldiez/models/agents/swarm_agent/swarm_agent_data.py +3 -3
- waldiez/models/agents/swarm_agent/update_system_message.py +1 -0
- waldiez/models/chat/chat_message.py +1 -0
- waldiez/models/chat/chat_summary.py +1 -0
- waldiez/models/common/__init__.py +2 -0
- waldiez/models/common/method_utils.py +98 -0
- waldiez/models/model/extra_requirements.py +2 -0
- waldiez/models/model/model_data.py +1 -0
- waldiez/models/skill/__init__.py +4 -0
- waldiez/models/skill/extra_requirements.py +39 -0
- waldiez/models/skill/skill.py +157 -13
- waldiez/models/skill/skill_data.py +14 -0
- waldiez/models/skill/skill_type.py +8 -0
- waldiez/models/waldiez.py +36 -6
- waldiez/runner.py +19 -7
- waldiez/running/environment.py +30 -1
- waldiez/running/running.py +0 -6
- waldiez/utils/pysqlite3_checker.py +18 -5
- {waldiez-0.3.12.dist-info → waldiez-0.4.0.dist-info}/METADATA +24 -21
- {waldiez-0.3.12.dist-info → waldiez-0.4.0.dist-info}/RECORD +47 -44
- {waldiez-0.3.12.dist-info → waldiez-0.4.0.dist-info}/WHEEL +0 -0
- {waldiez-0.3.12.dist-info → waldiez-0.4.0.dist-info}/entry_points.txt +0 -0
- {waldiez-0.3.12.dist-info → waldiez-0.4.0.dist-info}/licenses/LICENSE +0 -0
- {waldiez-0.3.12.dist-info → waldiez-0.4.0.dist-info}/licenses/NOTICE.md +0 -0
waldiez/_version.py
CHANGED
waldiez/cli.py
CHANGED
|
@@ -86,10 +86,8 @@ def run(
|
|
|
86
86
|
),
|
|
87
87
|
) -> None:
|
|
88
88
|
"""Run a Waldiez flow."""
|
|
89
|
-
# a swarm chat without a user agent
|
|
90
|
-
# creates a new user (this has a default code execution with docker)
|
|
91
|
-
# temp (until we handle/detect docker setup)
|
|
92
89
|
os.environ["AUTOGEN_USE_DOCKER"] = "0"
|
|
90
|
+
os.environ["NEP50_DISABLE_WARNING"] = "1"
|
|
93
91
|
output_path = _get_output_path(output, force)
|
|
94
92
|
with file.open("r", encoding="utf-8") as _file:
|
|
95
93
|
try:
|
|
@@ -239,7 +239,11 @@ class AgentExporter(BaseExporter, ExporterMixin):
|
|
|
239
239
|
extras = (
|
|
240
240
|
f"{group_chat_arg}{retrieve_arg}{self._reasoning}{self._captain}"
|
|
241
241
|
)
|
|
242
|
-
|
|
242
|
+
ag2_class = self.agent.ag2_class
|
|
243
|
+
if agent.agent_type == "swarm":
|
|
244
|
+
# SwarmAgent is deprecated.
|
|
245
|
+
ag2_class = "ConversableAgent"
|
|
246
|
+
agent_str = f"""{agent_name} = {ag2_class}(
|
|
243
247
|
name="{agent_name}",
|
|
244
248
|
description="{agent.description}"{system_message_arg},
|
|
245
249
|
human_input_mode="{agent.data.human_input_mode}",
|
|
@@ -47,7 +47,7 @@ def get_captain_agent_extras(
|
|
|
47
47
|
return ""
|
|
48
48
|
agent_name = agent_names[agent.id]
|
|
49
49
|
save_path = str(output_dir) if output_dir else "."
|
|
50
|
-
extra_args_content = "\n" +
|
|
50
|
+
extra_args_content = "\n" + " agent_config_save_path=os.getcwd(),"
|
|
51
51
|
if agent.data.agent_lib:
|
|
52
52
|
lib_dict = [
|
|
53
53
|
lib.model_dump(by_alias=False) for lib in agent.data.agent_lib
|
|
@@ -56,7 +56,7 @@ def get_captain_agent_extras(
|
|
|
56
56
|
agent_lib_path = os.path.join(save_path, lib_json_name)
|
|
57
57
|
with open(agent_lib_path, "w", encoding="utf-8", newline="\n") as f:
|
|
58
58
|
json.dump(lib_dict, f, ensure_ascii=False, indent=4)
|
|
59
|
-
extra_args_content += "\n" + f' agent_lib=
|
|
59
|
+
extra_args_content += "\n" + f' agent_lib="{lib_json_name}",'
|
|
60
60
|
if agent.data.tool_lib:
|
|
61
61
|
extra_args_content += "\n" + f' tool_lib="{agent.data.tool_lib}",'
|
|
62
62
|
nested_config = generate_nested_config(
|
|
@@ -97,19 +97,15 @@ def generate_nested_config(
|
|
|
97
97
|
"""
|
|
98
98
|
config_file_or_env_name = f"{agent_name}_llm_config.json"
|
|
99
99
|
llm_config = get_llm_config(agent, all_models)
|
|
100
|
-
to_serialize = {
|
|
101
|
-
"config_list": [llm_config],
|
|
102
|
-
}
|
|
103
100
|
os.makedirs(save_path, exist_ok=True)
|
|
104
101
|
config_file_or_env_path = os.path.join(save_path, config_file_or_env_name)
|
|
105
102
|
with open(
|
|
106
103
|
config_file_or_env_path, "w", encoding="utf-8", newline="\n"
|
|
107
104
|
) as f:
|
|
108
|
-
json.dump(
|
|
109
|
-
config_file_or_env = f'r"{config_file_or_env_path}"'
|
|
105
|
+
json.dump([llm_config], f, ensure_ascii=False, indent=4)
|
|
110
106
|
nested_config = {
|
|
111
107
|
"autobuild_init_config": {
|
|
112
|
-
"config_file_or_env":
|
|
108
|
+
"config_file_or_env": config_file_or_env_name,
|
|
113
109
|
"builder_model": llm_config["model"],
|
|
114
110
|
"agent_model": llm_config["model"],
|
|
115
111
|
},
|
|
@@ -21,11 +21,11 @@ from waldiez.models import (
|
|
|
21
21
|
# functions (List[Callable]):
|
|
22
22
|
# -A list of functions to register with the agent.
|
|
23
23
|
# update_agent_state_before_reply (List[Callable]):
|
|
24
|
-
# - A list of functions, including
|
|
24
|
+
# - A list of functions, including UpdateSystemMessage,
|
|
25
25
|
# called to update the agent before it replies.
|
|
26
26
|
|
|
27
27
|
# Additional methods:
|
|
28
|
-
# register_hand_off(hand_offs: List[AfterWork|OnCondition]):
|
|
28
|
+
# register_hand_off(agent, hand_offs: List[AfterWork|OnCondition]):
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
def get_swarm_extras(
|
|
@@ -173,14 +173,14 @@ def get_update_agent_state_before_reply_arg(
|
|
|
173
173
|
name_suffix=agent_names[agent.id],
|
|
174
174
|
)
|
|
175
175
|
arg_string += (
|
|
176
|
-
"\n" + f"{tab}{tab}
|
|
176
|
+
"\n" + f"{tab}{tab}UpdateSystemMessage({function_name}),"
|
|
177
177
|
)
|
|
178
178
|
before_agent += "\n" + function_content + "\n"
|
|
179
179
|
else:
|
|
180
180
|
escaped_function = string_escape(function.update_function)
|
|
181
181
|
arg_string += (
|
|
182
182
|
"\n"
|
|
183
|
-
+ f'{tab}{tab}
|
|
183
|
+
+ f'{tab}{tab}UpdateSystemMessage("{escaped_function}"),'
|
|
184
184
|
)
|
|
185
185
|
else:
|
|
186
186
|
skill_name = skill_names.get(function, "")
|
|
@@ -233,7 +233,12 @@ def get_agent_handoff_registrations(
|
|
|
233
233
|
if not agent.handoffs:
|
|
234
234
|
return before_agent, after_agent
|
|
235
235
|
tab = " "
|
|
236
|
-
|
|
236
|
+
# change {agent}.register_hand_off([...
|
|
237
|
+
# to register_hand_off({agent}, [...
|
|
238
|
+
# after_agent = f"{agent_name}.register_hand_off(" + "\n" + f"{tab}[" + "\n"
|
|
239
|
+
after_agent = (
|
|
240
|
+
"register_hand_off(\n" + f"{tab}{agent_name}," + "\n" + f"{tab}[" + "\n"
|
|
241
|
+
)
|
|
237
242
|
for hand_off in agent.handoffs:
|
|
238
243
|
if isinstance(hand_off, WaldiezSwarmOnCondition):
|
|
239
244
|
registration, before_handoff = get_agent_on_condition_handoff(
|
|
@@ -385,7 +390,7 @@ def _get_agent_on_condition_handoff_to_agent(
|
|
|
385
390
|
before_agent = ""
|
|
386
391
|
tab = " "
|
|
387
392
|
on_condition = (
|
|
388
|
-
f"{tab}{tab}
|
|
393
|
+
f"{tab}{tab}OnCondition(" + "\n"
|
|
389
394
|
f"{tab}{tab}{tab}target={recipient}," + "\n"
|
|
390
395
|
f'{tab}{tab}{tab}condition="{condition}",' + "\n"
|
|
391
396
|
)
|
|
@@ -431,7 +436,7 @@ def _get_agent_on_condition_handoff_to_nested_chat(
|
|
|
431
436
|
before_agent += f"{chat_queue_var_name} = {chat_queue} " + "\n"
|
|
432
437
|
condition_string = string_escape(condition)
|
|
433
438
|
on_condition = (
|
|
434
|
-
f"{tab}{tab}
|
|
439
|
+
f"{tab}{tab}OnCondition(" + "\n"
|
|
435
440
|
f"{tab}{tab}{tab}target=" + "{\n"
|
|
436
441
|
f'{tab}{tab}{tab}{tab}"chat_queue": {chat_queue_var_name},' + "\n"
|
|
437
442
|
f'{tab}{tab}{tab}{tab}"config": None,' + "\n"
|
|
@@ -196,7 +196,7 @@ def get_swarm_after_work_string(
|
|
|
196
196
|
The after work string and the additional methods string.
|
|
197
197
|
"""
|
|
198
198
|
if not chat.after_work:
|
|
199
|
-
return "
|
|
199
|
+
return "AfterWork(AfterWorkOption.TERMINATE)", ""
|
|
200
200
|
additional_methods = ""
|
|
201
201
|
after_work_string, function_content = chat.after_work.get_recipient(
|
|
202
202
|
agent_names=agent_names,
|
|
@@ -52,6 +52,7 @@ from .utils import (
|
|
|
52
52
|
get_after_run_content,
|
|
53
53
|
get_def_main,
|
|
54
54
|
get_ipynb_content_start,
|
|
55
|
+
get_np_no_nep50_handle,
|
|
55
56
|
get_py_content_start,
|
|
56
57
|
get_sqlite_out,
|
|
57
58
|
get_start_logging,
|
|
@@ -210,6 +211,7 @@ class FlowExporter(BaseExporter, ExporterMixin):
|
|
|
210
211
|
)
|
|
211
212
|
content += self.get_comment("imports", self.for_notebook) + "\n"
|
|
212
213
|
content += imports[0] + "\n"
|
|
214
|
+
content += get_np_no_nep50_handle() + "\n"
|
|
213
215
|
content += self.get_comment("logging", self.for_notebook) + "\n"
|
|
214
216
|
content += get_start_logging(tabs=0) + "\n"
|
|
215
217
|
content += "start_logging()\n\n"
|
|
@@ -14,14 +14,11 @@ from .def_main import get_def_main
|
|
|
14
14
|
from .flow_content import (
|
|
15
15
|
get_after_run_content,
|
|
16
16
|
get_ipynb_content_start,
|
|
17
|
+
get_np_no_nep50_handle,
|
|
17
18
|
get_py_content_start,
|
|
18
19
|
)
|
|
19
20
|
from .flow_names import ensure_unique_names
|
|
20
|
-
from .importing_utils import
|
|
21
|
-
gather_imports,
|
|
22
|
-
get_standard_imports,
|
|
23
|
-
get_the_imports_string,
|
|
24
|
-
)
|
|
21
|
+
from .importing_utils import gather_imports, get_the_imports_string
|
|
25
22
|
from .logging_utils import (
|
|
26
23
|
get_sqlite_out,
|
|
27
24
|
get_sqlite_out_call,
|
|
@@ -41,12 +38,12 @@ __all__ = [
|
|
|
41
38
|
"gather_imports",
|
|
42
39
|
"get_after_run_content",
|
|
43
40
|
"get_def_main",
|
|
41
|
+
"get_np_no_nep50_handle",
|
|
44
42
|
"get_py_content_start",
|
|
45
43
|
"get_ipynb_content_start",
|
|
46
44
|
"get_start_logging",
|
|
47
45
|
"get_stop_logging",
|
|
48
46
|
"get_sqlite_out",
|
|
49
47
|
"get_sqlite_out_call",
|
|
50
|
-
"get_standard_imports",
|
|
51
48
|
"get_the_imports_string",
|
|
52
49
|
]
|
|
@@ -71,12 +71,14 @@ PYLINT_RULES = [
|
|
|
71
71
|
"unknown-option-value",
|
|
72
72
|
"unused-argument",
|
|
73
73
|
"unused-import",
|
|
74
|
+
"unused-variable",
|
|
74
75
|
"invalid-name",
|
|
75
76
|
"import-error",
|
|
76
77
|
"inconsistent-quotes",
|
|
77
78
|
"missing-function-docstring",
|
|
78
79
|
"missing-param-doc",
|
|
79
80
|
"missing-return-doc",
|
|
81
|
+
"ungrouped-imports",
|
|
80
82
|
]
|
|
81
83
|
|
|
82
84
|
|
|
@@ -159,3 +161,39 @@ def get_after_run_content(
|
|
|
159
161
|
{space}{tab}pass
|
|
160
162
|
"""
|
|
161
163
|
return content
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def get_np_no_nep50_handle() -> str:
|
|
167
|
+
"""Handle catching the "module numpy has no attribute _no_pep50_warning" error.
|
|
168
|
+
|
|
169
|
+
Returns
|
|
170
|
+
-------
|
|
171
|
+
str
|
|
172
|
+
The content to handle the error.
|
|
173
|
+
"""
|
|
174
|
+
# https://github.com/numpy/numpy/blob/v2.2.2/\
|
|
175
|
+
# doc/source/release/2.2.0-notes.rst#nep-50-promotion-state-option-removed
|
|
176
|
+
content = '''
|
|
177
|
+
# try to make sure we don't get:
|
|
178
|
+
# module 'numpy' has no attribute '_no_nep50_warning'"
|
|
179
|
+
os.environ["NEP50_DEPRECATION_WARNING"] = "0"
|
|
180
|
+
os.environ["NEP50_DISABLE_WARNING"] = "1"
|
|
181
|
+
os.environ["NPY_PROMOTION_STATE"] = "weak"
|
|
182
|
+
if not hasattr(np, "_no_pep50_warning"):
|
|
183
|
+
|
|
184
|
+
import contextlib
|
|
185
|
+
from typing import Generator
|
|
186
|
+
|
|
187
|
+
@contextlib.contextmanager
|
|
188
|
+
def _np_no_nep50_warning() -> Generator[None, None, None]:
|
|
189
|
+
"""Dummy function to avoid the warning.
|
|
190
|
+
|
|
191
|
+
Yields
|
|
192
|
+
------
|
|
193
|
+
None
|
|
194
|
+
Nothing.
|
|
195
|
+
"""
|
|
196
|
+
yield
|
|
197
|
+
setattr(np, "_no_pep50_warning", _np_no_nep50_warning) # noqa
|
|
198
|
+
'''
|
|
199
|
+
return content
|
|
@@ -36,21 +36,6 @@ COMMON_AUTOGEN_IMPORTS = [
|
|
|
36
36
|
]
|
|
37
37
|
|
|
38
38
|
|
|
39
|
-
def get_standard_imports() -> str:
|
|
40
|
-
"""Get the standard imports.
|
|
41
|
-
|
|
42
|
-
Returns
|
|
43
|
-
-------
|
|
44
|
-
str
|
|
45
|
-
The standard imports.
|
|
46
|
-
"""
|
|
47
|
-
builtin_imports = BUILTIN_IMPORTS.copy()
|
|
48
|
-
imports_string = "\n".join(builtin_imports) + "\n"
|
|
49
|
-
typing_imports = "from typing import " + ", ".join(TYPING_IMPORTS)
|
|
50
|
-
imports_string += typing_imports
|
|
51
|
-
return imports_string
|
|
52
|
-
|
|
53
|
-
|
|
54
39
|
def sort_imports(
|
|
55
40
|
all_imports: List[Tuple[str, ImportPosition]],
|
|
56
41
|
) -> Tuple[List[str], List[str], List[str], List[str], bool]:
|
|
@@ -66,10 +51,10 @@ def sort_imports(
|
|
|
66
51
|
Tuple[List[str], List[str], List[str], List[str], bool]
|
|
67
52
|
The sorted imports and a flag if we got `import autogen`.
|
|
68
53
|
"""
|
|
69
|
-
builtin_imports = []
|
|
70
|
-
third_party_imports = []
|
|
71
|
-
local_imports = []
|
|
72
|
-
autogen_imports = COMMON_AUTOGEN_IMPORTS.copy()
|
|
54
|
+
builtin_imports: List[str] = []
|
|
55
|
+
third_party_imports: List[str] = []
|
|
56
|
+
local_imports: List[str] = []
|
|
57
|
+
autogen_imports: List[str] = COMMON_AUTOGEN_IMPORTS.copy()
|
|
73
58
|
got_import_autogen = False
|
|
74
59
|
for import_string, position in all_imports:
|
|
75
60
|
if "import autogen" in import_string:
|
|
@@ -85,11 +70,22 @@ def sort_imports(
|
|
|
85
70
|
elif position == ImportPosition.LOCAL:
|
|
86
71
|
local_imports.append(import_string)
|
|
87
72
|
autogen_imports = list(set(autogen_imports))
|
|
73
|
+
third_party_imports = ensure_np_import(third_party_imports)
|
|
74
|
+
sorted_builtins = sorted(
|
|
75
|
+
[imp for imp in builtin_imports if imp.startswith("import ")]
|
|
76
|
+
) + sorted([imp for imp in builtin_imports if imp.startswith("from ")])
|
|
77
|
+
sorted_third_party = sorted(
|
|
78
|
+
[imp for imp in third_party_imports if imp.startswith("import ")]
|
|
79
|
+
) + sorted([imp for imp in third_party_imports if imp.startswith("from ")])
|
|
80
|
+
sorted_locals = sorted(
|
|
81
|
+
[imp for imp in local_imports if imp.startswith("import ")]
|
|
82
|
+
) + sorted([imp for imp in local_imports if imp.startswith("from ")])
|
|
83
|
+
|
|
88
84
|
return (
|
|
89
|
-
|
|
85
|
+
sorted_builtins,
|
|
90
86
|
sorted(autogen_imports),
|
|
91
|
-
|
|
92
|
-
|
|
87
|
+
sorted_third_party,
|
|
88
|
+
sorted_locals,
|
|
93
89
|
got_import_autogen,
|
|
94
90
|
)
|
|
95
91
|
|
|
@@ -150,6 +146,27 @@ def get_the_imports_string(
|
|
|
150
146
|
return final_string.replace("\n\n\n", "\n\n") # avoid too many newlines
|
|
151
147
|
|
|
152
148
|
|
|
149
|
+
def ensure_np_import(third_party_imports: List[str]) -> List[str]:
|
|
150
|
+
"""Ensure numpy is imported.
|
|
151
|
+
|
|
152
|
+
Parameters
|
|
153
|
+
----------
|
|
154
|
+
third_party_imports : List[str]
|
|
155
|
+
The third party imports.
|
|
156
|
+
|
|
157
|
+
Returns
|
|
158
|
+
-------
|
|
159
|
+
List[str]
|
|
160
|
+
The third party imports with numpy.
|
|
161
|
+
"""
|
|
162
|
+
if (
|
|
163
|
+
not third_party_imports
|
|
164
|
+
or "import numpy as np" not in third_party_imports
|
|
165
|
+
):
|
|
166
|
+
third_party_imports.append("import numpy as np")
|
|
167
|
+
return third_party_imports
|
|
168
|
+
|
|
169
|
+
|
|
153
170
|
def gather_imports(
|
|
154
171
|
model_imports: Optional[List[Tuple[str, ImportPosition]]],
|
|
155
172
|
skill_imports: Optional[List[Tuple[str, ImportPosition]]],
|
|
@@ -174,13 +191,14 @@ def gather_imports(
|
|
|
174
191
|
Tuple[str, ImportPosition]
|
|
175
192
|
The gathered imports.
|
|
176
193
|
"""
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
(
|
|
180
|
-
|
|
181
|
-
|
|
194
|
+
all_imports: List[Tuple[str, ImportPosition]] = []
|
|
195
|
+
for import_statement in BUILTIN_IMPORTS:
|
|
196
|
+
all_imports.append(
|
|
197
|
+
(
|
|
198
|
+
import_statement,
|
|
199
|
+
ImportPosition.BUILTINS,
|
|
200
|
+
)
|
|
182
201
|
)
|
|
183
|
-
]
|
|
184
202
|
if model_imports:
|
|
185
203
|
all_imports.extend(model_imports)
|
|
186
204
|
if skill_imports:
|
|
@@ -189,4 +207,21 @@ def gather_imports(
|
|
|
189
207
|
all_imports.extend(chat_imports)
|
|
190
208
|
if agent_imports:
|
|
191
209
|
all_imports.extend(agent_imports)
|
|
192
|
-
|
|
210
|
+
# let's try to avoid this:
|
|
211
|
+
# from typing import Annotated
|
|
212
|
+
# from typing import Annotated, Any, Callable, Dict, ...Union
|
|
213
|
+
all_typing_imports = TYPING_IMPORTS.copy()
|
|
214
|
+
final_imports: List[Tuple[str, ImportPosition]] = []
|
|
215
|
+
for import_statement, import_position in all_imports:
|
|
216
|
+
if import_statement.startswith("from typing"):
|
|
217
|
+
to_import = import_statement.split("import")[1].strip()
|
|
218
|
+
if to_import:
|
|
219
|
+
all_typing_imports.append(to_import)
|
|
220
|
+
else:
|
|
221
|
+
final_imports.append((import_statement, import_position))
|
|
222
|
+
unique_typing_imports = list(set(all_typing_imports))
|
|
223
|
+
one_typing_import = "from typing import " + ", ".join(
|
|
224
|
+
sorted(unique_typing_imports)
|
|
225
|
+
)
|
|
226
|
+
final_imports.insert(1, (one_typing_import, ImportPosition.BUILTINS))
|
|
227
|
+
return list(set(final_imports))
|
|
@@ -89,12 +89,18 @@ class SkillsExporter(BaseExporter, ExporterMixin):
|
|
|
89
89
|
Tuple[str, int]
|
|
90
90
|
The exported imports and the position of the imports.
|
|
91
91
|
"""
|
|
92
|
-
if not self.skill_imports:
|
|
93
|
-
return []
|
|
94
92
|
imports: List[Tuple[str, ImportPosition]] = []
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
93
|
+
if not self.skill_imports:
|
|
94
|
+
return imports
|
|
95
|
+
# standard imports
|
|
96
|
+
for import_statement in self.skill_imports[0]:
|
|
97
|
+
imports.append((import_statement, ImportPosition.BUILTINS))
|
|
98
|
+
# third party imports
|
|
99
|
+
for import_statement in self.skill_imports[1]:
|
|
100
|
+
imports.append((import_statement, ImportPosition.THIRD_PARTY))
|
|
101
|
+
# secrets/local imports
|
|
102
|
+
for import_statement in self.skill_imports[2]:
|
|
103
|
+
imports.append((import_statement, ImportPosition.LOCAL))
|
|
98
104
|
return imports
|
|
99
105
|
|
|
100
106
|
def get_before_export(
|
|
@@ -156,11 +162,12 @@ class SkillsExporter(BaseExporter, ExporterMixin):
|
|
|
156
162
|
the before export strings, the after export strings,
|
|
157
163
|
and the environment variables.
|
|
158
164
|
"""
|
|
165
|
+
content = self.generate()
|
|
159
166
|
imports = self.get_imports()
|
|
160
167
|
after_export = self.get_after_export()
|
|
161
168
|
environment_variables = self.get_environment_variables()
|
|
162
169
|
result: ExporterReturnType = {
|
|
163
|
-
"content":
|
|
170
|
+
"content": content,
|
|
164
171
|
"imports": imports,
|
|
165
172
|
"before_export": None,
|
|
166
173
|
"after_export": after_export,
|
|
@@ -94,6 +94,7 @@ def _write_skill_secrets(
|
|
|
94
94
|
return
|
|
95
95
|
if not isinstance(output_dir, Path):
|
|
96
96
|
output_dir = Path(output_dir)
|
|
97
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
97
98
|
secrets_file = output_dir / f"{flow_name}_{skill_name}_secrets.py"
|
|
98
99
|
first_line = f'"""Secrets for the skill: {skill_name}."""' + "\n"
|
|
99
100
|
with secrets_file.open("w", encoding="utf-8", newline="\n") as f:
|
|
@@ -108,7 +109,7 @@ def export_skills(
|
|
|
108
109
|
skills: List[WaldiezSkill],
|
|
109
110
|
skill_names: Dict[str, str],
|
|
110
111
|
output_dir: Optional[Union[str, Path]] = None,
|
|
111
|
-
) -> Tuple[List[str], List[Tuple[str, str]], str]:
|
|
112
|
+
) -> Tuple[Tuple[List[str], List[str], List[str]], List[Tuple[str, str]], str]:
|
|
112
113
|
"""Get the skills' contents and secrets.
|
|
113
114
|
|
|
114
115
|
If `output_dir` is provided, the contents are saved to that directory.
|
|
@@ -126,19 +127,26 @@ def export_skills(
|
|
|
126
127
|
|
|
127
128
|
Returns
|
|
128
129
|
-------
|
|
129
|
-
Tuple[
|
|
130
|
+
Tuple[Tuple[List[str], List[str], List[str]], List[Tuple[str, str]], str]
|
|
130
131
|
- The skill imports to use in the main file.
|
|
131
132
|
- The skill secrets to set as environment variables.
|
|
132
133
|
- The skills contents.
|
|
133
134
|
"""
|
|
134
|
-
skill_imports: List[str] = []
|
|
135
|
+
skill_imports: Tuple[List[str], List[str], List[str]] = ([], [], [])
|
|
135
136
|
skill_secrets: List[Tuple[str, str]] = []
|
|
136
137
|
skill_contents: str = ""
|
|
137
138
|
# if the skill.is_shared,
|
|
138
139
|
# its contents must be first (before the other skills)
|
|
139
140
|
shared_skill_contents = ""
|
|
140
141
|
for skill in skills:
|
|
141
|
-
|
|
142
|
+
standard_skill_imports, third_party_skill_imports = skill.get_imports()
|
|
143
|
+
if standard_skill_imports:
|
|
144
|
+
skill_imports[0].extend(standard_skill_imports)
|
|
145
|
+
if third_party_skill_imports:
|
|
146
|
+
skill_imports[1].extend(third_party_skill_imports)
|
|
147
|
+
secrets_import = get_skill_secrets_import(flow_name, skill)
|
|
148
|
+
if secrets_import:
|
|
149
|
+
skill_imports[2].append(secrets_import)
|
|
142
150
|
for key, value in skill.secrets.items():
|
|
143
151
|
skill_secrets.append((key, value))
|
|
144
152
|
_write_skill_secrets(
|
|
@@ -153,8 +161,14 @@ def export_skills(
|
|
|
153
161
|
if skill.is_shared:
|
|
154
162
|
shared_skill_contents += skill_content + "\n\n"
|
|
155
163
|
else:
|
|
164
|
+
if skill.is_interop:
|
|
165
|
+
skill_content += _add_interop_extras(
|
|
166
|
+
skill=skill, skill_names=skill_names
|
|
167
|
+
)
|
|
156
168
|
skill_contents += skill_content + "\n\n"
|
|
157
169
|
skill_contents = shared_skill_contents + skill_contents
|
|
170
|
+
# remove dupes from imports if any and sort them
|
|
171
|
+
skill_imports = _sort_imports(skill_imports)
|
|
158
172
|
return (
|
|
159
173
|
skill_imports,
|
|
160
174
|
skill_secrets,
|
|
@@ -162,8 +176,77 @@ def export_skills(
|
|
|
162
176
|
)
|
|
163
177
|
|
|
164
178
|
|
|
165
|
-
def
|
|
166
|
-
|
|
179
|
+
def _add_interop_extras(
|
|
180
|
+
skill: WaldiezSkill,
|
|
181
|
+
skill_names: Dict[str, str],
|
|
182
|
+
) -> str:
|
|
183
|
+
"""Add the interop conversion.
|
|
184
|
+
|
|
185
|
+
Parameters
|
|
186
|
+
----------
|
|
187
|
+
skill : WaldiezSkill
|
|
188
|
+
The skill
|
|
189
|
+
skill_names : Dict[str, str]
|
|
190
|
+
The skill names.
|
|
191
|
+
|
|
192
|
+
Returns
|
|
193
|
+
-------
|
|
194
|
+
str
|
|
195
|
+
The extra content to convert the tool.
|
|
196
|
+
"""
|
|
197
|
+
skill_name = skill_names[skill.id]
|
|
198
|
+
interop_instance = f"ag2_{skill_name}_interop = Interoperability()" + "\n"
|
|
199
|
+
extra_content = (
|
|
200
|
+
f"ag2_{skill_name} = "
|
|
201
|
+
f"ag2_{skill_name}_interop.convert_tool("
|
|
202
|
+
f"tool={skill_name}, "
|
|
203
|
+
f'type="{skill.skill_type}")'
|
|
204
|
+
)
|
|
205
|
+
return "\n" + interop_instance + extra_content
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def _sort_imports(
|
|
209
|
+
skill_imports: Tuple[List[str], List[str], List[str]],
|
|
210
|
+
) -> Tuple[List[str], List[str], List[str]]:
|
|
211
|
+
"""Sort the imports.
|
|
212
|
+
|
|
213
|
+
Parameters
|
|
214
|
+
----------
|
|
215
|
+
skill_imports : Tuple[List[str], List[str], List[str]]
|
|
216
|
+
The skill imports.
|
|
217
|
+
|
|
218
|
+
Returns
|
|
219
|
+
-------
|
|
220
|
+
Tuple[List[str], List[str], List[str]]
|
|
221
|
+
The sorted skill imports.
|
|
222
|
+
"""
|
|
223
|
+
|
|
224
|
+
# "from x import y" and "import z"
|
|
225
|
+
# the "import a" should be first (and sorted)
|
|
226
|
+
# then the "from b import c" (and sorted)
|
|
227
|
+
standard_lib_imports = skill_imports[0]
|
|
228
|
+
third_party_imports = skill_imports[1]
|
|
229
|
+
secrets_imports = skill_imports[2]
|
|
230
|
+
|
|
231
|
+
sorted_standard_lib_imports = sorted(
|
|
232
|
+
[imp for imp in standard_lib_imports if imp.startswith("import ")]
|
|
233
|
+
) + sorted([imp for imp in standard_lib_imports if imp.startswith("from ")])
|
|
234
|
+
|
|
235
|
+
sorted_third_party_imports = sorted(
|
|
236
|
+
[imp for imp in third_party_imports if imp.startswith("import ")]
|
|
237
|
+
) + sorted([imp for imp in third_party_imports if imp.startswith("from ")])
|
|
238
|
+
|
|
239
|
+
sorted_secrets_imports = sorted(secrets_imports)
|
|
240
|
+
|
|
241
|
+
return (
|
|
242
|
+
sorted_standard_lib_imports,
|
|
243
|
+
sorted_third_party_imports,
|
|
244
|
+
sorted_secrets_imports,
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def get_skill_secrets_import(flow_name: str, skill: WaldiezSkill) -> str:
|
|
249
|
+
"""Get the skill secrets import string.
|
|
167
250
|
|
|
168
251
|
Parameters
|
|
169
252
|
----------
|
|
@@ -263,6 +346,9 @@ def get_agent_skill_registrations(
|
|
|
263
346
|
skill for skill in all_skills if skill.id == linked_skill.id
|
|
264
347
|
)
|
|
265
348
|
skill_name = skill_names[linked_skill.id]
|
|
349
|
+
if waldiez_skill.is_interop:
|
|
350
|
+
# the name of the the converted to ag2 tool
|
|
351
|
+
skill_name = f"ag2_{skill_name}"
|
|
266
352
|
skill_description = (
|
|
267
353
|
waldiez_skill.description or f"Description of {skill_name}"
|
|
268
354
|
)
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
# Copyright (c) 2024 - 2025 Waldiez and contributors.
|
|
3
3
|
"""Base agent class to be inherited by all other agents."""
|
|
4
4
|
|
|
5
|
-
from .agent import WaldiezAgent
|
|
5
|
+
from .agent import WaldiezAgent
|
|
6
6
|
from .agent_data import WaldiezAgentData
|
|
7
|
+
from .agent_type import WaldiezAgentType
|
|
7
8
|
from .code_execution import WaldiezAgentCodeExecutionConfig
|
|
8
9
|
from .linked_skill import WaldiezAgentLinkedSkill
|
|
9
10
|
from .nested_chat import WaldiezAgentNestedChat, WaldiezAgentNestedChatMessage
|
|
@@ -9,12 +9,9 @@ from typing_extensions import Annotated, Literal
|
|
|
9
9
|
|
|
10
10
|
from ...common import WaldiezBase, now
|
|
11
11
|
from .agent_data import WaldiezAgentData
|
|
12
|
+
from .agent_type import WaldiezAgentType
|
|
12
13
|
from .code_execution import WaldiezAgentCodeExecutionConfig
|
|
13
14
|
|
|
14
|
-
WaldiezAgentType = Literal[
|
|
15
|
-
"user", "assistant", "manager", "rag_user", "swarm", "reasoning", "captain"
|
|
16
|
-
]
|
|
17
|
-
|
|
18
15
|
|
|
19
16
|
class WaldiezAgent(WaldiezBase):
|
|
20
17
|
"""Waldiez Agent.
|
|
@@ -171,11 +168,11 @@ class WaldiezAgent(WaldiezBase):
|
|
|
171
168
|
elif agent_class == "SwarmAgent":
|
|
172
169
|
imports.add(
|
|
173
170
|
"from autogen import "
|
|
174
|
-
"
|
|
175
|
-
"
|
|
176
|
-
"
|
|
171
|
+
"register_hand_off, "
|
|
172
|
+
"AfterWork, "
|
|
173
|
+
"OnCondition, "
|
|
174
|
+
"UpdateSystemMessage, "
|
|
177
175
|
"AfterWorkOption, "
|
|
178
|
-
"SwarmAgent, "
|
|
179
176
|
"SwarmResult"
|
|
180
177
|
)
|
|
181
178
|
elif agent_class == "ReasoningAgent":
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0.
|
|
2
|
+
# Copyright (c) 2024 - 2025 Waldiez and contributors.
|
|
3
|
+
"""Waldiez Agent types."""
|
|
4
|
+
|
|
5
|
+
from typing_extensions import Literal
|
|
6
|
+
|
|
7
|
+
# pylint: disable=line-too-long
|
|
8
|
+
# fmt: off
|
|
9
|
+
WaldiezAgentType = Literal["user", "assistant", "manager", "rag_user", "swarm", "reasoning", "captain"] # noqa: E501
|
|
10
|
+
"""Possible types of a Waldiez Agent: user, assistant, manager, rag_user, swarm, reasoning, captain.""" # noqa: E501
|
|
11
|
+
# fmt: on
|
|
@@ -16,8 +16,11 @@ WaldiezGroupManagerSpeakersSelectionMethod = Literal[
|
|
|
16
16
|
"round_robin",
|
|
17
17
|
"custom",
|
|
18
18
|
]
|
|
19
|
+
"""Possible methods for the speaker selection."""
|
|
19
20
|
WaldiezGroupManagerSpeakersSelectionMode = Literal["repeat", "transition"]
|
|
21
|
+
"""Possible selection modes: repeat, transition."""
|
|
20
22
|
WaldiezGroupManagerSpeakersTransitionsType = Literal["allowed", "disallowed"]
|
|
23
|
+
"""Possible transitions types: allowed, disallowed."""
|
|
21
24
|
|
|
22
25
|
CUSTOM_SPEAKER_SELECTION = "custom_speaker_selection"
|
|
23
26
|
CUSTOM_SPEAKER_SELECTION_ARGS = ["last_speaker", "groupchat"]
|