agentscope-runtime 1.0.2__py3-none-any.whl → 1.0.4__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/adapters/agentscope/stream.py +2 -9
- agentscope_runtime/adapters/ms_agent_framework/__init__.py +0 -0
- agentscope_runtime/adapters/ms_agent_framework/message.py +205 -0
- agentscope_runtime/adapters/ms_agent_framework/stream.py +418 -0
- agentscope_runtime/adapters/utils.py +6 -0
- agentscope_runtime/cli/commands/deploy.py +383 -0
- agentscope_runtime/common/collections/redis_mapping.py +4 -1
- agentscope_runtime/common/container_clients/knative_client.py +466 -0
- agentscope_runtime/engine/__init__.py +4 -0
- agentscope_runtime/engine/app/agent_app.py +48 -5
- agentscope_runtime/engine/constant.py +1 -0
- agentscope_runtime/engine/deployers/__init__.py +12 -0
- agentscope_runtime/engine/deployers/adapter/a2a/__init__.py +31 -1
- agentscope_runtime/engine/deployers/adapter/a2a/a2a_protocol_adapter.py +458 -41
- agentscope_runtime/engine/deployers/adapter/a2a/a2a_registry.py +76 -0
- agentscope_runtime/engine/deployers/adapter/a2a/nacos_a2a_registry.py +749 -0
- agentscope_runtime/engine/deployers/agentrun_deployer.py +2 -2
- agentscope_runtime/engine/deployers/fc_deployer.py +1506 -0
- agentscope_runtime/engine/deployers/knative_deployer.py +290 -0
- agentscope_runtime/engine/deployers/kubernetes_deployer.py +3 -0
- agentscope_runtime/engine/deployers/utils/docker_image_utils/dockerfile_generator.py +8 -2
- agentscope_runtime/engine/deployers/utils/docker_image_utils/image_factory.py +5 -0
- agentscope_runtime/engine/deployers/utils/net_utils.py +65 -0
- agentscope_runtime/engine/runner.py +17 -3
- agentscope_runtime/engine/schemas/exception.py +24 -0
- agentscope_runtime/engine/services/agent_state/redis_state_service.py +61 -8
- agentscope_runtime/engine/services/agent_state/state_service_factory.py +2 -5
- agentscope_runtime/engine/services/memory/redis_memory_service.py +129 -25
- agentscope_runtime/engine/services/session_history/redis_session_history_service.py +160 -34
- agentscope_runtime/engine/tracing/wrapper.py +18 -4
- agentscope_runtime/sandbox/__init__.py +14 -6
- agentscope_runtime/sandbox/box/base/__init__.py +2 -2
- agentscope_runtime/sandbox/box/base/base_sandbox.py +51 -1
- agentscope_runtime/sandbox/box/browser/__init__.py +2 -2
- agentscope_runtime/sandbox/box/browser/browser_sandbox.py +198 -2
- agentscope_runtime/sandbox/box/filesystem/__init__.py +2 -2
- agentscope_runtime/sandbox/box/filesystem/filesystem_sandbox.py +99 -2
- agentscope_runtime/sandbox/box/gui/__init__.py +2 -2
- agentscope_runtime/sandbox/box/gui/gui_sandbox.py +117 -1
- agentscope_runtime/sandbox/box/mobile/__init__.py +2 -2
- agentscope_runtime/sandbox/box/mobile/mobile_sandbox.py +247 -100
- agentscope_runtime/sandbox/box/sandbox.py +98 -65
- agentscope_runtime/sandbox/box/shared/routers/generic.py +36 -29
- agentscope_runtime/sandbox/build.py +50 -57
- agentscope_runtime/sandbox/client/__init__.py +6 -1
- agentscope_runtime/sandbox/client/async_http_client.py +339 -0
- agentscope_runtime/sandbox/client/base.py +74 -0
- agentscope_runtime/sandbox/client/http_client.py +108 -329
- agentscope_runtime/sandbox/enums.py +7 -0
- agentscope_runtime/sandbox/manager/sandbox_manager.py +264 -4
- agentscope_runtime/sandbox/manager/server/app.py +7 -1
- agentscope_runtime/version.py +1 -1
- {agentscope_runtime-1.0.2.dist-info → agentscope_runtime-1.0.4.dist-info}/METADATA +109 -29
- {agentscope_runtime-1.0.2.dist-info → agentscope_runtime-1.0.4.dist-info}/RECORD +58 -46
- {agentscope_runtime-1.0.2.dist-info → agentscope_runtime-1.0.4.dist-info}/WHEEL +0 -0
- {agentscope_runtime-1.0.2.dist-info → agentscope_runtime-1.0.4.dist-info}/entry_points.txt +0 -0
- {agentscope_runtime-1.0.2.dist-info → agentscope_runtime-1.0.4.dist-info}/licenses/LICENSE +0 -0
- {agentscope_runtime-1.0.2.dist-info → agentscope_runtime-1.0.4.dist-info}/top_level.txt +0 -0
|
@@ -9,6 +9,7 @@ from urllib.parse import urlparse
|
|
|
9
9
|
from agentscope import setup_logger
|
|
10
10
|
from agentscope.message import Msg
|
|
11
11
|
|
|
12
|
+
from ..utils import _update_obj_attrs
|
|
12
13
|
from ...engine.schemas.agent_schemas import (
|
|
13
14
|
Message,
|
|
14
15
|
Content,
|
|
@@ -26,13 +27,6 @@ from ...engine.schemas.agent_schemas import (
|
|
|
26
27
|
setup_logger("ERROR")
|
|
27
28
|
|
|
28
29
|
|
|
29
|
-
def _update_obj_attrs(obj, **attrs):
|
|
30
|
-
for key, value in attrs.items():
|
|
31
|
-
if hasattr(obj, key):
|
|
32
|
-
setattr(obj, key, value)
|
|
33
|
-
return obj
|
|
34
|
-
|
|
35
|
-
|
|
36
30
|
async def adapt_agentscope_message_stream(
|
|
37
31
|
source_stream: AsyncIterator[Tuple[Msg, bool]],
|
|
38
32
|
) -> AsyncIterator[Union[Message, Content]]:
|
|
@@ -97,10 +91,9 @@ async def adapt_agentscope_message_stream(
|
|
|
97
91
|
else:
|
|
98
92
|
new_tool_blocks.append(block)
|
|
99
93
|
if new_tool_blocks:
|
|
100
|
-
if tool_start:
|
|
94
|
+
if tool_start: # Only for close the last msg
|
|
101
95
|
msg.content = new_tool_blocks
|
|
102
96
|
else:
|
|
103
|
-
msg.content = new_blocks
|
|
104
97
|
tool_start = True
|
|
105
98
|
|
|
106
99
|
else:
|
|
File without changes
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# pylint: disable=too-many-branches,too-many-statements
|
|
3
|
+
import json
|
|
4
|
+
from typing import Union, List
|
|
5
|
+
from collections import OrderedDict
|
|
6
|
+
|
|
7
|
+
from agent_framework import (
|
|
8
|
+
ChatMessage,
|
|
9
|
+
TextContent as MSTextContent,
|
|
10
|
+
DataContent as MSDataContent,
|
|
11
|
+
TextReasoningContent,
|
|
12
|
+
UriContent,
|
|
13
|
+
FunctionCallContent,
|
|
14
|
+
FunctionResultContent,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
from ...engine.schemas.agent_schemas import (
|
|
18
|
+
Message,
|
|
19
|
+
MessageType,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def message_to_ms_agent_framework_message(
|
|
24
|
+
messages: Union[Message, List[Message]],
|
|
25
|
+
) -> Union[ChatMessage, List[ChatMessage]]:
|
|
26
|
+
"""
|
|
27
|
+
Convert AgentScope runtime Message(s) to Microsoft agent framework
|
|
28
|
+
Message(s).
|
|
29
|
+
|
|
30
|
+
Reference:
|
|
31
|
+
https://learn.microsoft.com/en-us/agent-framework/user-guide/agents
|
|
32
|
+
/running-agents?pivots=programming-language-python
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
messages: A single AgentScope runtime Message or list of Messages.
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
A single Microsoft agent framework Message object or a list of
|
|
39
|
+
Microsoft agent framework Message objects.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
def _try_loads(v, default, keep_original=False):
|
|
43
|
+
if isinstance(v, (dict, list)):
|
|
44
|
+
return v
|
|
45
|
+
if isinstance(v, str) and v.strip():
|
|
46
|
+
try:
|
|
47
|
+
return json.loads(v)
|
|
48
|
+
except Exception:
|
|
49
|
+
return v if keep_original else default
|
|
50
|
+
return default
|
|
51
|
+
|
|
52
|
+
def _convert_one(message: Message) -> ChatMessage:
|
|
53
|
+
result = {
|
|
54
|
+
"author_name": getattr(message, "name", message.role),
|
|
55
|
+
"role": message.role or "assistant",
|
|
56
|
+
}
|
|
57
|
+
_id = getattr(message, "id")
|
|
58
|
+
|
|
59
|
+
# if meta exists, prefer original id/name from meta
|
|
60
|
+
if hasattr(message, "metadata") and isinstance(message.metadata, dict):
|
|
61
|
+
if "original_id" in message.metadata:
|
|
62
|
+
_id = message.metadata["original_id"]
|
|
63
|
+
if "original_name" in message.metadata:
|
|
64
|
+
result["author_name"] = message.metadata["original_name"]
|
|
65
|
+
if "metadata" in message.metadata:
|
|
66
|
+
result["additional_properties"] = message.metadata["metadata"]
|
|
67
|
+
result["message_id"] = _id
|
|
68
|
+
|
|
69
|
+
if message.type in (
|
|
70
|
+
MessageType.PLUGIN_CALL,
|
|
71
|
+
MessageType.MCP_TOOL_CALL,
|
|
72
|
+
MessageType.FUNCTION_CALL,
|
|
73
|
+
):
|
|
74
|
+
# convert CALL to ToolUseBlock
|
|
75
|
+
tool_args = None
|
|
76
|
+
for cnt in reversed(message.content):
|
|
77
|
+
if hasattr(cnt, "data"):
|
|
78
|
+
v = cnt.data.get("arguments")
|
|
79
|
+
if isinstance(v, (dict, list)) or (
|
|
80
|
+
isinstance(v, str) and v.strip()
|
|
81
|
+
):
|
|
82
|
+
tool_args = _try_loads(v, {}, keep_original=False)
|
|
83
|
+
break
|
|
84
|
+
if tool_args is None:
|
|
85
|
+
tool_args = {}
|
|
86
|
+
result["contents"] = [
|
|
87
|
+
FunctionCallContent(
|
|
88
|
+
call_id=message.content[0].data["call_id"],
|
|
89
|
+
name=message.content[0].data.get("name"),
|
|
90
|
+
arguments=tool_args,
|
|
91
|
+
),
|
|
92
|
+
]
|
|
93
|
+
elif message.type in (
|
|
94
|
+
MessageType.PLUGIN_CALL_OUTPUT,
|
|
95
|
+
MessageType.MCP_TOOL_CALL_OUTPUT,
|
|
96
|
+
MessageType.FUNCTION_CALL_OUTPUT,
|
|
97
|
+
):
|
|
98
|
+
result["role"] = "tool"
|
|
99
|
+
out = None
|
|
100
|
+
for cnt in reversed(message.content):
|
|
101
|
+
if hasattr(cnt, "data"):
|
|
102
|
+
v = cnt.data.get("output")
|
|
103
|
+
if isinstance(v, (dict, list)) or (
|
|
104
|
+
isinstance(v, str) and v.strip()
|
|
105
|
+
):
|
|
106
|
+
out = _try_loads(v, "", keep_original=True)
|
|
107
|
+
break
|
|
108
|
+
if out is None:
|
|
109
|
+
out = ""
|
|
110
|
+
blk = out
|
|
111
|
+
|
|
112
|
+
result["contents"] = [
|
|
113
|
+
FunctionResultContent(
|
|
114
|
+
call_id=message.content[0].data["call_id"],
|
|
115
|
+
result=blk,
|
|
116
|
+
),
|
|
117
|
+
]
|
|
118
|
+
elif message.type in (MessageType.REASONING,):
|
|
119
|
+
result["contents"] = [
|
|
120
|
+
TextReasoningContent(
|
|
121
|
+
text=message.content[0].text,
|
|
122
|
+
),
|
|
123
|
+
]
|
|
124
|
+
else:
|
|
125
|
+
type_mapping = {
|
|
126
|
+
"text": (MSTextContent, "text"),
|
|
127
|
+
"image": (UriContent, "image_url"),
|
|
128
|
+
"audio": (UriContent, "data"),
|
|
129
|
+
"data": (MSDataContent, "data"),
|
|
130
|
+
"file": (MSDataContent, "file_url"), # Support file_url
|
|
131
|
+
# "video": (VideoBlock, "video_url", True),
|
|
132
|
+
# TODO: support video
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
msg_content = []
|
|
136
|
+
for cnt in message.content:
|
|
137
|
+
cnt_type = cnt.type or "text"
|
|
138
|
+
|
|
139
|
+
if cnt_type not in type_mapping:
|
|
140
|
+
raise ValueError(f"Unsupported message type: {cnt_type}")
|
|
141
|
+
|
|
142
|
+
block_cls, attr_name = type_mapping[cnt_type]
|
|
143
|
+
value = getattr(cnt, attr_name)
|
|
144
|
+
|
|
145
|
+
if cnt_type in ("image", "audio", "file", "data"):
|
|
146
|
+
msg_content.append(
|
|
147
|
+
block_cls(
|
|
148
|
+
data=value,
|
|
149
|
+
type=cnt.type,
|
|
150
|
+
),
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
else:
|
|
154
|
+
# text
|
|
155
|
+
if isinstance(value, str):
|
|
156
|
+
msg_content.append(
|
|
157
|
+
MSTextContent(text=value),
|
|
158
|
+
)
|
|
159
|
+
else:
|
|
160
|
+
try:
|
|
161
|
+
json_str = json.dumps(value, ensure_ascii=False)
|
|
162
|
+
except Exception:
|
|
163
|
+
json_str = str(value)
|
|
164
|
+
msg_content.append(MSTextContent(text=json_str))
|
|
165
|
+
|
|
166
|
+
result["contents"] = msg_content
|
|
167
|
+
_msg = ChatMessage(**result)
|
|
168
|
+
return _msg
|
|
169
|
+
|
|
170
|
+
# Handle single or list input
|
|
171
|
+
if isinstance(messages, Message):
|
|
172
|
+
return _convert_one(messages)
|
|
173
|
+
elif isinstance(messages, list):
|
|
174
|
+
converted_list = [_convert_one(m) for m in messages]
|
|
175
|
+
|
|
176
|
+
# Group by original_id
|
|
177
|
+
grouped = OrderedDict()
|
|
178
|
+
for msg, orig_msg in zip(messages, converted_list):
|
|
179
|
+
metadata = getattr(msg, "metadata")
|
|
180
|
+
if metadata:
|
|
181
|
+
orig_id = metadata.get(
|
|
182
|
+
"original_id",
|
|
183
|
+
orig_msg.message_id,
|
|
184
|
+
)
|
|
185
|
+
else:
|
|
186
|
+
# In case metadata is not provided, use the original id
|
|
187
|
+
orig_id = msg.id
|
|
188
|
+
|
|
189
|
+
if orig_id not in grouped:
|
|
190
|
+
ms_msg = ChatMessage(
|
|
191
|
+
author_name=orig_msg.author_name,
|
|
192
|
+
role=orig_msg.role,
|
|
193
|
+
additional_properties=orig_msg.additional_properties,
|
|
194
|
+
contents=list(orig_msg.contents),
|
|
195
|
+
)
|
|
196
|
+
ms_msg.message_id = orig_id
|
|
197
|
+
grouped[orig_id] = ms_msg
|
|
198
|
+
else:
|
|
199
|
+
grouped[orig_id].contents.extend(orig_msg.contents)
|
|
200
|
+
|
|
201
|
+
return list(grouped.values())
|
|
202
|
+
else:
|
|
203
|
+
raise TypeError(
|
|
204
|
+
f"Expected Message or list[Message], got {type(messages)}",
|
|
205
|
+
)
|
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# pylint: disable=too-many-branches,too-many-statements
|
|
3
|
+
import copy
|
|
4
|
+
import json
|
|
5
|
+
|
|
6
|
+
from typing import AsyncIterator, Union, List
|
|
7
|
+
|
|
8
|
+
from agent_framework import (
|
|
9
|
+
AgentRunResponseUpdate,
|
|
10
|
+
TextContent as MSTextContent,
|
|
11
|
+
DataContent as MSDataContent,
|
|
12
|
+
TextReasoningContent,
|
|
13
|
+
UriContent,
|
|
14
|
+
FunctionCallContent,
|
|
15
|
+
FunctionResultContent,
|
|
16
|
+
ErrorContent,
|
|
17
|
+
UsageContent,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
from ..utils import _update_obj_attrs
|
|
21
|
+
from ...engine.schemas.exception import AgentRuntimeErrorException
|
|
22
|
+
from ...engine.schemas.agent_schemas import (
|
|
23
|
+
Message,
|
|
24
|
+
TextContent,
|
|
25
|
+
Content,
|
|
26
|
+
DataContent,
|
|
27
|
+
FunctionCall,
|
|
28
|
+
FunctionCallOutput,
|
|
29
|
+
MessageType,
|
|
30
|
+
ImageContent,
|
|
31
|
+
AudioContent,
|
|
32
|
+
FileContent,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
async def adapt_ms_agent_framework_message_stream(
|
|
37
|
+
source_stream: AsyncIterator[AgentRunResponseUpdate],
|
|
38
|
+
) -> AsyncIterator[Union[Message, Content]]:
|
|
39
|
+
# Initialize variables to avoid uncaught errors
|
|
40
|
+
msg_id = None
|
|
41
|
+
usage = None
|
|
42
|
+
tool_start = False
|
|
43
|
+
message = None
|
|
44
|
+
reasoning_message = None
|
|
45
|
+
plugin_call_message = None
|
|
46
|
+
call_id = None
|
|
47
|
+
text_delta_content = None
|
|
48
|
+
data_delta_content = None
|
|
49
|
+
index = None
|
|
50
|
+
|
|
51
|
+
# Run agent
|
|
52
|
+
async for msg in source_stream:
|
|
53
|
+
# deepcopy required to avoid modifying the original message object
|
|
54
|
+
# which may be used elsewhere in the streaming pipeline
|
|
55
|
+
msg = copy.deepcopy(msg)
|
|
56
|
+
|
|
57
|
+
assert isinstance(
|
|
58
|
+
msg,
|
|
59
|
+
AgentRunResponseUpdate,
|
|
60
|
+
), f"Expected AgentRunResponseUpdate, got {type(msg)}"
|
|
61
|
+
|
|
62
|
+
# If a new message, create new Message
|
|
63
|
+
if msg.message_id != msg_id:
|
|
64
|
+
# If a new message, yield previous content
|
|
65
|
+
if text_delta_content is not None:
|
|
66
|
+
yield text_delta_content.completed()
|
|
67
|
+
text_delta_content = None
|
|
68
|
+
|
|
69
|
+
if data_delta_content is not None:
|
|
70
|
+
yield data_delta_content.completed()
|
|
71
|
+
data_delta_content = None
|
|
72
|
+
|
|
73
|
+
if message is not None:
|
|
74
|
+
message = _update_obj_attrs(
|
|
75
|
+
message,
|
|
76
|
+
usage=usage,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
yield message.completed()
|
|
80
|
+
message = None
|
|
81
|
+
|
|
82
|
+
if reasoning_message is not None:
|
|
83
|
+
reasoning_message = _update_obj_attrs(
|
|
84
|
+
reasoning_message,
|
|
85
|
+
usage=usage,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
yield reasoning_message.completed()
|
|
89
|
+
reasoning_message = None
|
|
90
|
+
|
|
91
|
+
if plugin_call_message is not None:
|
|
92
|
+
plugin_call_message = _update_obj_attrs(
|
|
93
|
+
plugin_call_message,
|
|
94
|
+
usage=usage,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
yield plugin_call_message.completed()
|
|
98
|
+
plugin_call_message = None
|
|
99
|
+
|
|
100
|
+
index = None
|
|
101
|
+
|
|
102
|
+
# Note: Tool use content only happens in the last of messages
|
|
103
|
+
tool_start = False
|
|
104
|
+
|
|
105
|
+
# Cache msg id
|
|
106
|
+
msg_id = msg.message_id
|
|
107
|
+
|
|
108
|
+
new_blocks = []
|
|
109
|
+
new_tool_blocks = []
|
|
110
|
+
if isinstance(msg.contents, List):
|
|
111
|
+
for block in msg.contents:
|
|
112
|
+
if block.type != "function_call":
|
|
113
|
+
new_blocks.append(block)
|
|
114
|
+
else:
|
|
115
|
+
new_tool_blocks.append(block)
|
|
116
|
+
if new_tool_blocks:
|
|
117
|
+
if tool_start:
|
|
118
|
+
msg.contents = new_tool_blocks
|
|
119
|
+
else:
|
|
120
|
+
msg.contents = new_blocks
|
|
121
|
+
tool_start = True
|
|
122
|
+
|
|
123
|
+
else:
|
|
124
|
+
msg.contents = new_blocks
|
|
125
|
+
|
|
126
|
+
if not msg.contents:
|
|
127
|
+
continue
|
|
128
|
+
|
|
129
|
+
# msg content
|
|
130
|
+
content = msg.contents
|
|
131
|
+
|
|
132
|
+
for element in content:
|
|
133
|
+
if isinstance(element, UsageContent):
|
|
134
|
+
# TODO: consider keeping the same format with as
|
|
135
|
+
usage = element.details.to_dict()
|
|
136
|
+
|
|
137
|
+
elif isinstance(element, MSTextContent): # Text
|
|
138
|
+
text = element.text
|
|
139
|
+
if text:
|
|
140
|
+
if message is None:
|
|
141
|
+
message = Message(
|
|
142
|
+
role="assistant",
|
|
143
|
+
type=MessageType.MESSAGE,
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
index = None
|
|
147
|
+
message = _update_obj_attrs(
|
|
148
|
+
message,
|
|
149
|
+
usage=usage,
|
|
150
|
+
)
|
|
151
|
+
yield message.in_progress()
|
|
152
|
+
|
|
153
|
+
text_delta_content = TextContent(
|
|
154
|
+
delta=True,
|
|
155
|
+
index=index,
|
|
156
|
+
text=text,
|
|
157
|
+
)
|
|
158
|
+
text_delta_content = message.add_delta_content(
|
|
159
|
+
new_content=text_delta_content,
|
|
160
|
+
)
|
|
161
|
+
index = text_delta_content.index
|
|
162
|
+
|
|
163
|
+
# Only yield valid text
|
|
164
|
+
if text_delta_content.text:
|
|
165
|
+
yield text_delta_content
|
|
166
|
+
|
|
167
|
+
if tool_start:
|
|
168
|
+
text_delta_content = message.content[index]
|
|
169
|
+
if text_delta_content.text:
|
|
170
|
+
yield text_delta_content.completed()
|
|
171
|
+
text_delta_content = None
|
|
172
|
+
|
|
173
|
+
message = _update_obj_attrs(
|
|
174
|
+
message,
|
|
175
|
+
usage=usage,
|
|
176
|
+
)
|
|
177
|
+
yield message.completed()
|
|
178
|
+
message = None
|
|
179
|
+
index = None
|
|
180
|
+
|
|
181
|
+
elif isinstance(element, TextReasoningContent): # Thinking
|
|
182
|
+
reasoning = element.text
|
|
183
|
+
if reasoning:
|
|
184
|
+
if reasoning_message is None:
|
|
185
|
+
index = None
|
|
186
|
+
reasoning_message = Message(
|
|
187
|
+
role="assistant",
|
|
188
|
+
type=MessageType.REASONING,
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
reasoning_message = _update_obj_attrs(
|
|
192
|
+
reasoning_message,
|
|
193
|
+
usage=usage,
|
|
194
|
+
)
|
|
195
|
+
yield reasoning_message.in_progress()
|
|
196
|
+
|
|
197
|
+
text_delta_content = TextContent(
|
|
198
|
+
delta=True,
|
|
199
|
+
index=index,
|
|
200
|
+
text=reasoning,
|
|
201
|
+
)
|
|
202
|
+
text_delta_content = reasoning_message.add_delta_content(
|
|
203
|
+
new_content=text_delta_content,
|
|
204
|
+
)
|
|
205
|
+
index = text_delta_content.index
|
|
206
|
+
|
|
207
|
+
# Only yield valid text
|
|
208
|
+
if text_delta_content.text:
|
|
209
|
+
yield text_delta_content
|
|
210
|
+
|
|
211
|
+
if tool_start:
|
|
212
|
+
text_delta_content = reasoning_message.content[index]
|
|
213
|
+
if text_delta_content.text:
|
|
214
|
+
yield text_delta_content.completed()
|
|
215
|
+
text_delta_content = None
|
|
216
|
+
|
|
217
|
+
reasoning_message = _update_obj_attrs(
|
|
218
|
+
reasoning_message,
|
|
219
|
+
usage=usage,
|
|
220
|
+
)
|
|
221
|
+
yield reasoning_message.completed()
|
|
222
|
+
reasoning_message = None
|
|
223
|
+
index = None
|
|
224
|
+
|
|
225
|
+
elif isinstance(element, FunctionCallContent): # Tool use
|
|
226
|
+
msg_type = MessageType.PLUGIN_CALL
|
|
227
|
+
fc_cls = FunctionCall
|
|
228
|
+
fc_kwargs = {}
|
|
229
|
+
|
|
230
|
+
if element.call_id is not None: # New tool call
|
|
231
|
+
index = None
|
|
232
|
+
call_id = element.call_id
|
|
233
|
+
plugin_call_message = Message(
|
|
234
|
+
type=msg_type,
|
|
235
|
+
role="assistant",
|
|
236
|
+
)
|
|
237
|
+
plugin_call_message = _update_obj_attrs(
|
|
238
|
+
plugin_call_message,
|
|
239
|
+
usage=usage,
|
|
240
|
+
)
|
|
241
|
+
yield plugin_call_message.in_progress()
|
|
242
|
+
else:
|
|
243
|
+
# The last plugin_call_message is completed
|
|
244
|
+
if data_delta_content is not None:
|
|
245
|
+
yield data_delta_content.completed()
|
|
246
|
+
data_delta_content = None
|
|
247
|
+
if plugin_call_message is not None:
|
|
248
|
+
plugin_call_message = _update_obj_attrs(
|
|
249
|
+
plugin_call_message,
|
|
250
|
+
usage=usage,
|
|
251
|
+
)
|
|
252
|
+
yield plugin_call_message.completed()
|
|
253
|
+
plugin_call_message = None
|
|
254
|
+
index = None
|
|
255
|
+
|
|
256
|
+
data_delta_content = DataContent(
|
|
257
|
+
index=index,
|
|
258
|
+
data=fc_cls(
|
|
259
|
+
call_id=call_id,
|
|
260
|
+
name=element.name,
|
|
261
|
+
arguments=element.arguments,
|
|
262
|
+
**fc_kwargs,
|
|
263
|
+
).model_dump(),
|
|
264
|
+
delta=True,
|
|
265
|
+
msg_id=plugin_call_message.id,
|
|
266
|
+
)
|
|
267
|
+
yield data_delta_content.in_progress()
|
|
268
|
+
|
|
269
|
+
plugin_call_message = _update_obj_attrs(
|
|
270
|
+
plugin_call_message,
|
|
271
|
+
usage=usage,
|
|
272
|
+
)
|
|
273
|
+
yield plugin_call_message.in_progress()
|
|
274
|
+
|
|
275
|
+
elif isinstance(element, FunctionResultContent): # Tool result
|
|
276
|
+
try:
|
|
277
|
+
json_str = json.dumps(
|
|
278
|
+
element.result,
|
|
279
|
+
ensure_ascii=False,
|
|
280
|
+
)
|
|
281
|
+
except Exception:
|
|
282
|
+
json_str = str(element.result)
|
|
283
|
+
|
|
284
|
+
data_delta_content = DataContent(
|
|
285
|
+
index=None,
|
|
286
|
+
data=FunctionCallOutput(
|
|
287
|
+
call_id=element.call_id,
|
|
288
|
+
output=json_str,
|
|
289
|
+
).model_dump(),
|
|
290
|
+
)
|
|
291
|
+
plugin_output_message = Message(
|
|
292
|
+
type=MessageType.PLUGIN_CALL_OUTPUT,
|
|
293
|
+
role="tool",
|
|
294
|
+
content=[data_delta_content],
|
|
295
|
+
)
|
|
296
|
+
plugin_output_message = _update_obj_attrs(
|
|
297
|
+
plugin_output_message,
|
|
298
|
+
usage=usage,
|
|
299
|
+
)
|
|
300
|
+
yield plugin_output_message.completed()
|
|
301
|
+
message = None
|
|
302
|
+
reasoning_message = None
|
|
303
|
+
|
|
304
|
+
index = None
|
|
305
|
+
|
|
306
|
+
elif isinstance(element, MSDataContent):
|
|
307
|
+
delta_content = DataContent(
|
|
308
|
+
delta=True,
|
|
309
|
+
index=index,
|
|
310
|
+
data=element.uri,
|
|
311
|
+
type=element.type,
|
|
312
|
+
)
|
|
313
|
+
delta_content = message.add_delta_content(
|
|
314
|
+
new_content=delta_content,
|
|
315
|
+
)
|
|
316
|
+
index = delta_content.index
|
|
317
|
+
yield delta_content
|
|
318
|
+
|
|
319
|
+
elif isinstance(element, UriContent):
|
|
320
|
+
kwargs = {}
|
|
321
|
+
|
|
322
|
+
if "image" in element.type:
|
|
323
|
+
cnt_cls = ImageContent
|
|
324
|
+
kwargs.update(
|
|
325
|
+
{
|
|
326
|
+
"type": element.type,
|
|
327
|
+
"image_url": element.uri,
|
|
328
|
+
},
|
|
329
|
+
)
|
|
330
|
+
elif "audio" in element.type:
|
|
331
|
+
cnt_cls = AudioContent
|
|
332
|
+
kwargs.update(
|
|
333
|
+
{
|
|
334
|
+
"type": element.type,
|
|
335
|
+
"data": element.uri,
|
|
336
|
+
"format": element.media_type,
|
|
337
|
+
},
|
|
338
|
+
)
|
|
339
|
+
elif "video" in element.type:
|
|
340
|
+
# TODO: support video type
|
|
341
|
+
cnt_cls = ImageContent
|
|
342
|
+
kwargs.update(
|
|
343
|
+
{
|
|
344
|
+
"type": element.media_type,
|
|
345
|
+
"image_url": element.uri,
|
|
346
|
+
},
|
|
347
|
+
)
|
|
348
|
+
else:
|
|
349
|
+
cnt_cls = FileContent
|
|
350
|
+
kwargs.update(
|
|
351
|
+
{
|
|
352
|
+
"type": element.type,
|
|
353
|
+
"file_url": element.uri,
|
|
354
|
+
},
|
|
355
|
+
)
|
|
356
|
+
|
|
357
|
+
delta_content = cnt_cls(
|
|
358
|
+
delta=False,
|
|
359
|
+
index=index,
|
|
360
|
+
**kwargs,
|
|
361
|
+
)
|
|
362
|
+
delta_content = message.add_delta_content(
|
|
363
|
+
new_content=delta_content,
|
|
364
|
+
)
|
|
365
|
+
index = delta_content.index
|
|
366
|
+
yield delta_content
|
|
367
|
+
|
|
368
|
+
elif isinstance(element, ErrorContent):
|
|
369
|
+
raise AgentRuntimeErrorException(
|
|
370
|
+
code=element.error_code,
|
|
371
|
+
message=element.message,
|
|
372
|
+
details=element.details,
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
else:
|
|
376
|
+
raise ValueError(f"Unknown element type: {type(element)}")
|
|
377
|
+
|
|
378
|
+
if (
|
|
379
|
+
text_delta_content is not None
|
|
380
|
+
and text_delta_content.status == "in_progress"
|
|
381
|
+
):
|
|
382
|
+
yield text_delta_content.completed()
|
|
383
|
+
|
|
384
|
+
if (
|
|
385
|
+
data_delta_content is not None
|
|
386
|
+
and data_delta_content.status == "in_progress"
|
|
387
|
+
):
|
|
388
|
+
yield data_delta_content.completed()
|
|
389
|
+
|
|
390
|
+
if message is not None and message.status == "in_progress":
|
|
391
|
+
message = _update_obj_attrs(
|
|
392
|
+
message,
|
|
393
|
+
usage=usage,
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
yield message.completed()
|
|
397
|
+
|
|
398
|
+
if (
|
|
399
|
+
reasoning_message is not None
|
|
400
|
+
and reasoning_message.status == "in_progress"
|
|
401
|
+
):
|
|
402
|
+
reasoning_message = _update_obj_attrs(
|
|
403
|
+
reasoning_message,
|
|
404
|
+
usage=usage,
|
|
405
|
+
)
|
|
406
|
+
|
|
407
|
+
yield reasoning_message.completed()
|
|
408
|
+
|
|
409
|
+
if (
|
|
410
|
+
plugin_call_message is not None
|
|
411
|
+
and plugin_call_message.status == "in_progress"
|
|
412
|
+
):
|
|
413
|
+
plugin_call_message = _update_obj_attrs(
|
|
414
|
+
plugin_call_message,
|
|
415
|
+
usage=usage,
|
|
416
|
+
)
|
|
417
|
+
|
|
418
|
+
yield plugin_call_message.completed()
|