agentscope-runtime 1.0.5__py3-none-any.whl → 1.1.0b2__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.
- agentscope_runtime/__init__.py +3 -0
- agentscope_runtime/adapters/agentscope/message.py +36 -295
- agentscope_runtime/adapters/agentscope/stream.py +89 -2
- agentscope_runtime/adapters/agno/message.py +11 -2
- agentscope_runtime/adapters/agno/stream.py +1 -0
- agentscope_runtime/adapters/langgraph/__init__.py +1 -3
- agentscope_runtime/adapters/langgraph/message.py +11 -106
- agentscope_runtime/adapters/langgraph/stream.py +1 -0
- agentscope_runtime/adapters/ms_agent_framework/message.py +11 -1
- agentscope_runtime/adapters/ms_agent_framework/stream.py +1 -0
- agentscope_runtime/adapters/text/stream.py +1 -0
- agentscope_runtime/common/container_clients/agentrun_client.py +0 -3
- agentscope_runtime/common/container_clients/boxlite_client.py +26 -15
- agentscope_runtime/common/container_clients/fc_client.py +0 -11
- agentscope_runtime/common/utils/deprecation.py +14 -17
- agentscope_runtime/common/utils/logging.py +44 -0
- agentscope_runtime/engine/app/agent_app.py +5 -5
- agentscope_runtime/engine/app/celery_mixin.py +43 -4
- agentscope_runtime/engine/deployers/adapter/agui/__init__.py +8 -1
- agentscope_runtime/engine/deployers/adapter/agui/agui_adapter_utils.py +6 -1
- agentscope_runtime/engine/deployers/adapter/agui/agui_protocol_adapter.py +2 -2
- agentscope_runtime/engine/deployers/utils/service_utils/fastapi_factory.py +13 -0
- agentscope_runtime/engine/runner.py +31 -6
- agentscope_runtime/engine/schemas/agent_schemas.py +28 -0
- agentscope_runtime/engine/services/sandbox/sandbox_service.py +41 -9
- agentscope_runtime/sandbox/box/base/base_sandbox.py +4 -0
- agentscope_runtime/sandbox/box/browser/browser_sandbox.py +4 -0
- agentscope_runtime/sandbox/box/dummy/dummy_sandbox.py +9 -2
- agentscope_runtime/sandbox/box/filesystem/filesystem_sandbox.py +4 -0
- agentscope_runtime/sandbox/box/gui/gui_sandbox.py +5 -1
- agentscope_runtime/sandbox/box/mobile/mobile_sandbox.py +4 -0
- agentscope_runtime/sandbox/box/sandbox.py +122 -13
- agentscope_runtime/sandbox/client/async_http_client.py +1 -0
- agentscope_runtime/sandbox/client/base.py +0 -1
- agentscope_runtime/sandbox/client/http_client.py +0 -2
- agentscope_runtime/sandbox/manager/heartbeat_mixin.py +486 -0
- agentscope_runtime/sandbox/manager/sandbox_manager.py +740 -153
- agentscope_runtime/sandbox/manager/server/app.py +18 -11
- agentscope_runtime/sandbox/manager/server/config.py +10 -2
- agentscope_runtime/sandbox/mcp_server.py +0 -1
- agentscope_runtime/sandbox/model/__init__.py +2 -1
- agentscope_runtime/sandbox/model/container.py +90 -3
- agentscope_runtime/sandbox/model/manager_config.py +45 -1
- agentscope_runtime/version.py +1 -1
- {agentscope_runtime-1.0.5.dist-info → agentscope_runtime-1.1.0b2.dist-info}/METADATA +36 -54
- {agentscope_runtime-1.0.5.dist-info → agentscope_runtime-1.1.0b2.dist-info}/RECORD +50 -69
- {agentscope_runtime-1.0.5.dist-info → agentscope_runtime-1.1.0b2.dist-info}/WHEEL +1 -1
- agentscope_runtime/adapters/agentscope/long_term_memory/__init__.py +0 -6
- agentscope_runtime/adapters/agentscope/long_term_memory/_long_term_memory_adapter.py +0 -258
- agentscope_runtime/adapters/agentscope/memory/__init__.py +0 -6
- agentscope_runtime/adapters/agentscope/memory/_memory_adapter.py +0 -152
- agentscope_runtime/engine/services/agent_state/__init__.py +0 -25
- agentscope_runtime/engine/services/agent_state/redis_state_service.py +0 -166
- agentscope_runtime/engine/services/agent_state/state_service.py +0 -179
- agentscope_runtime/engine/services/agent_state/state_service_factory.py +0 -52
- agentscope_runtime/engine/services/memory/__init__.py +0 -33
- agentscope_runtime/engine/services/memory/mem0_memory_service.py +0 -128
- agentscope_runtime/engine/services/memory/memory_service.py +0 -292
- agentscope_runtime/engine/services/memory/memory_service_factory.py +0 -126
- agentscope_runtime/engine/services/memory/redis_memory_service.py +0 -290
- agentscope_runtime/engine/services/memory/reme_personal_memory_service.py +0 -109
- agentscope_runtime/engine/services/memory/reme_task_memory_service.py +0 -11
- agentscope_runtime/engine/services/memory/tablestore_memory_service.py +0 -301
- agentscope_runtime/engine/services/session_history/__init__.py +0 -32
- agentscope_runtime/engine/services/session_history/redis_session_history_service.py +0 -283
- agentscope_runtime/engine/services/session_history/session_history_service.py +0 -267
- agentscope_runtime/engine/services/session_history/session_history_service_factory.py +0 -73
- agentscope_runtime/engine/services/session_history/tablestore_session_history_service.py +0 -288
- {agentscope_runtime-1.0.5.dist-info → agentscope_runtime-1.1.0b2.dist-info}/entry_points.txt +0 -0
- {agentscope_runtime-1.0.5.dist-info → agentscope_runtime-1.1.0b2.dist-info}/licenses/LICENSE +0 -0
- {agentscope_runtime-1.0.5.dist-info → agentscope_runtime-1.1.0b2.dist-info}/top_level.txt +0 -0
agentscope_runtime/__init__.py
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import json
|
|
5
5
|
|
|
6
6
|
from collections import OrderedDict
|
|
7
|
-
from typing import Union, List
|
|
7
|
+
from typing import Union, List, Callable, Optional, Dict
|
|
8
8
|
from urllib.parse import urlparse
|
|
9
9
|
|
|
10
10
|
from mcp.types import CallToolResult
|
|
@@ -24,11 +24,8 @@ from agentscope.mcp._client_base import MCPClientBase
|
|
|
24
24
|
|
|
25
25
|
from ...engine.schemas.agent_schemas import (
|
|
26
26
|
Message,
|
|
27
|
-
FunctionCall,
|
|
28
|
-
FunctionCallOutput,
|
|
29
27
|
MessageType,
|
|
30
28
|
)
|
|
31
|
-
from ...engine.helpers.agent_api_builder import ResponseBuilder
|
|
32
29
|
|
|
33
30
|
|
|
34
31
|
def matches_typed_dict_structure(obj, typed_dict_cls):
|
|
@@ -38,303 +35,20 @@ def matches_typed_dict_structure(obj, typed_dict_cls):
|
|
|
38
35
|
return expected_keys == set(obj.keys())
|
|
39
36
|
|
|
40
37
|
|
|
41
|
-
def agentscope_msg_to_message(
|
|
42
|
-
messages: Union[Msg, List[Msg]],
|
|
43
|
-
) -> List[Message]:
|
|
44
|
-
"""
|
|
45
|
-
Convert AgentScope Msg(s) into one or more runtime Message objects
|
|
46
|
-
|
|
47
|
-
Args:
|
|
48
|
-
messages: AgentScope message(s) from streaming.
|
|
49
|
-
|
|
50
|
-
Returns:
|
|
51
|
-
List[Message]: One or more constructed runtime Message objects.
|
|
52
|
-
"""
|
|
53
|
-
if isinstance(messages, Msg):
|
|
54
|
-
msgs = [messages]
|
|
55
|
-
elif isinstance(messages, list):
|
|
56
|
-
msgs = messages
|
|
57
|
-
else:
|
|
58
|
-
raise TypeError(f"Expected Msg or list[Msg], got {type(messages)}")
|
|
59
|
-
|
|
60
|
-
results: List[Message] = []
|
|
61
|
-
|
|
62
|
-
for msg in msgs:
|
|
63
|
-
role = msg.role or "assistant"
|
|
64
|
-
|
|
65
|
-
if isinstance(msg.content, str):
|
|
66
|
-
# Only text
|
|
67
|
-
rb = ResponseBuilder()
|
|
68
|
-
mb = rb.create_message_builder(
|
|
69
|
-
role=role,
|
|
70
|
-
message_type=MessageType.MESSAGE,
|
|
71
|
-
)
|
|
72
|
-
# add meta field to store old id and name
|
|
73
|
-
mb.message.metadata = {
|
|
74
|
-
"original_id": msg.id,
|
|
75
|
-
"original_name": msg.name,
|
|
76
|
-
"metadata": msg.metadata,
|
|
77
|
-
}
|
|
78
|
-
cb = mb.create_content_builder(content_type="text")
|
|
79
|
-
cb.set_text(msg.content)
|
|
80
|
-
cb.complete()
|
|
81
|
-
mb.complete()
|
|
82
|
-
results.append(mb.get_message_data())
|
|
83
|
-
continue
|
|
84
|
-
|
|
85
|
-
# msg.content is a list of blocks
|
|
86
|
-
# We group blocks by high-level message type
|
|
87
|
-
current_mb = None
|
|
88
|
-
current_type = None
|
|
89
|
-
|
|
90
|
-
for block in msg.content:
|
|
91
|
-
if isinstance(block, dict):
|
|
92
|
-
btype = block.get("type", "text")
|
|
93
|
-
else:
|
|
94
|
-
continue
|
|
95
|
-
|
|
96
|
-
if btype == "text":
|
|
97
|
-
# Create/continue MESSAGE type
|
|
98
|
-
if current_type != MessageType.MESSAGE:
|
|
99
|
-
if current_mb:
|
|
100
|
-
current_mb.complete()
|
|
101
|
-
results.append(current_mb.get_message_data())
|
|
102
|
-
rb = ResponseBuilder()
|
|
103
|
-
current_mb = rb.create_message_builder(
|
|
104
|
-
role=role,
|
|
105
|
-
message_type=MessageType.MESSAGE,
|
|
106
|
-
)
|
|
107
|
-
# add meta field to store old id and name
|
|
108
|
-
current_mb.message.metadata = {
|
|
109
|
-
"original_id": msg.id,
|
|
110
|
-
"original_name": msg.name,
|
|
111
|
-
"metadata": msg.metadata,
|
|
112
|
-
}
|
|
113
|
-
current_type = MessageType.MESSAGE
|
|
114
|
-
cb = current_mb.create_content_builder(content_type="text")
|
|
115
|
-
cb.set_text(block.get("text", ""))
|
|
116
|
-
cb.complete()
|
|
117
|
-
|
|
118
|
-
elif btype == "thinking":
|
|
119
|
-
# Create/continue REASONING type
|
|
120
|
-
if current_type != MessageType.REASONING:
|
|
121
|
-
if current_mb:
|
|
122
|
-
current_mb.complete()
|
|
123
|
-
results.append(current_mb.get_message_data())
|
|
124
|
-
rb = ResponseBuilder()
|
|
125
|
-
current_mb = rb.create_message_builder(
|
|
126
|
-
role=role,
|
|
127
|
-
message_type=MessageType.REASONING,
|
|
128
|
-
)
|
|
129
|
-
# add meta field to store old id and name
|
|
130
|
-
current_mb.message.metadata = {
|
|
131
|
-
"original_id": msg.id,
|
|
132
|
-
"original_name": msg.name,
|
|
133
|
-
"metadata": msg.metadata,
|
|
134
|
-
}
|
|
135
|
-
current_type = MessageType.REASONING
|
|
136
|
-
cb = current_mb.create_content_builder(content_type="text")
|
|
137
|
-
cb.set_text(block.get("thinking", ""))
|
|
138
|
-
cb.complete()
|
|
139
|
-
|
|
140
|
-
elif btype == "tool_use":
|
|
141
|
-
# Always start a new PLUGIN_CALL message
|
|
142
|
-
if current_mb:
|
|
143
|
-
current_mb.complete()
|
|
144
|
-
results.append(current_mb.get_message_data())
|
|
145
|
-
rb = ResponseBuilder()
|
|
146
|
-
current_mb = rb.create_message_builder(
|
|
147
|
-
role=role,
|
|
148
|
-
message_type=MessageType.PLUGIN_CALL,
|
|
149
|
-
)
|
|
150
|
-
# add meta field to store old id and name
|
|
151
|
-
current_mb.message.metadata = {
|
|
152
|
-
"original_id": msg.id,
|
|
153
|
-
"original_name": msg.name,
|
|
154
|
-
"metadata": msg.metadata,
|
|
155
|
-
}
|
|
156
|
-
current_type = MessageType.PLUGIN_CALL
|
|
157
|
-
cb = current_mb.create_content_builder(content_type="data")
|
|
158
|
-
|
|
159
|
-
if isinstance(block.get("input"), (dict, list)):
|
|
160
|
-
arguments = json.dumps(block.get("input"))
|
|
161
|
-
else:
|
|
162
|
-
arguments = block.get("input")
|
|
163
|
-
|
|
164
|
-
call_data = FunctionCall(
|
|
165
|
-
call_id=block.get("id"),
|
|
166
|
-
name=block.get("name"),
|
|
167
|
-
arguments=arguments,
|
|
168
|
-
).model_dump()
|
|
169
|
-
cb.set_data(call_data)
|
|
170
|
-
cb.complete()
|
|
171
|
-
|
|
172
|
-
elif btype == "tool_result":
|
|
173
|
-
# Always start a new PLUGIN_CALL_OUTPUT message
|
|
174
|
-
if current_mb:
|
|
175
|
-
current_mb.complete()
|
|
176
|
-
results.append(current_mb.get_message_data())
|
|
177
|
-
rb = ResponseBuilder()
|
|
178
|
-
current_mb = rb.create_message_builder(
|
|
179
|
-
role=role,
|
|
180
|
-
message_type=MessageType.PLUGIN_CALL_OUTPUT,
|
|
181
|
-
)
|
|
182
|
-
# add meta field to store old id and name
|
|
183
|
-
current_mb.message.metadata = {
|
|
184
|
-
"original_id": msg.id,
|
|
185
|
-
"original_name": msg.name,
|
|
186
|
-
"metadata": msg.metadata,
|
|
187
|
-
}
|
|
188
|
-
current_type = MessageType.PLUGIN_CALL_OUTPUT
|
|
189
|
-
cb = current_mb.create_content_builder(content_type="data")
|
|
190
|
-
|
|
191
|
-
if isinstance(block.get("output"), (dict, list)):
|
|
192
|
-
output = json.dumps(block.get("output"))
|
|
193
|
-
else:
|
|
194
|
-
output = block.get("output")
|
|
195
|
-
|
|
196
|
-
output_data = FunctionCallOutput(
|
|
197
|
-
call_id=block.get("id"),
|
|
198
|
-
name=block.get("name"),
|
|
199
|
-
output=output,
|
|
200
|
-
).model_dump(exclude_none=True)
|
|
201
|
-
cb.set_data(output_data)
|
|
202
|
-
cb.complete()
|
|
203
|
-
|
|
204
|
-
elif btype == "image":
|
|
205
|
-
# Create/continue MESSAGE type with image
|
|
206
|
-
if current_type != MessageType.MESSAGE:
|
|
207
|
-
if current_mb:
|
|
208
|
-
current_mb.complete()
|
|
209
|
-
results.append(current_mb.get_message_data())
|
|
210
|
-
rb = ResponseBuilder()
|
|
211
|
-
current_mb = rb.create_message_builder(
|
|
212
|
-
role=role,
|
|
213
|
-
message_type=MessageType.MESSAGE,
|
|
214
|
-
)
|
|
215
|
-
# add meta field to store old id and name
|
|
216
|
-
current_mb.message.metadata = {
|
|
217
|
-
"original_id": msg.id,
|
|
218
|
-
"original_name": msg.name,
|
|
219
|
-
"metadata": msg.metadata,
|
|
220
|
-
}
|
|
221
|
-
current_type = MessageType.MESSAGE
|
|
222
|
-
cb = current_mb.create_content_builder(content_type="image")
|
|
223
|
-
|
|
224
|
-
if (
|
|
225
|
-
isinstance(block.get("source"), dict)
|
|
226
|
-
and block.get("source", {}).get("type") == "url"
|
|
227
|
-
):
|
|
228
|
-
cb.set_image_url(block.get("source", {}).get("url"))
|
|
229
|
-
|
|
230
|
-
elif (
|
|
231
|
-
isinstance(block.get("source"), dict)
|
|
232
|
-
and block.get("source").get(
|
|
233
|
-
"type",
|
|
234
|
-
)
|
|
235
|
-
== "base64"
|
|
236
|
-
):
|
|
237
|
-
media_type = block.get("source", {}).get(
|
|
238
|
-
"media_type",
|
|
239
|
-
"image/jpeg",
|
|
240
|
-
)
|
|
241
|
-
base64_data = block.get("source", {}).get("data", "")
|
|
242
|
-
url = f"data:{media_type};base64,{base64_data}"
|
|
243
|
-
cb.set_image_url(url)
|
|
244
|
-
|
|
245
|
-
cb.complete()
|
|
246
|
-
|
|
247
|
-
elif btype == "audio":
|
|
248
|
-
# Create/continue MESSAGE type with audio
|
|
249
|
-
if current_type != MessageType.MESSAGE:
|
|
250
|
-
if current_mb:
|
|
251
|
-
current_mb.complete()
|
|
252
|
-
results.append(current_mb.get_message_data())
|
|
253
|
-
rb = ResponseBuilder()
|
|
254
|
-
current_mb = rb.create_message_builder(
|
|
255
|
-
role=role,
|
|
256
|
-
message_type=MessageType.MESSAGE,
|
|
257
|
-
)
|
|
258
|
-
# add meta field to store old id and name
|
|
259
|
-
current_mb.message.metadata = {
|
|
260
|
-
"original_id": msg.id,
|
|
261
|
-
"original_name": msg.name,
|
|
262
|
-
"metadata": msg.metadata,
|
|
263
|
-
}
|
|
264
|
-
current_type = MessageType.MESSAGE
|
|
265
|
-
cb = current_mb.create_content_builder(content_type="audio")
|
|
266
|
-
# URLSource runtime check (dict with type == "url")
|
|
267
|
-
if (
|
|
268
|
-
isinstance(block.get("source"), dict)
|
|
269
|
-
and block.get("source", {}).get(
|
|
270
|
-
"type",
|
|
271
|
-
)
|
|
272
|
-
== "url"
|
|
273
|
-
):
|
|
274
|
-
url = block.get("source", {}).get("url")
|
|
275
|
-
cb.content.data = url
|
|
276
|
-
try:
|
|
277
|
-
cb.content.format = urlparse(url).path.split(".")[-1]
|
|
278
|
-
except (AttributeError, IndexError, ValueError):
|
|
279
|
-
cb.content.format = None
|
|
280
|
-
|
|
281
|
-
# Base64Source runtime check (dict with type == "base64")
|
|
282
|
-
elif (
|
|
283
|
-
isinstance(block.get("source"), dict)
|
|
284
|
-
and block.get("source").get(
|
|
285
|
-
"type",
|
|
286
|
-
)
|
|
287
|
-
== "base64"
|
|
288
|
-
):
|
|
289
|
-
media_type = block.get("source", {}).get(
|
|
290
|
-
"media_type",
|
|
291
|
-
)
|
|
292
|
-
base64_data = block.get("source", {}).get("data", "")
|
|
293
|
-
url = f"data:{media_type};base64,{base64_data}"
|
|
294
|
-
|
|
295
|
-
cb.content.data = url
|
|
296
|
-
cb.content.format = media_type
|
|
297
|
-
|
|
298
|
-
cb.complete()
|
|
299
|
-
|
|
300
|
-
else:
|
|
301
|
-
# Fallback to MESSAGE type
|
|
302
|
-
if current_type != MessageType.MESSAGE:
|
|
303
|
-
if current_mb:
|
|
304
|
-
current_mb.complete()
|
|
305
|
-
results.append(current_mb.get_message_data())
|
|
306
|
-
rb = ResponseBuilder()
|
|
307
|
-
current_mb = rb.create_message_builder(
|
|
308
|
-
role=role,
|
|
309
|
-
message_type=MessageType.MESSAGE,
|
|
310
|
-
)
|
|
311
|
-
# add meta field to store old id and name
|
|
312
|
-
current_mb.message.metadata = {
|
|
313
|
-
"original_id": msg.id,
|
|
314
|
-
"original_name": msg.name,
|
|
315
|
-
"metadata": msg.metadata,
|
|
316
|
-
}
|
|
317
|
-
current_type = MessageType.MESSAGE
|
|
318
|
-
cb = current_mb.create_content_builder(content_type="text")
|
|
319
|
-
cb.set_text(str(block))
|
|
320
|
-
cb.complete()
|
|
321
|
-
|
|
322
|
-
# finalize last open message builder
|
|
323
|
-
if current_mb:
|
|
324
|
-
current_mb.complete()
|
|
325
|
-
results.append(current_mb.get_message_data())
|
|
326
|
-
|
|
327
|
-
return results
|
|
328
|
-
|
|
329
|
-
|
|
330
38
|
def message_to_agentscope_msg(
|
|
331
39
|
messages: Union[Message, List[Message]],
|
|
40
|
+
type_converters: Optional[Dict[str, Callable]] = None,
|
|
332
41
|
) -> Union[Msg, List[Msg]]:
|
|
333
42
|
"""
|
|
334
43
|
Convert AgentScope runtime Message(s) to AgentScope Msg(s).
|
|
335
44
|
|
|
336
45
|
Args:
|
|
337
46
|
messages: A single AgentScope runtime Message or list of Messages.
|
|
47
|
+
type_converters: Optional mapping from ``message.type`` to a callable
|
|
48
|
+
``converter(message)``. When provided and the current
|
|
49
|
+
``message.type`` exists in the mapping, the corresponding converter
|
|
50
|
+
will be used and the built-in conversion logic will be skipped for
|
|
51
|
+
that message.
|
|
338
52
|
|
|
339
53
|
Returns:
|
|
340
54
|
A single Msg object or a list of Msg objects.
|
|
@@ -351,6 +65,10 @@ def message_to_agentscope_msg(
|
|
|
351
65
|
return default
|
|
352
66
|
|
|
353
67
|
def _convert_one(message: Message) -> Msg:
|
|
68
|
+
# Used for custom conversion
|
|
69
|
+
if type_converters and message.type in type_converters:
|
|
70
|
+
return type_converters[message.type](message)
|
|
71
|
+
|
|
354
72
|
# Normalize role
|
|
355
73
|
if message.role == "tool":
|
|
356
74
|
role_label = "system" # AgentScope not support tool as role
|
|
@@ -474,8 +192,7 @@ def message_to_agentscope_msg(
|
|
|
474
192
|
"image": (ImageBlock, "image_url"),
|
|
475
193
|
"audio": (AudioBlock, "data"),
|
|
476
194
|
"data": (TextBlock, "data"),
|
|
477
|
-
|
|
478
|
-
# TODO: support video
|
|
195
|
+
"video": (VideoBlock, "video_url"),
|
|
479
196
|
}
|
|
480
197
|
|
|
481
198
|
msg_content = []
|
|
@@ -547,6 +264,30 @@ def message_to_agentscope_msg(
|
|
|
547
264
|
msg_content.append(
|
|
548
265
|
block_cls(type=cnt_type, source=base64_source),
|
|
549
266
|
)
|
|
267
|
+
elif cnt_type == "video":
|
|
268
|
+
if (
|
|
269
|
+
value
|
|
270
|
+
and isinstance(value, str)
|
|
271
|
+
and value.startswith("data:")
|
|
272
|
+
):
|
|
273
|
+
mediatype_part = value.split(";")[0].replace(
|
|
274
|
+
"data:",
|
|
275
|
+
"",
|
|
276
|
+
)
|
|
277
|
+
base64_data = value.split(",")[1]
|
|
278
|
+
base64_source = Base64Source(
|
|
279
|
+
type="base64",
|
|
280
|
+
media_type=mediatype_part,
|
|
281
|
+
data=base64_data,
|
|
282
|
+
)
|
|
283
|
+
msg_content.append(
|
|
284
|
+
block_cls(type=cnt_type, source=base64_source),
|
|
285
|
+
)
|
|
286
|
+
else:
|
|
287
|
+
url_source = URLSource(type="url", url=value)
|
|
288
|
+
msg_content.append(
|
|
289
|
+
block_cls(type=cnt_type, source=url_source),
|
|
290
|
+
)
|
|
550
291
|
else:
|
|
551
292
|
# text & data
|
|
552
293
|
if isinstance(value, str):
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
# pylint: disable=too-many-nested-blocks,too-many-branches,too-many-statements
|
|
3
3
|
import copy
|
|
4
|
+
import inspect
|
|
4
5
|
import json
|
|
5
6
|
|
|
6
|
-
from typing import AsyncIterator, Tuple, List, Union
|
|
7
|
+
from typing import AsyncIterator, Tuple, List, Union, Optional, Callable, Dict
|
|
7
8
|
from urllib.parse import urlparse
|
|
8
9
|
|
|
9
10
|
from agentscope import setup_logger
|
|
@@ -16,6 +17,7 @@ from ...engine.schemas.agent_schemas import (
|
|
|
16
17
|
TextContent,
|
|
17
18
|
ImageContent,
|
|
18
19
|
AudioContent,
|
|
20
|
+
VideoContent,
|
|
19
21
|
DataContent,
|
|
20
22
|
McpCall,
|
|
21
23
|
McpCallOutput,
|
|
@@ -29,6 +31,8 @@ setup_logger("ERROR")
|
|
|
29
31
|
|
|
30
32
|
async def adapt_agentscope_message_stream(
|
|
31
33
|
source_stream: AsyncIterator[Tuple[Msg, bool]],
|
|
34
|
+
type_converters: Optional[Dict[str, Callable]] = None,
|
|
35
|
+
**kwargs, # pylint:disable=unused-argument
|
|
32
36
|
) -> AsyncIterator[Union[Message, Content]]:
|
|
33
37
|
# Initialize variables to avoid uncaught errors
|
|
34
38
|
msg_id = None
|
|
@@ -136,6 +140,44 @@ async def adapt_agentscope_message_stream(
|
|
|
136
140
|
index = text_delta_content.index
|
|
137
141
|
yield text_delta_content
|
|
138
142
|
elif isinstance(element, dict):
|
|
143
|
+
# Used for custom conversion
|
|
144
|
+
if (
|
|
145
|
+
type_converters
|
|
146
|
+
and element.get("type") in type_converters
|
|
147
|
+
):
|
|
148
|
+
blk_type = element.get("type")
|
|
149
|
+
if not isinstance(blk_type, str):
|
|
150
|
+
continue
|
|
151
|
+
fn = type_converters[blk_type]
|
|
152
|
+
# Send message, element, last, tool_start, metadata
|
|
153
|
+
# and usage
|
|
154
|
+
out = fn(
|
|
155
|
+
element,
|
|
156
|
+
message,
|
|
157
|
+
last,
|
|
158
|
+
tool_start,
|
|
159
|
+
metadata,
|
|
160
|
+
usage,
|
|
161
|
+
)
|
|
162
|
+
# Case 1: async generator / async iterator
|
|
163
|
+
if hasattr(out, "__aiter__"):
|
|
164
|
+
async for ev in out:
|
|
165
|
+
yield ev
|
|
166
|
+
continue
|
|
167
|
+
|
|
168
|
+
# Case 2: sync generator / iterator
|
|
169
|
+
if inspect.isgenerator(out):
|
|
170
|
+
for ev in out:
|
|
171
|
+
yield ev
|
|
172
|
+
continue
|
|
173
|
+
|
|
174
|
+
# Only generator styles are supported
|
|
175
|
+
raise TypeError(
|
|
176
|
+
f"type_converters['{blk_type}'] must return a "
|
|
177
|
+
f"generator/iterator or an async generator/async "
|
|
178
|
+
f"iterator, got: {type(out)}",
|
|
179
|
+
)
|
|
180
|
+
|
|
139
181
|
if element.get("type") == "text": # Text
|
|
140
182
|
text = element.get(
|
|
141
183
|
"text",
|
|
@@ -446,7 +488,12 @@ async def adapt_agentscope_message_stream(
|
|
|
446
488
|
== "url"
|
|
447
489
|
):
|
|
448
490
|
kwargs.update(
|
|
449
|
-
{
|
|
491
|
+
{
|
|
492
|
+
"image_url": element.get(
|
|
493
|
+
"source",
|
|
494
|
+
{},
|
|
495
|
+
).get("url"),
|
|
496
|
+
},
|
|
450
497
|
)
|
|
451
498
|
|
|
452
499
|
elif (
|
|
@@ -515,6 +562,46 @@ async def adapt_agentscope_message_stream(
|
|
|
515
562
|
index=index,
|
|
516
563
|
**kwargs,
|
|
517
564
|
)
|
|
565
|
+
elif element.get("type") == "video":
|
|
566
|
+
kwargs = {}
|
|
567
|
+
if (
|
|
568
|
+
isinstance(element.get("source"), dict)
|
|
569
|
+
and element.get("source", {}).get(
|
|
570
|
+
"type",
|
|
571
|
+
)
|
|
572
|
+
== "url"
|
|
573
|
+
):
|
|
574
|
+
kwargs.update(
|
|
575
|
+
{
|
|
576
|
+
"video_url": element.get(
|
|
577
|
+
"source",
|
|
578
|
+
{},
|
|
579
|
+
).get("url"),
|
|
580
|
+
},
|
|
581
|
+
)
|
|
582
|
+
|
|
583
|
+
elif (
|
|
584
|
+
isinstance(element.get("source"), dict)
|
|
585
|
+
and element.get("source").get(
|
|
586
|
+
"type",
|
|
587
|
+
)
|
|
588
|
+
== "base64"
|
|
589
|
+
):
|
|
590
|
+
media_type = element.get("source", {}).get(
|
|
591
|
+
"media_type",
|
|
592
|
+
"video/mp4",
|
|
593
|
+
)
|
|
594
|
+
base64_data = element.get("source", {}).get(
|
|
595
|
+
"data",
|
|
596
|
+
"",
|
|
597
|
+
)
|
|
598
|
+
url = f"data:{media_type};base64,{base64_data}"
|
|
599
|
+
kwargs.update({"video_url": url})
|
|
600
|
+
delta_content = VideoContent(
|
|
601
|
+
delta=True,
|
|
602
|
+
index=index,
|
|
603
|
+
**kwargs,
|
|
604
|
+
)
|
|
518
605
|
else:
|
|
519
606
|
delta_content = TextContent(
|
|
520
607
|
delta=True,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
|
-
from typing import Union, List
|
|
2
|
+
from typing import Union, List, Callable, Optional, Dict
|
|
3
3
|
|
|
4
4
|
from agentscope.formatter import OpenAIChatFormatter
|
|
5
5
|
|
|
@@ -9,18 +9,27 @@ from ..agentscope.message import message_to_agentscope_msg
|
|
|
9
9
|
|
|
10
10
|
async def message_to_agno_message(
|
|
11
11
|
messages: Union[Message, List[Message]],
|
|
12
|
+
type_converters: Optional[Dict[str, Callable]] = None,
|
|
12
13
|
) -> Union[dict, List[dict]]:
|
|
13
14
|
"""
|
|
14
15
|
Convert AgentScope runtime Message(s) to Agno Message(s).
|
|
15
16
|
|
|
16
17
|
Args:
|
|
17
18
|
messages: A single AgentScope runtime Message or list of Messages.
|
|
19
|
+
type_converters: Optional mapping from ``message.type`` to a callable
|
|
20
|
+
``converter(message)``. When provided and the current
|
|
21
|
+
``message.type`` exists in the mapping, the corresponding converter
|
|
22
|
+
will be used and the built-in conversion logic will be skipped for
|
|
23
|
+
that message.
|
|
18
24
|
|
|
19
25
|
Returns:
|
|
20
26
|
A single AgnoMessage object or a list of AgnoMessage objects.
|
|
21
27
|
"""
|
|
22
28
|
|
|
23
|
-
as_msgs = message_to_agentscope_msg(
|
|
29
|
+
as_msgs = message_to_agentscope_msg(
|
|
30
|
+
messages,
|
|
31
|
+
type_converters=type_converters,
|
|
32
|
+
)
|
|
24
33
|
raw_list = isinstance(as_msgs, list)
|
|
25
34
|
as_msgs = as_msgs if raw_list else [as_msgs]
|
|
26
35
|
|
|
@@ -31,6 +31,7 @@ from ...engine.helpers.agent_api_builder import ResponseBuilder
|
|
|
31
31
|
|
|
32
32
|
async def adapt_agno_message_stream(
|
|
33
33
|
source_stream: AsyncIterator[BaseAgentRunEvent],
|
|
34
|
+
**kwargs, # pylint:disable=unused-argument
|
|
34
35
|
) -> AsyncIterator[Union[Message, Content]]:
|
|
35
36
|
rb = ResponseBuilder()
|
|
36
37
|
mb = None
|
|
@@ -2,11 +2,9 @@
|
|
|
2
2
|
"""LangGraph adapter for AgentScope runtime."""
|
|
3
3
|
|
|
4
4
|
# todo Message(reasoning) Adapter
|
|
5
|
-
# todo Memory Adapter
|
|
6
5
|
# todo Sandbox Tools Adapter
|
|
7
|
-
from .message import
|
|
6
|
+
from .message import message_to_langgraph_msg
|
|
8
7
|
|
|
9
8
|
__all__ = [
|
|
10
|
-
"langgraph_msg_to_message",
|
|
11
9
|
"message_to_langgraph_msg",
|
|
12
10
|
]
|