agentscope-runtime 0.1.4__py3-none-any.whl → 0.1.5__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 (48) hide show
  1. agentscope_runtime/engine/agents/agentscope_agent/agent.py +56 -12
  2. agentscope_runtime/engine/agents/agentscope_agent/hooks.py +2 -1
  3. agentscope_runtime/engine/agents/agno_agent.py +11 -5
  4. agentscope_runtime/engine/agents/autogen_agent.py +10 -4
  5. agentscope_runtime/engine/agents/utils.py +53 -0
  6. agentscope_runtime/engine/services/mem0_memory_service.py +124 -0
  7. agentscope_runtime/engine/services/memory_service.py +2 -1
  8. agentscope_runtime/engine/services/redis_session_history_service.py +4 -3
  9. agentscope_runtime/engine/services/sandbox_service.py +6 -16
  10. agentscope_runtime/engine/services/session_history_service.py +4 -3
  11. agentscope_runtime/engine/services/tablestore_memory_service.py +304 -0
  12. agentscope_runtime/engine/services/tablestore_rag_service.py +143 -0
  13. agentscope_runtime/engine/services/tablestore_session_history_service.py +293 -0
  14. agentscope_runtime/engine/services/utils/__init__.py +0 -0
  15. agentscope_runtime/engine/services/utils/tablestore_service_utils.py +352 -0
  16. agentscope_runtime/sandbox/box/base/base_sandbox.py +2 -2
  17. agentscope_runtime/sandbox/box/browser/browser_sandbox.py +2 -2
  18. agentscope_runtime/sandbox/box/filesystem/filesystem_sandbox.py +2 -2
  19. agentscope_runtime/sandbox/box/training_box/training_box.py +4 -12
  20. agentscope_runtime/sandbox/build.py +37 -17
  21. agentscope_runtime/sandbox/client/http_client.py +42 -10
  22. agentscope_runtime/sandbox/client/training_client.py +0 -1
  23. agentscope_runtime/sandbox/constant.py +26 -0
  24. agentscope_runtime/sandbox/custom/custom_sandbox.py +5 -5
  25. agentscope_runtime/sandbox/custom/example.py +2 -2
  26. agentscope_runtime/sandbox/manager/collections/in_memory_mapping.py +4 -2
  27. agentscope_runtime/sandbox/manager/collections/redis_mapping.py +25 -9
  28. agentscope_runtime/sandbox/manager/container_clients/__init__.py +0 -10
  29. agentscope_runtime/sandbox/manager/container_clients/agentrun_client.py +1096 -0
  30. agentscope_runtime/sandbox/manager/container_clients/docker_client.py +25 -201
  31. agentscope_runtime/sandbox/manager/container_clients/kubernetes_client.py +1 -3
  32. agentscope_runtime/sandbox/manager/sandbox_manager.py +40 -13
  33. agentscope_runtime/sandbox/manager/server/app.py +27 -0
  34. agentscope_runtime/sandbox/manager/server/config.py +30 -2
  35. agentscope_runtime/sandbox/model/container.py +1 -1
  36. agentscope_runtime/sandbox/model/manager_config.py +93 -5
  37. agentscope_runtime/sandbox/utils.py +97 -0
  38. agentscope_runtime/version.py +1 -1
  39. {agentscope_runtime-0.1.4.dist-info → agentscope_runtime-0.1.5.dist-info}/METADATA +52 -56
  40. {agentscope_runtime-0.1.4.dist-info → agentscope_runtime-0.1.5.dist-info}/RECORD +44 -39
  41. agentscope_runtime/engine/agents/llm_agent.py +0 -51
  42. agentscope_runtime/engine/llms/__init__.py +0 -3
  43. agentscope_runtime/engine/llms/base_llm.py +0 -60
  44. agentscope_runtime/engine/llms/qwen_llm.py +0 -47
  45. {agentscope_runtime-0.1.4.dist-info → agentscope_runtime-0.1.5.dist-info}/WHEEL +0 -0
  46. {agentscope_runtime-0.1.4.dist-info → agentscope_runtime-0.1.5.dist-info}/entry_points.txt +0 -0
  47. {agentscope_runtime-0.1.4.dist-info → agentscope_runtime-0.1.5.dist-info}/licenses/LICENSE +0 -0
  48. {agentscope_runtime-0.1.4.dist-info → agentscope_runtime-0.1.5.dist-info}/top_level.txt +0 -0
