waldiez 0.4.6__py3-none-any.whl → 0.4.8__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 +5 -5
- waldiez/_version.py +1 -1
- waldiez/cli.py +112 -73
- waldiez/exporter.py +61 -19
- waldiez/exporting/__init__.py +25 -6
- waldiez/exporting/agent/__init__.py +7 -3
- waldiez/exporting/agent/code_execution.py +114 -0
- waldiez/exporting/agent/exporter.py +354 -0
- waldiez/exporting/agent/extras/__init__.py +15 -0
- waldiez/exporting/agent/extras/captain_agent_extras.py +315 -0
- waldiez/exporting/agent/extras/group/target.py +178 -0
- waldiez/exporting/agent/extras/group_manager_agent_extas.py +500 -0
- waldiez/exporting/agent/extras/group_member_extras.py +181 -0
- waldiez/exporting/agent/extras/handoffs/__init__.py +19 -0
- waldiez/exporting/agent/extras/handoffs/after_work.py +78 -0
- waldiez/exporting/agent/extras/handoffs/available.py +74 -0
- waldiez/exporting/agent/extras/handoffs/condition.py +158 -0
- waldiez/exporting/agent/extras/handoffs/handoff.py +171 -0
- waldiez/exporting/agent/extras/handoffs/target.py +189 -0
- waldiez/exporting/agent/extras/rag/__init__.py +10 -0
- waldiez/exporting/agent/{utils/rag_user/chroma_utils.py → extras/rag/chroma_extras.py} +16 -15
- waldiez/exporting/agent/{utils/rag_user/mongo_utils.py → extras/rag/mongo_extras.py} +10 -10
- waldiez/exporting/agent/{utils/rag_user/pgvector_utils.py → extras/rag/pgvector_extras.py} +13 -13
- waldiez/exporting/agent/{utils/rag_user/qdrant_utils.py → extras/rag/qdrant_extras.py} +13 -13
- waldiez/exporting/agent/{utils/rag_user/vector_db.py → extras/rag/vector_db_extras.py} +59 -46
- waldiez/exporting/agent/extras/rag_user_proxy_agent_extras.py +245 -0
- waldiez/exporting/agent/extras/reasoning_agent_extras.py +88 -0
- waldiez/exporting/agent/factory.py +95 -0
- waldiez/exporting/agent/processor.py +150 -0
- waldiez/exporting/agent/system_message.py +36 -0
- waldiez/exporting/agent/termination.py +50 -0
- waldiez/exporting/chats/__init__.py +7 -3
- waldiez/exporting/chats/exporter.py +97 -0
- waldiez/exporting/chats/factory.py +65 -0
- waldiez/exporting/chats/processor.py +226 -0
- waldiez/exporting/chats/utils/__init__.py +6 -5
- waldiez/exporting/chats/utils/common.py +11 -45
- waldiez/exporting/chats/utils/group.py +55 -0
- waldiez/exporting/chats/utils/nested.py +37 -52
- waldiez/exporting/chats/utils/sequential.py +72 -61
- waldiez/exporting/chats/utils/{single_chat.py → single.py} +48 -50
- waldiez/exporting/core/__init__.py +196 -0
- waldiez/exporting/core/constants.py +17 -0
- waldiez/exporting/core/content.py +69 -0
- waldiez/exporting/core/context.py +244 -0
- waldiez/exporting/core/enums.py +89 -0
- waldiez/exporting/core/errors.py +19 -0
- waldiez/exporting/core/exporter.py +390 -0
- waldiez/exporting/core/exporters.py +67 -0
- waldiez/exporting/core/extras/__init__.py +39 -0
- waldiez/exporting/core/extras/agent_extras/__init__.py +27 -0
- waldiez/exporting/core/extras/agent_extras/captain_extras.py +57 -0
- waldiez/exporting/core/extras/agent_extras/group_manager_extras.py +102 -0
- waldiez/exporting/core/extras/agent_extras/rag_user_extras.py +53 -0
- waldiez/exporting/core/extras/agent_extras/reasoning_extras.py +68 -0
- waldiez/exporting/core/extras/agent_extras/standard_extras.py +263 -0
- waldiez/exporting/core/extras/base.py +241 -0
- waldiez/exporting/core/extras/chat_extras.py +118 -0
- waldiez/exporting/core/extras/flow_extras.py +70 -0
- waldiez/exporting/core/extras/model_extras.py +73 -0
- waldiez/exporting/core/extras/path_resolver.py +93 -0
- waldiez/exporting/core/extras/serializer.py +138 -0
- waldiez/exporting/core/extras/tool_extras.py +82 -0
- waldiez/exporting/core/protocols.py +259 -0
- waldiez/exporting/core/result.py +705 -0
- waldiez/exporting/core/types.py +329 -0
- waldiez/exporting/core/utils/__init__.py +11 -0
- waldiez/exporting/core/utils/comment.py +33 -0
- waldiez/exporting/core/utils/llm_config.py +117 -0
- waldiez/exporting/core/validation.py +96 -0
- waldiez/exporting/flow/__init__.py +6 -2
- waldiez/exporting/flow/execution_generator.py +193 -0
- waldiez/exporting/flow/exporter.py +107 -0
- waldiez/exporting/flow/factory.py +94 -0
- waldiez/exporting/flow/file_generator.py +214 -0
- waldiez/exporting/flow/merger.py +387 -0
- waldiez/exporting/flow/orchestrator.py +411 -0
- waldiez/exporting/flow/utils/__init__.py +9 -36
- waldiez/exporting/flow/utils/common.py +206 -0
- waldiez/exporting/flow/utils/importing.py +373 -0
- waldiez/exporting/flow/utils/linting.py +200 -0
- waldiez/exporting/flow/utils/{logging_utils.py → logging.py} +23 -9
- waldiez/exporting/models/__init__.py +3 -1
- waldiez/exporting/models/exporter.py +233 -0
- waldiez/exporting/models/factory.py +66 -0
- waldiez/exporting/models/processor.py +139 -0
- waldiez/exporting/tools/__init__.py +11 -0
- waldiez/exporting/tools/exporter.py +207 -0
- waldiez/exporting/tools/factory.py +57 -0
- waldiez/exporting/tools/processor.py +248 -0
- waldiez/exporting/tools/registration.py +133 -0
- waldiez/io/__init__.py +128 -0
- waldiez/io/_ws.py +199 -0
- waldiez/io/models/__init__.py +60 -0
- waldiez/io/models/base.py +66 -0
- waldiez/io/models/constants.py +78 -0
- waldiez/io/models/content/__init__.py +23 -0
- waldiez/io/models/content/audio.py +43 -0
- waldiez/io/models/content/base.py +45 -0
- waldiez/io/models/content/file.py +43 -0
- waldiez/io/models/content/image.py +96 -0
- waldiez/io/models/content/text.py +37 -0
- waldiez/io/models/content/video.py +43 -0
- waldiez/io/models/user_input.py +269 -0
- waldiez/io/models/user_response.py +215 -0
- waldiez/io/mqtt.py +681 -0
- waldiez/io/redis.py +782 -0
- waldiez/io/structured.py +419 -0
- waldiez/io/utils.py +184 -0
- waldiez/io/ws.py +298 -0
- waldiez/logger.py +481 -0
- waldiez/models/__init__.py +108 -51
- waldiez/models/agents/__init__.py +34 -70
- waldiez/models/agents/agent/__init__.py +10 -4
- waldiez/models/agents/agent/agent.py +466 -65
- waldiez/models/agents/agent/agent_data.py +119 -47
- waldiez/models/agents/agent/agent_type.py +13 -2
- waldiez/models/agents/agent/code_execution.py +12 -12
- waldiez/models/agents/agent/human_input_mode.py +8 -0
- waldiez/models/agents/agent/{linked_skill.py → linked_tool.py} +7 -7
- waldiez/models/agents/agent/nested_chat.py +35 -7
- waldiez/models/agents/agent/termination_message.py +30 -22
- waldiez/models/agents/{swarm_agent → agent}/update_system_message.py +22 -22
- waldiez/models/agents/agents.py +58 -63
- waldiez/models/agents/assistant/assistant.py +4 -4
- waldiez/models/agents/assistant/assistant_data.py +13 -1
- waldiez/models/agents/{captain_agent → captain}/captain_agent.py +5 -5
- waldiez/models/agents/{captain_agent → captain}/captain_agent_data.py +5 -5
- waldiez/models/agents/extra_requirements.py +11 -16
- waldiez/models/agents/group_manager/group_manager.py +103 -13
- waldiez/models/agents/group_manager/group_manager_data.py +36 -14
- waldiez/models/agents/group_manager/speakers.py +77 -24
- waldiez/models/agents/{rag_user → rag_user_proxy}/__init__.py +16 -16
- waldiez/models/agents/rag_user_proxy/rag_user_proxy.py +64 -0
- waldiez/models/agents/{rag_user/rag_user_data.py → rag_user_proxy/rag_user_proxy_data.py} +6 -5
- waldiez/models/agents/{rag_user → rag_user_proxy}/retrieve_config.py +182 -114
- waldiez/models/agents/{rag_user → rag_user_proxy}/vector_db_config.py +13 -13
- waldiez/models/agents/reasoning/reasoning_agent.py +6 -6
- waldiez/models/agents/reasoning/reasoning_agent_data.py +110 -63
- waldiez/models/agents/reasoning/reasoning_agent_reason_config.py +38 -10
- waldiez/models/agents/user_proxy/user_proxy.py +11 -7
- waldiez/models/agents/user_proxy/user_proxy_data.py +2 -2
- waldiez/models/chat/__init__.py +2 -1
- waldiez/models/chat/chat.py +166 -87
- waldiez/models/chat/chat_data.py +99 -136
- waldiez/models/chat/chat_message.py +33 -23
- waldiez/models/chat/chat_nested.py +31 -30
- waldiez/models/chat/chat_summary.py +10 -8
- waldiez/models/common/__init__.py +52 -2
- waldiez/models/common/ag2_version.py +1 -1
- waldiez/models/common/base.py +38 -7
- waldiez/models/common/dict_utils.py +42 -17
- waldiez/models/common/handoff.py +459 -0
- waldiez/models/common/id_generator.py +19 -0
- waldiez/models/common/method_utils.py +130 -68
- waldiez/{exporting/base/utils → models/common}/naming.py +38 -61
- waldiez/models/common/waldiez_version.py +37 -0
- waldiez/models/flow/__init__.py +9 -2
- waldiez/models/flow/connection.py +18 -0
- waldiez/models/flow/flow.py +311 -215
- waldiez/models/flow/flow_data.py +207 -40
- waldiez/models/flow/info.py +85 -0
- waldiez/models/flow/naming.py +131 -0
- waldiez/models/model/__init__.py +7 -1
- waldiez/models/model/extra_requirements.py +3 -12
- waldiez/models/model/model.py +76 -21
- waldiez/models/model/model_data.py +108 -20
- waldiez/models/tool/__init__.py +16 -0
- waldiez/models/tool/extra_requirements.py +36 -0
- waldiez/models/{skill/skill.py → tool/tool.py} +88 -88
- waldiez/models/tool/tool_data.py +51 -0
- waldiez/models/tool/tool_type.py +8 -0
- waldiez/models/waldiez.py +97 -80
- waldiez/runner.py +114 -49
- waldiez/running/__init__.py +1 -1
- waldiez/running/environment.py +49 -68
- waldiez/running/gen_seq_diagram.py +16 -14
- waldiez/running/running.py +53 -34
- waldiez/utils/__init__.py +0 -4
- waldiez/utils/cli_extras/jupyter.py +5 -3
- waldiez/utils/cli_extras/runner.py +6 -4
- waldiez/utils/cli_extras/studio.py +6 -4
- waldiez/utils/conflict_checker.py +15 -9
- waldiez/utils/flaml_warnings.py +5 -5
- {waldiez-0.4.6.dist-info → waldiez-0.4.8.dist-info}/METADATA +235 -91
- waldiez-0.4.8.dist-info/RECORD +200 -0
- waldiez/exporting/agent/agent_exporter.py +0 -297
- waldiez/exporting/agent/utils/__init__.py +0 -23
- waldiez/exporting/agent/utils/captain_agent.py +0 -263
- waldiez/exporting/agent/utils/code_execution.py +0 -65
- waldiez/exporting/agent/utils/group_manager.py +0 -220
- waldiez/exporting/agent/utils/rag_user/__init__.py +0 -7
- waldiez/exporting/agent/utils/rag_user/rag_user.py +0 -209
- waldiez/exporting/agent/utils/reasoning.py +0 -36
- waldiez/exporting/agent/utils/swarm_agent.py +0 -469
- waldiez/exporting/agent/utils/teachability.py +0 -41
- waldiez/exporting/agent/utils/termination_message.py +0 -44
- waldiez/exporting/base/__init__.py +0 -25
- waldiez/exporting/base/agent_position.py +0 -75
- waldiez/exporting/base/base_exporter.py +0 -118
- waldiez/exporting/base/export_position.py +0 -48
- waldiez/exporting/base/import_position.py +0 -23
- waldiez/exporting/base/mixin.py +0 -137
- waldiez/exporting/base/utils/__init__.py +0 -18
- waldiez/exporting/base/utils/comments.py +0 -96
- waldiez/exporting/base/utils/path_check.py +0 -68
- waldiez/exporting/base/utils/to_string.py +0 -84
- waldiez/exporting/chats/chats_exporter.py +0 -240
- waldiez/exporting/chats/utils/swarm.py +0 -210
- waldiez/exporting/flow/flow_exporter.py +0 -528
- waldiez/exporting/flow/utils/agent_utils.py +0 -204
- waldiez/exporting/flow/utils/chat_utils.py +0 -71
- waldiez/exporting/flow/utils/def_main.py +0 -77
- waldiez/exporting/flow/utils/flow_content.py +0 -202
- waldiez/exporting/flow/utils/flow_names.py +0 -116
- waldiez/exporting/flow/utils/importing_utils.py +0 -227
- waldiez/exporting/models/models_exporter.py +0 -199
- waldiez/exporting/models/utils.py +0 -174
- waldiez/exporting/skills/__init__.py +0 -9
- waldiez/exporting/skills/skills_exporter.py +0 -176
- waldiez/exporting/skills/utils.py +0 -369
- waldiez/models/agents/agent/teachability.py +0 -70
- waldiez/models/agents/rag_user/rag_user.py +0 -60
- waldiez/models/agents/swarm_agent/__init__.py +0 -50
- waldiez/models/agents/swarm_agent/after_work.py +0 -179
- waldiez/models/agents/swarm_agent/on_condition.py +0 -105
- waldiez/models/agents/swarm_agent/on_condition_available.py +0 -142
- waldiez/models/agents/swarm_agent/on_condition_target.py +0 -40
- waldiez/models/agents/swarm_agent/swarm_agent.py +0 -107
- waldiez/models/agents/swarm_agent/swarm_agent_data.py +0 -124
- waldiez/models/flow/utils.py +0 -232
- waldiez/models/skill/__init__.py +0 -16
- waldiez/models/skill/extra_requirements.py +0 -36
- waldiez/models/skill/skill_data.py +0 -53
- waldiez/models/skill/skill_type.py +0 -8
- waldiez/utils/pysqlite3_checker.py +0 -308
- waldiez/utils/rdps_checker.py +0 -122
- waldiez-0.4.6.dist-info/RECORD +0 -149
- /waldiez/models/agents/{captain_agent → captain}/__init__.py +0 -0
- /waldiez/models/agents/{captain_agent → captain}/captain_agent_lib_entry.py +0 -0
- {waldiez-0.4.6.dist-info → waldiez-0.4.8.dist-info}/WHEEL +0 -0
- {waldiez-0.4.6.dist-info → waldiez-0.4.8.dist-info}/entry_points.txt +0 -0
- {waldiez-0.4.6.dist-info → waldiez-0.4.8.dist-info}/licenses/LICENSE +0 -0
- {waldiez-0.4.6.dist-info → waldiez-0.4.8.dist-info}/licenses/NOTICE.md +0 -0
waldiez/io/ws.py
ADDED
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0.
|
|
2
|
+
# Copyright (c) 2024 - 2025 Waldiez and contributors.
|
|
3
|
+
# pylint: disable=too-many-try-statements
|
|
4
|
+
"""WebSocket IOStream implementation for AsyncIO."""
|
|
5
|
+
|
|
6
|
+
import asyncio
|
|
7
|
+
import json
|
|
8
|
+
import logging
|
|
9
|
+
import uuid
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
from autogen.events import BaseEvent # type: ignore
|
|
14
|
+
from autogen.io import IOStream # type: ignore
|
|
15
|
+
|
|
16
|
+
from ._ws import (
|
|
17
|
+
WebSocketConnection,
|
|
18
|
+
create_websocket_adapter,
|
|
19
|
+
is_websocket_available,
|
|
20
|
+
)
|
|
21
|
+
from .models import UserResponse
|
|
22
|
+
from .utils import is_json_dumped, now, try_parse_maybe_serialized
|
|
23
|
+
|
|
24
|
+
LOG = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
if not is_websocket_available():
|
|
27
|
+
raise ImportError(
|
|
28
|
+
"WebSocket support requires "
|
|
29
|
+
"either the 'websockets' or 'starlette' package. "
|
|
30
|
+
"Please install one of them with "
|
|
31
|
+
"`pip install websockets` or `pip install starlette`."
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class AsyncWebsocketsIOStream(IOStream):
|
|
36
|
+
"""AsyncIO WebSocket class to handle communication."""
|
|
37
|
+
|
|
38
|
+
def __init__(
|
|
39
|
+
self,
|
|
40
|
+
websocket: Any,
|
|
41
|
+
is_async: bool = False,
|
|
42
|
+
uploads_root: str | Path | None = None,
|
|
43
|
+
verbose: bool = False,
|
|
44
|
+
receive_timeout: float | None = 120.0,
|
|
45
|
+
) -> None:
|
|
46
|
+
"""Initialize the AsyncWebsocketsIOStream instance.
|
|
47
|
+
|
|
48
|
+
Parameters
|
|
49
|
+
----------
|
|
50
|
+
websocket : Any
|
|
51
|
+
The WebSocket connection (either websockets or Starlette/FastAPI).
|
|
52
|
+
is_async : bool
|
|
53
|
+
Whether the connection is asynchronous.
|
|
54
|
+
uploads_root : str | Path | None
|
|
55
|
+
The root directory for uploads.
|
|
56
|
+
verbose : bool
|
|
57
|
+
Whether to enable verbose logging.
|
|
58
|
+
receive_timeout : float | None
|
|
59
|
+
Default timeout for receiving messages in seconds.
|
|
60
|
+
If None, defaults to 120 seconds.
|
|
61
|
+
"""
|
|
62
|
+
super().__init__()
|
|
63
|
+
|
|
64
|
+
# Create the WebSocket adapter
|
|
65
|
+
if hasattr(websocket, "send_message") and hasattr(
|
|
66
|
+
websocket, "receive_message"
|
|
67
|
+
):
|
|
68
|
+
self.websocket: WebSocketConnection = websocket
|
|
69
|
+
else:
|
|
70
|
+
self.websocket = create_websocket_adapter(websocket)
|
|
71
|
+
|
|
72
|
+
self.is_async = is_async
|
|
73
|
+
self.verbose = verbose
|
|
74
|
+
self.receive_timeout = receive_timeout
|
|
75
|
+
|
|
76
|
+
if isinstance(uploads_root, str):
|
|
77
|
+
uploads_root = Path(uploads_root)
|
|
78
|
+
if uploads_root is not None:
|
|
79
|
+
uploads_root = uploads_root.resolve()
|
|
80
|
+
self.uploads_root = uploads_root
|
|
81
|
+
|
|
82
|
+
def _try_send(self, json_dump: str) -> None:
|
|
83
|
+
try:
|
|
84
|
+
# Check if we're already in an async context
|
|
85
|
+
loop = None
|
|
86
|
+
try:
|
|
87
|
+
loop = asyncio.get_running_loop()
|
|
88
|
+
except RuntimeError:
|
|
89
|
+
pass
|
|
90
|
+
|
|
91
|
+
if loop is not None:
|
|
92
|
+
# We're in an async context, create a task
|
|
93
|
+
asyncio.create_task(self.websocket.send_message(json_dump))
|
|
94
|
+
else:
|
|
95
|
+
# We're not in an async context, run the coroutine
|
|
96
|
+
asyncio.run(self.websocket.send_message(json_dump))
|
|
97
|
+
|
|
98
|
+
except BaseException as error: # pylint: disable=broad-exception-caught
|
|
99
|
+
LOG.error("Error sending message: %s", error)
|
|
100
|
+
|
|
101
|
+
def print(self, *args: Any, **kwargs: Any) -> None:
|
|
102
|
+
"""Print to the WebSocket connection.
|
|
103
|
+
|
|
104
|
+
Parameters
|
|
105
|
+
----------
|
|
106
|
+
args : tuple
|
|
107
|
+
The arguments to print.
|
|
108
|
+
kwargs : dict
|
|
109
|
+
The keyword arguments to print.
|
|
110
|
+
"""
|
|
111
|
+
sep = kwargs.get("sep", " ")
|
|
112
|
+
end = kwargs.get("end", "\n")
|
|
113
|
+
msg = sep.join(str(arg) for arg in args)
|
|
114
|
+
|
|
115
|
+
is_dumped = is_json_dumped(msg)
|
|
116
|
+
if is_dumped and end.endswith("\n"):
|
|
117
|
+
msg = json.loads(msg)
|
|
118
|
+
else:
|
|
119
|
+
msg = f"{msg}{end}"
|
|
120
|
+
|
|
121
|
+
json_dump = json.dumps(
|
|
122
|
+
{
|
|
123
|
+
"type": "print",
|
|
124
|
+
"data": msg,
|
|
125
|
+
}
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
if self.verbose:
|
|
129
|
+
LOG.info(json_dump)
|
|
130
|
+
self._try_send(json_dump)
|
|
131
|
+
|
|
132
|
+
def send(self, message: BaseEvent) -> None:
|
|
133
|
+
"""Send a message to the WebSocket connection.
|
|
134
|
+
|
|
135
|
+
Parameters
|
|
136
|
+
----------
|
|
137
|
+
message : BaseEvent
|
|
138
|
+
The message to send.
|
|
139
|
+
"""
|
|
140
|
+
message_dump = message.model_dump(mode="json")
|
|
141
|
+
|
|
142
|
+
if message_dump.get("type") == "text":
|
|
143
|
+
content_block = message_dump.get("content")
|
|
144
|
+
if (
|
|
145
|
+
isinstance(content_block, dict)
|
|
146
|
+
and "content" in content_block
|
|
147
|
+
and isinstance(content_block["content"], str)
|
|
148
|
+
):
|
|
149
|
+
inner_content = content_block["content"]
|
|
150
|
+
content_block["content"] = try_parse_maybe_serialized(
|
|
151
|
+
inner_content
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
json_dump = json.dumps(message_dump, ensure_ascii=False)
|
|
155
|
+
|
|
156
|
+
if self.verbose:
|
|
157
|
+
LOG.info("sending: \n%s\n", json_dump)
|
|
158
|
+
|
|
159
|
+
self._try_send(json_dump)
|
|
160
|
+
|
|
161
|
+
def input(self, prompt: str = "", *, password: bool = False) -> str:
|
|
162
|
+
"""Sync-compatible input (will run the async version in the loop).
|
|
163
|
+
|
|
164
|
+
Parameters
|
|
165
|
+
----------
|
|
166
|
+
prompt : str
|
|
167
|
+
The prompt to display.
|
|
168
|
+
password : bool
|
|
169
|
+
Whether to hide the input.
|
|
170
|
+
|
|
171
|
+
Returns
|
|
172
|
+
-------
|
|
173
|
+
str
|
|
174
|
+
The user input.
|
|
175
|
+
"""
|
|
176
|
+
coro = self.a_input(prompt, password=password)
|
|
177
|
+
if self.is_async:
|
|
178
|
+
return coro # type: ignore # pragma: no cover
|
|
179
|
+
|
|
180
|
+
try:
|
|
181
|
+
return asyncio.run(coro)
|
|
182
|
+
except RuntimeError:
|
|
183
|
+
loop = asyncio.get_event_loop()
|
|
184
|
+
future = asyncio.run_coroutine_threadsafe(coro, loop)
|
|
185
|
+
return future.result()
|
|
186
|
+
|
|
187
|
+
async def a_input(
|
|
188
|
+
self,
|
|
189
|
+
prompt: str = "",
|
|
190
|
+
*,
|
|
191
|
+
password: bool = False,
|
|
192
|
+
timeout: float | None = None,
|
|
193
|
+
) -> str:
|
|
194
|
+
"""Get input from the WebSocket connection.
|
|
195
|
+
|
|
196
|
+
Parameters
|
|
197
|
+
----------
|
|
198
|
+
prompt : str
|
|
199
|
+
The prompt to display.
|
|
200
|
+
password : bool
|
|
201
|
+
Whether to hide the input.
|
|
202
|
+
timeout : float | None
|
|
203
|
+
Timeout for receiving the response in seconds.
|
|
204
|
+
If None, uses the instance's default receive_timeout.
|
|
205
|
+
|
|
206
|
+
Returns
|
|
207
|
+
-------
|
|
208
|
+
str
|
|
209
|
+
The user input, or empty string if timeout exceeded.
|
|
210
|
+
"""
|
|
211
|
+
if timeout is None:
|
|
212
|
+
timeout = self.receive_timeout or 120.0
|
|
213
|
+
|
|
214
|
+
request_id = uuid.uuid4().hex
|
|
215
|
+
prompt_dump = json.dumps(
|
|
216
|
+
{
|
|
217
|
+
"id": request_id,
|
|
218
|
+
"timestamp": now(),
|
|
219
|
+
"type": "input_request",
|
|
220
|
+
"request_id": request_id,
|
|
221
|
+
"prompt": prompt,
|
|
222
|
+
"password": password,
|
|
223
|
+
"content": [
|
|
224
|
+
{
|
|
225
|
+
"type": "text",
|
|
226
|
+
"text": prompt,
|
|
227
|
+
}
|
|
228
|
+
],
|
|
229
|
+
}
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
await self.websocket.send_message(prompt_dump)
|
|
233
|
+
response = await self.websocket.receive_message(timeout=timeout)
|
|
234
|
+
|
|
235
|
+
# Handle empty response from timeout
|
|
236
|
+
if not response:
|
|
237
|
+
LOG.warning("Input request timed out after %s seconds", timeout)
|
|
238
|
+
return ""
|
|
239
|
+
|
|
240
|
+
if self.verbose:
|
|
241
|
+
LOG.info("Got input: %s ...", response[:300])
|
|
242
|
+
|
|
243
|
+
response_dict: dict[str, Any] | str
|
|
244
|
+
try:
|
|
245
|
+
response_dict = json.loads(response)
|
|
246
|
+
except json.JSONDecodeError:
|
|
247
|
+
return response
|
|
248
|
+
|
|
249
|
+
if not isinstance(response_dict, dict):
|
|
250
|
+
LOG.error("Invalid input response: %s", response)
|
|
251
|
+
return ""
|
|
252
|
+
|
|
253
|
+
return self._parse_response(response_dict, request_id)
|
|
254
|
+
|
|
255
|
+
def _parse_response(self, response: dict[str, Any], request_id: str) -> str:
|
|
256
|
+
"""Parse the response from the WebSocket connection.
|
|
257
|
+
|
|
258
|
+
Parameters
|
|
259
|
+
----------
|
|
260
|
+
response : dict
|
|
261
|
+
The response from the WebSocket connection.
|
|
262
|
+
request_id : str
|
|
263
|
+
The request ID of the input request.
|
|
264
|
+
|
|
265
|
+
Returns
|
|
266
|
+
-------
|
|
267
|
+
str
|
|
268
|
+
The parsed response content.
|
|
269
|
+
"""
|
|
270
|
+
if "data" in response:
|
|
271
|
+
response_data = response["data"]
|
|
272
|
+
if isinstance(response_data, str): # pragma: no branch
|
|
273
|
+
try:
|
|
274
|
+
# double dumped?
|
|
275
|
+
parsed = json.loads(response_data)
|
|
276
|
+
except json.JSONDecodeError:
|
|
277
|
+
pass
|
|
278
|
+
else:
|
|
279
|
+
response["data"] = parsed
|
|
280
|
+
|
|
281
|
+
try:
|
|
282
|
+
user_response = UserResponse.model_validate(response)
|
|
283
|
+
except Exception as error: # pylint: disable=broad-exception-caught
|
|
284
|
+
LOG.error("Error parsing user input response: %s", error)
|
|
285
|
+
return ""
|
|
286
|
+
|
|
287
|
+
if user_response.request_id != request_id:
|
|
288
|
+
LOG.error(
|
|
289
|
+
"User response request ID mismatch: %s != %s",
|
|
290
|
+
user_response.request_id,
|
|
291
|
+
request_id,
|
|
292
|
+
)
|
|
293
|
+
return ""
|
|
294
|
+
|
|
295
|
+
return user_response.to_string(
|
|
296
|
+
uploads_root=self.uploads_root,
|
|
297
|
+
base_name=request_id,
|
|
298
|
+
)
|