waldiez 0.6.0__py3-none-any.whl → 0.6.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of waldiez might be problematic. Click here for more details.
- waldiez/__init__.py +1 -1
- waldiez/_version.py +1 -1
- waldiez/cli.py +18 -7
- waldiez/cli_extras/jupyter.py +3 -0
- waldiez/cli_extras/runner.py +3 -1
- waldiez/cli_extras/studio.py +3 -1
- waldiez/exporter.py +9 -3
- waldiez/exporting/agent/exporter.py +9 -10
- waldiez/exporting/agent/extras/captain_agent_extras.py +6 -6
- waldiez/exporting/agent/extras/doc_agent_extras.py +6 -6
- waldiez/exporting/agent/extras/group_manager_agent_extas.py +34 -23
- waldiez/exporting/agent/extras/group_member_extras.py +6 -5
- waldiez/exporting/agent/extras/handoffs/after_work.py +1 -1
- waldiez/exporting/agent/extras/handoffs/available.py +1 -1
- waldiez/exporting/agent/extras/handoffs/condition.py +3 -2
- waldiez/exporting/agent/extras/handoffs/handoff.py +1 -1
- waldiez/exporting/agent/extras/handoffs/target.py +6 -4
- waldiez/exporting/agent/extras/rag/chroma_extras.py +27 -19
- waldiez/exporting/agent/extras/rag/mongo_extras.py +8 -8
- waldiez/exporting/agent/extras/rag/pgvector_extras.py +5 -5
- waldiez/exporting/agent/extras/rag/qdrant_extras.py +5 -4
- waldiez/exporting/agent/extras/rag/vector_db_extras.py +1 -1
- waldiez/exporting/agent/extras/rag_user_proxy_agent_extras.py +5 -7
- waldiez/exporting/agent/extras/reasoning_agent_extras.py +3 -5
- waldiez/exporting/chats/exporter.py +4 -4
- waldiez/exporting/chats/processor.py +1 -2
- waldiez/exporting/chats/utils/common.py +89 -48
- waldiez/exporting/chats/utils/group.py +9 -9
- waldiez/exporting/chats/utils/nested.py +7 -7
- waldiez/exporting/chats/utils/sequential.py +1 -1
- waldiez/exporting/chats/utils/single.py +2 -2
- waldiez/exporting/core/content.py +7 -7
- waldiez/exporting/core/context.py +5 -3
- waldiez/exporting/core/exporter.py +5 -3
- waldiez/exporting/core/exporters.py +2 -2
- waldiez/exporting/core/extras/agent_extras/captain_extras.py +2 -2
- waldiez/exporting/core/extras/agent_extras/group_manager_extras.py +2 -2
- waldiez/exporting/core/extras/agent_extras/rag_user_extras.py +2 -2
- waldiez/exporting/core/extras/agent_extras/standard_extras.py +3 -8
- waldiez/exporting/core/extras/base.py +7 -5
- waldiez/exporting/core/extras/flow_extras.py +4 -5
- waldiez/exporting/core/extras/model_extras.py +2 -2
- waldiez/exporting/core/extras/path_resolver.py +1 -2
- waldiez/exporting/core/extras/serializer.py +2 -2
- waldiez/exporting/core/protocols.py +6 -5
- waldiez/exporting/core/result.py +25 -28
- waldiez/exporting/core/types.py +10 -10
- waldiez/exporting/core/utils/llm_config.py +2 -2
- waldiez/exporting/core/validation.py +10 -11
- waldiez/exporting/flow/execution_generator.py +98 -10
- waldiez/exporting/flow/exporter.py +2 -2
- waldiez/exporting/flow/factory.py +2 -2
- waldiez/exporting/flow/file_generator.py +4 -2
- waldiez/exporting/flow/merger.py +5 -3
- waldiez/exporting/flow/orchestrator.py +72 -2
- waldiez/exporting/flow/utils/common.py +5 -5
- waldiez/exporting/flow/utils/importing.py +6 -7
- waldiez/exporting/flow/utils/linting.py +25 -9
- waldiez/exporting/flow/utils/logging.py +2 -2
- waldiez/exporting/models/exporter.py +8 -8
- waldiez/exporting/models/processor.py +5 -5
- waldiez/exporting/tools/exporter.py +2 -2
- waldiez/exporting/tools/processor.py +7 -4
- waldiez/io/__init__.py +8 -4
- waldiez/io/_ws.py +10 -6
- waldiez/io/models/constants.py +10 -10
- waldiez/io/models/content/audio.py +1 -0
- waldiez/io/models/content/base.py +20 -18
- waldiez/io/models/content/file.py +1 -0
- waldiez/io/models/content/image.py +1 -0
- waldiez/io/models/content/text.py +1 -0
- waldiez/io/models/content/video.py +1 -0
- waldiez/io/models/user_input.py +10 -5
- waldiez/io/models/user_response.py +17 -16
- waldiez/io/mqtt.py +18 -31
- waldiez/io/redis.py +18 -22
- waldiez/io/structured.py +52 -53
- waldiez/io/utils.py +3 -0
- waldiez/io/ws.py +5 -1
- waldiez/logger.py +16 -3
- waldiez/models/agents/__init__.py +3 -0
- waldiez/models/agents/agent/agent.py +23 -16
- waldiez/models/agents/agent/agent_data.py +25 -22
- waldiez/models/agents/agent/code_execution.py +9 -11
- waldiez/models/agents/agent/termination_message.py +10 -12
- waldiez/models/agents/agent/update_system_message.py +2 -4
- waldiez/models/agents/agents.py +8 -8
- waldiez/models/agents/assistant/assistant.py +6 -3
- waldiez/models/agents/assistant/assistant_data.py +2 -2
- waldiez/models/agents/captain/captain_agent.py +7 -4
- waldiez/models/agents/captain/captain_agent_data.py +5 -7
- waldiez/models/agents/doc_agent/doc_agent.py +7 -4
- waldiez/models/agents/doc_agent/doc_agent_data.py +9 -10
- waldiez/models/agents/doc_agent/rag_query_engine.py +10 -12
- waldiez/models/agents/extra_requirements.py +3 -3
- waldiez/models/agents/group_manager/group_manager.py +12 -7
- waldiez/models/agents/group_manager/group_manager_data.py +13 -12
- waldiez/models/agents/group_manager/speakers.py +17 -19
- waldiez/models/agents/rag_user_proxy/rag_user_proxy.py +7 -4
- waldiez/models/agents/rag_user_proxy/rag_user_proxy_data.py +4 -1
- waldiez/models/agents/rag_user_proxy/retrieve_config.py +69 -63
- waldiez/models/agents/rag_user_proxy/vector_db_config.py +19 -19
- waldiez/models/agents/reasoning/reasoning_agent.py +7 -4
- waldiez/models/agents/reasoning/reasoning_agent_data.py +3 -2
- waldiez/models/agents/reasoning/reasoning_agent_reason_config.py +8 -8
- waldiez/models/agents/user_proxy/user_proxy.py +6 -3
- waldiez/models/agents/user_proxy/user_proxy_data.py +1 -1
- waldiez/models/chat/chat.py +27 -20
- waldiez/models/chat/chat_data.py +22 -19
- waldiez/models/chat/chat_message.py +9 -9
- waldiez/models/chat/chat_nested.py +9 -9
- waldiez/models/chat/chat_summary.py +6 -6
- waldiez/models/common/__init__.py +2 -0
- waldiez/models/common/ag2_version.py +2 -0
- waldiez/models/common/dict_utils.py +8 -6
- waldiez/models/common/handoff.py +18 -17
- waldiez/models/common/method_utils.py +7 -7
- waldiez/models/common/naming.py +49 -0
- waldiez/models/flow/flow.py +11 -6
- waldiez/models/flow/flow_data.py +23 -17
- waldiez/models/flow/info.py +3 -3
- waldiez/models/flow/naming.py +2 -1
- waldiez/models/model/_aws.py +11 -13
- waldiez/models/model/_llm.py +5 -0
- waldiez/models/model/_price.py +2 -4
- waldiez/models/model/extra_requirements.py +1 -3
- waldiez/models/model/model.py +2 -2
- waldiez/models/model/model_data.py +21 -21
- waldiez/models/tool/extra_requirements.py +2 -4
- waldiez/models/tool/predefined/_duckduckgo.py +1 -0
- waldiez/models/tool/predefined/_email.py +1 -0
- waldiez/models/tool/predefined/_google.py +1 -0
- waldiez/models/tool/predefined/_perplexity.py +1 -0
- waldiez/models/tool/predefined/_searxng.py +1 -0
- waldiez/models/tool/predefined/_tavily.py +1 -0
- waldiez/models/tool/predefined/_wikipedia.py +1 -0
- waldiez/models/tool/predefined/_youtube.py +1 -0
- waldiez/models/tool/tool.py +8 -5
- waldiez/models/tool/tool_data.py +2 -2
- waldiez/models/waldiez.py +152 -4
- waldiez/runner.py +11 -5
- waldiez/running/async_utils.py +192 -0
- waldiez/running/base_runner.py +117 -264
- waldiez/running/dir_utils.py +52 -0
- waldiez/running/environment.py +10 -44
- waldiez/running/events_mixin.py +252 -0
- waldiez/running/exceptions.py +20 -0
- waldiez/running/gen_seq_diagram.py +18 -15
- waldiez/running/io_utils.py +216 -0
- waldiez/running/protocol.py +11 -5
- waldiez/running/requirements_mixin.py +65 -0
- waldiez/running/results_mixin.py +926 -0
- waldiez/running/standard_runner.py +22 -25
- waldiez/running/step_by_step/breakpoints_mixin.py +192 -60
- waldiez/running/step_by_step/command_handler.py +3 -0
- waldiez/running/step_by_step/events_processor.py +194 -14
- waldiez/running/step_by_step/step_by_step_models.py +110 -43
- waldiez/running/step_by_step/step_by_step_runner.py +107 -57
- waldiez/running/subprocess_runner/__base__.py +9 -1
- waldiez/running/subprocess_runner/_async_runner.py +5 -3
- waldiez/running/subprocess_runner/_sync_runner.py +6 -2
- waldiez/running/subprocess_runner/runner.py +39 -23
- waldiez/running/timeline_processor.py +1 -1
- waldiez/utils/__init__.py +2 -0
- waldiez/utils/conflict_checker.py +4 -4
- waldiez/utils/python_manager.py +415 -0
- waldiez/ws/_file_handler.py +18 -18
- waldiez/ws/_mock.py +2 -1
- waldiez/ws/cli.py +36 -12
- waldiez/ws/client_manager.py +35 -27
- waldiez/ws/errors.py +3 -0
- waldiez/ws/models.py +43 -52
- waldiez/ws/reloader.py +12 -4
- waldiez/ws/server.py +85 -55
- waldiez/ws/session_manager.py +8 -9
- waldiez/ws/session_stats.py +1 -1
- waldiez/ws/utils.py +4 -1
- {waldiez-0.6.0.dist-info → waldiez-0.6.1.dist-info}/METADATA +82 -93
- waldiez-0.6.1.dist-info/RECORD +254 -0
- waldiez/running/post_run.py +0 -186
- waldiez/running/pre_run.py +0 -281
- waldiez/running/run_results.py +0 -14
- waldiez/running/utils.py +0 -625
- waldiez-0.6.0.dist-info/RECORD +0 -251
- {waldiez-0.6.0.dist-info → waldiez-0.6.1.dist-info}/WHEEL +0 -0
- {waldiez-0.6.0.dist-info → waldiez-0.6.1.dist-info}/entry_points.txt +0 -0
- {waldiez-0.6.0.dist-info → waldiez-0.6.1.dist-info}/licenses/LICENSE +0 -0
- {waldiez-0.6.0.dist-info → waldiez-0.6.1.dist-info}/licenses/NOTICE.md +0 -0
waldiez/io/redis.py
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
# flake8: noqa: E501
|
|
5
5
|
# pylint: disable=too-many-try-statements,broad-exception-caught
|
|
6
6
|
# pylint: disable=line-too-long,duplicate-code
|
|
7
|
+
# pyright: reportMissingTypeStubs=false,reportUnknownArgumentType=false
|
|
8
|
+
# pyright: reportUnknownMemberType=false
|
|
7
9
|
|
|
8
10
|
"""A Redis I/O stream for handling print and input messages."""
|
|
9
11
|
|
|
@@ -12,16 +14,10 @@ import logging
|
|
|
12
14
|
import time
|
|
13
15
|
import traceback as tb
|
|
14
16
|
import uuid
|
|
17
|
+
from collections.abc import Awaitable
|
|
15
18
|
from pathlib import Path
|
|
16
19
|
from types import TracebackType
|
|
17
|
-
from typing import
|
|
18
|
-
TYPE_CHECKING,
|
|
19
|
-
Any,
|
|
20
|
-
Awaitable,
|
|
21
|
-
Callable,
|
|
22
|
-
Optional,
|
|
23
|
-
Type,
|
|
24
|
-
)
|
|
20
|
+
from typing import TYPE_CHECKING, Any, Callable
|
|
25
21
|
|
|
26
22
|
try:
|
|
27
23
|
import redis
|
|
@@ -60,10 +56,10 @@ class RedisIOStream(IOStream):
|
|
|
60
56
|
redis: Redis
|
|
61
57
|
task_id: str
|
|
62
58
|
input_timeout: int
|
|
63
|
-
on_input_request:
|
|
64
|
-
|
|
59
|
+
on_input_request: Callable[[str, str, str], None] | None
|
|
60
|
+
on_input_response: Callable[[str, str], None] | None
|
|
65
61
|
max_stream_size: int
|
|
66
|
-
|
|
62
|
+
task_output_stream: str
|
|
67
63
|
input_request_channel: str
|
|
68
64
|
input_response_channel: str
|
|
69
65
|
|
|
@@ -73,8 +69,8 @@ class RedisIOStream(IOStream):
|
|
|
73
69
|
task_id: str | None = None,
|
|
74
70
|
input_timeout: int = 120,
|
|
75
71
|
max_stream_size: int = 1000,
|
|
76
|
-
on_input_request:
|
|
77
|
-
on_input_response:
|
|
72
|
+
on_input_request: Callable[[str, str, str], None] | None = None,
|
|
73
|
+
on_input_response: Callable[[str, str], None] | None = None,
|
|
78
74
|
redis_connection_kwargs: dict[str, Any] | None = None,
|
|
79
75
|
uploads_root: Path | str | None = None,
|
|
80
76
|
) -> None:
|
|
@@ -127,7 +123,7 @@ class RedisIOStream(IOStream):
|
|
|
127
123
|
|
|
128
124
|
def __exit__(
|
|
129
125
|
self,
|
|
130
|
-
exc_type:
|
|
126
|
+
exc_type: type[Exception] | None,
|
|
131
127
|
exc_value: Exception | None,
|
|
132
128
|
traceback: TracebackType | None,
|
|
133
129
|
) -> None:
|
|
@@ -165,7 +161,7 @@ class RedisIOStream(IOStream):
|
|
|
165
161
|
"""
|
|
166
162
|
LOG.debug("Sending print message: %s", payload)
|
|
167
163
|
RedisIOStream.try_do(
|
|
168
|
-
self.redis.xadd,
|
|
164
|
+
self.redis.xadd,
|
|
169
165
|
self.task_output_stream,
|
|
170
166
|
payload,
|
|
171
167
|
maxlen=self.max_stream_size,
|
|
@@ -182,7 +178,7 @@ class RedisIOStream(IOStream):
|
|
|
182
178
|
"""
|
|
183
179
|
LOG.debug("Sending print message: %s", payload)
|
|
184
180
|
RedisIOStream.try_do(
|
|
185
|
-
self.redis.xadd,
|
|
181
|
+
self.redis.xadd,
|
|
186
182
|
self.common_output_stream,
|
|
187
183
|
payload,
|
|
188
184
|
maxlen=self.max_stream_size,
|
|
@@ -414,7 +410,7 @@ class RedisIOStream(IOStream):
|
|
|
414
410
|
)
|
|
415
411
|
|
|
416
412
|
@staticmethod
|
|
417
|
-
def _extract_message_data(data: Any) ->
|
|
413
|
+
def _extract_message_data(data: Any) -> dict[str, Any] | None:
|
|
418
414
|
"""Extract and parse the message data field."""
|
|
419
415
|
message_data = data
|
|
420
416
|
|
|
@@ -431,7 +427,7 @@ class RedisIOStream(IOStream):
|
|
|
431
427
|
LOG.error("Invalid message data format: %s", message_data)
|
|
432
428
|
return None
|
|
433
429
|
|
|
434
|
-
return message_data # pyright: ignore
|
|
430
|
+
return message_data # pyright: ignore[reportUnknownVariableType]
|
|
435
431
|
|
|
436
432
|
@staticmethod
|
|
437
433
|
def _message_has_required_fields(message_data: dict[str, Any]) -> bool:
|
|
@@ -445,7 +441,7 @@ class RedisIOStream(IOStream):
|
|
|
445
441
|
@staticmethod
|
|
446
442
|
def _process_nested_data(
|
|
447
443
|
message_data: dict[str, Any],
|
|
448
|
-
) ->
|
|
444
|
+
) -> dict[str, Any] | None:
|
|
449
445
|
"""Process nested JSON data if present."""
|
|
450
446
|
# Create a copy to avoid modifying the original
|
|
451
447
|
processed_data = message_data.copy()
|
|
@@ -467,7 +463,7 @@ class RedisIOStream(IOStream):
|
|
|
467
463
|
@staticmethod
|
|
468
464
|
def _create_user_response(
|
|
469
465
|
message_data: dict[str, Any],
|
|
470
|
-
) ->
|
|
466
|
+
) -> UserResponse | None:
|
|
471
467
|
"""Create UserResponse object from validated data."""
|
|
472
468
|
try:
|
|
473
469
|
return UserResponse.model_validate(message_data)
|
|
@@ -684,7 +680,7 @@ class RedisIOStream(IOStream):
|
|
|
684
680
|
"""
|
|
685
681
|
for key in redis_client.scan_iter("task:*:output", count=100):
|
|
686
682
|
RedisIOStream.try_do(
|
|
687
|
-
redis_client.xtrim,
|
|
683
|
+
redis_client.xtrim,
|
|
688
684
|
key,
|
|
689
685
|
maxlen=maxlen,
|
|
690
686
|
approximate=approximate,
|
|
@@ -760,7 +756,7 @@ class RedisIOStream(IOStream):
|
|
|
760
756
|
): # pragma: no branch
|
|
761
757
|
before = await redis_client.xlen(key)
|
|
762
758
|
await RedisIOStream.a_try_do(
|
|
763
|
-
redis_client.xtrim,
|
|
759
|
+
redis_client.xtrim,
|
|
764
760
|
key,
|
|
765
761
|
maxlen=maxlen,
|
|
766
762
|
approximate=approximate,
|
waldiez/io/structured.py
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
# SPDX-License-Identifier: Apache-2.0.
|
|
2
2
|
# Copyright (c) 2024 - 2025 Waldiez and contributors.
|
|
3
3
|
|
|
4
|
+
# pyright: reportMissingTypeStubs=false,reportUnknownVariableType=false
|
|
5
|
+
# pyright: reportUnknownArgumentType=false,reportArgumentType=false
|
|
6
|
+
# pyright: reportUnknownMemberType=false,reportAttributeAccessIssue=false
|
|
7
|
+
# pyright: reportPrivateImportUsage=false
|
|
8
|
+
|
|
4
9
|
"""Structured I/O stream for JSON-based communication over stdin/stdout."""
|
|
5
10
|
|
|
6
11
|
import json
|
|
7
|
-
import queue
|
|
8
12
|
import sys
|
|
9
|
-
import threading
|
|
10
13
|
from getpass import getpass
|
|
11
14
|
from pathlib import Path
|
|
12
15
|
from typing import Any
|
|
@@ -63,14 +66,16 @@ class StructuredIOStream(IOStream):
|
|
|
63
66
|
The data to print.
|
|
64
67
|
kwargs : Any
|
|
65
68
|
"""
|
|
66
|
-
sep = kwargs.get("sep", " ")
|
|
67
|
-
end = kwargs.get("end", "\n")
|
|
68
|
-
flush = kwargs.get("flush", True)
|
|
69
|
-
payload_type = kwargs.get("type", "print")
|
|
69
|
+
sep = str(kwargs.get("sep", " "))
|
|
70
|
+
end = str(kwargs.get("end", "\n"))
|
|
71
|
+
flush = bool(kwargs.get("flush", True))
|
|
72
|
+
payload_type = str(kwargs.get("type", "print"))
|
|
70
73
|
message = sep.join(map(str, args))
|
|
71
74
|
if len(args) == 1 and isinstance(args[0], dict):
|
|
72
|
-
message = args[0] #
|
|
73
|
-
|
|
75
|
+
message = args[0] # type: ignore
|
|
76
|
+
payload_type_ = message.get("type", payload_type) # type: ignore
|
|
77
|
+
if isinstance(payload_type_, str):
|
|
78
|
+
payload_type = payload_type_
|
|
74
79
|
is_dumped = True
|
|
75
80
|
else:
|
|
76
81
|
is_dumped, message = is_json_dumped(message)
|
|
@@ -83,27 +88,35 @@ class StructuredIOStream(IOStream):
|
|
|
83
88
|
# "data": message,
|
|
84
89
|
}
|
|
85
90
|
if isinstance(message, dict):
|
|
86
|
-
payload.update(message)
|
|
91
|
+
payload.update(message)
|
|
92
|
+
end = ""
|
|
87
93
|
else:
|
|
88
94
|
payload["data"] = message
|
|
89
95
|
if "type" not in payload:
|
|
90
96
|
payload["type"] = payload_type
|
|
91
97
|
else:
|
|
92
|
-
print_message = PrintMessage(data=message)
|
|
98
|
+
print_message = PrintMessage(data=message)
|
|
93
99
|
payload = print_message.model_dump(mode="json", fallback=str)
|
|
94
100
|
payload["type"] = payload_type
|
|
95
101
|
dumped = json.dumps(payload, default=str, ensure_ascii=False) + end
|
|
96
|
-
|
|
102
|
+
file = kwargs.get("file", None)
|
|
103
|
+
if file and file in [
|
|
97
104
|
sys.stderr,
|
|
98
105
|
sys.__stderr__,
|
|
99
106
|
sys.stdout,
|
|
100
107
|
sys.__stdout__,
|
|
101
108
|
]:
|
|
102
|
-
print(dumped, file=
|
|
109
|
+
print(dumped, file=file, flush=flush)
|
|
103
110
|
else:
|
|
104
111
|
print(dumped, flush=flush)
|
|
105
112
|
|
|
106
|
-
def input(
|
|
113
|
+
def input(
|
|
114
|
+
self,
|
|
115
|
+
prompt: str = "",
|
|
116
|
+
*,
|
|
117
|
+
password: bool = False,
|
|
118
|
+
request_id: str | None = None,
|
|
119
|
+
) -> str:
|
|
107
120
|
"""Structured input from stdin.
|
|
108
121
|
|
|
109
122
|
Parameters
|
|
@@ -112,13 +125,15 @@ class StructuredIOStream(IOStream):
|
|
|
112
125
|
The prompt to display. Defaults to "".
|
|
113
126
|
password : bool, optional
|
|
114
127
|
Whether to read a password. Defaults to False.
|
|
128
|
+
request_id : str, optional
|
|
129
|
+
The request id. If not provided, a new will be generated.
|
|
115
130
|
|
|
116
131
|
Returns
|
|
117
132
|
-------
|
|
118
133
|
str
|
|
119
134
|
The line read from the input stream.
|
|
120
135
|
"""
|
|
121
|
-
|
|
136
|
+
input_request_id = request_id or gen_id()
|
|
122
137
|
prompt = prompt or ">"
|
|
123
138
|
if not prompt or prompt in [">", "> "]: # pragma: no cover
|
|
124
139
|
# if the prompt is just ">" or "> ",
|
|
@@ -129,15 +144,17 @@ class StructuredIOStream(IOStream):
|
|
|
129
144
|
input_type = "debug"
|
|
130
145
|
self._send_input_request(
|
|
131
146
|
prompt,
|
|
132
|
-
|
|
147
|
+
input_request_id,
|
|
133
148
|
password,
|
|
134
149
|
input_type=input_type,
|
|
135
150
|
)
|
|
136
|
-
user_input_raw = self._read_user_input(
|
|
137
|
-
|
|
151
|
+
user_input_raw = self._read_user_input(
|
|
152
|
+
prompt, password, input_request_id
|
|
153
|
+
)
|
|
154
|
+
response = self._handle_user_input(user_input_raw, input_request_id)
|
|
138
155
|
user_response = response.to_string(
|
|
139
156
|
uploads_root=self.uploads_root,
|
|
140
|
-
base_name=
|
|
157
|
+
base_name=input_request_id,
|
|
141
158
|
)
|
|
142
159
|
return user_response
|
|
143
160
|
|
|
@@ -163,11 +180,8 @@ class StructuredIOStream(IOStream):
|
|
|
163
180
|
content_block["content"] = try_parse_maybe_serialized(
|
|
164
181
|
inner_content
|
|
165
182
|
)
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
flush=True,
|
|
169
|
-
file=sys.stdout,
|
|
170
|
-
)
|
|
183
|
+
message_dump["timestamp"] = now()
|
|
184
|
+
print(json.dumps(message_dump, default=str), flush=True)
|
|
171
185
|
|
|
172
186
|
# noinspection PyMethodMayBeStatic
|
|
173
187
|
# pylint: disable=no-self-use
|
|
@@ -197,33 +211,15 @@ class StructuredIOStream(IOStream):
|
|
|
197
211
|
password: bool,
|
|
198
212
|
request_id: str,
|
|
199
213
|
) -> str:
|
|
200
|
-
input_queue: queue.Queue[str] = queue.Queue()
|
|
201
|
-
|
|
202
|
-
def read_input() -> None:
|
|
203
|
-
"""Read user input from stdin."""
|
|
204
|
-
try:
|
|
205
|
-
user_input = (
|
|
206
|
-
getpass(prompt).strip()
|
|
207
|
-
if password
|
|
208
|
-
else input(prompt).strip()
|
|
209
|
-
)
|
|
210
|
-
input_queue.put(user_input)
|
|
211
|
-
except EOFError:
|
|
212
|
-
input_queue.put("")
|
|
213
|
-
|
|
214
|
-
input_thread = threading.Thread(target=read_input, daemon=True)
|
|
215
|
-
input_thread.start()
|
|
216
|
-
|
|
217
214
|
try:
|
|
218
|
-
return
|
|
219
|
-
|
|
220
|
-
|
|
215
|
+
return (
|
|
216
|
+
getpass(prompt).strip() if password else input(prompt).strip()
|
|
217
|
+
)
|
|
218
|
+
except EOFError:
|
|
221
219
|
return ""
|
|
222
220
|
except BaseException as e:
|
|
223
221
|
self._send_error_message(request_id, str(e))
|
|
224
222
|
return ""
|
|
225
|
-
finally:
|
|
226
|
-
input_thread.join(timeout=1)
|
|
227
223
|
|
|
228
224
|
def _send_timeout_message(self, request_id: str) -> None:
|
|
229
225
|
timeout_payload = {
|
|
@@ -330,7 +326,10 @@ class StructuredIOStream(IOStream):
|
|
|
330
326
|
response_type = "input_response"
|
|
331
327
|
else:
|
|
332
328
|
response_type = _response_type
|
|
333
|
-
if
|
|
329
|
+
if (
|
|
330
|
+
user_input.get("request_id") == request_id
|
|
331
|
+
or response_type == "debug_input_response"
|
|
332
|
+
):
|
|
334
333
|
# We have a valid response to our request
|
|
335
334
|
data = user_input.get("data")
|
|
336
335
|
if not data:
|
|
@@ -345,7 +344,7 @@ class StructuredIOStream(IOStream):
|
|
|
345
344
|
)
|
|
346
345
|
if isinstance(data, list):
|
|
347
346
|
return self._handle_list_response(
|
|
348
|
-
data,
|
|
347
|
+
data,
|
|
349
348
|
request_id=request_id,
|
|
350
349
|
response_type=response_type,
|
|
351
350
|
)
|
|
@@ -365,7 +364,7 @@ class StructuredIOStream(IOStream):
|
|
|
365
364
|
type=response_type,
|
|
366
365
|
data=self._format_multimedia_response(
|
|
367
366
|
request_id=request_id,
|
|
368
|
-
data=data,
|
|
367
|
+
data=data,
|
|
369
368
|
),
|
|
370
369
|
request_id=request_id,
|
|
371
370
|
)
|
|
@@ -391,7 +390,7 @@ class StructuredIOStream(IOStream):
|
|
|
391
390
|
request_id: str,
|
|
392
391
|
response_type: MessageType,
|
|
393
392
|
) -> UserResponse:
|
|
394
|
-
if len(data) == 0:
|
|
393
|
+
if len(data) == 0:
|
|
395
394
|
# Empty list, return empty response
|
|
396
395
|
return UserResponse(
|
|
397
396
|
type=response_type,
|
|
@@ -400,7 +399,7 @@ class StructuredIOStream(IOStream):
|
|
|
400
399
|
)
|
|
401
400
|
|
|
402
401
|
input_data: list[UserInputData] = []
|
|
403
|
-
for entry in data:
|
|
402
|
+
for entry in data:
|
|
404
403
|
# pylint: disable=broad-exception-caught
|
|
405
404
|
try:
|
|
406
405
|
content = UserInputData.model_validate(entry)
|
|
@@ -435,8 +434,8 @@ class StructuredIOStream(IOStream):
|
|
|
435
434
|
# Create a log message
|
|
436
435
|
got_id: str | None = None
|
|
437
436
|
if isinstance(response, dict):
|
|
438
|
-
got_id = response.get("request_id")
|
|
439
|
-
response_str = str(response)
|
|
437
|
+
got_id = response.get("request_id")
|
|
438
|
+
response_str = str(response)
|
|
440
439
|
message = response_str[:100] + (
|
|
441
440
|
"..." if len(response_str) > 100 else ""
|
|
442
441
|
)
|
|
@@ -481,7 +480,7 @@ class StructuredIOStream(IOStream):
|
|
|
481
480
|
result: list[str] = []
|
|
482
481
|
if "content" in data and isinstance(data["content"], dict):
|
|
483
482
|
return self._format_multimedia_response(
|
|
484
|
-
data=data["content"],
|
|
483
|
+
data=data["content"],
|
|
485
484
|
request_id=request_id,
|
|
486
485
|
)
|
|
487
486
|
|
waldiez/io/utils.py
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# SPDX-License-Identifier: Apache-2.0.
|
|
2
2
|
# Copyright (c) 2024 - 2025 Waldiez and contributors.
|
|
3
3
|
|
|
4
|
+
# pyright: reportMissingTypeStubs=false,reportDeprecated=false
|
|
5
|
+
|
|
4
6
|
"""Utility functions for the waldiez.io package."""
|
|
5
7
|
|
|
6
8
|
import ast
|
|
@@ -15,6 +17,7 @@ from autogen.events import BaseEvent # type: ignore
|
|
|
15
17
|
from autogen.messages import BaseMessage # type: ignore
|
|
16
18
|
|
|
17
19
|
DEBUG_INPUT_PROMPT = (
|
|
20
|
+
# cspell: disable-next-line
|
|
18
21
|
"[Step] (c)ontinue, (r)un, (q)uit, (i)nfo, (h)elp, (st)ats: "
|
|
19
22
|
)
|
|
20
23
|
START_CHAT_PROMPT = "Enter your message to start the conversation: "
|
waldiez/io/ws.py
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
# SPDX-License-Identifier: Apache-2.0.
|
|
2
2
|
# Copyright (c) 2024 - 2025 Waldiez and contributors.
|
|
3
|
+
|
|
3
4
|
# pylint: disable=too-many-try-statements
|
|
5
|
+
# pyright: reportMissingTypeStubs=false,reportReturnType=false
|
|
6
|
+
|
|
4
7
|
"""WebSocket IOStream implementation for AsyncIO."""
|
|
5
8
|
|
|
6
9
|
import asyncio
|
|
@@ -30,12 +33,13 @@ from .utils import (
|
|
|
30
33
|
LOG = logging.getLogger(__name__)
|
|
31
34
|
|
|
32
35
|
if not is_websocket_available(): # pragma: no cover
|
|
33
|
-
|
|
36
|
+
_msg = ( # pylint: disable=invalid-name
|
|
34
37
|
"WebSocket support requires "
|
|
35
38
|
"either the 'websockets' or 'starlette' package. "
|
|
36
39
|
"Please install one of them with "
|
|
37
40
|
"`pip install websockets` or `pip install starlette`."
|
|
38
41
|
)
|
|
42
|
+
raise ImportError(_msg)
|
|
39
43
|
|
|
40
44
|
|
|
41
45
|
class AsyncWebsocketsIOStream(IOStream):
|
waldiez/logger.py
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
# pylint: disable=broad-exception-caught,too-many-try-statements
|
|
4
4
|
"""Waldiez logger."""
|
|
5
5
|
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
6
8
|
import inspect
|
|
7
9
|
import logging
|
|
8
10
|
import os
|
|
@@ -10,13 +12,15 @@ import re
|
|
|
10
12
|
import string
|
|
11
13
|
import threading
|
|
12
14
|
import traceback
|
|
15
|
+
from collections.abc import Mapping
|
|
13
16
|
from datetime import datetime
|
|
14
17
|
from enum import IntEnum
|
|
15
18
|
from pathlib import Path
|
|
16
19
|
from types import TracebackType
|
|
17
|
-
from typing import Any, Callable
|
|
20
|
+
from typing import Any, Callable
|
|
18
21
|
|
|
19
22
|
import click
|
|
23
|
+
from typing_extensions import override
|
|
20
24
|
|
|
21
25
|
HERE = Path(__file__).parent
|
|
22
26
|
|
|
@@ -41,7 +45,7 @@ class WaldiezLogger(logging.Logger):
|
|
|
41
45
|
- logger.info("User {user} has {count} items", user="john", count=5)
|
|
42
46
|
"""
|
|
43
47
|
|
|
44
|
-
_instance:
|
|
48
|
+
_instance: WaldiezLogger | None = None
|
|
45
49
|
_lock: threading.Lock = threading.Lock()
|
|
46
50
|
|
|
47
51
|
_INTERNAL_METHODS = {
|
|
@@ -179,6 +183,7 @@ class WaldiezLogger(logging.Logger):
|
|
|
179
183
|
return self._level_map.get(level.upper(), LogLevel.INFO)
|
|
180
184
|
|
|
181
185
|
# pylint: disable=unused-argument
|
|
186
|
+
@override
|
|
182
187
|
def log(
|
|
183
188
|
self,
|
|
184
189
|
level: int,
|
|
@@ -249,6 +254,7 @@ class WaldiezLogger(logging.Logger):
|
|
|
249
254
|
level_int = self._get_level_number(level)
|
|
250
255
|
self.log(level_int, msg, *args, **kwargs)
|
|
251
256
|
|
|
257
|
+
@override
|
|
252
258
|
def debug(self, msg: Any, *args: Any, **kwargs: Any) -> None:
|
|
253
259
|
"""Log a debug message.
|
|
254
260
|
|
|
@@ -263,6 +269,7 @@ class WaldiezLogger(logging.Logger):
|
|
|
263
269
|
"""
|
|
264
270
|
self.do_log(msg, *args, level="debug", **kwargs)
|
|
265
271
|
|
|
272
|
+
@override
|
|
266
273
|
def info(self, msg: Any, *args: Any, **kwargs: Any) -> None:
|
|
267
274
|
"""Log an informational message.
|
|
268
275
|
|
|
@@ -291,6 +298,7 @@ class WaldiezLogger(logging.Logger):
|
|
|
291
298
|
"""
|
|
292
299
|
self.do_log(msg, *args, level="success", **kwargs)
|
|
293
300
|
|
|
301
|
+
@override
|
|
294
302
|
def warning(self, msg: Any, *args: Any, **kwargs: Any) -> None:
|
|
295
303
|
"""Log a warning message.
|
|
296
304
|
|
|
@@ -305,6 +313,7 @@ class WaldiezLogger(logging.Logger):
|
|
|
305
313
|
"""
|
|
306
314
|
self.do_log(msg, *args, level="warning", **kwargs)
|
|
307
315
|
|
|
316
|
+
@override
|
|
308
317
|
def error(self, msg: Any, *args: Any, **kwargs: Any) -> None:
|
|
309
318
|
"""Log an error message.
|
|
310
319
|
|
|
@@ -319,6 +328,7 @@ class WaldiezLogger(logging.Logger):
|
|
|
319
328
|
"""
|
|
320
329
|
self.do_log(msg, *args, level="error", **kwargs)
|
|
321
330
|
|
|
331
|
+
@override
|
|
322
332
|
def critical(self, msg: Any, *args: Any, **kwargs: Any) -> None:
|
|
323
333
|
"""Log a critical error message.
|
|
324
334
|
|
|
@@ -333,6 +343,7 @@ class WaldiezLogger(logging.Logger):
|
|
|
333
343
|
"""
|
|
334
344
|
self.do_log(msg, *args, level="critical", **kwargs)
|
|
335
345
|
|
|
346
|
+
@override
|
|
336
347
|
def exception(self, msg: Any, *args: Any, **kwargs: Any) -> None:
|
|
337
348
|
"""Log an exception message.
|
|
338
349
|
|
|
@@ -354,6 +365,7 @@ class WaldiezLogger(logging.Logger):
|
|
|
354
365
|
if tb and "NoneType: None" not in tb: # pragma: no branch
|
|
355
366
|
click.echo(click.style(tb, fg="red", dim=True))
|
|
356
367
|
|
|
368
|
+
@override
|
|
357
369
|
def setLevel(self, level: int | str) -> None:
|
|
358
370
|
"""
|
|
359
371
|
Set the logging level.
|
|
@@ -388,10 +400,11 @@ class WaldiezLogger(logging.Logger):
|
|
|
388
400
|
self._level = level_upper
|
|
389
401
|
super().setLevel(self._level_map[level_upper].value)
|
|
390
402
|
else:
|
|
391
|
-
|
|
403
|
+
msg = (
|
|
392
404
|
f"Invalid log level: {level}. "
|
|
393
405
|
f"Valid levels are: {list(self._level_map.keys())}"
|
|
394
406
|
)
|
|
407
|
+
raise ValueError(msg)
|
|
395
408
|
|
|
396
409
|
def get_level(self) -> str:
|
|
397
410
|
"""Get the current logging level.
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# SPDX-License-Identifier: Apache-2.0.
|
|
2
2
|
# Copyright (c) 2024 - 2025 Waldiez and contributors.
|
|
3
|
+
|
|
3
4
|
# pylint: disable=too-many-public-methods
|
|
5
|
+
# pyright: reportArgumentType=false
|
|
4
6
|
"""Base agent class to be inherited by all agents."""
|
|
5
7
|
|
|
6
8
|
import warnings
|
|
@@ -71,7 +73,7 @@ class WaldiezAgent(WaldiezBase):
|
|
|
71
73
|
title="Type",
|
|
72
74
|
description="The type of the 'node' in a graph.",
|
|
73
75
|
),
|
|
74
|
-
]
|
|
76
|
+
]
|
|
75
77
|
agent_type: Annotated[
|
|
76
78
|
WaldiezAgentType,
|
|
77
79
|
Field(
|
|
@@ -93,7 +95,7 @@ class WaldiezAgent(WaldiezBase):
|
|
|
93
95
|
title="Description",
|
|
94
96
|
description="The description of the agent",
|
|
95
97
|
),
|
|
96
|
-
]
|
|
98
|
+
]
|
|
97
99
|
tags: Annotated[
|
|
98
100
|
list[str],
|
|
99
101
|
Field(
|
|
@@ -101,7 +103,7 @@ class WaldiezAgent(WaldiezBase):
|
|
|
101
103
|
description="Tags of the agent",
|
|
102
104
|
default_factory=list,
|
|
103
105
|
),
|
|
104
|
-
]
|
|
106
|
+
]
|
|
105
107
|
requirements: Annotated[
|
|
106
108
|
list[str],
|
|
107
109
|
Field(
|
|
@@ -109,7 +111,7 @@ class WaldiezAgent(WaldiezBase):
|
|
|
109
111
|
description="Python requirements for the agent",
|
|
110
112
|
default_factory=list,
|
|
111
113
|
),
|
|
112
|
-
]
|
|
114
|
+
]
|
|
113
115
|
created_at: Annotated[
|
|
114
116
|
str,
|
|
115
117
|
Field(
|
|
@@ -131,7 +133,7 @@ class WaldiezAgent(WaldiezBase):
|
|
|
131
133
|
Field(
|
|
132
134
|
title="Data",
|
|
133
135
|
description="The data (properties) of the agent",
|
|
134
|
-
default_factory=WaldiezAgentData,
|
|
136
|
+
default_factory=WaldiezAgentData,
|
|
135
137
|
),
|
|
136
138
|
]
|
|
137
139
|
|
|
@@ -139,7 +141,7 @@ class WaldiezAgent(WaldiezBase):
|
|
|
139
141
|
list[WaldiezHandoff],
|
|
140
142
|
Field(
|
|
141
143
|
init=False, # this is not a field in the constructor
|
|
142
|
-
default_factory=list
|
|
144
|
+
default_factory=list,
|
|
143
145
|
title="Handoffs",
|
|
144
146
|
description=(
|
|
145
147
|
"A list of handoffs (target ids) to register. "
|
|
@@ -148,10 +150,10 @@ class WaldiezAgent(WaldiezBase):
|
|
|
148
150
|
),
|
|
149
151
|
] = []
|
|
150
152
|
|
|
151
|
-
_checked_nested_chats: Annotated[bool, Field(
|
|
153
|
+
_checked_nested_chats: Annotated[bool, Field(default=False, init=False)] = (
|
|
152
154
|
False
|
|
153
155
|
)
|
|
154
|
-
_checked_handoffs: Annotated[bool, Field(
|
|
156
|
+
_checked_handoffs: Annotated[bool, Field(default=False, init=False)] = False
|
|
155
157
|
|
|
156
158
|
@property
|
|
157
159
|
def args_to_skip(self) -> list[str]:
|
|
@@ -190,10 +192,11 @@ class WaldiezAgent(WaldiezBase):
|
|
|
190
192
|
If handoffs have not been gathered yet.
|
|
191
193
|
"""
|
|
192
194
|
if not self._checked_handoffs:
|
|
193
|
-
|
|
195
|
+
msg = (
|
|
194
196
|
"Handoffs have not been gathered yet. "
|
|
195
197
|
"Call gather_handoffs() first."
|
|
196
198
|
)
|
|
199
|
+
raise RuntimeError(msg)
|
|
197
200
|
return self._handoffs
|
|
198
201
|
|
|
199
202
|
@field_validator("agent_type")
|
|
@@ -392,7 +395,7 @@ class WaldiezAgent(WaldiezBase):
|
|
|
392
395
|
return class_name # pragma: no cover
|
|
393
396
|
|
|
394
397
|
@property
|
|
395
|
-
def ag2_imports(self) -> set[str]:
|
|
398
|
+
def ag2_imports(self) -> set[str]: # pylint: disable=too-complex
|
|
396
399
|
"""Return the AG2 imports of the agent.
|
|
397
400
|
|
|
398
401
|
Returns
|
|
@@ -413,25 +416,28 @@ class WaldiezAgent(WaldiezBase):
|
|
|
413
416
|
case "UserProxyAgent":
|
|
414
417
|
imports.add("from autogen import UserProxyAgent")
|
|
415
418
|
case "RetrieveUserProxyAgent":
|
|
416
|
-
|
|
419
|
+
_imp = (
|
|
417
420
|
"from autogen.agentchat.contrib.retrieve_user_proxy_agent "
|
|
418
421
|
"import RetrieveUserProxyAgent"
|
|
419
422
|
)
|
|
423
|
+
imports.add(_imp)
|
|
420
424
|
case "MultimodalConversableAgent":
|
|
421
|
-
|
|
425
|
+
_imp = (
|
|
422
426
|
"from "
|
|
423
427
|
"autogen.agentchat.contrib.multimodal_conversable_agent "
|
|
424
428
|
"import MultimodalConversableAgent"
|
|
425
429
|
)
|
|
430
|
+
imports.add(_imp)
|
|
426
431
|
case "ReasoningAgent":
|
|
427
432
|
imports.add(
|
|
428
433
|
"from autogen.agents.experimental import ReasoningAgent"
|
|
429
434
|
)
|
|
430
435
|
case "CaptainAgent":
|
|
431
|
-
|
|
436
|
+
_imp = (
|
|
432
437
|
"from autogen.agentchat.contrib.captainagent "
|
|
433
438
|
"import CaptainAgent"
|
|
434
439
|
)
|
|
440
|
+
imports.add(_imp)
|
|
435
441
|
case "GroupChatManager": # pragma: no branch
|
|
436
442
|
imports.add("from autogen import GroupChat")
|
|
437
443
|
imports.add("from autogen.agentchat import GroupChatManager")
|
|
@@ -443,10 +449,11 @@ class WaldiezAgent(WaldiezBase):
|
|
|
443
449
|
case "ConversableAgent":
|
|
444
450
|
imports.add("from autogen import ConversableAgent")
|
|
445
451
|
case _: # pragma: no cover
|
|
446
|
-
|
|
452
|
+
msg = (
|
|
447
453
|
f"Unknown agent class: {agent_class}. "
|
|
448
454
|
"Please implement the imports for this class."
|
|
449
455
|
)
|
|
456
|
+
raise ValueError(msg)
|
|
450
457
|
return imports
|
|
451
458
|
|
|
452
459
|
def validate_linked_tools(
|
|
@@ -678,8 +685,8 @@ class WaldiezAgent(WaldiezBase):
|
|
|
678
685
|
nested_chat = WaldiezAgentNestedChat(
|
|
679
686
|
triggered_by=triggered_by,
|
|
680
687
|
messages=messages,
|
|
681
|
-
condition=chat_with_condition.condition,
|
|
682
|
-
available=chat_with_condition.available,
|
|
688
|
+
condition=chat_with_condition.condition,
|
|
689
|
+
available=chat_with_condition.available,
|
|
683
690
|
)
|
|
684
691
|
self.data.nested_chats.append(nested_chat)
|
|
685
692
|
|