waldiez 0.5.9__py3-none-any.whl → 0.5.10__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 +112 -24
- waldiez/exporting/agent/exporter.py +3 -0
- waldiez/exporting/agent/extras/captain_agent_extras.py +44 -7
- waldiez/exporting/agent/extras/handoffs/condition.py +3 -1
- waldiez/exporting/chats/utils/common.py +25 -23
- waldiez/exporting/core/__init__.py +0 -2
- waldiez/exporting/core/context.py +13 -13
- waldiez/exporting/core/protocols.py +0 -141
- waldiez/exporting/core/result.py +5 -5
- waldiez/exporting/flow/merger.py +2 -2
- waldiez/exporting/flow/orchestrator.py +1 -0
- waldiez/exporting/flow/utils/common.py +2 -2
- waldiez/exporting/flow/utils/importing.py +1 -0
- waldiez/exporting/flow/utils/logging.py +6 -7
- waldiez/exporting/tools/exporter.py +5 -0
- waldiez/exporting/tools/factory.py +4 -0
- waldiez/exporting/tools/processor.py +5 -1
- waldiez/io/_ws.py +13 -5
- waldiez/io/models/content/image.py +1 -0
- waldiez/io/models/user_input.py +4 -4
- waldiez/io/models/user_response.py +1 -0
- waldiez/io/mqtt.py +1 -1
- waldiez/io/structured.py +17 -17
- waldiez/io/utils.py +1 -1
- waldiez/io/ws.py +9 -11
- waldiez/logger.py +180 -63
- waldiez/models/agents/agent/update_system_message.py +0 -2
- waldiez/models/agents/doc_agent/doc_agent.py +8 -1
- waldiez/models/common/dict_utils.py +169 -40
- waldiez/models/flow/flow.py +6 -6
- waldiez/models/flow/info.py +5 -1
- waldiez/models/model/_llm.py +28 -14
- waldiez/models/model/model.py +4 -1
- waldiez/models/model/model_data.py +18 -5
- waldiez/models/tool/predefined/_config.py +5 -1
- waldiez/models/tool/predefined/_duckduckgo.py +4 -0
- waldiez/models/tool/predefined/_email.py +474 -0
- waldiez/models/tool/predefined/_google.py +4 -1
- waldiez/models/tool/predefined/_perplexity.py +3 -0
- waldiez/models/tool/predefined/_searxng.py +3 -0
- waldiez/models/tool/predefined/_tavily.py +4 -1
- waldiez/models/tool/predefined/_wikipedia.py +4 -1
- waldiez/models/tool/predefined/_youtube.py +4 -1
- waldiez/models/tool/predefined/protocol.py +3 -0
- waldiez/models/tool/tool.py +22 -4
- waldiez/models/waldiez.py +12 -0
- waldiez/runner.py +37 -54
- waldiez/running/__init__.py +6 -0
- waldiez/running/base_runner.py +310 -353
- waldiez/running/environment.py +1 -0
- waldiez/running/exceptions.py +9 -0
- waldiez/running/post_run.py +4 -4
- waldiez/running/pre_run.py +51 -40
- waldiez/running/protocol.py +21 -101
- waldiez/running/run_results.py +1 -1
- waldiez/running/standard_runner.py +84 -277
- waldiez/running/step_by_step/__init__.py +46 -0
- waldiez/running/step_by_step/breakpoints_mixin.py +188 -0
- waldiez/running/step_by_step/step_by_step_models.py +224 -0
- waldiez/running/step_by_step/step_by_step_runner.py +745 -0
- waldiez/running/subprocess_runner/__base__.py +282 -0
- waldiez/running/subprocess_runner/__init__.py +16 -0
- waldiez/running/subprocess_runner/_async_runner.py +362 -0
- waldiez/running/subprocess_runner/_sync_runner.py +455 -0
- waldiez/running/subprocess_runner/runner.py +561 -0
- waldiez/running/timeline_processor.py +1 -1
- waldiez/running/utils.py +376 -1
- waldiez/utils/version.py +2 -6
- waldiez/ws/__init__.py +70 -0
- waldiez/ws/__main__.py +15 -0
- waldiez/ws/_file_handler.py +201 -0
- waldiez/ws/cli.py +211 -0
- waldiez/ws/client_manager.py +835 -0
- waldiez/ws/errors.py +416 -0
- waldiez/ws/models.py +971 -0
- waldiez/ws/reloader.py +342 -0
- waldiez/ws/server.py +469 -0
- waldiez/ws/session_manager.py +393 -0
- waldiez/ws/session_stats.py +83 -0
- waldiez/ws/utils.py +385 -0
- {waldiez-0.5.9.dist-info → waldiez-0.5.10.dist-info}/METADATA +74 -74
- {waldiez-0.5.9.dist-info → waldiez-0.5.10.dist-info}/RECORD +87 -65
- waldiez/running/patch_io_stream.py +0 -210
- {waldiez-0.5.9.dist-info → waldiez-0.5.10.dist-info}/WHEEL +0 -0
- {waldiez-0.5.9.dist-info → waldiez-0.5.10.dist-info}/entry_points.txt +0 -0
- {waldiez-0.5.9.dist-info → waldiez-0.5.10.dist-info}/licenses/LICENSE +0 -0
- {waldiez-0.5.9.dist-info → waldiez-0.5.10.dist-info}/licenses/NOTICE.md +0 -0
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0.
|
|
2
|
+
# Copyright (c) 2024 - 2025 Waldiez and contributors.
|
|
3
|
+
# pylint: disable=line-too-long
|
|
4
|
+
# flake8: noqa: E501
|
|
5
|
+
"""Predefined send email tool."""
|
|
6
|
+
|
|
7
|
+
from copy import deepcopy
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from ._config import PredefinedToolConfig
|
|
11
|
+
from .protocol import PredefinedTool
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class SendEmailToolImpl(PredefinedTool):
|
|
15
|
+
"""Predefined tool for sending emails."""
|
|
16
|
+
|
|
17
|
+
required_secrets: list[str] = [
|
|
18
|
+
"SMTP_HOST",
|
|
19
|
+
"SMTP_PORT",
|
|
20
|
+
"SMTP_USERNAME",
|
|
21
|
+
"SMTP_PASSWORD",
|
|
22
|
+
]
|
|
23
|
+
required_kwargs: dict[str, type] = {}
|
|
24
|
+
|
|
25
|
+
DEFAULT_KWARGS: dict[str, Any] = {
|
|
26
|
+
"is_async": False, # controls whether the exported tool function is async or sync
|
|
27
|
+
"subject": "",
|
|
28
|
+
"body_text": "",
|
|
29
|
+
"body_html": "",
|
|
30
|
+
"to": [],
|
|
31
|
+
"attachments": [],
|
|
32
|
+
"cc": [],
|
|
33
|
+
"bcc": [],
|
|
34
|
+
"reply_to": [],
|
|
35
|
+
"sender": "",
|
|
36
|
+
"use_ssl": False,
|
|
37
|
+
"use_starttls": True,
|
|
38
|
+
"timeout": 30.0,
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
kwarg_types: dict[str, type] = {
|
|
42
|
+
"is_async": bool,
|
|
43
|
+
"subject": str,
|
|
44
|
+
"body_text": str,
|
|
45
|
+
"body_html": str,
|
|
46
|
+
"to": list,
|
|
47
|
+
"attachments": list,
|
|
48
|
+
"cc": list,
|
|
49
|
+
"bcc": list,
|
|
50
|
+
"reply_to": list,
|
|
51
|
+
"sender": str,
|
|
52
|
+
"use_ssl": bool,
|
|
53
|
+
"use_starttls": bool,
|
|
54
|
+
"timeout": float,
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
def __init__(self) -> None:
|
|
58
|
+
self._kwargs: dict[str, Any] = deepcopy(self.DEFAULT_KWARGS)
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
def name(self) -> str:
|
|
62
|
+
return "send_email"
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def description(self) -> str:
|
|
66
|
+
return "A tool for sending emails with support for attachments, HTML/text content, and various SMTP configurations."
|
|
67
|
+
|
|
68
|
+
@property
|
|
69
|
+
def kwargs(self) -> dict[str, Any]:
|
|
70
|
+
"""
|
|
71
|
+
Get the keyword arguments for the tool.
|
|
72
|
+
|
|
73
|
+
Returns
|
|
74
|
+
-------
|
|
75
|
+
dict[str, Any]
|
|
76
|
+
The keyword arguments for the tool.
|
|
77
|
+
"""
|
|
78
|
+
return dict(self._kwargs)
|
|
79
|
+
|
|
80
|
+
@property
|
|
81
|
+
def tags(self) -> list[str]:
|
|
82
|
+
return ["email", "communication", "smtp"]
|
|
83
|
+
|
|
84
|
+
@property
|
|
85
|
+
def requirements(self) -> list[str]:
|
|
86
|
+
return []
|
|
87
|
+
|
|
88
|
+
@property
|
|
89
|
+
def tool_imports(self) -> list[str]:
|
|
90
|
+
return [
|
|
91
|
+
"import asyncio",
|
|
92
|
+
"import smtplib",
|
|
93
|
+
"import ssl",
|
|
94
|
+
"from email.message import EmailMessage",
|
|
95
|
+
"from email.utils import formatdate, make_msgid",
|
|
96
|
+
"from pathlib import Path",
|
|
97
|
+
]
|
|
98
|
+
|
|
99
|
+
def validate_secrets(self, secrets: dict[str, str]) -> list[str]:
|
|
100
|
+
"""
|
|
101
|
+
Validate the provided secrets against the tool's requirements.
|
|
102
|
+
|
|
103
|
+
Parameters
|
|
104
|
+
----------
|
|
105
|
+
secrets: dict[str, str]
|
|
106
|
+
The secrets to validate.
|
|
107
|
+
|
|
108
|
+
Returns
|
|
109
|
+
-------
|
|
110
|
+
list[str]
|
|
111
|
+
A list of validation error messages, if any.
|
|
112
|
+
"""
|
|
113
|
+
problems: list[str] = []
|
|
114
|
+
for key in self.required_secrets:
|
|
115
|
+
val = secrets.get(key)
|
|
116
|
+
if val is None or str(val).strip() == "":
|
|
117
|
+
problems.append(key)
|
|
118
|
+
# pylint: disable=broad-exception-caught
|
|
119
|
+
try:
|
|
120
|
+
port = int(secrets.get("SMTP_PORT", ""))
|
|
121
|
+
if not 1 <= port <= 65535:
|
|
122
|
+
problems.append("SMTP_PORT (invalid port range)")
|
|
123
|
+
except Exception:
|
|
124
|
+
problems.append("SMTP_PORT (not a valid integer)")
|
|
125
|
+
return problems
|
|
126
|
+
|
|
127
|
+
def validate_kwargs(self, kwargs: dict[str, Any]) -> list[str]:
|
|
128
|
+
"""
|
|
129
|
+
Validate the provided keyword arguments against the tool's requirements.
|
|
130
|
+
|
|
131
|
+
Parameters
|
|
132
|
+
----------
|
|
133
|
+
kwargs: dict[str, Any]
|
|
134
|
+
The keyword arguments to validate.
|
|
135
|
+
|
|
136
|
+
Returns
|
|
137
|
+
-------
|
|
138
|
+
list[str]
|
|
139
|
+
A list of validation error messages, if any.
|
|
140
|
+
"""
|
|
141
|
+
updated = dict(self._kwargs)
|
|
142
|
+
for key, value in kwargs.items():
|
|
143
|
+
if key in self.kwarg_types:
|
|
144
|
+
typ = self.kwarg_types[key]
|
|
145
|
+
# pylint: disable=broad-exception-caught
|
|
146
|
+
try:
|
|
147
|
+
if typ is list and isinstance(value, str):
|
|
148
|
+
updated[key] = [
|
|
149
|
+
s.strip() for s in value.split(",") if s.strip()
|
|
150
|
+
]
|
|
151
|
+
else:
|
|
152
|
+
updated[key] = typ(value)
|
|
153
|
+
except Exception:
|
|
154
|
+
pass
|
|
155
|
+
self._kwargs = updated
|
|
156
|
+
return []
|
|
157
|
+
|
|
158
|
+
def _get_effective_kwargs(
|
|
159
|
+
self,
|
|
160
|
+
runtime_kwargs: dict[str, Any] | None = None,
|
|
161
|
+
) -> dict[str, Any]:
|
|
162
|
+
"""Get effective keyword arguments."""
|
|
163
|
+
effective = dict(self._kwargs)
|
|
164
|
+
if runtime_kwargs:
|
|
165
|
+
# cast only known keys, same rules as validate_kwargs
|
|
166
|
+
for k, v in runtime_kwargs.items():
|
|
167
|
+
if k in self.kwarg_types:
|
|
168
|
+
typ = self.kwarg_types[k]
|
|
169
|
+
# pylint: disable=broad-exception-caught
|
|
170
|
+
try:
|
|
171
|
+
if typ is list and isinstance(v, str):
|
|
172
|
+
effective[k] = [
|
|
173
|
+
s.strip() for s in v.split(",") if s.strip()
|
|
174
|
+
]
|
|
175
|
+
else:
|
|
176
|
+
effective[k] = typ(v)
|
|
177
|
+
except Exception:
|
|
178
|
+
pass
|
|
179
|
+
return effective
|
|
180
|
+
|
|
181
|
+
def get_content(
|
|
182
|
+
self,
|
|
183
|
+
secrets: dict[str, str],
|
|
184
|
+
runtime_kwargs: dict[str, Any] | None = None,
|
|
185
|
+
) -> str:
|
|
186
|
+
"""
|
|
187
|
+
Generate the email content based on the provided secrets and tool kwargs.
|
|
188
|
+
|
|
189
|
+
Parameters
|
|
190
|
+
----------
|
|
191
|
+
secrets: dict[str, str]
|
|
192
|
+
The secrets required for SMTP authentication and configuration.
|
|
193
|
+
runtime_kwargs: dict[str, Any] | None, optional
|
|
194
|
+
Runtime keyword arguments to customize the content generation.
|
|
195
|
+
|
|
196
|
+
Returns
|
|
197
|
+
-------
|
|
198
|
+
str
|
|
199
|
+
The generated email content.
|
|
200
|
+
"""
|
|
201
|
+
smtp_host = secrets.get("SMTP_HOST", "smtp.gmail.com")
|
|
202
|
+
try:
|
|
203
|
+
smtp_port = int(secrets.get("SMTP_PORT", "587"))
|
|
204
|
+
except (ValueError, TypeError):
|
|
205
|
+
smtp_port = 587
|
|
206
|
+
smtp_user = secrets.get("SMTP_USERNAME", "")
|
|
207
|
+
smtp_pass = secrets.get("SMTP_PASSWORD", "")
|
|
208
|
+
|
|
209
|
+
effective_kwargs = self._get_effective_kwargs(runtime_kwargs)
|
|
210
|
+
|
|
211
|
+
is_async = effective_kwargs.get("is_async", False)
|
|
212
|
+
use_ssl = effective_kwargs.get("use_ssl", False)
|
|
213
|
+
use_starttls = effective_kwargs.get("use_starttls", True)
|
|
214
|
+
timeout = effective_kwargs.get("timeout", 30.0)
|
|
215
|
+
|
|
216
|
+
base = f"""
|
|
217
|
+
def _collect_rcpts(to, cc, bcc):
|
|
218
|
+
rcpts = []
|
|
219
|
+
for part in (to, cc, bcc):
|
|
220
|
+
if not part:
|
|
221
|
+
continue
|
|
222
|
+
if isinstance(part, str):
|
|
223
|
+
rcpts.extend([s.strip() for s in part.split(",") if s.strip()])
|
|
224
|
+
else:
|
|
225
|
+
rcpts.extend(part)
|
|
226
|
+
seen, out = set(), []
|
|
227
|
+
for r in rcpts:
|
|
228
|
+
if r not in seen:
|
|
229
|
+
seen.add(r)
|
|
230
|
+
out.append(r)
|
|
231
|
+
return out
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def _split_emails(value) -> list[str]:
|
|
235
|
+
if not value:
|
|
236
|
+
return []
|
|
237
|
+
if isinstance(value, str):
|
|
238
|
+
return [s.strip() for s in value.split(",") if s.strip()]
|
|
239
|
+
return list(value)
|
|
240
|
+
|
|
241
|
+
def _build_message(
|
|
242
|
+
subject: str,
|
|
243
|
+
sender: str,
|
|
244
|
+
recipients: str | list[str],
|
|
245
|
+
body_text: str | None = None,
|
|
246
|
+
body_html: str | None = None,
|
|
247
|
+
attachments: list | None = None,
|
|
248
|
+
cc: list[str] | None = None,
|
|
249
|
+
reply_to: list[str] | None = None,
|
|
250
|
+
) -> EmailMessage:
|
|
251
|
+
recipients = _split_emails(recipients)
|
|
252
|
+
cc = _split_emails(cc)
|
|
253
|
+
reply_to = _split_emails(reply_to)
|
|
254
|
+
attachments = attachments or []
|
|
255
|
+
|
|
256
|
+
if not (body_text or body_html):
|
|
257
|
+
raise ValueError("Provide at least one of body_text or body_html.")
|
|
258
|
+
|
|
259
|
+
msg = EmailMessage()
|
|
260
|
+
msg["Subject"] = subject
|
|
261
|
+
msg["From"] = sender
|
|
262
|
+
msg["To"] = ", ".join(recipients)
|
|
263
|
+
msg["Date"] = formatdate(localtime=True)
|
|
264
|
+
msg["Message-ID"] = make_msgid()
|
|
265
|
+
if cc:
|
|
266
|
+
msg["Cc"] = ", ".join(cc)
|
|
267
|
+
if reply_to:
|
|
268
|
+
msg["Reply-To"] = ", ".join(reply_to)
|
|
269
|
+
|
|
270
|
+
if body_html and body_text:
|
|
271
|
+
msg.set_content(body_text)
|
|
272
|
+
msg.add_alternative(body_html, subtype="html")
|
|
273
|
+
elif body_html:
|
|
274
|
+
msg.add_alternative(body_html, subtype="html")
|
|
275
|
+
else:
|
|
276
|
+
msg.set_content(body_text or "")
|
|
277
|
+
|
|
278
|
+
for item in attachments:
|
|
279
|
+
if isinstance(item, Path):
|
|
280
|
+
data = item.read_bytes()
|
|
281
|
+
filename = item.name
|
|
282
|
+
mime = "application/octet-stream"
|
|
283
|
+
else:
|
|
284
|
+
filename, data, mime = item
|
|
285
|
+
maintype, _, subtype = mime.partition("/")
|
|
286
|
+
msg.add_attachment(data, maintype=maintype, subtype=subtype, filename=filename)
|
|
287
|
+
|
|
288
|
+
return msg
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def _send_email_sync(
|
|
292
|
+
subject: str,
|
|
293
|
+
to: str | list[str],
|
|
294
|
+
body_text: str | None = None,
|
|
295
|
+
body_html: str | None = None,
|
|
296
|
+
attachments: list | None = None,
|
|
297
|
+
cc: list[str] | None = None,
|
|
298
|
+
bcc: list[str] | None = None,
|
|
299
|
+
reply_to: list[str] | None = None,
|
|
300
|
+
sender: str | None = None,
|
|
301
|
+
use_ssl: bool = {use_ssl},
|
|
302
|
+
use_starttls: bool = {use_starttls},
|
|
303
|
+
timeout: float = {timeout},
|
|
304
|
+
) -> dict:
|
|
305
|
+
smtp_host = "{smtp_host}"
|
|
306
|
+
smtp_port = {smtp_port}
|
|
307
|
+
username = "{smtp_user}"
|
|
308
|
+
password = "{smtp_pass}"
|
|
309
|
+
sender = sender or username
|
|
310
|
+
|
|
311
|
+
context = ssl.create_default_context()
|
|
312
|
+
msg = _build_message(
|
|
313
|
+
subject=subject,
|
|
314
|
+
sender=sender,
|
|
315
|
+
recipients=to,
|
|
316
|
+
body_text=body_text,
|
|
317
|
+
body_html=body_html,
|
|
318
|
+
attachments=attachments,
|
|
319
|
+
cc=cc,
|
|
320
|
+
reply_to=reply_to,
|
|
321
|
+
)
|
|
322
|
+
rcpts = _collect_rcpts(to, cc, bcc)
|
|
323
|
+
if not rcpts:
|
|
324
|
+
raise ValueError("No recipients provided (to/cc/bcc are all empty).")
|
|
325
|
+
if use_ssl:
|
|
326
|
+
with smtplib.SMTP_SSL(smtp_host, smtp_port, context=context, timeout=timeout) as server:
|
|
327
|
+
if username and password:
|
|
328
|
+
server.login(username, password)
|
|
329
|
+
return server.send_message(msg, to_addrs=rcpts)
|
|
330
|
+
else:
|
|
331
|
+
with smtplib.SMTP(smtp_host, smtp_port, timeout=timeout) as server:
|
|
332
|
+
server.ehlo()
|
|
333
|
+
if use_starttls:
|
|
334
|
+
server.starttls(context=context)
|
|
335
|
+
server.ehlo()
|
|
336
|
+
if username and password:
|
|
337
|
+
server.login(username, password)
|
|
338
|
+
return server.send_message(msg, to_addrs=rcpts)
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
async def _send_email_async(
|
|
342
|
+
subject: str,
|
|
343
|
+
to: str | list[str],
|
|
344
|
+
body_text: str | None = None,
|
|
345
|
+
body_html: str | None = None,
|
|
346
|
+
attachments: list | None = None,
|
|
347
|
+
cc: list[str] | None = None,
|
|
348
|
+
bcc: list[str] | None = None,
|
|
349
|
+
reply_to: list[str] | None = None,
|
|
350
|
+
sender: str | None = None,
|
|
351
|
+
use_ssl: bool = {use_ssl},
|
|
352
|
+
use_starttls: bool = {use_starttls},
|
|
353
|
+
timeout: float = {timeout},
|
|
354
|
+
) -> dict:
|
|
355
|
+
return await asyncio.to_thread(
|
|
356
|
+
_send_email_sync,
|
|
357
|
+
subject=subject,
|
|
358
|
+
to=to,
|
|
359
|
+
body_text=body_text,
|
|
360
|
+
body_html=body_html,
|
|
361
|
+
attachments=attachments,
|
|
362
|
+
cc=cc,
|
|
363
|
+
bcc=bcc,
|
|
364
|
+
reply_to=reply_to,
|
|
365
|
+
sender=sender,
|
|
366
|
+
use_ssl=use_ssl,
|
|
367
|
+
use_starttls=use_starttls,
|
|
368
|
+
timeout=timeout,
|
|
369
|
+
)
|
|
370
|
+
"""
|
|
371
|
+
|
|
372
|
+
# exported callable is chosen by config: async or sync
|
|
373
|
+
if is_async:
|
|
374
|
+
exported = f'''
|
|
375
|
+
async def {self.name}(
|
|
376
|
+
subject: str,
|
|
377
|
+
to: str | list[str],
|
|
378
|
+
body_text: str | None = None,
|
|
379
|
+
body_html: str | None = None,
|
|
380
|
+
attachments: list | None = None,
|
|
381
|
+
cc: list[str] | None = None,
|
|
382
|
+
bcc: list[str] | None = None,
|
|
383
|
+
reply_to: list[str] | None = None,
|
|
384
|
+
sender: str | None = None,
|
|
385
|
+
) -> dict:
|
|
386
|
+
"""Send email asynchronously.
|
|
387
|
+
|
|
388
|
+
At least one recipient must be specified.
|
|
389
|
+
At least one of `body_text` or `body_html` must be provided.
|
|
390
|
+
|
|
391
|
+
Args:
|
|
392
|
+
subject: The email subject.
|
|
393
|
+
to: The recipient email address(es).
|
|
394
|
+
body_text: The plain text body of the email.
|
|
395
|
+
body_html: The HTML body of the email.
|
|
396
|
+
attachments: Any file attachments to include.
|
|
397
|
+
cc: The CC email address(es).
|
|
398
|
+
bcc: The BCC email address(es).
|
|
399
|
+
reply_to: The reply-to email address(es).
|
|
400
|
+
sender: The sender email address.
|
|
401
|
+
|
|
402
|
+
Returns:
|
|
403
|
+
A dictionary containing the result of the email sending operation.
|
|
404
|
+
"""
|
|
405
|
+
return await _send_email_async(
|
|
406
|
+
subject=subject,
|
|
407
|
+
to=to,
|
|
408
|
+
body_text=body_text,
|
|
409
|
+
body_html=body_html,
|
|
410
|
+
attachments=attachments,
|
|
411
|
+
cc=cc,
|
|
412
|
+
bcc=bcc,
|
|
413
|
+
reply_to=reply_to,
|
|
414
|
+
sender=sender,
|
|
415
|
+
)
|
|
416
|
+
'''
|
|
417
|
+
else:
|
|
418
|
+
exported = f'''
|
|
419
|
+
def {self.name}(
|
|
420
|
+
subject: str,
|
|
421
|
+
to: str | list[str],
|
|
422
|
+
body_text: str | None = None,
|
|
423
|
+
body_html: str | None = None,
|
|
424
|
+
attachments: list | None = None,
|
|
425
|
+
cc: list[str] | None = None,
|
|
426
|
+
bcc: list[str] | None = None,
|
|
427
|
+
reply_to: list[str] | None = None,
|
|
428
|
+
sender: str | None = None,
|
|
429
|
+
) -> dict:
|
|
430
|
+
"""Send email synchronously.
|
|
431
|
+
|
|
432
|
+
At least one recipient must be specified.
|
|
433
|
+
At least one of `body_text` or `body_html` must be provided.
|
|
434
|
+
|
|
435
|
+
Args:
|
|
436
|
+
subject: The email subject.
|
|
437
|
+
to: The recipient email address(es).
|
|
438
|
+
body_text: The plain text body of the email.
|
|
439
|
+
body_html: The HTML body of the email.
|
|
440
|
+
attachments: Any file attachments to include.
|
|
441
|
+
cc: The CC email address(es).
|
|
442
|
+
bcc: The BCC email address(es).
|
|
443
|
+
reply_to: The reply-to email address(es).
|
|
444
|
+
sender: The sender email address.
|
|
445
|
+
|
|
446
|
+
Returns:
|
|
447
|
+
A dictionary containing the result of the email sending operation.
|
|
448
|
+
"""
|
|
449
|
+
return _send_email_sync(
|
|
450
|
+
subject=subject,
|
|
451
|
+
to=to,
|
|
452
|
+
body_text=body_text,
|
|
453
|
+
body_html=body_html,
|
|
454
|
+
attachments=attachments,
|
|
455
|
+
cc=cc,
|
|
456
|
+
bcc=bcc,
|
|
457
|
+
reply_to=reply_to,
|
|
458
|
+
sender=sender,
|
|
459
|
+
)
|
|
460
|
+
'''
|
|
461
|
+
|
|
462
|
+
return base + exported
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
SendEmailTool = SendEmailToolImpl()
|
|
466
|
+
SendEmailConfig = PredefinedToolConfig(
|
|
467
|
+
name=SendEmailTool.name,
|
|
468
|
+
description=SendEmailTool.description,
|
|
469
|
+
tags=SendEmailTool.tags,
|
|
470
|
+
requirements=SendEmailTool.requirements,
|
|
471
|
+
required_secrets=SendEmailTool.required_secrets,
|
|
472
|
+
required_kwargs=SendEmailTool.required_kwargs,
|
|
473
|
+
implementation=SendEmailTool,
|
|
474
|
+
)
|
|
@@ -39,7 +39,7 @@ class GoogleSearchToolImpl(PredefinedTool):
|
|
|
39
39
|
@property
|
|
40
40
|
def requirements(self) -> list[str]:
|
|
41
41
|
"""Python requirements for the tool."""
|
|
42
|
-
return ["ag2[google-search,gemini
|
|
42
|
+
return ["ag2[google-search,gemini]"]
|
|
43
43
|
|
|
44
44
|
@property
|
|
45
45
|
def tags(self) -> list[str]:
|
|
@@ -124,6 +124,7 @@ class GoogleSearchToolImpl(PredefinedTool):
|
|
|
124
124
|
def get_content(
|
|
125
125
|
self,
|
|
126
126
|
secrets: dict[str, str],
|
|
127
|
+
runtime_kwargs: dict[str, Any] | None = None,
|
|
127
128
|
) -> str:
|
|
128
129
|
"""Get content for the tool.
|
|
129
130
|
|
|
@@ -131,6 +132,8 @@ class GoogleSearchToolImpl(PredefinedTool):
|
|
|
131
132
|
----------
|
|
132
133
|
secrets : dict[str, str]
|
|
133
134
|
Dictionary of secrets/environment variables.
|
|
135
|
+
runtime_kwargs : dict[str, Any] | None, optional
|
|
136
|
+
Runtime keyword arguments to customize the content generation.
|
|
134
137
|
|
|
135
138
|
Returns
|
|
136
139
|
-------
|
|
@@ -106,6 +106,7 @@ class PerplexitySearchToolImpl(PredefinedTool):
|
|
|
106
106
|
def get_content(
|
|
107
107
|
self,
|
|
108
108
|
secrets: dict[str, str],
|
|
109
|
+
runtime_kwargs: dict[str, Any] | None = None,
|
|
109
110
|
) -> str:
|
|
110
111
|
"""Get content for the tool.
|
|
111
112
|
|
|
@@ -113,6 +114,8 @@ class PerplexitySearchToolImpl(PredefinedTool):
|
|
|
113
114
|
----------
|
|
114
115
|
secrets : dict[str, str]
|
|
115
116
|
Dictionary of secrets/environment variables.
|
|
117
|
+
runtime_kwargs : dict[str, Any] | None, optional
|
|
118
|
+
Runtime keyword arguments to customize the content generation.
|
|
116
119
|
|
|
117
120
|
Returns
|
|
118
121
|
-------
|
|
@@ -98,6 +98,7 @@ class SearxNGSearchToolImpl(PredefinedTool):
|
|
|
98
98
|
def get_content(
|
|
99
99
|
self,
|
|
100
100
|
secrets: dict[str, str],
|
|
101
|
+
runtime_kwargs: dict[str, Any] | None = None,
|
|
101
102
|
) -> str:
|
|
102
103
|
"""Get content for the tool.
|
|
103
104
|
|
|
@@ -105,6 +106,8 @@ class SearxNGSearchToolImpl(PredefinedTool):
|
|
|
105
106
|
----------
|
|
106
107
|
secrets : dict[str, str]
|
|
107
108
|
Dictionary of secrets/environment variables.
|
|
109
|
+
runtime_kwargs : dict[str, Any] | None, optional
|
|
110
|
+
Runtime keyword arguments to customize the content generation.
|
|
108
111
|
|
|
109
112
|
Returns
|
|
110
113
|
-------
|
|
@@ -34,7 +34,7 @@ class TavilySearchToolImpl(PredefinedTool):
|
|
|
34
34
|
@property
|
|
35
35
|
def requirements(self) -> list[str]:
|
|
36
36
|
"""Python requirements for the tool."""
|
|
37
|
-
return ["ag2[tavily
|
|
37
|
+
return ["ag2[tavily]"]
|
|
38
38
|
|
|
39
39
|
@property
|
|
40
40
|
def tags(self) -> list[str]:
|
|
@@ -84,6 +84,7 @@ class TavilySearchToolImpl(PredefinedTool):
|
|
|
84
84
|
def get_content(
|
|
85
85
|
self,
|
|
86
86
|
secrets: dict[str, str],
|
|
87
|
+
runtime_kwargs: dict[str, Any] | None = None,
|
|
87
88
|
) -> str:
|
|
88
89
|
"""Get content for the tool.
|
|
89
90
|
|
|
@@ -91,6 +92,8 @@ class TavilySearchToolImpl(PredefinedTool):
|
|
|
91
92
|
----------
|
|
92
93
|
secrets : dict[str, str]
|
|
93
94
|
Dictionary of secrets/environment variables.
|
|
95
|
+
runtime_kwargs : dict[str, Any] | None, optional
|
|
96
|
+
Runtime keyword arguments to customize the content generation.
|
|
94
97
|
|
|
95
98
|
Returns
|
|
96
99
|
-------
|
|
@@ -43,7 +43,7 @@ class WikipediaSearchToolImpl(PredefinedTool):
|
|
|
43
43
|
@property
|
|
44
44
|
def requirements(self) -> list[str]:
|
|
45
45
|
"""Python requirements for the tool."""
|
|
46
|
-
return ["ag2[wikipedia
|
|
46
|
+
return ["ag2[wikipedia]"]
|
|
47
47
|
|
|
48
48
|
@property
|
|
49
49
|
def tags(self) -> list[str]:
|
|
@@ -104,6 +104,7 @@ class WikipediaSearchToolImpl(PredefinedTool):
|
|
|
104
104
|
def get_content(
|
|
105
105
|
self,
|
|
106
106
|
secrets: dict[str, str],
|
|
107
|
+
runtime_kwargs: dict[str, Any] | None = None,
|
|
107
108
|
) -> str:
|
|
108
109
|
"""Get the content of the tool.
|
|
109
110
|
|
|
@@ -111,6 +112,8 @@ class WikipediaSearchToolImpl(PredefinedTool):
|
|
|
111
112
|
----------
|
|
112
113
|
secrets : dict[str, str]
|
|
113
114
|
Dictionary of secrets/environment variables.
|
|
115
|
+
runtime_kwargs : dict[str, Any] | None, optional
|
|
116
|
+
Runtime keyword arguments to customize the content generation.
|
|
114
117
|
|
|
115
118
|
Returns
|
|
116
119
|
-------
|
|
@@ -36,7 +36,7 @@ class YouTubeSearchToolImpl(PredefinedTool):
|
|
|
36
36
|
@property
|
|
37
37
|
def requirements(self) -> list[str]:
|
|
38
38
|
"""Python requirements for the tool."""
|
|
39
|
-
return ["ag2[google-search
|
|
39
|
+
return ["ag2[google-search]"]
|
|
40
40
|
|
|
41
41
|
@property
|
|
42
42
|
def tags(self) -> list[str]:
|
|
@@ -87,6 +87,7 @@ class YouTubeSearchToolImpl(PredefinedTool):
|
|
|
87
87
|
def get_content(
|
|
88
88
|
self,
|
|
89
89
|
secrets: dict[str, str],
|
|
90
|
+
runtime_kwargs: dict[str, Any] | None = None,
|
|
90
91
|
) -> str:
|
|
91
92
|
"""Get content for the tool.
|
|
92
93
|
|
|
@@ -94,6 +95,8 @@ class YouTubeSearchToolImpl(PredefinedTool):
|
|
|
94
95
|
----------
|
|
95
96
|
secrets : dict[str, str]
|
|
96
97
|
Dictionary of secrets/environment variables.
|
|
98
|
+
runtime_kwargs : dict[str, Any] | None, optional
|
|
99
|
+
Runtime keyword arguments to customize the content generation.
|
|
97
100
|
|
|
98
101
|
Returns
|
|
99
102
|
-------
|
|
@@ -37,6 +37,7 @@ class PredefinedTool(Protocol):
|
|
|
37
37
|
def get_content(
|
|
38
38
|
self,
|
|
39
39
|
secrets: dict[str, str],
|
|
40
|
+
runtime_kwargs: dict[str, Any] | None = None,
|
|
40
41
|
) -> str:
|
|
41
42
|
"""Get the content of the tool.
|
|
42
43
|
|
|
@@ -44,6 +45,8 @@ class PredefinedTool(Protocol):
|
|
|
44
45
|
----------
|
|
45
46
|
secrets : dict[str, str]
|
|
46
47
|
Dictionary of secrets/environment variables.
|
|
48
|
+
runtime_kwargs : dict[str, Any] | None, optional
|
|
49
|
+
Runtime keyword arguments to customize the content generation.
|
|
47
50
|
|
|
48
51
|
Returns
|
|
49
52
|
-------
|
waldiez/models/tool/tool.py
CHANGED
|
@@ -215,24 +215,39 @@ class WaldiezTool(WaldiezBase):
|
|
|
215
215
|
"""Get the secrets (environment variables) of the tool."""
|
|
216
216
|
return self.data.secrets or {}
|
|
217
217
|
|
|
218
|
-
def get_content(self) -> str:
|
|
218
|
+
def get_content(self, runtime_kwargs: dict[str, Any] | None = None) -> str:
|
|
219
219
|
"""Get the content of the tool.
|
|
220
220
|
|
|
221
|
+
Parameters
|
|
222
|
+
----------
|
|
223
|
+
runtime_kwargs : dict[str, Any] | None, optional
|
|
224
|
+
Runtime keyword arguments to customize the content generation.
|
|
225
|
+
|
|
221
226
|
Returns
|
|
222
227
|
-------
|
|
223
228
|
str
|
|
224
229
|
The content of the tool.
|
|
225
230
|
"""
|
|
226
231
|
if self.is_predefined:
|
|
227
|
-
return self._generate_predefined_content(
|
|
232
|
+
return self._generate_predefined_content(
|
|
233
|
+
runtime_kwargs=runtime_kwargs
|
|
234
|
+
)
|
|
228
235
|
if self.is_shared or self.is_interop:
|
|
229
236
|
return self.data.content
|
|
230
237
|
# if custom, only the function content
|
|
231
238
|
return get_function(self.data.content, self.name)
|
|
232
239
|
|
|
233
|
-
def _generate_predefined_content(
|
|
240
|
+
def _generate_predefined_content(
|
|
241
|
+
self,
|
|
242
|
+
runtime_kwargs: dict[str, Any] | None = None,
|
|
243
|
+
) -> str:
|
|
234
244
|
"""Generate the content for a predefined tool.
|
|
235
245
|
|
|
246
|
+
Parameters
|
|
247
|
+
----------
|
|
248
|
+
runtime_kwargs : dict[str, Any] | None, optional
|
|
249
|
+
Runtime keyword arguments to customize the content generation.
|
|
250
|
+
|
|
236
251
|
Returns
|
|
237
252
|
-------
|
|
238
253
|
str
|
|
@@ -241,7 +256,10 @@ class WaldiezTool(WaldiezBase):
|
|
|
241
256
|
config = get_predefined_tool_config(self.name)
|
|
242
257
|
if not config:
|
|
243
258
|
return ""
|
|
244
|
-
return config.get_content(
|
|
259
|
+
return config.get_content(
|
|
260
|
+
self.data.secrets,
|
|
261
|
+
runtime_kwargs=runtime_kwargs,
|
|
262
|
+
)
|
|
245
263
|
|
|
246
264
|
def _validate_interop_tool(self) -> None:
|
|
247
265
|
"""Validate the interoperability tool.
|
waldiez/models/waldiez.py
CHANGED
|
@@ -39,6 +39,18 @@ class Waldiez:
|
|
|
39
39
|
|
|
40
40
|
flow: WaldiezFlow
|
|
41
41
|
|
|
42
|
+
@classmethod
|
|
43
|
+
def default(cls) -> "Waldiez":
|
|
44
|
+
"""Create a default Waldiez instance.
|
|
45
|
+
|
|
46
|
+
Returns
|
|
47
|
+
-------
|
|
48
|
+
Waldiez
|
|
49
|
+
The default Waldiez instance.
|
|
50
|
+
"""
|
|
51
|
+
flow = WaldiezFlow.default()
|
|
52
|
+
return cls(flow=flow)
|
|
53
|
+
|
|
42
54
|
@classmethod
|
|
43
55
|
def from_dict(
|
|
44
56
|
cls,
|