@@ -1,7 +1,9 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  # pylint:disable=too-many-nested-blocks, too-many-branches, too-many-statements
3
3
  # pylint:disable=line-too-long, protected-access
4
-
4
+ import copy
5
+ import os
6
+ import logging
5
7
  import json
6
8
  import threading
7
9
  import uuid
@@ -9,7 +11,7 @@ from functools import partial
9
11
  from typing import Optional, Type
10
12
 
11
13
  from agentscope import setup_logger
12
- from agentscope.agent import ReActAgent
14
+ from agentscope.agent import AgentBase, ReActAgent
13
15
  from agentscope.formatter import (
14
16
  FormatterBase,
15
17
  DashScopeChatFormatter,
@@ -40,6 +42,7 @@ from .hooks import (
40
42
  clear_msg_instances,
41
43
  run_async_in_thread,
42
44
  )
45
+ from ..utils import build_agent
43
46
  from ...agents import Agent
44
47
  from ...schemas.agent_schemas import (
45
48
  Message,
@@ -53,6 +56,7 @@ from ...schemas.context import Context
53
56
 
54
57
  # Disable logging from agentscope
55
58
  setup_logger(level="CRITICAL")
59
+ logger = logging.getLogger(__name__)
56
60
 
57
61
 
58
62
  class AgentScopeContextAdapter:
@@ -95,7 +99,11 @@ class AgentScopeContextAdapter:
95
99
  "name": message.role,
96
100
  "role": role_label,
97
101
  }
98
- if message.type == MessageType.PLUGIN_CALL:
102
+ if message.type in (
103
+ MessageType.PLUGIN_CALL,
104
+ MessageType.FUNCTION_CALL,
105
+ ):
106
+ # convert PLUGIN_CALL, FUNCTION_CALL to ToolUseBlock
99
107
  result["content"] = [
100
108
  ToolUseBlock(
101
109
  type="tool_use",
@@ -104,7 +112,12 @@ class AgentScopeContextAdapter:
104
112
  input=json.loads(message.content[0].data["arguments"]),
105
113
  ),
106
114
  ]
107
- elif message.type == MessageType.PLUGIN_CALL_OUTPUT:
115
+ elif message.type in (
116
+ MessageType.PLUGIN_CALL_OUTPUT,
117
+ MessageType.FUNCTION_CALL_OUTPUT,
118
+ ):
119
+ # convert PLUGIN_CALL_OUTPUT, FUNCTION_CALL_OUTPUT to
120
+ # ToolResultBlock
108
121
  result["content"] = [
109
122
  ToolResultBlock(
110
123
  type="tool_result",
@@ -150,6 +163,10 @@ class AgentScopeContextAdapter:
150
163
  )
151
164
 
152
165
  toolkit = self.attr["agent_config"].get("toolkit", Toolkit())
166
+ # Deepcopy to avoid modify the original toolkit
167
+ # TODO: when toolkit contains live sessions, deepcopy fails,
168
+ # need further fixed in AgentScope
169
+ toolkit = copy.deepcopy(toolkit)
153
170
  tools = self.attr["tools"]
154
171
 
155
172
  # in case, tools is None and tools == []
@@ -195,7 +212,7 @@ class AgentScopeAgent(Agent):
195
212
  model: ChatModelBase,
196
213
  tools=None,
197
214
  agent_config=None,
198
- agent_builder: Optional[Type[ReActAgent]] = ReActAgent,
215
+ agent_builder: Optional[Type[AgentBase]] = ReActAgent,
199
216
  ):
200
217
  super().__init__(name=name, agent_config=agent_config)
201
218
  assert isinstance(
@@ -209,7 +226,7 @@ class AgentScopeAgent(Agent):
209
226
 
210
227
  assert issubclass(
211
228
  agent_builder,
212
- ReActAgent,
229
+ AgentBase,
213
230
  ), "agent_builder must be a subclass of AgentBase in AgentScope"
214
231
 
215
232
  # Replace name if not exists
@@ -228,14 +245,41 @@ class AgentScopeAgent(Agent):
228
245
  return AgentScopeAgent(**self._attr)
229
246
 
230
247
  def build(self, as_context):
231
- self._agent = self._attr["agent_builder"](
248
+ params = {
232
249
  **self._attr["agent_config"],
233
- model=as_context.model,
234
- formatter=as_context.formatter,
235
- memory=as_context.memory,
236
- toolkit=as_context.toolkit,
250
+ **{
251
+ "model": as_context.model,
252
+ "formatter": self._attr["agent_config"].get(
253
+ "formatter",
254
+ as_context.formatter,
255
+ ),
256
+ "memory": as_context.memory,
257
+ "toolkit": as_context.toolkit,
258
+ },
259
+ }
260
+
261
+ builder_cls = self._attr["agent_builder"]
262
+ self._agent = build_agent(builder_cls, params)
263
+
264
+ # Read env variable (default = false)
265
+ console_output_env = (
266
+ os.getenv(
267
+ "AGENTSCOPE_AGENT_CONSOLE_OUTPUT",
268
+ "false",
269
+ )
270
+ .strip()
271
+ .lower()
237
272
  )
238
- self._agent._disable_console_output = True
273
+
274
+ if console_output_env not in ("true", "false"):
275
+ raise ValueError(
276
+ f"Invalid value for AGENTSCOPE_AGENT_CONSOLE_OUTPUT: "
277
+ f"'{console_output_env}'. "
278
+ f"Only 'true' or 'false' is allowed.",
279
+ )
280
+
281
+ # If true → enable output; if false → disable output
282
+ self._agent._disable_console_output = console_output_env == "false"
239
283
 
240
284
  self._agent.register_instance_hook(
241
285
  "pre_print",
@@ -6,6 +6,7 @@ import os
6
6
  import time
7
7
  import threading
8
8
  import logging
9
+ import traceback
9
10
 
10
11
  from collections import defaultdict
11
12
  from typing import Union, Optional, Generator, Any, List
@@ -33,7 +34,7 @@ def run_async_in_thread(coro):
33
34
  logging.error(f"Runtime error in async thread: {e}")
34
35
  return None
35
36
  except Exception as e:
36
- logging.error(f"Error in async thread: {e}")
37
+ logging.error(f"Error in async thread: {e}, {traceback.format_exc()}")
37
38
  return None
38
39
 
39
40
 
@@ -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 == []
@@ -138,11 +139,16 @@ class AgnoAgent(Agent):
138
139
  return AgnoAgent(**self._attr)
139
140
 
140
141
  def build(self, as_context):
141
- self._agent = self._attr["agent_builder"](
142
+ params = {
142
143
  **self._attr["agent_config"],
143
- model=as_context.model,
144
- tools=as_context.toolkit,
145
- )
144
+ **{
145
+ "model": as_context.model,
146
+ "tools": as_context.toolkit,
147
+ }, # Context will be added at `self._agent.arun`
148
+ }
149
+
150
+ builder_cls = self._attr["agent_builder"]
151
+ self._agent = build_agent(builder_cls, params)
146
152
 
147
153
  return self._agent
148
154
 
@@ -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 (
@@ -143,11 +144,16 @@ class AutogenAgent(Agent):
143
144
  return AutogenAgent(**self._attr)
144
145
 
145
146
  def build(self, as_context):
146
- self._agent = self._attr["agent_builder"](
147
+ params = {
147
148
  **self._attr["agent_config"],
148
- model_client=as_context.model,
149
- tools=as_context.toolkit,
150
- )
149
+ **{
150
+ "model_client": as_context.model,
151
+ "tools": as_context.toolkit,
152
+ }, # Context will be added at `self._agent.run_stream`
153
+ }
154
+
155
+ builder_cls = self._attr["agent_builder"]
156
+ self._agent = build_agent(builder_cls, params)
151
157
 
152
158
  return self._agent
153
159
 
@@ -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,124 @@
1
+ # -*- coding: utf-8 -*-
2
+ import os
3
+ from typing import Optional, Dict, Any, List
4
+ from .memory_service import MemoryService
5
+ from ..schemas.agent_schemas import Message, MessageType, ContentType
6
+
7
+
8
+ class Mem0MemoryService(MemoryService):
9
+ """
10
+ Memory service that uses mem0 to store and retrieve memories.
11
+ To get the api key, please refer to the following link:
12
+ https://docs.mem0.ai/platform/quickstart
13
+ """
14
+
15
+ def __init__(self, **kwargs):
16
+ super().__init__(**kwargs)
17
+ from mem0 import AsyncMemoryClient
18
+
19
+ mem0_api_key = os.getenv("MEM0_API_KEY")
20
+ if mem0_api_key is None:
21
+ raise ValueError("MEM0_API_KEY is not set")
22
+ mem0_api_key = os.getenv("MEM0_API_KEY")
23
+
24
+ # get the mem0 client instance
25
+ self.service = AsyncMemoryClient(api_key=mem0_api_key)
26
+
27
+ @staticmethod
28
+ async def get_query_text(message: Message) -> str:
29
+ """
30
+ Get the query text from the message.
31
+ """
32
+ if message:
33
+ if message.type == MessageType.MESSAGE:
34
+ for content in message.content:
35
+ if content.type == ContentType.TEXT:
36
+ return content.text
37
+ return ""
38
+
39
+ @staticmethod
40
+ def transform_message(message: Message) -> dict:
41
+ content_text = None
42
+
43
+ try:
44
+ if hasattr(message, "content") and isinstance(
45
+ message.content,
46
+ list,
47
+ ):
48
+ if len(message.content) > 0 and hasattr(
49
+ message.content[0],
50
+ "text",
51
+ ):
52
+ content_text = message.content[0].text
53
+ except (AttributeError, IndexError):
54
+ # Log error or handle appropriately
55
+ pass
56
+
57
+ return {
58
+ "role": getattr(message, "role", None),
59
+ "content": content_text,
60
+ }
61
+
62
+ async def transform_messages(self, messages: List[Message]) -> List[dict]:
63
+ return [self.transform_message(message) for message in messages]
64
+
65
+ async def start(self):
66
+ pass
67
+
68
+ async def stop(self):
69
+ pass
70
+
71
+ async def health(self):
72
+ pass
73
+
74
+ async def add_memory(
75
+ self,
76
+ user_id: str,
77
+ messages: list,
78
+ session_id: Optional[str] = None,
79
+ ):
80
+ messages = await self.transform_messages(messages)
81
+ return await self.service.add(
82
+ messages=messages,
83
+ user_id=user_id,
84
+ run_id=session_id,
85
+ # async_mode=True,
86
+ )
87
+
88
+ async def search_memory(
89
+ self,
90
+ user_id: str,
91
+ messages: list,
92
+ filters: Optional[Dict[str, Any]] = None,
93
+ ) -> list:
94
+ query = await self.get_query_text(messages[-1])
95
+ kwargs = {
96
+ "query": query,
97
+ "user_id": user_id,
98
+ }
99
+ if filters:
100
+ kwargs["filters"] = filters
101
+ return await self.service.search(**kwargs)
102
+
103
+ async def list_memory(
104
+ self,
105
+ user_id: str,
106
+ filters: Optional[Dict[str, Any]] = None,
107
+ ) -> list:
108
+ kwargs = {"user_id": user_id}
109
+ if filters:
110
+ kwargs["filters"] = filters
111
+ return await self.service.get_all(**kwargs)
112
+
113
+ async def delete_memory(
114
+ self,
115
+ user_id: str,
116
+ session_id: Optional[str] = None,
117
+ ) -> None:
118
+ if session_id:
119
+ return await self.service.delete_all(
120
+ user_id=user_id,
121
+ run_id=session_id,
122
+ )
123
+ else:
124
+ return await self.service.delete_all(user_id=user_id)
@@ -137,7 +137,8 @@ class InMemoryMemoryService(MemoryService):
137
137
  if storage_key not in self._store[user_id]:
138
138
  self._store[user_id][storage_key] = []
139
139
 
140
- self._store[user_id][storage_key].extend(messages)
140
+ if messages:
141
+ self._store[user_id][storage_key].extend(messages)
141
142
 
142
143
  async def search_memory(
143
144
  self,
@@ -112,9 +112,10 @@ class RedisSessionHistoryService(SessionHistoryService):
112
112
  message = [message]
113
113
  norm_message = []
114
114
  for msg in message:
115
- if not isinstance(msg, Message):
116
- msg = Message.model_validate(msg)
117
- norm_message.append(msg)
115
+ if msg is not None:
116
+ if not isinstance(msg, Message):
117
+ msg = Message.model_validate(msg)
118
+ norm_message.append(msg)
118
119
 
119
120
  session.messages.extend(norm_message)
120
121
 
@@ -2,27 +2,17 @@
2
2
  # pylint: disable=too-many-branches
3
3
  from typing import List
4
4
 
5
- try:
6
- from ...sandbox.enums import SandboxType
7
- from ...sandbox.manager import SandboxManager
8
- from ...sandbox.registry import SandboxRegistry
9
- from ...sandbox.tools.mcp_tool import MCPTool
10
- from ...sandbox.tools.sandbox_tool import SandboxTool
11
- from ...sandbox.tools.function_tool import FunctionTool
12
- except ImportError:
13
- pass
14
-
5
+ from ...sandbox.enums import SandboxType
6
+ from ...sandbox.manager import SandboxManager
7
+ from ...sandbox.registry import SandboxRegistry
8
+ from ...sandbox.tools.mcp_tool import MCPTool
9
+ from ...sandbox.tools.sandbox_tool import SandboxTool
10
+ from ...sandbox.tools.function_tool import FunctionTool
15
11
  from ...engine.services.base import ServiceWithLifecycleManager
16
12
 
17
13
 
18
14
  class SandboxService(ServiceWithLifecycleManager):
19
15
  def __init__(self, base_url=None, bearer_token=None):
20
- if SandboxManager is None:
21
- raise ImportError(
22
- "SandboxManager is not available. "
23
- "Please install agentscope-runtime[sandbox]",
24
- )
25
-
26
16
  self.manager_api = SandboxManager(
27
17
  base_url=base_url,
28
18
  bearer_token=bearer_token,
@@ -238,9 +238,10 @@ class InMemorySessionHistoryService(SessionHistoryService):
238
238
 
239
239
  norm_message = []
240
240
  for msg in message:
241
- if not isinstance(msg, Message):
242
- msg = Message.model_validate(msg)
243
- norm_message.append(msg)
241
+ if msg is not None:
242
+ if not isinstance(msg, Message):
243
+ msg = Message.model_validate(msg)
244
+ norm_message.append(msg)
244
245
  session.messages.extend(norm_message)
245
246
 
246
247
  # update the in memory copy