agentscope-runtime 0.1.5b2__py3-none-any.whl → 0.2.0__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/common/__init__.py +0 -0
- agentscope_runtime/common/collections/in_memory_mapping.py +27 -0
- agentscope_runtime/common/collections/redis_mapping.py +42 -0
- agentscope_runtime/common/container_clients/__init__.py +0 -0
- agentscope_runtime/common/container_clients/agentrun_client.py +1098 -0
- agentscope_runtime/common/container_clients/docker_client.py +250 -0
- agentscope_runtime/{sandbox/manager → common}/container_clients/kubernetes_client.py +6 -13
- agentscope_runtime/engine/__init__.py +12 -0
- agentscope_runtime/engine/agents/agentscope_agent.py +567 -0
- agentscope_runtime/engine/agents/agno_agent.py +26 -27
- agentscope_runtime/engine/agents/autogen_agent.py +13 -8
- agentscope_runtime/engine/agents/langgraph_agent.py +52 -9
- agentscope_runtime/engine/agents/utils.py +53 -0
- agentscope_runtime/engine/app/__init__.py +6 -0
- agentscope_runtime/engine/app/agent_app.py +239 -0
- agentscope_runtime/engine/app/base_app.py +181 -0
- agentscope_runtime/engine/app/celery_mixin.py +92 -0
- agentscope_runtime/engine/deployers/adapter/responses/response_api_adapter_utils.py +5 -1
- agentscope_runtime/engine/deployers/base.py +1 -0
- agentscope_runtime/engine/deployers/cli_fc_deploy.py +39 -20
- agentscope_runtime/engine/deployers/kubernetes_deployer.py +12 -5
- agentscope_runtime/engine/deployers/local_deployer.py +61 -3
- agentscope_runtime/engine/deployers/modelstudio_deployer.py +201 -40
- agentscope_runtime/engine/deployers/utils/docker_image_utils/runner_image_factory.py +9 -0
- agentscope_runtime/engine/deployers/utils/package_project_utils.py +234 -3
- agentscope_runtime/engine/deployers/utils/service_utils/fastapi_factory.py +567 -7
- agentscope_runtime/engine/deployers/utils/service_utils/standalone_main.py.j2 +211 -0
- agentscope_runtime/engine/deployers/utils/wheel_packager.py +1 -1
- agentscope_runtime/engine/helpers/helper.py +60 -41
- agentscope_runtime/engine/runner.py +40 -24
- agentscope_runtime/engine/schemas/agent_schemas.py +42 -0
- agentscope_runtime/engine/schemas/modelstudio_llm.py +14 -14
- agentscope_runtime/engine/services/sandbox_service.py +62 -70
- agentscope_runtime/engine/services/tablestore_memory_service.py +307 -0
- agentscope_runtime/engine/services/tablestore_rag_service.py +143 -0
- agentscope_runtime/engine/services/tablestore_session_history_service.py +293 -0
- agentscope_runtime/engine/services/utils/__init__.py +0 -0
- agentscope_runtime/engine/services/utils/tablestore_service_utils.py +352 -0
- agentscope_runtime/engine/tracing/__init__.py +9 -3
- agentscope_runtime/engine/tracing/asyncio_util.py +24 -0
- agentscope_runtime/engine/tracing/base.py +66 -34
- agentscope_runtime/engine/tracing/local_logging_handler.py +45 -31
- agentscope_runtime/engine/tracing/message_util.py +528 -0
- agentscope_runtime/engine/tracing/tracing_metric.py +20 -8
- agentscope_runtime/engine/tracing/tracing_util.py +130 -0
- agentscope_runtime/engine/tracing/wrapper.py +794 -169
- agentscope_runtime/sandbox/__init__.py +2 -0
- agentscope_runtime/sandbox/box/base/__init__.py +4 -0
- agentscope_runtime/sandbox/box/base/base_sandbox.py +6 -4
- agentscope_runtime/sandbox/box/browser/__init__.py +4 -0
- agentscope_runtime/sandbox/box/browser/browser_sandbox.py +10 -14
- agentscope_runtime/sandbox/box/dummy/__init__.py +4 -0
- agentscope_runtime/sandbox/box/dummy/dummy_sandbox.py +2 -1
- agentscope_runtime/sandbox/box/filesystem/__init__.py +4 -0
- agentscope_runtime/sandbox/box/filesystem/filesystem_sandbox.py +10 -7
- agentscope_runtime/sandbox/box/gui/__init__.py +4 -0
- agentscope_runtime/sandbox/box/gui/box/__init__.py +0 -0
- agentscope_runtime/sandbox/box/gui/gui_sandbox.py +81 -0
- agentscope_runtime/sandbox/box/sandbox.py +5 -2
- agentscope_runtime/sandbox/box/shared/routers/generic.py +20 -1
- agentscope_runtime/sandbox/box/training_box/__init__.py +4 -0
- agentscope_runtime/sandbox/box/training_box/training_box.py +7 -54
- agentscope_runtime/sandbox/build.py +143 -58
- agentscope_runtime/sandbox/client/http_client.py +87 -59
- agentscope_runtime/sandbox/client/training_client.py +0 -1
- agentscope_runtime/sandbox/constant.py +27 -1
- agentscope_runtime/sandbox/custom/custom_sandbox.py +7 -6
- agentscope_runtime/sandbox/custom/example.py +4 -3
- agentscope_runtime/sandbox/enums.py +1 -1
- agentscope_runtime/sandbox/manager/sandbox_manager.py +212 -106
- agentscope_runtime/sandbox/manager/server/app.py +82 -14
- agentscope_runtime/sandbox/manager/server/config.py +50 -3
- agentscope_runtime/sandbox/model/container.py +12 -23
- agentscope_runtime/sandbox/model/manager_config.py +93 -5
- agentscope_runtime/sandbox/registry.py +1 -1
- agentscope_runtime/sandbox/tools/gui/__init__.py +7 -0
- agentscope_runtime/sandbox/tools/gui/tool.py +77 -0
- agentscope_runtime/sandbox/tools/mcp_tool.py +6 -2
- agentscope_runtime/sandbox/tools/tool.py +4 -0
- agentscope_runtime/sandbox/utils.py +124 -0
- agentscope_runtime/version.py +1 -1
- {agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.2.0.dist-info}/METADATA +246 -111
- {agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.2.0.dist-info}/RECORD +96 -80
- agentscope_runtime/engine/agents/agentscope_agent/__init__.py +0 -6
- agentscope_runtime/engine/agents/agentscope_agent/agent.py +0 -401
- agentscope_runtime/engine/agents/agentscope_agent/hooks.py +0 -169
- agentscope_runtime/engine/agents/llm_agent.py +0 -51
- agentscope_runtime/engine/llms/__init__.py +0 -3
- agentscope_runtime/engine/llms/base_llm.py +0 -60
- agentscope_runtime/engine/llms/qwen_llm.py +0 -47
- agentscope_runtime/sandbox/manager/collections/in_memory_mapping.py +0 -22
- agentscope_runtime/sandbox/manager/collections/redis_mapping.py +0 -26
- agentscope_runtime/sandbox/manager/container_clients/__init__.py +0 -10
- agentscope_runtime/sandbox/manager/container_clients/docker_client.py +0 -422
- /agentscope_runtime/{sandbox/manager → common}/collections/__init__.py +0 -0
- /agentscope_runtime/{sandbox/manager → common}/collections/base_mapping.py +0 -0
- /agentscope_runtime/{sandbox/manager → common}/collections/base_queue.py +0 -0
- /agentscope_runtime/{sandbox/manager → common}/collections/base_set.py +0 -0
- /agentscope_runtime/{sandbox/manager → common}/collections/in_memory_queue.py +0 -0
- /agentscope_runtime/{sandbox/manager → common}/collections/in_memory_set.py +0 -0
- /agentscope_runtime/{sandbox/manager → common}/collections/redis_queue.py +0 -0
- /agentscope_runtime/{sandbox/manager → common}/collections/redis_set.py +0 -0
- /agentscope_runtime/{sandbox/manager → common}/container_clients/base_client.py +0 -0
- {agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.2.0.dist-info}/WHEEL +0 -0
- {agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.2.0.dist-info}/entry_points.txt +0 -0
- {agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.2.0.dist-info}/licenses/LICENSE +0 -0
- {agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.2.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,567 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# pylint:disable=too-many-nested-blocks, too-many-branches, too-many-statements
|
|
3
|
+
# pylint:disable=line-too-long, protected-access
|
|
4
|
+
import copy
|
|
5
|
+
import logging
|
|
6
|
+
import json
|
|
7
|
+
import traceback
|
|
8
|
+
from functools import partial
|
|
9
|
+
from typing import Optional, Type, List
|
|
10
|
+
from urllib.parse import urlparse
|
|
11
|
+
|
|
12
|
+
from agentscope import setup_logger
|
|
13
|
+
from agentscope.agent import AgentBase, ReActAgent
|
|
14
|
+
from agentscope.formatter import (
|
|
15
|
+
FormatterBase,
|
|
16
|
+
DashScopeChatFormatter,
|
|
17
|
+
OpenAIChatFormatter,
|
|
18
|
+
AnthropicChatFormatter,
|
|
19
|
+
OllamaChatFormatter,
|
|
20
|
+
GeminiChatFormatter,
|
|
21
|
+
)
|
|
22
|
+
from agentscope.memory import InMemoryMemory
|
|
23
|
+
from agentscope.message import (
|
|
24
|
+
Msg,
|
|
25
|
+
ToolUseBlock,
|
|
26
|
+
ToolResultBlock,
|
|
27
|
+
TextBlock,
|
|
28
|
+
ThinkingBlock,
|
|
29
|
+
ImageBlock,
|
|
30
|
+
AudioBlock,
|
|
31
|
+
# VideoBlock, # TODO: support
|
|
32
|
+
URLSource,
|
|
33
|
+
Base64Source,
|
|
34
|
+
)
|
|
35
|
+
from agentscope.model import (
|
|
36
|
+
ChatModelBase,
|
|
37
|
+
DashScopeChatModel,
|
|
38
|
+
OpenAIChatModel,
|
|
39
|
+
AnthropicChatModel,
|
|
40
|
+
OllamaChatModel,
|
|
41
|
+
GeminiChatModel,
|
|
42
|
+
)
|
|
43
|
+
from agentscope.pipeline import stream_printing_messages
|
|
44
|
+
from agentscope.tool import (
|
|
45
|
+
Toolkit,
|
|
46
|
+
ToolResponse,
|
|
47
|
+
)
|
|
48
|
+
from agentscope.tool._toolkit import RegisteredToolFunction
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
from .utils import build_agent
|
|
52
|
+
from ..agents import Agent
|
|
53
|
+
from ..schemas.agent_schemas import (
|
|
54
|
+
Message,
|
|
55
|
+
TextContent,
|
|
56
|
+
DataContent,
|
|
57
|
+
FunctionCall,
|
|
58
|
+
FunctionCallOutput,
|
|
59
|
+
MessageType,
|
|
60
|
+
RunStatus,
|
|
61
|
+
)
|
|
62
|
+
from ..schemas.context import Context
|
|
63
|
+
|
|
64
|
+
# Disable logging from agentscope
|
|
65
|
+
setup_logger(level="CRITICAL")
|
|
66
|
+
logger = logging.getLogger(__name__)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class AgentScopeContextAdapter:
|
|
70
|
+
def __init__(self, context: Context, attr: dict):
|
|
71
|
+
self.context = context
|
|
72
|
+
self.attr = attr
|
|
73
|
+
|
|
74
|
+
# Adapted attribute
|
|
75
|
+
self.toolkit = None
|
|
76
|
+
self.model = None
|
|
77
|
+
self.memory = None
|
|
78
|
+
self.new_message = None
|
|
79
|
+
|
|
80
|
+
async def initialize(self):
|
|
81
|
+
self.model, self.formatter = await self.adapt_model()
|
|
82
|
+
self.memory = await self.adapt_memory()
|
|
83
|
+
self.new_message = await self.adapt_new_message()
|
|
84
|
+
self.toolkit = await self.adapt_tools()
|
|
85
|
+
|
|
86
|
+
async def adapt_memory(self):
|
|
87
|
+
memory = self.attr["agent_config"].get("memory", InMemoryMemory())
|
|
88
|
+
messages = []
|
|
89
|
+
|
|
90
|
+
# Build context
|
|
91
|
+
for msg in self.context.session.messages[:-1]: # Exclude the last one
|
|
92
|
+
messages.append(AgentScopeContextAdapter.converter(msg))
|
|
93
|
+
|
|
94
|
+
state_dict = {"content": [_.to_dict() for _ in messages]}
|
|
95
|
+
memory.load_state_dict(state_dict)
|
|
96
|
+
|
|
97
|
+
return memory
|
|
98
|
+
|
|
99
|
+
@staticmethod
|
|
100
|
+
def converter(message: Message) -> Msg:
|
|
101
|
+
if message.role not in ["user", "system", "assistant"]:
|
|
102
|
+
role_label = "user"
|
|
103
|
+
else:
|
|
104
|
+
role_label = message.role
|
|
105
|
+
|
|
106
|
+
result = {
|
|
107
|
+
"name": message.role, # TODO: protocol support
|
|
108
|
+
"role": role_label,
|
|
109
|
+
"invocation_id": message.id,
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if message.type in (
|
|
113
|
+
MessageType.PLUGIN_CALL,
|
|
114
|
+
MessageType.FUNCTION_CALL,
|
|
115
|
+
):
|
|
116
|
+
# convert PLUGIN_CALL, FUNCTION_CALL to ToolUseBlock
|
|
117
|
+
result["content"] = [
|
|
118
|
+
ToolUseBlock(
|
|
119
|
+
type="tool_use",
|
|
120
|
+
id=message.content[0].data["call_id"],
|
|
121
|
+
name=message.content[0].data["name"],
|
|
122
|
+
input=json.loads(message.content[0].data["arguments"]),
|
|
123
|
+
),
|
|
124
|
+
]
|
|
125
|
+
elif message.type in (
|
|
126
|
+
MessageType.PLUGIN_CALL_OUTPUT,
|
|
127
|
+
MessageType.FUNCTION_CALL_OUTPUT,
|
|
128
|
+
):
|
|
129
|
+
# convert PLUGIN_CALL_OUTPUT, FUNCTION_CALL_OUTPUT to
|
|
130
|
+
# ToolResultBlock
|
|
131
|
+
result["content"] = [
|
|
132
|
+
ToolResultBlock(
|
|
133
|
+
type="tool_result",
|
|
134
|
+
id=message.content[0].data["call_id"],
|
|
135
|
+
name=message.role, # TODO: match id of ToolUseBlock
|
|
136
|
+
output=json.loads(message.content[0].data["output"]),
|
|
137
|
+
),
|
|
138
|
+
]
|
|
139
|
+
elif message.type in (MessageType.REASONING,):
|
|
140
|
+
result["content"] = [
|
|
141
|
+
ThinkingBlock(
|
|
142
|
+
type="thinking",
|
|
143
|
+
thinking=message.content[0].text,
|
|
144
|
+
),
|
|
145
|
+
]
|
|
146
|
+
else:
|
|
147
|
+
type_mapping = {
|
|
148
|
+
"text": (TextBlock, "text", None),
|
|
149
|
+
"image": (ImageBlock, "image_url", True),
|
|
150
|
+
"audio": (AudioBlock, "data", None),
|
|
151
|
+
# "video": (VideoBlock, "video_url", True), # TODO: support
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
msg_content = []
|
|
155
|
+
for cnt in message.content:
|
|
156
|
+
cnt_type = cnt.type or "text"
|
|
157
|
+
|
|
158
|
+
if cnt_type not in type_mapping:
|
|
159
|
+
raise ValueError(f"Unsupported message type: {cnt_type}")
|
|
160
|
+
|
|
161
|
+
block_cls, attr_name, is_url = type_mapping[cnt_type]
|
|
162
|
+
value = getattr(cnt, attr_name)
|
|
163
|
+
if cnt_type == "audio":
|
|
164
|
+
result = urlparse(value)
|
|
165
|
+
is_url = all([result.scheme, result.netloc])
|
|
166
|
+
if is_url:
|
|
167
|
+
url_source = URLSource(type="url", url=value)
|
|
168
|
+
msg_content.append(
|
|
169
|
+
block_cls(type=cnt_type, source=url_source),
|
|
170
|
+
)
|
|
171
|
+
else:
|
|
172
|
+
if cnt_type == "audio":
|
|
173
|
+
audio_format = getattr(cnt, "format")
|
|
174
|
+
base64_source = Base64Source(
|
|
175
|
+
type="base64",
|
|
176
|
+
media_type=audio_format,
|
|
177
|
+
data=value,
|
|
178
|
+
)
|
|
179
|
+
msg_content.append(
|
|
180
|
+
block_cls(
|
|
181
|
+
type=cnt_type,
|
|
182
|
+
source=base64_source,
|
|
183
|
+
),
|
|
184
|
+
)
|
|
185
|
+
else:
|
|
186
|
+
msg_content.append(
|
|
187
|
+
block_cls(type=cnt_type, text=value),
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
result["content"] = msg_content
|
|
191
|
+
return Msg(**result)
|
|
192
|
+
|
|
193
|
+
async def adapt_new_message(self):
|
|
194
|
+
last_message = self.context.session.messages[-1]
|
|
195
|
+
return AgentScopeContextAdapter.converter(last_message)
|
|
196
|
+
|
|
197
|
+
async def adapt_model(self):
|
|
198
|
+
model = self.attr["model"]
|
|
199
|
+
|
|
200
|
+
if hasattr(model, "stream"):
|
|
201
|
+
model.stream = True
|
|
202
|
+
|
|
203
|
+
formatter = self.attr["agent_config"].get("formatter")
|
|
204
|
+
if formatter and isinstance(formatter, FormatterBase):
|
|
205
|
+
return model, formatter
|
|
206
|
+
|
|
207
|
+
if isinstance(model, OpenAIChatModel):
|
|
208
|
+
formatter = OpenAIChatFormatter()
|
|
209
|
+
elif isinstance(model, DashScopeChatModel):
|
|
210
|
+
formatter = DashScopeChatFormatter()
|
|
211
|
+
elif isinstance(model, AnthropicChatModel):
|
|
212
|
+
formatter = AnthropicChatFormatter()
|
|
213
|
+
elif isinstance(model, OllamaChatModel):
|
|
214
|
+
formatter = OllamaChatFormatter()
|
|
215
|
+
elif isinstance(model, GeminiChatModel):
|
|
216
|
+
formatter = GeminiChatFormatter()
|
|
217
|
+
|
|
218
|
+
return model, formatter
|
|
219
|
+
|
|
220
|
+
async def adapt_tools(self):
|
|
221
|
+
def func_wrapper(func, **kwargs):
|
|
222
|
+
func_res = func(**kwargs)
|
|
223
|
+
return ToolResponse(
|
|
224
|
+
content=func_res["content"],
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
toolkit = self.attr["agent_config"].get("toolkit", Toolkit())
|
|
228
|
+
|
|
229
|
+
# Deepcopy to avoid modify the original toolkit
|
|
230
|
+
try:
|
|
231
|
+
toolkit = copy.deepcopy(toolkit)
|
|
232
|
+
except Exception as e:
|
|
233
|
+
logger.warning(
|
|
234
|
+
f"Failed to deepcopy toolkit for agent "
|
|
235
|
+
f"'{self.attr.get('agent_config', {}).get('name')}' "
|
|
236
|
+
f"Error: {e}\nTraceback:\n{traceback.format_exc()}",
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
tools = self.attr["tools"]
|
|
240
|
+
|
|
241
|
+
# in case, tools is None and tools == []
|
|
242
|
+
if not tools:
|
|
243
|
+
return toolkit
|
|
244
|
+
|
|
245
|
+
if self.context.activate_tools:
|
|
246
|
+
# Only add activated tool
|
|
247
|
+
activated_tools = self.context.activate_tools
|
|
248
|
+
else:
|
|
249
|
+
# Lazy import
|
|
250
|
+
from ...sandbox.tools.utils import setup_tools
|
|
251
|
+
|
|
252
|
+
activated_tools = setup_tools(
|
|
253
|
+
tools=self.attr["tools"],
|
|
254
|
+
environment_manager=self.context.environment_manager,
|
|
255
|
+
session_id=self.context.session.id,
|
|
256
|
+
user_id=self.context.session.user_id,
|
|
257
|
+
include_schemas=False,
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
for tool in activated_tools:
|
|
261
|
+
function = RegisteredToolFunction(
|
|
262
|
+
name=tool.name,
|
|
263
|
+
source="mcp_server",
|
|
264
|
+
mcp_name=tool.tool_type,
|
|
265
|
+
original_func=partial(
|
|
266
|
+
func_wrapper,
|
|
267
|
+
tool,
|
|
268
|
+
),
|
|
269
|
+
json_schema=tool.schema,
|
|
270
|
+
group="basic",
|
|
271
|
+
)
|
|
272
|
+
toolkit.tools[tool.name] = function
|
|
273
|
+
|
|
274
|
+
return toolkit
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
class AgentScopeAgent(Agent):
|
|
278
|
+
def __init__(
|
|
279
|
+
self,
|
|
280
|
+
name: str,
|
|
281
|
+
model: ChatModelBase,
|
|
282
|
+
tools=None,
|
|
283
|
+
agent_config=None,
|
|
284
|
+
agent_builder: Optional[Type[AgentBase]] = ReActAgent,
|
|
285
|
+
):
|
|
286
|
+
super().__init__(name=name, agent_config=agent_config)
|
|
287
|
+
assert isinstance(
|
|
288
|
+
model,
|
|
289
|
+
ChatModelBase,
|
|
290
|
+
), "model must be a subclass of ChatModelBase in AgentScope"
|
|
291
|
+
|
|
292
|
+
# Set default agent_builder
|
|
293
|
+
if agent_builder is None:
|
|
294
|
+
agent_builder = ReActAgent
|
|
295
|
+
|
|
296
|
+
assert issubclass(
|
|
297
|
+
agent_builder,
|
|
298
|
+
AgentBase,
|
|
299
|
+
), "agent_builder must be a subclass of AgentBase in AgentScope"
|
|
300
|
+
|
|
301
|
+
# Replace name if not exists
|
|
302
|
+
self.agent_config["name"] = self.agent_config.get("name") or name
|
|
303
|
+
|
|
304
|
+
self._attr = {
|
|
305
|
+
"model": model,
|
|
306
|
+
"tools": tools,
|
|
307
|
+
"agent_config": self.agent_config,
|
|
308
|
+
"agent_builder": agent_builder,
|
|
309
|
+
}
|
|
310
|
+
self.tools = tools
|
|
311
|
+
|
|
312
|
+
def copy(self) -> "AgentScopeAgent":
|
|
313
|
+
return AgentScopeAgent(**self._attr)
|
|
314
|
+
|
|
315
|
+
def build(self, as_context) -> AgentBase:
|
|
316
|
+
params = {
|
|
317
|
+
**self._attr["agent_config"],
|
|
318
|
+
**{
|
|
319
|
+
"model": as_context.model,
|
|
320
|
+
"formatter": self._attr["agent_config"].get(
|
|
321
|
+
"formatter",
|
|
322
|
+
as_context.formatter,
|
|
323
|
+
),
|
|
324
|
+
"memory": as_context.memory,
|
|
325
|
+
"toolkit": as_context.toolkit,
|
|
326
|
+
},
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
builder_cls = self._attr["agent_builder"]
|
|
330
|
+
_agent = build_agent(builder_cls, params)
|
|
331
|
+
_agent.set_console_output_enabled(False)
|
|
332
|
+
|
|
333
|
+
return _agent
|
|
334
|
+
|
|
335
|
+
async def run_async(
|
|
336
|
+
self,
|
|
337
|
+
context,
|
|
338
|
+
**kwargs,
|
|
339
|
+
):
|
|
340
|
+
as_context = AgentScopeContextAdapter(context=context, attr=self._attr)
|
|
341
|
+
await as_context.initialize()
|
|
342
|
+
local_truncate_memory = ""
|
|
343
|
+
local_truncate_reasoning_memory = ""
|
|
344
|
+
|
|
345
|
+
# We should always build a new agent since the state is manage outside
|
|
346
|
+
# the agent
|
|
347
|
+
_agent = self.build(as_context)
|
|
348
|
+
|
|
349
|
+
# Yield new Msg instances as they are logged
|
|
350
|
+
last_content = ""
|
|
351
|
+
|
|
352
|
+
message = Message(type=MessageType.MESSAGE, role="assistant")
|
|
353
|
+
reasoning_message = Message(
|
|
354
|
+
type=MessageType.REASONING,
|
|
355
|
+
role="assistant",
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
should_start_message = True
|
|
359
|
+
should_start_reasoning_message = True
|
|
360
|
+
|
|
361
|
+
index = None
|
|
362
|
+
|
|
363
|
+
# Run agent
|
|
364
|
+
async for msg, last in stream_printing_messages(
|
|
365
|
+
agents=[_agent],
|
|
366
|
+
coroutine_task=_agent(as_context.new_message),
|
|
367
|
+
):
|
|
368
|
+
# deepcopy required to avoid modifying the original message object
|
|
369
|
+
# which may be used elsewhere in the streaming pipeline
|
|
370
|
+
msg = copy.deepcopy(msg)
|
|
371
|
+
|
|
372
|
+
# Filter out unfinished tool_use messages
|
|
373
|
+
if not last:
|
|
374
|
+
new_blocks = []
|
|
375
|
+
if isinstance(msg.content, List):
|
|
376
|
+
for block in msg.content:
|
|
377
|
+
if block.get("type", "") != "tool_use":
|
|
378
|
+
new_blocks.append(block)
|
|
379
|
+
msg.content = new_blocks
|
|
380
|
+
|
|
381
|
+
if not msg.content:
|
|
382
|
+
continue
|
|
383
|
+
|
|
384
|
+
# TODO: make this as a message converter
|
|
385
|
+
content = msg.content
|
|
386
|
+
if isinstance(content, str):
|
|
387
|
+
last_content = content
|
|
388
|
+
else:
|
|
389
|
+
for element in content:
|
|
390
|
+
if isinstance(element, str) and element:
|
|
391
|
+
if should_start_message:
|
|
392
|
+
index = None
|
|
393
|
+
yield message.in_progress()
|
|
394
|
+
should_start_message = False
|
|
395
|
+
text_delta_content = TextContent(
|
|
396
|
+
delta=True,
|
|
397
|
+
index=index,
|
|
398
|
+
text=element,
|
|
399
|
+
)
|
|
400
|
+
text_delta_content = message.add_delta_content(
|
|
401
|
+
new_content=text_delta_content,
|
|
402
|
+
)
|
|
403
|
+
index = text_delta_content.index
|
|
404
|
+
yield text_delta_content
|
|
405
|
+
elif isinstance(element, dict):
|
|
406
|
+
if element.get("type") == "text":
|
|
407
|
+
text = element.get(
|
|
408
|
+
"text",
|
|
409
|
+
"",
|
|
410
|
+
)
|
|
411
|
+
if text:
|
|
412
|
+
if should_start_message:
|
|
413
|
+
index = None
|
|
414
|
+
yield message.in_progress()
|
|
415
|
+
should_start_message = False
|
|
416
|
+
|
|
417
|
+
text_delta_content = TextContent(
|
|
418
|
+
delta=True,
|
|
419
|
+
index=index,
|
|
420
|
+
text=text.removeprefix(
|
|
421
|
+
local_truncate_memory,
|
|
422
|
+
),
|
|
423
|
+
)
|
|
424
|
+
local_truncate_memory = element.get(
|
|
425
|
+
"text",
|
|
426
|
+
"",
|
|
427
|
+
)
|
|
428
|
+
text_delta_content = message.add_delta_content(
|
|
429
|
+
new_content=text_delta_content,
|
|
430
|
+
)
|
|
431
|
+
index = text_delta_content.index
|
|
432
|
+
|
|
433
|
+
# Only yield valid text
|
|
434
|
+
if text_delta_content.text:
|
|
435
|
+
yield text_delta_content
|
|
436
|
+
|
|
437
|
+
if last:
|
|
438
|
+
yield message.completed()
|
|
439
|
+
message = Message(
|
|
440
|
+
type=MessageType.MESSAGE,
|
|
441
|
+
role="assistant",
|
|
442
|
+
)
|
|
443
|
+
index = None
|
|
444
|
+
should_start_message = True
|
|
445
|
+
|
|
446
|
+
elif element.get("type") == "tool_use":
|
|
447
|
+
if (
|
|
448
|
+
reasoning_message.status
|
|
449
|
+
== RunStatus.InProgress
|
|
450
|
+
):
|
|
451
|
+
yield reasoning_message.completed()
|
|
452
|
+
reasoning_message = Message(
|
|
453
|
+
type=MessageType.REASONING,
|
|
454
|
+
role="assistant",
|
|
455
|
+
)
|
|
456
|
+
index = None
|
|
457
|
+
|
|
458
|
+
json_str = json.dumps(element.get("input"))
|
|
459
|
+
data_delta_content = DataContent(
|
|
460
|
+
index=index,
|
|
461
|
+
data=FunctionCall(
|
|
462
|
+
call_id=element.get("id"),
|
|
463
|
+
name=element.get("name"),
|
|
464
|
+
arguments=json_str,
|
|
465
|
+
).model_dump(),
|
|
466
|
+
)
|
|
467
|
+
plugin_call_message = Message(
|
|
468
|
+
type=MessageType.PLUGIN_CALL,
|
|
469
|
+
role="assistant",
|
|
470
|
+
content=[data_delta_content],
|
|
471
|
+
)
|
|
472
|
+
yield plugin_call_message.completed()
|
|
473
|
+
index = None
|
|
474
|
+
|
|
475
|
+
elif element.get("type") == "tool_result":
|
|
476
|
+
json_str = json.dumps(element.get("output"))
|
|
477
|
+
data_delta_content = DataContent(
|
|
478
|
+
index=index,
|
|
479
|
+
data=FunctionCallOutput(
|
|
480
|
+
call_id=element.get("id"),
|
|
481
|
+
output=json_str,
|
|
482
|
+
).model_dump(),
|
|
483
|
+
)
|
|
484
|
+
plugin_output_message = Message(
|
|
485
|
+
type=MessageType.PLUGIN_CALL_OUTPUT,
|
|
486
|
+
role="assistant",
|
|
487
|
+
content=[data_delta_content],
|
|
488
|
+
)
|
|
489
|
+
yield plugin_output_message.completed()
|
|
490
|
+
message = Message(
|
|
491
|
+
type=MessageType.MESSAGE,
|
|
492
|
+
role="assistant",
|
|
493
|
+
)
|
|
494
|
+
should_start_message = True
|
|
495
|
+
index = None
|
|
496
|
+
|
|
497
|
+
elif element.get("type") == "thinking":
|
|
498
|
+
reasoning = element.get(
|
|
499
|
+
"thinking",
|
|
500
|
+
"",
|
|
501
|
+
)
|
|
502
|
+
if reasoning:
|
|
503
|
+
if should_start_reasoning_message:
|
|
504
|
+
index = None
|
|
505
|
+
yield reasoning_message.in_progress()
|
|
506
|
+
should_start_reasoning_message = False
|
|
507
|
+
text_delta_content = TextContent(
|
|
508
|
+
delta=True,
|
|
509
|
+
index=index,
|
|
510
|
+
text=reasoning.removeprefix(
|
|
511
|
+
local_truncate_reasoning_memory,
|
|
512
|
+
),
|
|
513
|
+
)
|
|
514
|
+
local_truncate_reasoning_memory = element.get(
|
|
515
|
+
"thinking",
|
|
516
|
+
"",
|
|
517
|
+
)
|
|
518
|
+
text_delta_content = (
|
|
519
|
+
reasoning_message.add_delta_content(
|
|
520
|
+
new_content=text_delta_content,
|
|
521
|
+
)
|
|
522
|
+
)
|
|
523
|
+
index = text_delta_content.index
|
|
524
|
+
|
|
525
|
+
# Only yield valid text
|
|
526
|
+
if text_delta_content.text:
|
|
527
|
+
yield text_delta_content
|
|
528
|
+
|
|
529
|
+
# The last won't happen in the thinking message
|
|
530
|
+
if last:
|
|
531
|
+
yield reasoning_message.completed()
|
|
532
|
+
reasoning_message = Message(
|
|
533
|
+
type=MessageType.REASONING,
|
|
534
|
+
role="assistant",
|
|
535
|
+
)
|
|
536
|
+
index = None
|
|
537
|
+
else:
|
|
538
|
+
if should_start_message:
|
|
539
|
+
index = None
|
|
540
|
+
yield message.in_progress()
|
|
541
|
+
should_start_message = False
|
|
542
|
+
|
|
543
|
+
text_delta_content = TextContent(
|
|
544
|
+
delta=True,
|
|
545
|
+
index=index,
|
|
546
|
+
text=f"{element}",
|
|
547
|
+
)
|
|
548
|
+
text_delta_content = message.add_delta_content(
|
|
549
|
+
new_content=text_delta_content,
|
|
550
|
+
)
|
|
551
|
+
index = text_delta_content.index
|
|
552
|
+
yield text_delta_content
|
|
553
|
+
|
|
554
|
+
if last_content:
|
|
555
|
+
if should_start_message:
|
|
556
|
+
index = None
|
|
557
|
+
yield message.in_progress()
|
|
558
|
+
text_delta_content = TextContent(
|
|
559
|
+
delta=True,
|
|
560
|
+
index=index,
|
|
561
|
+
text=last_content,
|
|
562
|
+
)
|
|
563
|
+
text_delta_content = message.add_delta_content(
|
|
564
|
+
new_content=text_delta_content,
|
|
565
|
+
)
|
|
566
|
+
yield text_delta_content
|
|
567
|
+
yield message.completed()
|
|
@@ -5,13 +5,14 @@ from typing import Optional, Type
|
|
|
5
5
|
|
|
6
6
|
from agno.agent import Agent as AgAgent
|
|
7
7
|
from agno.models.base import Model
|
|
8
|
-
from agno.run.
|
|
9
|
-
|
|
8
|
+
from agno.run.agent import (
|
|
9
|
+
RunContentEvent,
|
|
10
10
|
ToolCallStartedEvent,
|
|
11
11
|
ToolCallCompletedEvent,
|
|
12
12
|
)
|
|
13
13
|
from agno.tools.function import Function
|
|
14
14
|
|
|
15
|
+
from .utils import build_agent
|
|
15
16
|
from ..agents import Agent
|
|
16
17
|
from ..schemas.context import Context
|
|
17
18
|
from ..schemas.agent_schemas import (
|
|
@@ -64,7 +65,7 @@ class AgnoContextAdapter:
|
|
|
64
65
|
return self.attr["model"]
|
|
65
66
|
|
|
66
67
|
async def adapt_tools(self):
|
|
67
|
-
toolkit = self.attr["agent_config"].get("
|
|
68
|
+
toolkit = self.attr["agent_config"].get("tools", [])
|
|
68
69
|
tools = self.attr["tools"]
|
|
69
70
|
|
|
70
71
|
# in case, tools is None and tools == []
|
|
@@ -131,34 +132,36 @@ class AgnoAgent(Agent):
|
|
|
131
132
|
"agent_config": self.agent_config,
|
|
132
133
|
"agent_builder": agent_builder,
|
|
133
134
|
}
|
|
134
|
-
self._agent = None
|
|
135
135
|
self.tools = tools
|
|
136
136
|
|
|
137
137
|
def copy(self) -> "AgnoAgent":
|
|
138
138
|
return AgnoAgent(**self._attr)
|
|
139
139
|
|
|
140
140
|
def build(self, as_context):
|
|
141
|
-
|
|
141
|
+
params = {
|
|
142
142
|
**self._attr["agent_config"],
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
143
|
+
**{
|
|
144
|
+
"model": as_context.model,
|
|
145
|
+
"tools": as_context.toolkit,
|
|
146
|
+
}, # Context will be added at `_agent.arun`
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
builder_cls = self._attr["agent_builder"]
|
|
150
|
+
_agent = build_agent(builder_cls, params)
|
|
146
151
|
|
|
147
|
-
return
|
|
152
|
+
return _agent
|
|
148
153
|
|
|
149
|
-
async def
|
|
154
|
+
async def run_async(
|
|
155
|
+
self,
|
|
156
|
+
context,
|
|
157
|
+
**kwargs,
|
|
158
|
+
):
|
|
150
159
|
ag_context = AgnoContextAdapter(context=context, attr=self._attr)
|
|
151
160
|
await ag_context.initialize()
|
|
152
161
|
|
|
153
162
|
# We should always build a new agent since the state is manage outside
|
|
154
163
|
# the agent
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
resp = await self._agent.arun(
|
|
158
|
-
ag_context.new_message,
|
|
159
|
-
messages=ag_context.memory,
|
|
160
|
-
stream=True,
|
|
161
|
-
)
|
|
164
|
+
_agent = self.build(ag_context)
|
|
162
165
|
|
|
163
166
|
text_message = Message(
|
|
164
167
|
type=MessageType.MESSAGE,
|
|
@@ -169,8 +172,12 @@ class AgnoAgent(Agent):
|
|
|
169
172
|
|
|
170
173
|
text_delta_content = TextContent(delta=True)
|
|
171
174
|
is_text_delta = False
|
|
172
|
-
async for event in
|
|
173
|
-
|
|
175
|
+
async for event in _agent.arun(
|
|
176
|
+
ag_context.new_message,
|
|
177
|
+
session_state=ag_context.memory,
|
|
178
|
+
stream=True,
|
|
179
|
+
):
|
|
180
|
+
if isinstance(event, RunContentEvent):
|
|
174
181
|
is_text_delta = True
|
|
175
182
|
text_delta_content.text = event.content
|
|
176
183
|
text_delta_content = text_message.add_delta_content(
|
|
@@ -211,11 +218,3 @@ class AgnoAgent(Agent):
|
|
|
211
218
|
if is_text_delta:
|
|
212
219
|
yield text_message.content_completed(text_delta_content.index)
|
|
213
220
|
yield text_message.completed()
|
|
214
|
-
|
|
215
|
-
async def run_async(
|
|
216
|
-
self,
|
|
217
|
-
context,
|
|
218
|
-
**kwargs,
|
|
219
|
-
):
|
|
220
|
-
async for event in self.run(context):
|
|
221
|
-
yield event
|