agentscope-runtime 1.0.4a1__py3-none-any.whl → 1.0.5.post1__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 (79) hide show
  1. agentscope_runtime/adapters/agentscope/stream.py +2 -8
  2. agentscope_runtime/adapters/langgraph/stream.py +120 -70
  3. agentscope_runtime/adapters/ms_agent_framework/__init__.py +0 -0
  4. agentscope_runtime/adapters/ms_agent_framework/message.py +205 -0
  5. agentscope_runtime/adapters/ms_agent_framework/stream.py +418 -0
  6. agentscope_runtime/adapters/utils.py +6 -0
  7. agentscope_runtime/cli/commands/deploy.py +836 -1
  8. agentscope_runtime/cli/commands/stop.py +16 -0
  9. agentscope_runtime/common/container_clients/__init__.py +52 -0
  10. agentscope_runtime/common/container_clients/agentrun_client.py +6 -4
  11. agentscope_runtime/common/container_clients/boxlite_client.py +442 -0
  12. agentscope_runtime/common/container_clients/docker_client.py +0 -20
  13. agentscope_runtime/common/container_clients/fc_client.py +6 -4
  14. agentscope_runtime/common/container_clients/gvisor_client.py +38 -0
  15. agentscope_runtime/common/container_clients/knative_client.py +467 -0
  16. agentscope_runtime/common/utils/deprecation.py +164 -0
  17. agentscope_runtime/engine/__init__.py +4 -0
  18. agentscope_runtime/engine/app/agent_app.py +16 -4
  19. agentscope_runtime/engine/constant.py +1 -0
  20. agentscope_runtime/engine/deployers/__init__.py +34 -11
  21. agentscope_runtime/engine/deployers/adapter/__init__.py +8 -0
  22. agentscope_runtime/engine/deployers/adapter/a2a/__init__.py +26 -51
  23. agentscope_runtime/engine/deployers/adapter/a2a/a2a_protocol_adapter.py +23 -13
  24. agentscope_runtime/engine/deployers/adapter/a2a/a2a_registry.py +4 -201
  25. agentscope_runtime/engine/deployers/adapter/a2a/nacos_a2a_registry.py +152 -25
  26. agentscope_runtime/engine/deployers/adapter/agui/__init__.py +8 -0
  27. agentscope_runtime/engine/deployers/adapter/agui/agui_adapter_utils.py +652 -0
  28. agentscope_runtime/engine/deployers/adapter/agui/agui_protocol_adapter.py +225 -0
  29. agentscope_runtime/engine/deployers/agentrun_deployer.py +2 -2
  30. agentscope_runtime/engine/deployers/fc_deployer.py +1506 -0
  31. agentscope_runtime/engine/deployers/knative_deployer.py +290 -0
  32. agentscope_runtime/engine/deployers/pai_deployer.py +2335 -0
  33. agentscope_runtime/engine/deployers/utils/net_utils.py +37 -0
  34. agentscope_runtime/engine/deployers/utils/oss_utils.py +38 -0
  35. agentscope_runtime/engine/deployers/utils/package.py +46 -42
  36. agentscope_runtime/engine/helpers/agent_api_client.py +372 -0
  37. agentscope_runtime/engine/runner.py +13 -0
  38. agentscope_runtime/engine/schemas/agent_schemas.py +9 -3
  39. agentscope_runtime/engine/services/agent_state/__init__.py +7 -0
  40. agentscope_runtime/engine/services/memory/__init__.py +7 -0
  41. agentscope_runtime/engine/services/memory/redis_memory_service.py +15 -16
  42. agentscope_runtime/engine/services/session_history/__init__.py +7 -0
  43. agentscope_runtime/engine/tracing/local_logging_handler.py +2 -3
  44. agentscope_runtime/engine/tracing/wrapper.py +18 -4
  45. agentscope_runtime/sandbox/__init__.py +14 -6
  46. agentscope_runtime/sandbox/box/base/__init__.py +2 -2
  47. agentscope_runtime/sandbox/box/base/base_sandbox.py +51 -1
  48. agentscope_runtime/sandbox/box/browser/__init__.py +2 -2
  49. agentscope_runtime/sandbox/box/browser/browser_sandbox.py +198 -2
  50. agentscope_runtime/sandbox/box/filesystem/__init__.py +2 -2
  51. agentscope_runtime/sandbox/box/filesystem/filesystem_sandbox.py +99 -2
  52. agentscope_runtime/sandbox/box/gui/__init__.py +2 -2
  53. agentscope_runtime/sandbox/box/gui/gui_sandbox.py +117 -1
  54. agentscope_runtime/sandbox/box/mobile/__init__.py +2 -2
  55. agentscope_runtime/sandbox/box/mobile/mobile_sandbox.py +247 -100
  56. agentscope_runtime/sandbox/box/sandbox.py +102 -65
  57. agentscope_runtime/sandbox/box/shared/routers/generic.py +36 -29
  58. agentscope_runtime/sandbox/client/__init__.py +6 -1
  59. agentscope_runtime/sandbox/client/async_http_client.py +339 -0
  60. agentscope_runtime/sandbox/client/base.py +74 -0
  61. agentscope_runtime/sandbox/client/http_client.py +108 -329
  62. agentscope_runtime/sandbox/enums.py +7 -0
  63. agentscope_runtime/sandbox/manager/sandbox_manager.py +275 -29
  64. agentscope_runtime/sandbox/manager/server/app.py +7 -1
  65. agentscope_runtime/sandbox/manager/server/config.py +3 -1
  66. agentscope_runtime/sandbox/model/manager_config.py +11 -9
  67. agentscope_runtime/tools/modelstudio_memory/__init__.py +106 -0
  68. agentscope_runtime/tools/modelstudio_memory/base.py +220 -0
  69. agentscope_runtime/tools/modelstudio_memory/config.py +86 -0
  70. agentscope_runtime/tools/modelstudio_memory/core.py +594 -0
  71. agentscope_runtime/tools/modelstudio_memory/exceptions.py +60 -0
  72. agentscope_runtime/tools/modelstudio_memory/schemas.py +253 -0
  73. agentscope_runtime/version.py +1 -1
  74. {agentscope_runtime-1.0.4a1.dist-info → agentscope_runtime-1.0.5.post1.dist-info}/METADATA +187 -74
  75. {agentscope_runtime-1.0.4a1.dist-info → agentscope_runtime-1.0.5.post1.dist-info}/RECORD +79 -55
  76. {agentscope_runtime-1.0.4a1.dist-info → agentscope_runtime-1.0.5.post1.dist-info}/WHEEL +1 -1
  77. {agentscope_runtime-1.0.4a1.dist-info → agentscope_runtime-1.0.5.post1.dist-info}/entry_points.txt +0 -0
  78. {agentscope_runtime-1.0.4a1.dist-info → agentscope_runtime-1.0.5.post1.dist-info}/licenses/LICENSE +0 -0
  79. {agentscope_runtime-1.0.4a1.dist-info → agentscope_runtime-1.0.5.post1.dist-info}/top_level.txt +0 -0
