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