waldiez 0.5.8__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 +8 -6
- 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.8.dist-info → waldiez-0.5.10.dist-info}/METADATA +74 -74
- {waldiez-0.5.8.dist-info → waldiez-0.5.10.dist-info}/RECORD +87 -65
- waldiez/running/patch_io_stream.py +0 -210
- {waldiez-0.5.8.dist-info → waldiez-0.5.10.dist-info}/WHEEL +0 -0
- {waldiez-0.5.8.dist-info → waldiez-0.5.10.dist-info}/entry_points.txt +0 -0
- {waldiez-0.5.8.dist-info → waldiez-0.5.10.dist-info}/licenses/LICENSE +0 -0
- {waldiez-0.5.8.dist-info → waldiez-0.5.10.dist-info}/licenses/NOTICE.md +0 -0
waldiez/logger.py
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
"""Waldiez logger."""
|
|
5
5
|
|
|
6
6
|
import inspect
|
|
7
|
+
import logging
|
|
7
8
|
import os
|
|
8
9
|
import re
|
|
9
10
|
import string
|
|
@@ -11,23 +12,27 @@ import threading
|
|
|
11
12
|
import traceback
|
|
12
13
|
from datetime import datetime
|
|
13
14
|
from enum import IntEnum
|
|
14
|
-
from
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from types import TracebackType
|
|
17
|
+
from typing import Any, Callable, Mapping, Optional
|
|
15
18
|
|
|
16
19
|
import click
|
|
17
20
|
|
|
21
|
+
HERE = Path(__file__).parent
|
|
22
|
+
|
|
18
23
|
|
|
19
24
|
class LogLevel(IntEnum):
|
|
20
25
|
"""Log level enumeration for comparison."""
|
|
21
26
|
|
|
22
27
|
DEBUG = 10
|
|
23
28
|
INFO = 20
|
|
24
|
-
SUCCESS =
|
|
25
|
-
WARNING =
|
|
26
|
-
ERROR =
|
|
27
|
-
CRITICAL =
|
|
29
|
+
SUCCESS = 21
|
|
30
|
+
WARNING = 30
|
|
31
|
+
ERROR = 40
|
|
32
|
+
CRITICAL = 50
|
|
28
33
|
|
|
29
34
|
|
|
30
|
-
class WaldiezLogger:
|
|
35
|
+
class WaldiezLogger(logging.Logger):
|
|
31
36
|
"""A colorful logger implementation using Click.
|
|
32
37
|
|
|
33
38
|
Supports both .format() and %-style formatting:
|
|
@@ -51,9 +56,29 @@ class WaldiezLogger:
|
|
|
51
56
|
"critical",
|
|
52
57
|
"exception",
|
|
53
58
|
}
|
|
59
|
+
_level_map: dict[str, LogLevel] = {
|
|
60
|
+
"DEBUG": LogLevel.DEBUG,
|
|
61
|
+
"INFO": LogLevel.INFO,
|
|
62
|
+
"SUCCESS": LogLevel.SUCCESS,
|
|
63
|
+
"WARNING": LogLevel.WARNING,
|
|
64
|
+
"ERROR": LogLevel.ERROR,
|
|
65
|
+
"CRITICAL": LogLevel.CRITICAL,
|
|
66
|
+
}
|
|
67
|
+
# Map levels to click styling functions
|
|
68
|
+
_style_map: dict[str, Callable[[str], str]] = {
|
|
69
|
+
"DEBUG": lambda msg: click.style(msg, dim=True),
|
|
70
|
+
"INFO": lambda msg: click.style(msg, fg="blue"),
|
|
71
|
+
"SUCCESS": lambda msg: click.style(msg, fg="green"),
|
|
72
|
+
"WARNING": lambda msg: click.style(msg, fg="yellow"),
|
|
73
|
+
"ERROR": lambda msg: click.style(msg, fg="red"),
|
|
74
|
+
"CRITICAL": lambda msg: click.style(msg, fg="red", bold=True),
|
|
75
|
+
"EXCEPTION": lambda msg: click.style(msg, fg="red", bold=True),
|
|
76
|
+
}
|
|
54
77
|
|
|
55
78
|
def __new__(cls, *args: Any, **kwargs: Any) -> "WaldiezLogger":
|
|
56
79
|
"""Ensure only one instance of the logger is created."""
|
|
80
|
+
for level_name, level_value in LogLevel.__members__.items():
|
|
81
|
+
logging.addLevelName(level_value, level_name)
|
|
57
82
|
if cls._instance is None:
|
|
58
83
|
with cls._lock:
|
|
59
84
|
# Double-check locking pattern
|
|
@@ -75,6 +100,9 @@ class WaldiezLogger:
|
|
|
75
100
|
timestamp_format : str, optional
|
|
76
101
|
Timestamp format string, by default "%Y-%m-%d %H:%M:%S"
|
|
77
102
|
"""
|
|
103
|
+
super().__init__(self.__class__.__name__)
|
|
104
|
+
for level_name, level_value in LogLevel.__members__.items():
|
|
105
|
+
logging.addLevelName(level_value, level_name)
|
|
78
106
|
if getattr(self, "_initialized", False) is True:
|
|
79
107
|
if level != self.get_level():
|
|
80
108
|
self.set_level(level)
|
|
@@ -84,29 +112,16 @@ class WaldiezLogger:
|
|
|
84
112
|
):
|
|
85
113
|
self._instance.set_timestamp_format(timestamp_format)
|
|
86
114
|
return
|
|
115
|
+
self._initialized = True
|
|
87
116
|
self._level = level.upper()
|
|
88
117
|
self._timestamp_format = timestamp_format
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
"CRITICAL": LogLevel.CRITICAL,
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
# Map levels to click styling functions
|
|
100
|
-
self._style_map: dict[str, Callable[[str], str]] = {
|
|
101
|
-
"DEBUG": lambda msg: click.style(msg, dim=True),
|
|
102
|
-
"INFO": lambda msg: click.style(msg, fg="blue"),
|
|
103
|
-
"SUCCESS": lambda msg: click.style(msg, fg="green"),
|
|
104
|
-
"WARNING": lambda msg: click.style(msg, fg="yellow"),
|
|
105
|
-
"ERROR": lambda msg: click.style(msg, fg="red"),
|
|
106
|
-
"CRITICAL": lambda msg: click.style(msg, fg="red", bold=True),
|
|
107
|
-
"EXCEPTION": lambda msg: click.style(msg, fg="red", bold=True),
|
|
108
|
-
}
|
|
109
|
-
self._initialized = True
|
|
118
|
+
if self.get_level() != level:
|
|
119
|
+
self.set_level(level)
|
|
120
|
+
if (
|
|
121
|
+
self._instance
|
|
122
|
+
and self._instance.get_timestamp_format() != timestamp_format
|
|
123
|
+
):
|
|
124
|
+
self._instance.set_timestamp_format(timestamp_format)
|
|
110
125
|
|
|
111
126
|
@classmethod
|
|
112
127
|
def get_instance(
|
|
@@ -130,129 +145,207 @@ class WaldiezLogger:
|
|
|
130
145
|
"""
|
|
131
146
|
return cls(level, timestamp_format)
|
|
132
147
|
|
|
148
|
+
def _get_level_name(self, level: int) -> str:
|
|
149
|
+
"""Get the string name of the logging level.
|
|
150
|
+
|
|
151
|
+
Parameters
|
|
152
|
+
----------
|
|
153
|
+
level : int
|
|
154
|
+
The logging level.
|
|
155
|
+
|
|
156
|
+
Returns
|
|
157
|
+
-------
|
|
158
|
+
str
|
|
159
|
+
The string name of the logging level.
|
|
160
|
+
"""
|
|
161
|
+
for name, lvl in self._level_map.items():
|
|
162
|
+
if lvl == level:
|
|
163
|
+
return name
|
|
164
|
+
return self.get_level()
|
|
165
|
+
|
|
166
|
+
def _get_level_number(self, level: str) -> int:
|
|
167
|
+
"""Get the numeric value of the logging level.
|
|
168
|
+
|
|
169
|
+
Parameters
|
|
170
|
+
----------
|
|
171
|
+
level : str
|
|
172
|
+
The logging level.
|
|
173
|
+
|
|
174
|
+
Returns
|
|
175
|
+
-------
|
|
176
|
+
int
|
|
177
|
+
The numeric value of the logging level.
|
|
178
|
+
"""
|
|
179
|
+
return self._level_map.get(level.upper(), LogLevel.INFO)
|
|
180
|
+
|
|
181
|
+
# pylint: disable=unused-argument
|
|
133
182
|
def log(
|
|
134
|
-
self,
|
|
183
|
+
self,
|
|
184
|
+
level: int,
|
|
185
|
+
msg: object,
|
|
186
|
+
*args: object,
|
|
187
|
+
exc_info: (
|
|
188
|
+
bool
|
|
189
|
+
| tuple[type[BaseException], BaseException, TracebackType | None]
|
|
190
|
+
| tuple[None, None, None]
|
|
191
|
+
| BaseException
|
|
192
|
+
| None
|
|
193
|
+
) = None,
|
|
194
|
+
stack_info: bool = False,
|
|
195
|
+
stacklevel: int = 1,
|
|
196
|
+
extra: Mapping[str, object] | None = None,
|
|
197
|
+
**kwargs: object,
|
|
135
198
|
) -> None:
|
|
136
199
|
"""Log a message with the specified level.
|
|
137
200
|
|
|
138
201
|
Parameters
|
|
139
202
|
----------
|
|
140
|
-
|
|
203
|
+
level : int
|
|
204
|
+
The logging level to use (e.g., logging.DEBUG, logging.INFO, etc.).
|
|
205
|
+
msg : object
|
|
141
206
|
The message to log or message template for formatting.
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
207
|
+
*args : object
|
|
208
|
+
Arguments to format into the message.
|
|
209
|
+
exc_info :
|
|
210
|
+
bool |
|
|
211
|
+
tuple[type[BaseException], BaseException, TracebackType | None] |
|
|
212
|
+
tuple[None, None, None] |
|
|
213
|
+
BaseException |
|
|
214
|
+
None
|
|
215
|
+
Exception information to include in the log.
|
|
216
|
+
stack_info : bool
|
|
217
|
+
Whether to include stack information in the log.
|
|
218
|
+
stacklevel : int
|
|
219
|
+
The stack level to use for the log.
|
|
220
|
+
extra : Mapping[str, object] | None
|
|
221
|
+
Extra context information to include in the log.
|
|
222
|
+
**kwargs : object
|
|
148
223
|
Additional keyword arguments for formatting.
|
|
224
|
+
|
|
149
225
|
"""
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
)
|
|
226
|
+
level_str = self._get_level_name(level)
|
|
227
|
+
if self._should_log(level_str):
|
|
228
|
+
# noinspection PyArgumentList
|
|
229
|
+
formatted_message_content = self._format_args(msg, *args, **kwargs)
|
|
154
230
|
formatted_message = self._format_message(
|
|
155
|
-
formatted_message_content,
|
|
231
|
+
formatted_message_content, level_str
|
|
156
232
|
)
|
|
157
233
|
click.echo(formatted_message)
|
|
158
234
|
|
|
159
|
-
def
|
|
235
|
+
def do_log(self, msg: Any, *args: Any, level: str, **kwargs: Any) -> None:
|
|
236
|
+
"""Log a message with the specified level.
|
|
237
|
+
|
|
238
|
+
Parameters
|
|
239
|
+
----------
|
|
240
|
+
msg : Any
|
|
241
|
+
The message to log or message template for formatting.
|
|
242
|
+
*args : Any
|
|
243
|
+
Arguments to format into the message.
|
|
244
|
+
level : str
|
|
245
|
+
The logging level.
|
|
246
|
+
**kwargs : Any
|
|
247
|
+
Additional keyword arguments for formatting.
|
|
248
|
+
"""
|
|
249
|
+
level_int = self._get_level_number(level)
|
|
250
|
+
self.log(level_int, msg, *args, **kwargs)
|
|
251
|
+
|
|
252
|
+
def debug(self, msg: Any, *args: Any, **kwargs: Any) -> None:
|
|
160
253
|
"""Log a debug message.
|
|
161
254
|
|
|
162
255
|
Parameters
|
|
163
256
|
----------
|
|
164
|
-
|
|
257
|
+
msg : Any
|
|
165
258
|
The debug message to log or message template.
|
|
166
259
|
*args : Any
|
|
167
260
|
Arguments to format into the message.
|
|
168
261
|
**kwargs : Any
|
|
169
262
|
Additional keyword arguments for formatting.
|
|
170
263
|
"""
|
|
171
|
-
self.
|
|
264
|
+
self.do_log(msg, *args, level="debug", **kwargs)
|
|
172
265
|
|
|
173
|
-
def info(self,
|
|
266
|
+
def info(self, msg: Any, *args: Any, **kwargs: Any) -> None:
|
|
174
267
|
"""Log an informational message.
|
|
175
268
|
|
|
176
269
|
Parameters
|
|
177
270
|
----------
|
|
178
|
-
|
|
271
|
+
msg : Any
|
|
179
272
|
The informational message to log or message template.
|
|
180
273
|
*args : Any
|
|
181
274
|
Arguments to format into the message.
|
|
182
275
|
**kwargs : Any
|
|
183
276
|
Additional keyword arguments for formatting.
|
|
184
277
|
"""
|
|
185
|
-
self.
|
|
278
|
+
self.do_log(msg, *args, level="info", **kwargs)
|
|
186
279
|
|
|
187
|
-
def success(self,
|
|
280
|
+
def success(self, msg: Any, *args: Any, **kwargs: Any) -> None:
|
|
188
281
|
"""Log a success message.
|
|
189
282
|
|
|
190
283
|
Parameters
|
|
191
284
|
----------
|
|
192
|
-
|
|
285
|
+
msg : Any
|
|
193
286
|
The success message to log or message template.
|
|
194
287
|
*args : Any
|
|
195
288
|
Arguments to format into the message.
|
|
196
289
|
**kwargs : Any
|
|
197
290
|
Additional keyword arguments for formatting.
|
|
198
291
|
"""
|
|
199
|
-
self.
|
|
292
|
+
self.do_log(msg, *args, level="success", **kwargs)
|
|
200
293
|
|
|
201
|
-
def warning(self,
|
|
294
|
+
def warning(self, msg: Any, *args: Any, **kwargs: Any) -> None:
|
|
202
295
|
"""Log a warning message.
|
|
203
296
|
|
|
204
297
|
Parameters
|
|
205
298
|
----------
|
|
206
|
-
|
|
299
|
+
msg : Any
|
|
207
300
|
The warning message to log or message template.
|
|
208
301
|
*args : Any
|
|
209
302
|
Arguments to format into the message.
|
|
210
303
|
**kwargs : Any
|
|
211
304
|
Additional keyword arguments for formatting.
|
|
212
305
|
"""
|
|
213
|
-
self.
|
|
306
|
+
self.do_log(msg, *args, level="warning", **kwargs)
|
|
214
307
|
|
|
215
|
-
def error(self,
|
|
308
|
+
def error(self, msg: Any, *args: Any, **kwargs: Any) -> None:
|
|
216
309
|
"""Log an error message.
|
|
217
310
|
|
|
218
311
|
Parameters
|
|
219
312
|
----------
|
|
220
|
-
|
|
313
|
+
msg : Any
|
|
221
314
|
The error message to log or message template.
|
|
222
315
|
*args : Any
|
|
223
316
|
Arguments to format into the message.
|
|
224
317
|
**kwargs : Any
|
|
225
318
|
Additional keyword arguments for formatting.
|
|
226
319
|
"""
|
|
227
|
-
self.
|
|
320
|
+
self.do_log(msg, *args, level="error", **kwargs)
|
|
228
321
|
|
|
229
|
-
def critical(self,
|
|
322
|
+
def critical(self, msg: Any, *args: Any, **kwargs: Any) -> None:
|
|
230
323
|
"""Log a critical error message.
|
|
231
324
|
|
|
232
325
|
Parameters
|
|
233
326
|
----------
|
|
234
|
-
|
|
327
|
+
msg : Any
|
|
235
328
|
The critical error message to log or message template.
|
|
236
329
|
*args : Any
|
|
237
330
|
Arguments to format into the message.
|
|
238
331
|
**kwargs : Any
|
|
239
332
|
Additional keyword arguments for formatting.
|
|
240
333
|
"""
|
|
241
|
-
self.
|
|
334
|
+
self.do_log(msg, *args, level="critical", **kwargs)
|
|
242
335
|
|
|
243
|
-
def exception(self,
|
|
336
|
+
def exception(self, msg: Any, *args: Any, **kwargs: Any) -> None:
|
|
244
337
|
"""Log an exception message.
|
|
245
338
|
|
|
246
339
|
Parameters
|
|
247
340
|
----------
|
|
248
|
-
|
|
341
|
+
msg : Any
|
|
249
342
|
The exception message to log or message template.
|
|
250
343
|
*args : Any
|
|
251
344
|
Arguments to format into the message.
|
|
252
345
|
**kwargs : Any
|
|
253
346
|
Additional keyword arguments for formatting.
|
|
254
347
|
"""
|
|
255
|
-
formatted_message_content = self._format_args(
|
|
348
|
+
formatted_message_content = self._format_args(msg, *args, **kwargs)
|
|
256
349
|
formatted_message = self._format_message(
|
|
257
350
|
formatted_message_content, "exception"
|
|
258
351
|
)
|
|
@@ -261,6 +354,21 @@ class WaldiezLogger:
|
|
|
261
354
|
if tb and "NoneType: None" not in tb: # pragma: no branch
|
|
262
355
|
click.echo(click.style(tb, fg="red", dim=True))
|
|
263
356
|
|
|
357
|
+
def setLevel(self, level: int | str) -> None:
|
|
358
|
+
"""
|
|
359
|
+
Set the logging level.
|
|
360
|
+
|
|
361
|
+
Parameters
|
|
362
|
+
----------
|
|
363
|
+
level : int | str
|
|
364
|
+
The logging level to set
|
|
365
|
+
(e.g., "debug", "info", "warning", "error", "critical").
|
|
366
|
+
"""
|
|
367
|
+
level_str = (
|
|
368
|
+
level if isinstance(level, str) else self._get_level_name(level)
|
|
369
|
+
)
|
|
370
|
+
self.set_level(level_str)
|
|
371
|
+
|
|
264
372
|
def set_level(self, level: str) -> None:
|
|
265
373
|
"""Set the logging level.
|
|
266
374
|
|
|
@@ -278,6 +386,7 @@ class WaldiezLogger:
|
|
|
278
386
|
level_upper = level.upper()
|
|
279
387
|
if level_upper in self._level_map:
|
|
280
388
|
self._level = level_upper
|
|
389
|
+
super().setLevel(self._level_map[level_upper].value)
|
|
281
390
|
else:
|
|
282
391
|
raise ValueError(
|
|
283
392
|
f"Invalid log level: {level}. "
|
|
@@ -369,8 +478,15 @@ class WaldiezLogger:
|
|
|
369
478
|
@staticmethod
|
|
370
479
|
def _format_caller_display(filename: str, line_number: int) -> str:
|
|
371
480
|
"""Format the caller information for display."""
|
|
372
|
-
|
|
373
|
-
|
|
481
|
+
full_path = Path(filename).resolve()
|
|
482
|
+
try:
|
|
483
|
+
relative = full_path.relative_to(Path.cwd())
|
|
484
|
+
except ValueError: # pragma: no cover
|
|
485
|
+
try:
|
|
486
|
+
relative = full_path.relative_to(HERE.parent)
|
|
487
|
+
except ValueError: # pragma: no cover
|
|
488
|
+
relative = full_path
|
|
489
|
+
return f"{relative}:{line_number}"
|
|
374
490
|
|
|
375
491
|
def _get_timestamp(self) -> str:
|
|
376
492
|
"""Get the current timestamp in a human-readable format."""
|
|
@@ -400,6 +516,7 @@ class WaldiezLogger:
|
|
|
400
516
|
) -> tuple[bool, Any]:
|
|
401
517
|
"""Attempt .format() formatting, returning (success, result)."""
|
|
402
518
|
try:
|
|
519
|
+
# noinspection StrFormat
|
|
403
520
|
return True, msg_str.format(*args, **kwargs)
|
|
404
521
|
except Exception as e:
|
|
405
522
|
return False, e
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# SPDX-License-Identifier: Apache-2.0.
|
|
2
2
|
# Copyright (c) 2024 - 2025 Waldiez and contributors.
|
|
3
|
-
# SPDX-License-Identifier: Apache-2.0.
|
|
4
|
-
# Copyright (c) 2024 - 2025 Waldiez and contributors.
|
|
5
3
|
"""Update the agent's system message before they reply."""
|
|
6
4
|
|
|
7
5
|
from typing import Optional
|
|
@@ -114,7 +114,14 @@ class WaldiezDocAgent(WaldiezAgent):
|
|
|
114
114
|
requirements = {
|
|
115
115
|
"llama-index",
|
|
116
116
|
"llama-index-core",
|
|
117
|
-
f"ag2[rag]=={ag2_version}",
|
|
117
|
+
# f"ag2[rag]=={ag2_version}",
|
|
118
|
+
"chromadb>=0.5,<2",
|
|
119
|
+
"docling>=2.15.1,<3",
|
|
120
|
+
"selenium>=4.28.1,<5",
|
|
121
|
+
"webdriver-manager==4.0.2",
|
|
122
|
+
"llama-index-embeddings-huggingface",
|
|
123
|
+
"llama-index-llms-langchain",
|
|
124
|
+
"llama-index-vector-stores-chroma",
|
|
118
125
|
}
|
|
119
126
|
if not self.data.model_ids:
|
|
120
127
|
requirements.add("llama-index-llms-openai")
|
|
@@ -2,63 +2,192 @@
|
|
|
2
2
|
# Copyright (c) 2024 - 2025 Waldiez and contributors.
|
|
3
3
|
"""Dictionary related utilities."""
|
|
4
4
|
|
|
5
|
+
import ast
|
|
6
|
+
import json
|
|
5
7
|
import re
|
|
6
|
-
from typing import Any
|
|
8
|
+
from typing import Any, Union
|
|
7
9
|
|
|
8
10
|
BOOL_VALUES = {"true", "false"}
|
|
9
11
|
NULL_VALUES = {"none", "null", "nil", "undefined"}
|
|
10
12
|
|
|
11
13
|
|
|
14
|
+
def _strip_outer_quotes(value: str) -> str:
|
|
15
|
+
"""Remove outer quotes from a string if present."""
|
|
16
|
+
value_stripped = value.strip()
|
|
17
|
+
if (value_stripped.startswith('"') and value_stripped.endswith('"')) or (
|
|
18
|
+
value_stripped.startswith("'") and value_stripped.endswith("'")
|
|
19
|
+
):
|
|
20
|
+
return value_stripped[1:-1]
|
|
21
|
+
return value_stripped
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _detect_null_or_boolean(value: str) -> Union[None, bool, str]:
|
|
25
|
+
"""
|
|
26
|
+
Detect null values or booleans.
|
|
27
|
+
|
|
28
|
+
Parameters
|
|
29
|
+
----------
|
|
30
|
+
value : str
|
|
31
|
+
The string value to check.
|
|
32
|
+
|
|
33
|
+
Returns
|
|
34
|
+
-------
|
|
35
|
+
Union[None, bool, str]
|
|
36
|
+
None for null values, bool for booleans, or original string if neither.
|
|
37
|
+
"""
|
|
38
|
+
value_lower = value.lower()
|
|
39
|
+
|
|
40
|
+
if value_lower in NULL_VALUES:
|
|
41
|
+
return None
|
|
42
|
+
if value_lower in BOOL_VALUES:
|
|
43
|
+
return value_lower == "true"
|
|
44
|
+
|
|
45
|
+
return value
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _detect_numeric_type(value: str) -> Union[int, float, str]:
|
|
49
|
+
"""
|
|
50
|
+
Detect if string represents an integer or float.
|
|
51
|
+
|
|
52
|
+
Parameters
|
|
53
|
+
----------
|
|
54
|
+
value : str
|
|
55
|
+
The string value to check.
|
|
56
|
+
|
|
57
|
+
Returns
|
|
58
|
+
-------
|
|
59
|
+
Union[int, float, str]
|
|
60
|
+
int for integers, float for floats, or original string if neither.
|
|
61
|
+
"""
|
|
62
|
+
# Check for integer first (more specific)
|
|
63
|
+
if re.fullmatch(r"[-+]?\d+", value):
|
|
64
|
+
return int(value)
|
|
65
|
+
|
|
66
|
+
# Try float conversion
|
|
67
|
+
try:
|
|
68
|
+
return float(value)
|
|
69
|
+
except ValueError:
|
|
70
|
+
return value
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _detect_container_type(
|
|
74
|
+
value: str,
|
|
75
|
+
) -> Union[dict[str, Any], list[Any], tuple[Any], set[Any], str]:
|
|
76
|
+
"""
|
|
77
|
+
Detect if string represents a container type (dict, list, tuple, set).
|
|
78
|
+
|
|
79
|
+
Parameters
|
|
80
|
+
----------
|
|
81
|
+
value : str
|
|
82
|
+
The string value to check.
|
|
83
|
+
|
|
84
|
+
Returns
|
|
85
|
+
-------
|
|
86
|
+
Union[dict[str, Any], list[Any], tuple[Any], set[Any], str]
|
|
87
|
+
Parsed container or original string if not a container.
|
|
88
|
+
"""
|
|
89
|
+
if not (value[0] in "{[(" and value[-1] in "}])"):
|
|
90
|
+
return value
|
|
91
|
+
|
|
92
|
+
# Handle empty containers
|
|
93
|
+
if value in ("()", "[]", "{}"):
|
|
94
|
+
return ast.literal_eval(value)
|
|
95
|
+
|
|
96
|
+
# Try JSON first (expects double quotes)
|
|
97
|
+
try:
|
|
98
|
+
parsed = json.loads(value)
|
|
99
|
+
if isinstance(parsed, (dict, list)):
|
|
100
|
+
return parsed # pyright: ignore
|
|
101
|
+
except (json.JSONDecodeError, TypeError):
|
|
102
|
+
pass
|
|
103
|
+
|
|
104
|
+
# Fallback: Python literal (handles single quotes, tuples, sets)
|
|
105
|
+
try:
|
|
106
|
+
parsed = ast.literal_eval(value)
|
|
107
|
+
if isinstance(parsed, (dict, list, tuple, set)):
|
|
108
|
+
return parsed # pyright: ignore
|
|
109
|
+
except (ValueError, SyntaxError):
|
|
110
|
+
pass
|
|
111
|
+
|
|
112
|
+
return value
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _convert_string_value(value: str) -> Any:
|
|
116
|
+
"""
|
|
117
|
+
Convert a string value to its detected type.
|
|
118
|
+
|
|
119
|
+
Parameters
|
|
120
|
+
----------
|
|
121
|
+
value : str
|
|
122
|
+
The string value to convert.
|
|
123
|
+
|
|
124
|
+
Returns
|
|
125
|
+
-------
|
|
126
|
+
Any
|
|
127
|
+
The converted value or original string if no conversion possible.
|
|
128
|
+
"""
|
|
129
|
+
# Strip outer quotes if present
|
|
130
|
+
cleaned_value = _strip_outer_quotes(value)
|
|
131
|
+
|
|
132
|
+
# Skip conversion for empty strings
|
|
133
|
+
if not cleaned_value:
|
|
134
|
+
return value
|
|
135
|
+
|
|
136
|
+
# Try conversions in order of specificity
|
|
137
|
+
|
|
138
|
+
# 1. Container types (most specific structure)
|
|
139
|
+
container_result = _detect_container_type(cleaned_value)
|
|
140
|
+
if container_result != cleaned_value:
|
|
141
|
+
return container_result
|
|
142
|
+
|
|
143
|
+
# 2. Null and boolean values
|
|
144
|
+
null_bool_result = _detect_null_or_boolean(cleaned_value)
|
|
145
|
+
if null_bool_result != cleaned_value:
|
|
146
|
+
return null_bool_result
|
|
147
|
+
|
|
148
|
+
# 3. Numeric types
|
|
149
|
+
numeric_result = _detect_numeric_type(cleaned_value)
|
|
150
|
+
if numeric_result != cleaned_value:
|
|
151
|
+
return numeric_result
|
|
152
|
+
|
|
153
|
+
# 4. Keep as string if no conversion succeeded
|
|
154
|
+
return cleaned_value
|
|
155
|
+
|
|
156
|
+
|
|
12
157
|
def update_dict(original: dict[str, Any]) -> dict[str, Any]:
|
|
13
158
|
"""
|
|
14
|
-
|
|
159
|
+
Convert string values in a dictionary to their detected types.
|
|
160
|
+
|
|
161
|
+
Automatically detects and converts strings that represent:
|
|
162
|
+
- Boolean values (true/false)
|
|
163
|
+
- Null values (none/null/nil/undefined)
|
|
164
|
+
- Integers and floats
|
|
165
|
+
- Container types (lists, dicts, tuples, sets)
|
|
15
166
|
|
|
16
167
|
Parameters
|
|
17
168
|
----------
|
|
18
169
|
original : dict[str, Any]
|
|
19
|
-
The original dictionary.
|
|
170
|
+
The original dictionary with potentially string-encoded values.
|
|
20
171
|
|
|
21
172
|
Returns
|
|
22
173
|
-------
|
|
23
174
|
dict[str, Any]
|
|
24
|
-
|
|
175
|
+
A new dictionary with string values converted to their detected types.
|
|
176
|
+
Non-string values are preserved unchanged.
|
|
177
|
+
|
|
178
|
+
Examples
|
|
179
|
+
--------
|
|
180
|
+
>>> data = {"count": "42", "active": "true", "tags": "['a', 'b']"}
|
|
181
|
+
>>> update_dict(data)
|
|
182
|
+
{"count": 42, "active": True, "tags": ['a', 'b']}
|
|
25
183
|
"""
|
|
26
|
-
|
|
184
|
+
converted_dict: dict[str, Any] = {}
|
|
27
185
|
|
|
28
186
|
for key, value in original.items():
|
|
29
|
-
#
|
|
30
|
-
if
|
|
31
|
-
|
|
32
|
-
continue
|
|
33
|
-
|
|
34
|
-
value_stripped = value.strip()
|
|
35
|
-
if (
|
|
36
|
-
value_stripped.startswith('"') and value_stripped.endswith('"')
|
|
37
|
-
) or (value_stripped.startswith("'") and value_stripped.endswith("'")):
|
|
38
|
-
value_stripped = value_stripped[1:-1]
|
|
39
|
-
if not value_stripped: # Empty or whitespace-only
|
|
40
|
-
new_dict[key] = value
|
|
41
|
-
continue
|
|
42
|
-
|
|
43
|
-
value_lower = value_stripped.lower()
|
|
44
|
-
|
|
45
|
-
# Check for None/null
|
|
46
|
-
if value_lower in NULL_VALUES:
|
|
47
|
-
new_dict[key] = None
|
|
48
|
-
# Check for boolean
|
|
49
|
-
elif value_lower in BOOL_VALUES:
|
|
50
|
-
new_dict[key] = value_lower == "true"
|
|
51
|
-
# Check for integer
|
|
52
|
-
elif re.fullmatch(r"[-+]?\d+", value_stripped):
|
|
53
|
-
new_dict[key] = int(value_stripped)
|
|
54
|
-
# Check for float
|
|
187
|
+
# Only process string values
|
|
188
|
+
if isinstance(value, str):
|
|
189
|
+
converted_dict[key] = _convert_string_value(value)
|
|
55
190
|
else:
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
new_dict[key] = float_val
|
|
60
|
-
except ValueError:
|
|
61
|
-
# Keep as string if conversion fails
|
|
62
|
-
new_dict[key] = value_stripped
|
|
63
|
-
|
|
64
|
-
return new_dict
|
|
191
|
+
converted_dict[key] = value
|
|
192
|
+
|
|
193
|
+
return converted_dict
|