@@ -9,6 +9,7 @@ from urllib.parse import urlparse
9
9
  from agentscope import setup_logger
10
10
  from agentscope.message import Msg
11
11
 
12
+ from ..utils import _update_obj_attrs
12
13
  from ...engine.schemas.agent_schemas import (
13
14
  Message,
14
15
  Content,
@@ -26,13 +27,6 @@ from ...engine.schemas.agent_schemas import (
26
27
  setup_logger("ERROR")
27
28
 
28
29
 
29
- def _update_obj_attrs(obj, **attrs):
30
- for key, value in attrs.items():
31
- if hasattr(obj, key):
32
- setattr(obj, key, value)
33
- return obj
34
-
35
-
36
30
  async def adapt_agentscope_message_stream(
37
31
  source_stream: AsyncIterator[Tuple[Msg, bool]],
38
32
  ) -> AsyncIterator[Union[Message, Content]]:
@@ -277,7 +271,7 @@ async def adapt_agentscope_message_stream(
277
271
  role="assistant",
278
272
  )
279
273
  data_delta_content = DataContent(
280
- index=0,
274
+ index=None if last else 0,
281
275
  data=fc_cls(
282
276
  call_id=element["id"],
283
277
  name=element.get("name"),
@@ -62,79 +62,121 @@ async def adapt_langgraph_message_stream(
62
62
  yield message.completed()
63
63
  elif isinstance(msg, AIMessage):
64
64
  role = "assistant"
65
+ tool_calls = getattr(msg, "tool_calls", [])
65
66
  has_tool_call_chunk = (
66
- True if getattr(msg, "tool_call_chunks") else False
67
+ True if getattr(msg, "tool_call_chunks", "") else False
67
68
  )
68
- is_last_chunk = (
69
- True if getattr(msg, "chunk_position") == "last" else False
70
- )
71
-
72
- # Extract tool calls if present
73
- if tool_started:
74
- if has_tool_call_chunk:
75
- tool_call_chunk_msgs.append(msg)
76
- if is_last_chunk:
77
- # tool call finished
78
- tool_started = False
79
- result = reduce(lambda x, y: x + y, tool_call_chunk_msgs)
80
- tool_calls = result.tool_call_chunks
81
- for tool_call in tool_calls:
82
- call_id = tool_call.get("id", "")
83
- # Create new tool call message
84
- plugin_call_message = Message(
85
- type=MessageType.PLUGIN_CALL,
86
- role=role,
87
- )
88
- data_content = DataContent(
89
- index=index,
90
- data=FunctionCall(
91
- call_id=call_id,
92
- name=tool_call.get("name", ""),
93
- arguments=json.dumps(
94
- tool_call.get("args", {}),
95
- ensure_ascii=False,
96
- ),
97
- ).model_dump(),
98
- delta=True,
69
+ if tool_calls and not has_tool_call_chunk:
70
+ plugin_call_message = Message(
71
+ type=MessageType.PLUGIN_CALL,
72
+ role=role,
73
+ )
74
+ for tool_call in tool_calls:
75
+ tool_call_args = (
76
+ tool_call.get("args")
77
+ if isinstance(tool_call.get("args"), str)
78
+ else json.dumps(tool_call.get("args"))
79
+ )
80
+ data_content = DataContent(
81
+ index=index,
82
+ data=FunctionCall(
83
+ call_id=tool_call.get("id"),
84
+ name=tool_call.get("name"),
85
+ arguments=tool_call_args,
86
+ ).model_dump(),
87
+ )
88
+ plugin_call_message.add_content(
89
+ data_content,
90
+ )
91
+ yield data_content.completed()
92
+ yield plugin_call_message.completed()
93
+ else:
94
+ has_tool_call_chunk = (
95
+ True if getattr(msg, "tool_call_chunks", "") else False
96
+ )
97
+ is_last_chunk = (
98
+ True
99
+ if getattr(msg, "chunk_position", "") == "last"
100
+ else False
101
+ )
102
+ # Extract tool calls if present
103
+ if tool_started:
104
+ if has_tool_call_chunk:
105
+ tool_call_chunk_msgs.append(msg)
106
+ if is_last_chunk:
107
+ # tool call finished
108
+ tool_started = False
109
+ result = reduce(
110
+ lambda x, y: x + y,
111
+ tool_call_chunk_msgs,
99
112
  )
113
+ tool_calls = result.tool_call_chunks
114
+ for tool_call in tool_calls:
115
+ call_id = tool_call.get("id", "")
116
+ # Create new tool call message
117
+ plugin_call_message = Message(
118
+ type=MessageType.PLUGIN_CALL,
119
+ role=role,
120
+ )
121
+ tool_call_args = (
122
+ tool_call.get("args")
123
+ if isinstance(tool_call.get("args"), str)
124
+ else json.dumps(tool_call.get("args"))
125
+ )
100
126
 
101
- data_content = plugin_call_message.add_delta_content(
102
- new_content=data_content,
103
- )
104
- yield data_content
105
- yield plugin_call_message.completed()
106
- else:
107
- if has_tool_call_chunk:
108
- # tool call start, collect chunks and continue
109
- tool_started = True
110
- tool_call_chunk_msgs.append(msg)
111
- else:
112
- # normal message
113
- content = msg.content if hasattr(msg, "content") else None
114
- if msg_id != getattr(msg, "id"):
115
- index = None
116
- message = Message(type=MessageType.MESSAGE, role=role)
117
- msg_id = getattr(msg, "id")
118
- yield message.in_progress()
127
+ data_content = DataContent(
128
+ index=index,
129
+ data=FunctionCall(
130
+ call_id=call_id,
131
+ name=tool_call.get("name"),
132
+ arguments=tool_call_args,
133
+ ).model_dump(),
134
+ )
119
135
 
120
- if content:
121
- # todo support non str content
122
- text_delta_content = TextContent(
123
- delta=True,
124
- index=index,
125
- text=content,
126
- )
127
- text_delta_content = message.add_delta_content(
128
- new_content=text_delta_content,
136
+ data_content = (
137
+ plugin_call_message.add_delta_content(
138
+ new_content=data_content,
139
+ )
140
+ )
141
+ yield data_content.completed()
142
+ yield plugin_call_message.completed()
143
+ else:
144
+ if has_tool_call_chunk:
145
+ # tool call start, collect chunks and continue
146
+ tool_started = True
147
+ tool_call_chunk_msgs.append(msg)
148
+ else:
149
+ # normal message
150
+ content = (
151
+ msg.content if hasattr(msg, "content") else None
129
152
  )
130
- index = text_delta_content.index
131
- yield text_delta_content
132
- # Handle final completion
133
- if last:
134
- # completed_content = message.content[index]
135
- # if completed_content.text:
136
- # yield completed_content.completed()
137
- yield message.completed()
153
+ if msg_id != getattr(msg, "id"):
154
+ index = None
155
+ message = Message(
156
+ type=MessageType.MESSAGE,
157
+ role=role,
158
+ )
159
+ msg_id = getattr(msg, "id")
160
+ yield message.in_progress()
161
+
162
+ if content:
163
+ # todo support non str content
164
+ text_delta_content = TextContent(
165
+ delta=True,
166
+ index=index,
167
+ text=content,
168
+ )
169
+ text_delta_content = message.add_delta_content(
170
+ new_content=text_delta_content,
171
+ )
172
+ index = text_delta_content.index
173
+ yield text_delta_content
174
+ # Handle final completion
175
+ if last:
176
+ # completed_content = message.content[index]
177
+ # if completed_content.text:
178
+ # yield completed_content.completed()
179
+ yield message.completed()
138
180
  elif isinstance(msg, SystemMessage):
139
181
  role = "system"
140
182
  content = msg.content if hasattr(msg, "content") else None
@@ -163,18 +205,26 @@ async def adapt_langgraph_message_stream(
163
205
  type=MessageType.PLUGIN_CALL_OUTPUT,
164
206
  role="tool",
165
207
  )
208
+ tool_call_output = (
209
+ msg.content
210
+ if isinstance(msg.content, str)
211
+ else json.dumps(msg.content, ensure_ascii=False)
212
+ )
166
213
  # Create function call output data
167
214
  function_output_data = FunctionCallOutput(
168
215
  call_id=msg.tool_call_id,
169
216
  name=msg.name,
170
- output=json.dumps(content, ensure_ascii=False),
217
+ output=tool_call_output,
171
218
  )
172
219
 
173
220
  data_content = DataContent(
174
- index=None,
175
221
  data=function_output_data.model_dump(),
222
+ msg_id=plugin_output_message.id,
223
+ )
224
+ yield data_content.completed()
225
+ plugin_output_message.add_content(
226
+ data_content,
176
227
  )
177
- plugin_output_message.content = [data_content]
178
228
  yield plugin_output_message.completed()
179
229
  else:
180
230
  role = "assistant"
@@ -0,0 +1,205 @@
1
+ # -*- coding: utf-8 -*-
2
+ # pylint: disable=too-many-branches,too-many-statements
3
+ import json
4
+ from typing import Union, List
5
+ from collections import OrderedDict
6
+
7
+ from agent_framework import (
8
+ ChatMessage,
9
+ TextContent as MSTextContent,
10
+ DataContent as MSDataContent,
11
+ TextReasoningContent,
12
+ UriContent,
13
+ FunctionCallContent,
14
+ FunctionResultContent,
15
+ )
16
+
17
+ from ...engine.schemas.agent_schemas import (
18
+ Message,
19
+ MessageType,
20
+ )
21
+
22
+
23
+ def message_to_ms_agent_framework_message(
24
+ messages: Union[Message, List[Message]],
25
+ ) -> Union[ChatMessage, List[ChatMessage]]:
26
+ """
27
+ Convert AgentScope runtime Message(s) to Microsoft agent framework
28
+ Message(s).
29
+
30
+ Reference:
31
+ https://learn.microsoft.com/en-us/agent-framework/user-guide/agents
32
+ /running-agents?pivots=programming-language-python
33
+
34
+ Args:
35
+ messages: A single AgentScope runtime Message or list of Messages.
36
+
37
+ Returns:
38
+ A single Microsoft agent framework Message object or a list of
39
+ Microsoft agent framework Message objects.
40
+ """
41
+
42
+ def _try_loads(v, default, keep_original=False):
43
+ if isinstance(v, (dict, list)):
44
+ return v
45
+ if isinstance(v, str) and v.strip():
46
+ try:
47
+ return json.loads(v)
48
+ except Exception:
49
+ return v if keep_original else default
50
+ return default
51
+
52
+ def _convert_one(message: Message) -> ChatMessage:
53
+ result = {
54
+ "author_name": getattr(message, "name", message.role),
55
+ "role": message.role or "assistant",
56
+ }
57
+ _id = getattr(message, "id")
58
+
59
+ # if meta exists, prefer original id/name from meta
60
+ if hasattr(message, "metadata") and isinstance(message.metadata, dict):
61
+ if "original_id" in message.metadata:
62
+ _id = message.metadata["original_id"]
63
+ if "original_name" in message.metadata:
64
+ result["author_name"] = message.metadata["original_name"]
65
+ if "metadata" in message.metadata:
66
+ result["additional_properties"] = message.metadata["metadata"]
67
+ result["message_id"] = _id
68
+
69
+ if message.type in (
70
+ MessageType.PLUGIN_CALL,
71
+ MessageType.MCP_TOOL_CALL,
72
+ MessageType.FUNCTION_CALL,
73
+ ):
74
+ # convert CALL to ToolUseBlock
75
+ tool_args = None
76
+ for cnt in reversed(message.content):
77
+ if hasattr(cnt, "data"):
78
+ v = cnt.data.get("arguments")
79
+ if isinstance(v, (dict, list)) or (
80
+ isinstance(v, str) and v.strip()
81
+ ):
82
+ tool_args = _try_loads(v, {}, keep_original=False)
83
+ break
84
+ if tool_args is None:
85
+ tool_args = {}
86
+ result["contents"] = [
87
+ FunctionCallContent(
88
+ call_id=message.content[0].data["call_id"],
89
+ name=message.content[0].data.get("name"),
90
+ arguments=tool_args,
91
+ ),
92
+ ]
93
+ elif message.type in (
94
+ MessageType.PLUGIN_CALL_OUTPUT,
95
+ MessageType.MCP_TOOL_CALL_OUTPUT,
96
+ MessageType.FUNCTION_CALL_OUTPUT,
97
+ ):
98
+ result["role"] = "tool"
99
+ out = None
100
+ for cnt in reversed(message.content):
101
+ if hasattr(cnt, "data"):
102
+ v = cnt.data.get("output")
103
+ if isinstance(v, (dict, list)) or (
104
+ isinstance(v, str) and v.strip()
105
+ ):
106
+ out = _try_loads(v, "", keep_original=True)
107
+ break
108
+ if out is None:
109
+ out = ""
110
+ blk = out
111
+
112
+ result["contents"] = [
113
+ FunctionResultContent(
114
+ call_id=message.content[0].data["call_id"],
115
+ result=blk,
116
+ ),
117
+ ]
118
+ elif message.type in (MessageType.REASONING,):
119
+ result["contents"] = [
120
+ TextReasoningContent(
121
+ text=message.content[0].text,
122
+ ),
123
+ ]
124
+ else:
125
+ type_mapping = {
126
+ "text": (MSTextContent, "text"),
127
+ "image": (UriContent, "image_url"),
128
+ "audio": (UriContent, "data"),
129
+ "data": (MSDataContent, "data"),
130
+ "file": (MSDataContent, "file_url"), # Support file_url
131
+ # "video": (VideoBlock, "video_url", True),
132
+ # TODO: support video
133
+ }
134
+
135
+ msg_content = []
136
+ for cnt in message.content:
137
+ cnt_type = cnt.type or "text"
138
+
139
+ if cnt_type not in type_mapping:
140
+ raise ValueError(f"Unsupported message type: {cnt_type}")
141
+
142
+ block_cls, attr_name = type_mapping[cnt_type]
143
+ value = getattr(cnt, attr_name)
144
+
145
+ if cnt_type in ("image", "audio", "file", "data"):
146
+ msg_content.append(
147
+ block_cls(
148
+ data=value,
149
+ type=cnt.type,
150
+ ),
151
+ )
152
+
153
+ else:
154
+ # text
155
+ if isinstance(value, str):
156
+ msg_content.append(
157
+ MSTextContent(text=value),
158
+ )
159
+ else:
160
+ try:
161
+ json_str = json.dumps(value, ensure_ascii=False)
162
+ except Exception:
163
+ json_str = str(value)
164
+ msg_content.append(MSTextContent(text=json_str))
165
+
166
+ result["contents"] = msg_content
167
+ _msg = ChatMessage(**result)
168
+ return _msg
169
+
170
+ # Handle single or list input
171
+ if isinstance(messages, Message):
172
+ return _convert_one(messages)
173
+ elif isinstance(messages, list):
174
+ converted_list = [_convert_one(m) for m in messages]
175
+
176
+ # Group by original_id
177
+ grouped = OrderedDict()
178
+ for msg, orig_msg in zip(messages, converted_list):
179
+ metadata = getattr(msg, "metadata")
180
+ if metadata:
181
+ orig_id = metadata.get(
182
+ "original_id",
183
+ orig_msg.message_id,
184
+ )
185
+ else:
186
+ # In case metadata is not provided, use the original id
187
+ orig_id = msg.id
188
+
189
+ if orig_id not in grouped:
190
+ ms_msg = ChatMessage(
191
+ author_name=orig_msg.author_name,
192
+ role=orig_msg.role,
193
+ additional_properties=orig_msg.additional_properties,
194
+ contents=list(orig_msg.contents),
195
+ )
196
+ ms_msg.message_id = orig_id
197
+ grouped[orig_id] = ms_msg
198
+ else:
199
+ grouped[orig_id].contents.extend(orig_msg.contents)
200
+
201
+ return list(grouped.values())
202
+ else:
203
+ raise TypeError(
204
+ f"Expected Message or list[Message], got {type(messages)}",
205
+ )