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.
Files changed (107) hide show
  1. agentscope_runtime/common/__init__.py +0 -0
  2. agentscope_runtime/common/collections/in_memory_mapping.py +27 -0
  3. agentscope_runtime/common/collections/redis_mapping.py +42 -0
  4. agentscope_runtime/common/container_clients/__init__.py +0 -0
  5. agentscope_runtime/common/container_clients/agentrun_client.py +1098 -0
  6. agentscope_runtime/common/container_clients/docker_client.py +250 -0
  7. agentscope_runtime/{sandbox/manager → common}/container_clients/kubernetes_client.py +6 -13
  8. agentscope_runtime/engine/__init__.py +12 -0
  9. agentscope_runtime/engine/agents/agentscope_agent.py +567 -0
  10. agentscope_runtime/engine/agents/agno_agent.py +26 -27
  11. agentscope_runtime/engine/agents/autogen_agent.py +13 -8
  12. agentscope_runtime/engine/agents/langgraph_agent.py +52 -9
  13. agentscope_runtime/engine/agents/utils.py +53 -0
  14. agentscope_runtime/engine/app/__init__.py +6 -0
  15. agentscope_runtime/engine/app/agent_app.py +239 -0
  16. agentscope_runtime/engine/app/base_app.py +181 -0
  17. agentscope_runtime/engine/app/celery_mixin.py +92 -0
  18. agentscope_runtime/engine/deployers/adapter/responses/response_api_adapter_utils.py +5 -1
  19. agentscope_runtime/engine/deployers/base.py +1 -0
  20. agentscope_runtime/engine/deployers/cli_fc_deploy.py +39 -20
  21. agentscope_runtime/engine/deployers/kubernetes_deployer.py +12 -5
  22. agentscope_runtime/engine/deployers/local_deployer.py +61 -3
  23. agentscope_runtime/engine/deployers/modelstudio_deployer.py +201 -40
  24. agentscope_runtime/engine/deployers/utils/docker_image_utils/runner_image_factory.py +9 -0
  25. agentscope_runtime/engine/deployers/utils/package_project_utils.py +234 -3
  26. agentscope_runtime/engine/deployers/utils/service_utils/fastapi_factory.py +567 -7
  27. agentscope_runtime/engine/deployers/utils/service_utils/standalone_main.py.j2 +211 -0
  28. agentscope_runtime/engine/deployers/utils/wheel_packager.py +1 -1
  29. agentscope_runtime/engine/helpers/helper.py +60 -41
  30. agentscope_runtime/engine/runner.py +40 -24
  31. agentscope_runtime/engine/schemas/agent_schemas.py +42 -0
  32. agentscope_runtime/engine/schemas/modelstudio_llm.py +14 -14
  33. agentscope_runtime/engine/services/sandbox_service.py +62 -70
  34. agentscope_runtime/engine/services/tablestore_memory_service.py +307 -0
  35. agentscope_runtime/engine/services/tablestore_rag_service.py +143 -0
  36. agentscope_runtime/engine/services/tablestore_session_history_service.py +293 -0
  37. agentscope_runtime/engine/services/utils/__init__.py +0 -0
  38. agentscope_runtime/engine/services/utils/tablestore_service_utils.py +352 -0
  39. agentscope_runtime/engine/tracing/__init__.py +9 -3
  40. agentscope_runtime/engine/tracing/asyncio_util.py +24 -0
  41. agentscope_runtime/engine/tracing/base.py +66 -34
  42. agentscope_runtime/engine/tracing/local_logging_handler.py +45 -31
  43. agentscope_runtime/engine/tracing/message_util.py +528 -0
  44. agentscope_runtime/engine/tracing/tracing_metric.py +20 -8
  45. agentscope_runtime/engine/tracing/tracing_util.py +130 -0
  46. agentscope_runtime/engine/tracing/wrapper.py +794 -169
  47. agentscope_runtime/sandbox/__init__.py +2 -0
  48. agentscope_runtime/sandbox/box/base/__init__.py +4 -0
  49. agentscope_runtime/sandbox/box/base/base_sandbox.py +6 -4
  50. agentscope_runtime/sandbox/box/browser/__init__.py +4 -0
  51. agentscope_runtime/sandbox/box/browser/browser_sandbox.py +10 -14
  52. agentscope_runtime/sandbox/box/dummy/__init__.py +4 -0
  53. agentscope_runtime/sandbox/box/dummy/dummy_sandbox.py +2 -1
  54. agentscope_runtime/sandbox/box/filesystem/__init__.py +4 -0
  55. agentscope_runtime/sandbox/box/filesystem/filesystem_sandbox.py +10 -7
  56. agentscope_runtime/sandbox/box/gui/__init__.py +4 -0
  57. agentscope_runtime/sandbox/box/gui/box/__init__.py +0 -0
  58. agentscope_runtime/sandbox/box/gui/gui_sandbox.py +81 -0
  59. agentscope_runtime/sandbox/box/sandbox.py +5 -2
  60. agentscope_runtime/sandbox/box/shared/routers/generic.py +20 -1
  61. agentscope_runtime/sandbox/box/training_box/__init__.py +4 -0
  62. agentscope_runtime/sandbox/box/training_box/training_box.py +7 -54
  63. agentscope_runtime/sandbox/build.py +143 -58
  64. agentscope_runtime/sandbox/client/http_client.py +87 -59
  65. agentscope_runtime/sandbox/client/training_client.py +0 -1
  66. agentscope_runtime/sandbox/constant.py +27 -1
  67. agentscope_runtime/sandbox/custom/custom_sandbox.py +7 -6
  68. agentscope_runtime/sandbox/custom/example.py +4 -3
  69. agentscope_runtime/sandbox/enums.py +1 -1
  70. agentscope_runtime/sandbox/manager/sandbox_manager.py +212 -106
  71. agentscope_runtime/sandbox/manager/server/app.py +82 -14
  72. agentscope_runtime/sandbox/manager/server/config.py +50 -3
  73. agentscope_runtime/sandbox/model/container.py +12 -23
  74. agentscope_runtime/sandbox/model/manager_config.py +93 -5
  75. agentscope_runtime/sandbox/registry.py +1 -1
  76. agentscope_runtime/sandbox/tools/gui/__init__.py +7 -0
  77. agentscope_runtime/sandbox/tools/gui/tool.py +77 -0
  78. agentscope_runtime/sandbox/tools/mcp_tool.py +6 -2
  79. agentscope_runtime/sandbox/tools/tool.py +4 -0
  80. agentscope_runtime/sandbox/utils.py +124 -0
  81. agentscope_runtime/version.py +1 -1
  82. {agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.2.0.dist-info}/METADATA +246 -111
  83. {agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.2.0.dist-info}/RECORD +96 -80
  84. agentscope_runtime/engine/agents/agentscope_agent/__init__.py +0 -6
  85. agentscope_runtime/engine/agents/agentscope_agent/agent.py +0 -401
  86. agentscope_runtime/engine/agents/agentscope_agent/hooks.py +0 -169
  87. agentscope_runtime/engine/agents/llm_agent.py +0 -51
  88. agentscope_runtime/engine/llms/__init__.py +0 -3
  89. agentscope_runtime/engine/llms/base_llm.py +0 -60
  90. agentscope_runtime/engine/llms/qwen_llm.py +0 -47
  91. agentscope_runtime/sandbox/manager/collections/in_memory_mapping.py +0 -22
  92. agentscope_runtime/sandbox/manager/collections/redis_mapping.py +0 -26
  93. agentscope_runtime/sandbox/manager/container_clients/__init__.py +0 -10
  94. agentscope_runtime/sandbox/manager/container_clients/docker_client.py +0 -422
  95. /agentscope_runtime/{sandbox/manager → common}/collections/__init__.py +0 -0
  96. /agentscope_runtime/{sandbox/manager → common}/collections/base_mapping.py +0 -0
  97. /agentscope_runtime/{sandbox/manager → common}/collections/base_queue.py +0 -0
  98. /agentscope_runtime/{sandbox/manager → common}/collections/base_set.py +0 -0
  99. /agentscope_runtime/{sandbox/manager → common}/collections/in_memory_queue.py +0 -0
  100. /agentscope_runtime/{sandbox/manager → common}/collections/in_memory_set.py +0 -0
  101. /agentscope_runtime/{sandbox/manager → common}/collections/redis_queue.py +0 -0
  102. /agentscope_runtime/{sandbox/manager → common}/collections/redis_set.py +0 -0
  103. /agentscope_runtime/{sandbox/manager → common}/container_clients/base_client.py +0 -0
  104. {agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.2.0.dist-info}/WHEEL +0 -0
  105. {agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.2.0.dist-info}/entry_points.txt +0 -0
  106. {agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.2.0.dist-info}/licenses/LICENSE +0 -0
  107. {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.response import (
9
- RunResponseContentEvent,
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("toolkit", [])
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
- self._agent = self._attr["agent_builder"](
141
+ params = {
142
142
  **self._attr["agent_config"],
143
- model=as_context.model,
144
- tools=as_context.toolkit,
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 self._agent
152
+ return _agent
148
153
 
149
- async def run(self, context):
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
- self._agent = self.build(ag_context)
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 resp:
173
- if isinstance(event, RunResponseContentEvent):
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