waldiez 0.5.10__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 +19 -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 +15 -16
- 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 +40 -24
- waldiez/exporting/agent/extras/group_member_extras.py +6 -5
- waldiez/exporting/agent/extras/handoffs/after_work.py +2 -1
- waldiez/exporting/agent/extras/handoffs/available.py +2 -1
- waldiez/exporting/agent/extras/handoffs/condition.py +3 -2
- waldiez/exporting/agent/extras/handoffs/handoff.py +2 -1
- waldiez/exporting/agent/extras/handoffs/target.py +7 -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/agent/termination.py +1 -0
- 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/constants.py +3 -1
- 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 +13 -11
- waldiez/exporting/core/protocols.py +6 -5
- waldiez/exporting/core/result.py +25 -28
- waldiez/exporting/core/types.py +11 -10
- waldiez/exporting/core/utils/llm_config.py +4 -4
- waldiez/exporting/core/validation.py +10 -11
- waldiez/exporting/flow/execution_generator.py +99 -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 +6 -6
- waldiez/exporting/flow/utils/importing.py +7 -8
- waldiez/exporting/flow/utils/linting.py +25 -9
- waldiez/exporting/flow/utils/logging.py +5 -77
- 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 +11 -5
- waldiez/io/_ws.py +12 -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 +122 -70
- waldiez/io/utils.py +19 -10
- waldiez/io/ws.py +7 -3
- waldiez/logger.py +16 -3
- waldiez/models/agents/__init__.py +3 -0
- waldiez/models/agents/agent/agent.py +25 -17
- 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 +28 -20
- waldiez/models/chat/chat_data.py +22 -21
- 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/base.py +2 -0
- waldiez/models/common/dict_utils.py +8 -6
- waldiez/models/common/handoff.py +20 -17
- waldiez/models/common/method_utils.py +9 -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 +8 -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 +4 -0
- waldiez/models/tool/predefined/_google.py +1 -0
- waldiez/models/tool/predefined/_perplexity.py +2 -1
- waldiez/models/tool/predefined/_searxng.py +2 -1
- waldiez/models/tool/predefined/_tavily.py +1 -0
- waldiez/models/tool/predefined/_wikipedia.py +2 -1
- 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 +155 -241
- 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 +24 -27
- waldiez/running/step_by_step/breakpoints_mixin.py +503 -47
- waldiez/running/step_by_step/command_handler.py +154 -0
- waldiez/running/step_by_step/events_processor.py +379 -0
- waldiez/running/step_by_step/step_by_step_models.py +425 -41
- waldiez/running/step_by_step/step_by_step_runner.py +437 -382
- waldiez/running/subprocess_runner/__base__.py +13 -8
- waldiez/running/subprocess_runner/_async_runner.py +6 -4
- waldiez/running/subprocess_runner/_sync_runner.py +11 -6
- waldiez/running/subprocess_runner/runner.py +48 -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/__init__.py +8 -7
- waldiez/ws/_file_handler.py +18 -20
- waldiez/ws/_mock.py +75 -0
- waldiez/ws/cli.py +58 -10
- waldiez/ws/client_manager.py +77 -53
- waldiez/ws/errors.py +3 -0
- waldiez/ws/models.py +61 -53
- waldiez/ws/reloader.py +33 -4
- waldiez/ws/server.py +121 -52
- waldiez/ws/session_manager.py +8 -9
- waldiez/ws/session_stats.py +1 -1
- waldiez/ws/utils.py +33 -5
- {waldiez-0.5.10.dist-info → waldiez-0.6.1.dist-info}/METADATA +107 -109
- waldiez-0.6.1.dist-info/RECORD +254 -0
- waldiez/running/post_run.py +0 -180
- waldiez/running/pre_run.py +0 -159
- waldiez/running/run_results.py +0 -14
- waldiez/running/utils.py +0 -511
- waldiez-0.5.10.dist-info/RECORD +0 -248
- {waldiez-0.5.10.dist-info → waldiez-0.6.1.dist-info}/WHEEL +0 -0
- {waldiez-0.5.10.dist-info → waldiez-0.6.1.dist-info}/entry_points.txt +0 -0
- {waldiez-0.5.10.dist-info → waldiez-0.6.1.dist-info}/licenses/LICENSE +0 -0
- {waldiez-0.5.10.dist-info → waldiez-0.6.1.dist-info}/licenses/NOTICE.md +0 -0
waldiez/io/structured.py
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
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
|
|
13
|
-
from uuid import uuid4
|
|
14
16
|
|
|
15
17
|
from autogen.events import BaseEvent # type: ignore
|
|
16
18
|
from autogen.io import IOStream # type: ignore
|
|
@@ -23,6 +25,9 @@ from .models import (
|
|
|
23
25
|
UserResponse,
|
|
24
26
|
)
|
|
25
27
|
from .utils import (
|
|
28
|
+
DEBUG_INPUT_PROMPT,
|
|
29
|
+
START_CHAT_PROMPT,
|
|
30
|
+
MessageType,
|
|
26
31
|
gen_id,
|
|
27
32
|
get_image,
|
|
28
33
|
get_message_dump,
|
|
@@ -61,33 +66,57 @@ class StructuredIOStream(IOStream):
|
|
|
61
66
|
The data to print.
|
|
62
67
|
kwargs : Any
|
|
63
68
|
"""
|
|
64
|
-
sep = kwargs.get("sep", " ")
|
|
65
|
-
end = kwargs.get("end", "\n")
|
|
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"))
|
|
66
73
|
message = sep.join(map(str, args))
|
|
67
|
-
|
|
68
|
-
|
|
74
|
+
if len(args) == 1 and isinstance(args[0], dict):
|
|
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_
|
|
79
|
+
is_dumped = True
|
|
80
|
+
else:
|
|
81
|
+
is_dumped, message = is_json_dumped(message)
|
|
82
|
+
if is_dumped:
|
|
69
83
|
# If the message is already JSON-dumped,
|
|
70
84
|
# let's try not to double dump it
|
|
71
|
-
message = json.loads(message)
|
|
72
85
|
payload: dict[str, Any] = {
|
|
73
|
-
"
|
|
74
|
-
"id": uuid4().hex,
|
|
86
|
+
"id": gen_id(),
|
|
75
87
|
"timestamp": now(),
|
|
76
|
-
"data": message,
|
|
88
|
+
# "data": message,
|
|
77
89
|
}
|
|
90
|
+
if isinstance(message, dict):
|
|
91
|
+
payload.update(message)
|
|
92
|
+
end = ""
|
|
93
|
+
else:
|
|
94
|
+
payload["data"] = message
|
|
95
|
+
if "type" not in payload:
|
|
96
|
+
payload["type"] = payload_type
|
|
97
|
+
else:
|
|
98
|
+
print_message = PrintMessage(data=message)
|
|
99
|
+
payload = print_message.model_dump(mode="json", fallback=str)
|
|
100
|
+
payload["type"] = payload_type
|
|
101
|
+
dumped = json.dumps(payload, default=str, ensure_ascii=False) + end
|
|
102
|
+
file = kwargs.get("file", None)
|
|
103
|
+
if file and file in [
|
|
104
|
+
sys.stderr,
|
|
105
|
+
sys.__stderr__,
|
|
106
|
+
sys.stdout,
|
|
107
|
+
sys.__stdout__,
|
|
108
|
+
]:
|
|
109
|
+
print(dumped, file=file, flush=flush)
|
|
78
110
|
else:
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
print(json.dumps(payload, default=str), flush=flush)
|
|
89
|
-
|
|
90
|
-
def input(self, prompt: str = "", *, password: bool = False) -> str:
|
|
111
|
+
print(dumped, flush=flush)
|
|
112
|
+
|
|
113
|
+
def input(
|
|
114
|
+
self,
|
|
115
|
+
prompt: str = "",
|
|
116
|
+
*,
|
|
117
|
+
password: bool = False,
|
|
118
|
+
request_id: str | None = None,
|
|
119
|
+
) -> str:
|
|
91
120
|
"""Structured input from stdin.
|
|
92
121
|
|
|
93
122
|
Parameters
|
|
@@ -96,25 +125,36 @@ class StructuredIOStream(IOStream):
|
|
|
96
125
|
The prompt to display. Defaults to "".
|
|
97
126
|
password : bool, optional
|
|
98
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.
|
|
99
130
|
|
|
100
131
|
Returns
|
|
101
132
|
-------
|
|
102
133
|
str
|
|
103
134
|
The line read from the input stream.
|
|
104
135
|
"""
|
|
105
|
-
|
|
136
|
+
input_request_id = request_id or gen_id()
|
|
106
137
|
prompt = prompt or ">"
|
|
107
138
|
if not prompt or prompt in [">", "> "]: # pragma: no cover
|
|
108
139
|
# if the prompt is just ">" or "> ",
|
|
109
140
|
# let's use a more descriptive one
|
|
110
|
-
prompt =
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
141
|
+
prompt = START_CHAT_PROMPT
|
|
142
|
+
input_type = "chat"
|
|
143
|
+
if prompt.strip() == DEBUG_INPUT_PROMPT.strip():
|
|
144
|
+
input_type = "debug"
|
|
145
|
+
self._send_input_request(
|
|
146
|
+
prompt,
|
|
147
|
+
input_request_id,
|
|
148
|
+
password,
|
|
149
|
+
input_type=input_type,
|
|
150
|
+
)
|
|
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)
|
|
115
155
|
user_response = response.to_string(
|
|
116
156
|
uploads_root=self.uploads_root,
|
|
117
|
-
base_name=
|
|
157
|
+
base_name=input_request_id,
|
|
118
158
|
)
|
|
119
159
|
return user_response
|
|
120
160
|
|
|
@@ -140,11 +180,8 @@ class StructuredIOStream(IOStream):
|
|
|
140
180
|
content_block["content"] = try_parse_maybe_serialized(
|
|
141
181
|
inner_content
|
|
142
182
|
)
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
flush=True,
|
|
146
|
-
file=sys.stdout,
|
|
147
|
-
)
|
|
183
|
+
message_dump["timestamp"] = now()
|
|
184
|
+
print(json.dumps(message_dump, default=str), flush=True)
|
|
148
185
|
|
|
149
186
|
# noinspection PyMethodMayBeStatic
|
|
150
187
|
# pylint: disable=no-self-use
|
|
@@ -152,14 +189,21 @@ class StructuredIOStream(IOStream):
|
|
|
152
189
|
self,
|
|
153
190
|
prompt: str,
|
|
154
191
|
request_id: str,
|
|
155
|
-
password: bool,
|
|
192
|
+
password: bool = False,
|
|
193
|
+
input_type: str = "chat",
|
|
156
194
|
) -> None:
|
|
195
|
+
if input_type not in ("chat", "debug"):
|
|
196
|
+
input_type = "chat"
|
|
197
|
+
request_type = (
|
|
198
|
+
"debug_input_request" if input_type == "debug" else "input_request"
|
|
199
|
+
)
|
|
157
200
|
payload = UserInputRequest(
|
|
201
|
+
type=request_type, # type: ignore
|
|
158
202
|
request_id=request_id,
|
|
159
203
|
prompt=prompt,
|
|
160
204
|
password=password,
|
|
161
205
|
).model_dump(mode="json")
|
|
162
|
-
print(json.dumps(payload), flush=True)
|
|
206
|
+
print(json.dumps(payload, default=str), flush=True)
|
|
163
207
|
|
|
164
208
|
def _read_user_input(
|
|
165
209
|
self,
|
|
@@ -167,33 +211,15 @@ class StructuredIOStream(IOStream):
|
|
|
167
211
|
password: bool,
|
|
168
212
|
request_id: str,
|
|
169
213
|
) -> str:
|
|
170
|
-
input_queue: queue.Queue[str] = queue.Queue()
|
|
171
|
-
|
|
172
|
-
def read_input() -> None:
|
|
173
|
-
"""Read user input from stdin."""
|
|
174
|
-
try:
|
|
175
|
-
user_input = (
|
|
176
|
-
getpass(prompt).strip()
|
|
177
|
-
if password
|
|
178
|
-
else input(prompt).strip()
|
|
179
|
-
)
|
|
180
|
-
input_queue.put(user_input)
|
|
181
|
-
except EOFError:
|
|
182
|
-
input_queue.put("")
|
|
183
|
-
|
|
184
|
-
input_thread = threading.Thread(target=read_input, daemon=True)
|
|
185
|
-
input_thread.start()
|
|
186
|
-
|
|
187
214
|
try:
|
|
188
|
-
return
|
|
189
|
-
|
|
190
|
-
|
|
215
|
+
return (
|
|
216
|
+
getpass(prompt).strip() if password else input(prompt).strip()
|
|
217
|
+
)
|
|
218
|
+
except EOFError:
|
|
191
219
|
return ""
|
|
192
220
|
except BaseException as e:
|
|
193
221
|
self._send_error_message(request_id, str(e))
|
|
194
222
|
return ""
|
|
195
|
-
finally:
|
|
196
|
-
input_thread.join(timeout=1)
|
|
197
223
|
|
|
198
224
|
def _send_timeout_message(self, request_id: str) -> None:
|
|
199
225
|
timeout_payload = {
|
|
@@ -294,14 +320,23 @@ class StructuredIOStream(IOStream):
|
|
|
294
320
|
UserResponse
|
|
295
321
|
The structured user response.
|
|
296
322
|
"""
|
|
297
|
-
|
|
298
|
-
|
|
323
|
+
response_type: MessageType
|
|
324
|
+
_response_type = user_input.get("type", "input_response")
|
|
325
|
+
if _response_type not in ("input_response", "debug_input_response"):
|
|
326
|
+
response_type = "input_response"
|
|
327
|
+
else:
|
|
328
|
+
response_type = _response_type
|
|
329
|
+
if (
|
|
330
|
+
user_input.get("request_id") == request_id
|
|
331
|
+
or response_type == "debug_input_response"
|
|
332
|
+
):
|
|
299
333
|
# We have a valid response to our request
|
|
300
334
|
data = user_input.get("data")
|
|
301
335
|
if not data:
|
|
302
336
|
# let's check if text|image keys are sent (outside data)
|
|
303
337
|
if "image" in user_input or "text" in user_input:
|
|
304
338
|
return UserResponse(
|
|
339
|
+
type=response_type,
|
|
305
340
|
request_id=request_id,
|
|
306
341
|
data=self._format_multimedia_response(
|
|
307
342
|
request_id=request_id, data=user_input
|
|
@@ -309,12 +344,14 @@ class StructuredIOStream(IOStream):
|
|
|
309
344
|
)
|
|
310
345
|
if isinstance(data, list):
|
|
311
346
|
return self._handle_list_response(
|
|
312
|
-
data,
|
|
347
|
+
data,
|
|
313
348
|
request_id=request_id,
|
|
349
|
+
response_type=response_type,
|
|
314
350
|
)
|
|
315
351
|
if not data or not isinstance(data, (str, dict)):
|
|
316
352
|
# No / invalid data provided in the response
|
|
317
353
|
return UserResponse(
|
|
354
|
+
type=response_type,
|
|
318
355
|
request_id=request_id,
|
|
319
356
|
data="",
|
|
320
357
|
)
|
|
@@ -324,20 +361,24 @@ class StructuredIOStream(IOStream):
|
|
|
324
361
|
data = self._load_user_input(data)
|
|
325
362
|
if isinstance(data, dict):
|
|
326
363
|
return UserResponse(
|
|
364
|
+
type=response_type,
|
|
327
365
|
data=self._format_multimedia_response(
|
|
328
366
|
request_id=request_id,
|
|
329
|
-
data=data,
|
|
367
|
+
data=data,
|
|
330
368
|
),
|
|
331
369
|
request_id=request_id,
|
|
332
370
|
)
|
|
333
371
|
# For other types (numbers, bools ,...),
|
|
334
372
|
# let's just convert to string
|
|
335
373
|
return UserResponse(
|
|
336
|
-
|
|
374
|
+
type=response_type,
|
|
375
|
+
data=str(data),
|
|
376
|
+
request_id=request_id,
|
|
337
377
|
) # pragma: no cover
|
|
338
378
|
# This response doesn't match our request_id, log and return empty
|
|
339
379
|
self._log_mismatched_response(request_id, user_input)
|
|
340
380
|
return UserResponse(
|
|
381
|
+
type=response_type,
|
|
341
382
|
request_id=request_id,
|
|
342
383
|
data="",
|
|
343
384
|
)
|
|
@@ -347,16 +388,18 @@ class StructuredIOStream(IOStream):
|
|
|
347
388
|
self,
|
|
348
389
|
data: list[dict[str, Any]],
|
|
349
390
|
request_id: str,
|
|
391
|
+
response_type: MessageType,
|
|
350
392
|
) -> UserResponse:
|
|
351
|
-
if len(data) == 0:
|
|
393
|
+
if len(data) == 0:
|
|
352
394
|
# Empty list, return empty response
|
|
353
395
|
return UserResponse(
|
|
396
|
+
type=response_type,
|
|
354
397
|
request_id=request_id,
|
|
355
398
|
data="",
|
|
356
399
|
)
|
|
357
400
|
|
|
358
401
|
input_data: list[UserInputData] = []
|
|
359
|
-
for entry in data:
|
|
402
|
+
for entry in data:
|
|
360
403
|
# pylint: disable=broad-exception-caught
|
|
361
404
|
try:
|
|
362
405
|
content = UserInputData.model_validate(entry)
|
|
@@ -367,10 +410,12 @@ class StructuredIOStream(IOStream):
|
|
|
367
410
|
if not input_data: # pragma: no cover
|
|
368
411
|
# No valid data in the list, return empty response
|
|
369
412
|
return UserResponse(
|
|
413
|
+
type=response_type,
|
|
370
414
|
request_id=request_id,
|
|
371
415
|
data="",
|
|
372
416
|
)
|
|
373
417
|
return UserResponse(
|
|
418
|
+
type=response_type,
|
|
374
419
|
request_id=request_id,
|
|
375
420
|
data=input_data,
|
|
376
421
|
)
|
|
@@ -387,9 +432,16 @@ class StructuredIOStream(IOStream):
|
|
|
387
432
|
The response received
|
|
388
433
|
"""
|
|
389
434
|
# Create a log message
|
|
435
|
+
got_id: str | None = None
|
|
436
|
+
if isinstance(response, dict):
|
|
437
|
+
got_id = response.get("request_id")
|
|
438
|
+
response_str = str(response)
|
|
439
|
+
message = response_str[:100] + (
|
|
440
|
+
"..." if len(response_str) > 100 else ""
|
|
441
|
+
)
|
|
390
442
|
log_payload: dict[str, Any] = {
|
|
391
443
|
"type": "warning",
|
|
392
|
-
"id":
|
|
444
|
+
"id": gen_id(),
|
|
393
445
|
"timestamp": now(),
|
|
394
446
|
"data": {
|
|
395
447
|
"message": (
|
|
@@ -398,8 +450,8 @@ class StructuredIOStream(IOStream):
|
|
|
398
450
|
),
|
|
399
451
|
"details": {
|
|
400
452
|
"expected_id": expected_id,
|
|
401
|
-
"
|
|
402
|
-
|
|
453
|
+
"received_id": got_id,
|
|
454
|
+
"message": message,
|
|
403
455
|
},
|
|
404
456
|
},
|
|
405
457
|
}
|
|
@@ -428,7 +480,7 @@ class StructuredIOStream(IOStream):
|
|
|
428
480
|
result: list[str] = []
|
|
429
481
|
if "content" in data and isinstance(data["content"], dict):
|
|
430
482
|
return self._format_multimedia_response(
|
|
431
|
-
data=data["content"],
|
|
483
|
+
data=data["content"],
|
|
432
484
|
request_id=request_id,
|
|
433
485
|
)
|
|
434
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
|
|
@@ -14,9 +16,17 @@ from autogen.agentchat.contrib.img_utils import get_pil_image # type: ignore
|
|
|
14
16
|
from autogen.events import BaseEvent # type: ignore
|
|
15
17
|
from autogen.messages import BaseMessage # type: ignore
|
|
16
18
|
|
|
19
|
+
DEBUG_INPUT_PROMPT = (
|
|
20
|
+
# cspell: disable-next-line
|
|
21
|
+
"[Step] (c)ontinue, (r)un, (q)uit, (i)nfo, (h)elp, (st)ats: "
|
|
22
|
+
)
|
|
23
|
+
START_CHAT_PROMPT = "Enter your message to start the conversation: "
|
|
24
|
+
|
|
17
25
|
MessageType = Literal[
|
|
18
26
|
"input_request",
|
|
19
27
|
"input_response",
|
|
28
|
+
"debug_input_request",
|
|
29
|
+
"debug_input_response",
|
|
20
30
|
"print",
|
|
21
31
|
"input",
|
|
22
32
|
]
|
|
@@ -131,26 +141,25 @@ def get_image(
|
|
|
131
141
|
return image_data
|
|
132
142
|
|
|
133
143
|
|
|
134
|
-
def is_json_dumped(value:
|
|
135
|
-
"""Check if a
|
|
144
|
+
def is_json_dumped(value: Any) -> tuple[bool, Any]:
|
|
145
|
+
"""Check if a value is JSON-dumped.
|
|
136
146
|
|
|
137
147
|
Parameters
|
|
138
148
|
----------
|
|
139
|
-
value :
|
|
140
|
-
The
|
|
149
|
+
value : Any
|
|
150
|
+
The value to check.
|
|
141
151
|
|
|
142
152
|
Returns
|
|
143
153
|
-------
|
|
144
154
|
bool
|
|
145
|
-
True if the
|
|
155
|
+
True if the value is JSON-dumped, False otherwise.
|
|
146
156
|
"""
|
|
157
|
+
to_check = value.strip() if isinstance(value, str) else value
|
|
147
158
|
try:
|
|
148
|
-
parsed = json.loads(
|
|
149
|
-
|
|
150
|
-
# we consider it JSON-dumped
|
|
151
|
-
return not isinstance(parsed, str)
|
|
159
|
+
parsed = json.loads(to_check)
|
|
160
|
+
return True, parsed
|
|
152
161
|
except json.JSONDecodeError:
|
|
153
|
-
return False
|
|
162
|
+
return False, value
|
|
154
163
|
|
|
155
164
|
|
|
156
165
|
def try_parse_maybe_serialized(value: str) -> Any:
|
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):
|
|
@@ -118,8 +122,8 @@ class AsyncWebsocketsIOStream(IOStream):
|
|
|
118
122
|
end = kwargs.get("end", "\n")
|
|
119
123
|
msg = sep.join(str(arg) for arg in args)
|
|
120
124
|
|
|
121
|
-
is_dumped = is_json_dumped(msg)
|
|
122
|
-
if is_dumped
|
|
125
|
+
is_dumped, msg = is_json_dumped(msg)
|
|
126
|
+
if is_dumped: # pragma: no cover
|
|
123
127
|
msg = json.loads(msg)
|
|
124
128
|
else:
|
|
125
129
|
msg = f"{msg}{end}"
|
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.
|