agentscope-runtime 0.2.0b2__py3-none-any.whl → 1.0.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 (183) hide show
  1. agentscope_runtime/adapters/__init__.py +0 -0
  2. agentscope_runtime/adapters/agentscope/__init__.py +0 -0
  3. agentscope_runtime/adapters/agentscope/long_term_memory/__init__.py +6 -0
  4. agentscope_runtime/adapters/agentscope/long_term_memory/_long_term_memory_adapter.py +258 -0
  5. agentscope_runtime/adapters/agentscope/memory/__init__.py +6 -0
  6. agentscope_runtime/adapters/agentscope/memory/_memory_adapter.py +152 -0
  7. agentscope_runtime/adapters/agentscope/message.py +535 -0
  8. agentscope_runtime/adapters/agentscope/stream.py +474 -0
  9. agentscope_runtime/adapters/agentscope/tool/__init__.py +9 -0
  10. agentscope_runtime/adapters/agentscope/tool/sandbox_tool.py +69 -0
  11. agentscope_runtime/adapters/agentscope/tool/tool.py +233 -0
  12. agentscope_runtime/adapters/autogen/__init__.py +0 -0
  13. agentscope_runtime/adapters/autogen/tool/__init__.py +7 -0
  14. agentscope_runtime/adapters/autogen/tool/tool.py +211 -0
  15. agentscope_runtime/adapters/text/__init__.py +0 -0
  16. agentscope_runtime/adapters/text/stream.py +29 -0
  17. agentscope_runtime/common/collections/redis_mapping.py +4 -1
  18. agentscope_runtime/common/container_clients/fc_client.py +855 -0
  19. agentscope_runtime/common/utils/__init__.py +0 -0
  20. agentscope_runtime/common/utils/lazy_loader.py +57 -0
  21. agentscope_runtime/engine/__init__.py +25 -18
  22. agentscope_runtime/engine/app/agent_app.py +161 -91
  23. agentscope_runtime/engine/app/base_app.py +4 -118
  24. agentscope_runtime/engine/constant.py +8 -0
  25. agentscope_runtime/engine/deployers/__init__.py +8 -0
  26. agentscope_runtime/engine/deployers/adapter/__init__.py +2 -0
  27. agentscope_runtime/engine/deployers/adapter/a2a/a2a_adapter_utils.py +0 -21
  28. agentscope_runtime/engine/deployers/adapter/a2a/a2a_protocol_adapter.py +28 -9
  29. agentscope_runtime/engine/deployers/adapter/responses/__init__.py +2 -0
  30. agentscope_runtime/engine/deployers/adapter/responses/response_api_adapter_utils.py +5 -2
  31. agentscope_runtime/engine/deployers/adapter/responses/response_api_protocol_adapter.py +1 -1
  32. agentscope_runtime/engine/deployers/agentrun_deployer.py +2541 -0
  33. agentscope_runtime/engine/deployers/cli_fc_deploy.py +1 -1
  34. agentscope_runtime/engine/deployers/kubernetes_deployer.py +9 -21
  35. agentscope_runtime/engine/deployers/local_deployer.py +47 -74
  36. agentscope_runtime/engine/deployers/modelstudio_deployer.py +216 -50
  37. agentscope_runtime/engine/deployers/utils/app_runner_utils.py +29 -0
  38. agentscope_runtime/engine/deployers/utils/detached_app.py +510 -0
  39. agentscope_runtime/engine/deployers/utils/docker_image_utils/__init__.py +1 -1
  40. agentscope_runtime/engine/deployers/utils/docker_image_utils/dockerfile_generator.py +1 -1
  41. agentscope_runtime/engine/deployers/utils/docker_image_utils/{runner_image_factory.py → image_factory.py} +121 -61
  42. agentscope_runtime/engine/deployers/utils/package.py +693 -0
  43. agentscope_runtime/engine/deployers/utils/service_utils/__init__.py +0 -5
  44. agentscope_runtime/engine/deployers/utils/service_utils/fastapi_factory.py +256 -282
  45. agentscope_runtime/engine/deployers/utils/service_utils/fastapi_templates.py +2 -4
  46. agentscope_runtime/engine/deployers/utils/service_utils/process_manager.py +23 -1
  47. agentscope_runtime/engine/deployers/utils/templates/app_main.py.j2 +84 -0
  48. agentscope_runtime/engine/deployers/utils/templates/runner_main.py.j2 +95 -0
  49. agentscope_runtime/engine/deployers/utils/{service_utils → templates}/standalone_main.py.j2 +0 -45
  50. agentscope_runtime/engine/deployers/utils/wheel_packager.py +119 -18
  51. agentscope_runtime/engine/helpers/runner.py +40 -0
  52. agentscope_runtime/engine/runner.py +170 -130
  53. agentscope_runtime/engine/schemas/agent_schemas.py +114 -3
  54. agentscope_runtime/engine/schemas/modelstudio_llm.py +4 -2
  55. agentscope_runtime/engine/schemas/oai_llm.py +23 -23
  56. agentscope_runtime/engine/schemas/response_api.py +65 -0
  57. agentscope_runtime/engine/schemas/session.py +24 -0
  58. agentscope_runtime/engine/services/__init__.py +0 -9
  59. agentscope_runtime/engine/services/agent_state/__init__.py +16 -0
  60. agentscope_runtime/engine/services/agent_state/redis_state_service.py +113 -0
  61. agentscope_runtime/engine/services/agent_state/state_service.py +179 -0
  62. agentscope_runtime/engine/services/memory/__init__.py +24 -0
  63. agentscope_runtime/engine/services/{mem0_memory_service.py → memory/mem0_memory_service.py} +17 -13
  64. agentscope_runtime/engine/services/{memory_service.py → memory/memory_service.py} +28 -7
  65. agentscope_runtime/engine/services/{redis_memory_service.py → memory/redis_memory_service.py} +1 -1
  66. agentscope_runtime/engine/services/{reme_personal_memory_service.py → memory/reme_personal_memory_service.py} +9 -6
  67. agentscope_runtime/engine/services/{reme_task_memory_service.py → memory/reme_task_memory_service.py} +2 -2
  68. agentscope_runtime/engine/services/{tablestore_memory_service.py → memory/tablestore_memory_service.py} +12 -18
  69. agentscope_runtime/engine/services/sandbox/__init__.py +13 -0
  70. agentscope_runtime/engine/services/{sandbox_service.py → sandbox/sandbox_service.py} +86 -71
  71. agentscope_runtime/engine/services/session_history/__init__.py +23 -0
  72. agentscope_runtime/engine/services/{redis_session_history_service.py → session_history/redis_session_history_service.py} +3 -2
  73. agentscope_runtime/engine/services/{session_history_service.py → session_history/session_history_service.py} +44 -34
  74. agentscope_runtime/engine/services/{tablestore_session_history_service.py → session_history/tablestore_session_history_service.py} +14 -19
  75. agentscope_runtime/engine/services/utils/tablestore_service_utils.py +2 -2
  76. agentscope_runtime/engine/tracing/base.py +10 -9
  77. agentscope_runtime/engine/tracing/message_util.py +1 -1
  78. agentscope_runtime/engine/tracing/tracing_util.py +7 -2
  79. agentscope_runtime/sandbox/__init__.py +10 -2
  80. agentscope_runtime/sandbox/box/agentbay/__init__.py +4 -0
  81. agentscope_runtime/sandbox/box/agentbay/agentbay_sandbox.py +559 -0
  82. agentscope_runtime/sandbox/box/base/base_sandbox.py +12 -0
  83. agentscope_runtime/sandbox/box/browser/browser_sandbox.py +115 -11
  84. agentscope_runtime/sandbox/box/cloud/__init__.py +4 -0
  85. agentscope_runtime/sandbox/box/cloud/cloud_sandbox.py +254 -0
  86. agentscope_runtime/sandbox/box/filesystem/filesystem_sandbox.py +66 -0
  87. agentscope_runtime/sandbox/box/gui/gui_sandbox.py +42 -0
  88. agentscope_runtime/sandbox/box/mobile/__init__.py +4 -0
  89. agentscope_runtime/sandbox/box/mobile/box/__init__.py +0 -0
  90. agentscope_runtime/sandbox/box/mobile/mobile_sandbox.py +216 -0
  91. agentscope_runtime/sandbox/box/training_box/training_box.py +2 -2
  92. agentscope_runtime/sandbox/client/http_client.py +1 -0
  93. agentscope_runtime/sandbox/enums.py +2 -0
  94. agentscope_runtime/sandbox/manager/sandbox_manager.py +18 -2
  95. agentscope_runtime/sandbox/manager/server/app.py +12 -0
  96. agentscope_runtime/sandbox/manager/server/config.py +19 -0
  97. agentscope_runtime/sandbox/model/manager_config.py +79 -2
  98. agentscope_runtime/sandbox/utils.py +0 -18
  99. agentscope_runtime/tools/RAGs/__init__.py +0 -0
  100. agentscope_runtime/tools/RAGs/modelstudio_rag.py +377 -0
  101. agentscope_runtime/tools/RAGs/modelstudio_rag_lite.py +219 -0
  102. agentscope_runtime/tools/__init__.py +119 -0
  103. agentscope_runtime/tools/_constants.py +18 -0
  104. agentscope_runtime/tools/alipay/__init__.py +4 -0
  105. agentscope_runtime/tools/alipay/base.py +334 -0
  106. agentscope_runtime/tools/alipay/payment.py +835 -0
  107. agentscope_runtime/tools/alipay/subscribe.py +551 -0
  108. agentscope_runtime/tools/base.py +264 -0
  109. agentscope_runtime/tools/cli/__init__.py +0 -0
  110. agentscope_runtime/tools/cli/modelstudio_mcp_server.py +78 -0
  111. agentscope_runtime/tools/generations/__init__.py +75 -0
  112. agentscope_runtime/tools/generations/async_image_to_video.py +350 -0
  113. agentscope_runtime/tools/generations/async_image_to_video_wan25.py +366 -0
  114. agentscope_runtime/tools/generations/async_speech_to_video.py +422 -0
  115. agentscope_runtime/tools/generations/async_text_to_video.py +320 -0
  116. agentscope_runtime/tools/generations/async_text_to_video_wan25.py +334 -0
  117. agentscope_runtime/tools/generations/image_edit.py +208 -0
  118. agentscope_runtime/tools/generations/image_edit_wan25.py +193 -0
  119. agentscope_runtime/tools/generations/image_generation.py +202 -0
  120. agentscope_runtime/tools/generations/image_generation_wan25.py +201 -0
  121. agentscope_runtime/tools/generations/image_style_repaint.py +208 -0
  122. agentscope_runtime/tools/generations/image_to_video.py +233 -0
  123. agentscope_runtime/tools/generations/qwen_image_edit.py +205 -0
  124. agentscope_runtime/tools/generations/qwen_image_generation.py +214 -0
  125. agentscope_runtime/tools/generations/qwen_text_to_speech.py +154 -0
  126. agentscope_runtime/tools/generations/speech_to_text.py +260 -0
  127. agentscope_runtime/tools/generations/speech_to_video.py +314 -0
  128. agentscope_runtime/tools/generations/text_to_video.py +221 -0
  129. agentscope_runtime/tools/mcp_wrapper.py +215 -0
  130. agentscope_runtime/tools/realtime_clients/__init__.py +13 -0
  131. agentscope_runtime/tools/realtime_clients/asr_client.py +27 -0
  132. agentscope_runtime/tools/realtime_clients/azure_asr_client.py +195 -0
  133. agentscope_runtime/tools/realtime_clients/azure_tts_client.py +383 -0
  134. agentscope_runtime/tools/realtime_clients/modelstudio_asr_client.py +151 -0
  135. agentscope_runtime/tools/realtime_clients/modelstudio_tts_client.py +199 -0
  136. agentscope_runtime/tools/realtime_clients/realtime_tool.py +55 -0
  137. agentscope_runtime/tools/realtime_clients/tts_client.py +33 -0
  138. agentscope_runtime/tools/searches/__init__.py +3 -0
  139. agentscope_runtime/tools/searches/modelstudio_search.py +877 -0
  140. agentscope_runtime/tools/searches/modelstudio_search_lite.py +310 -0
  141. agentscope_runtime/tools/utils/__init__.py +0 -0
  142. agentscope_runtime/tools/utils/api_key_util.py +45 -0
  143. agentscope_runtime/tools/utils/crypto_utils.py +99 -0
  144. agentscope_runtime/tools/utils/mcp_util.py +35 -0
  145. agentscope_runtime/version.py +1 -1
  146. {agentscope_runtime-0.2.0b2.dist-info → agentscope_runtime-1.0.0b1.dist-info}/METADATA +234 -165
  147. agentscope_runtime-1.0.0b1.dist-info/RECORD +240 -0
  148. {agentscope_runtime-0.2.0b2.dist-info → agentscope_runtime-1.0.0b1.dist-info}/entry_points.txt +1 -0
  149. agentscope_runtime/engine/agents/__init__.py +0 -2
  150. agentscope_runtime/engine/agents/agentscope_agent.py +0 -488
  151. agentscope_runtime/engine/agents/agno_agent.py +0 -220
  152. agentscope_runtime/engine/agents/autogen_agent.py +0 -250
  153. agentscope_runtime/engine/agents/base_agent.py +0 -29
  154. agentscope_runtime/engine/agents/langgraph_agent.py +0 -59
  155. agentscope_runtime/engine/agents/utils.py +0 -53
  156. agentscope_runtime/engine/deployers/utils/package_project_utils.py +0 -1163
  157. agentscope_runtime/engine/deployers/utils/service_utils/service_config.py +0 -75
  158. agentscope_runtime/engine/deployers/utils/service_utils/service_factory.py +0 -220
  159. agentscope_runtime/engine/helpers/helper.py +0 -179
  160. agentscope_runtime/engine/schemas/context.py +0 -54
  161. agentscope_runtime/engine/services/context_manager.py +0 -164
  162. agentscope_runtime/engine/services/environment_manager.py +0 -50
  163. agentscope_runtime/engine/services/manager.py +0 -174
  164. agentscope_runtime/engine/services/rag_service.py +0 -195
  165. agentscope_runtime/engine/services/tablestore_rag_service.py +0 -143
  166. agentscope_runtime/sandbox/tools/__init__.py +0 -12
  167. agentscope_runtime/sandbox/tools/base/__init__.py +0 -8
  168. agentscope_runtime/sandbox/tools/base/tool.py +0 -52
  169. agentscope_runtime/sandbox/tools/browser/__init__.py +0 -57
  170. agentscope_runtime/sandbox/tools/browser/tool.py +0 -597
  171. agentscope_runtime/sandbox/tools/filesystem/__init__.py +0 -32
  172. agentscope_runtime/sandbox/tools/filesystem/tool.py +0 -319
  173. agentscope_runtime/sandbox/tools/function_tool.py +0 -321
  174. agentscope_runtime/sandbox/tools/gui/__init__.py +0 -7
  175. agentscope_runtime/sandbox/tools/gui/tool.py +0 -77
  176. agentscope_runtime/sandbox/tools/mcp_tool.py +0 -195
  177. agentscope_runtime/sandbox/tools/sandbox_tool.py +0 -104
  178. agentscope_runtime/sandbox/tools/tool.py +0 -238
  179. agentscope_runtime/sandbox/tools/utils.py +0 -68
  180. agentscope_runtime-0.2.0b2.dist-info/RECORD +0 -183
  181. {agentscope_runtime-0.2.0b2.dist-info → agentscope_runtime-1.0.0b1.dist-info}/WHEEL +0 -0
  182. {agentscope_runtime-0.2.0b2.dist-info → agentscope_runtime-1.0.0b1.dist-info}/licenses/LICENSE +0 -0
  183. {agentscope_runtime-0.2.0b2.dist-info → agentscope_runtime-1.0.0b1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,535 @@
1
+ # -*- coding: utf-8 -*-
2
+ # pylint:disable=too-many-branches,too-many-statements
3
+ # TODO: support file block
4
+ import json
5
+
6
+ from collections import OrderedDict
7
+ from typing import Union, List
8
+ from urllib.parse import urlparse
9
+
10
+ from agentscope.message import (
11
+ Msg,
12
+ ToolUseBlock,
13
+ ToolResultBlock,
14
+ TextBlock,
15
+ ThinkingBlock,
16
+ ImageBlock,
17
+ AudioBlock,
18
+ VideoBlock,
19
+ URLSource,
20
+ Base64Source,
21
+ )
22
+
23
+ from ...engine.schemas.agent_schemas import (
24
+ Message,
25
+ FunctionCall,
26
+ FunctionCallOutput,
27
+ MessageType,
28
+ )
29
+ from ...engine.helpers.agent_api_builder import ResponseBuilder
30
+
31
+
32
+ def matches_typed_dict_structure(obj, typed_dict_cls):
33
+ if not isinstance(obj, dict):
34
+ return False
35
+ expected_keys = set(typed_dict_cls.__annotations__.keys())
36
+ return expected_keys == set(obj.keys())
37
+
38
+
39
+ def agentscope_msg_to_message(
40
+ messages: Union[Msg, List[Msg]],
41
+ ) -> List[Message]:
42
+ """
43
+ Convert AgentScope Msg(s) into one or more runtime Message objects
44
+
45
+ Args:
46
+ messages: AgentScope message(s) from streaming.
47
+
48
+ Returns:
49
+ List[Message]: One or more constructed runtime Message objects.
50
+ """
51
+ if isinstance(messages, Msg):
52
+ msgs = [messages]
53
+ elif isinstance(messages, list):
54
+ msgs = messages
55
+ else:
56
+ raise TypeError(f"Expected Msg or list[Msg], got {type(messages)}")
57
+
58
+ results: List[Message] = []
59
+
60
+ for msg in msgs:
61
+ role = msg.role or "assistant"
62
+
63
+ if isinstance(msg.content, str):
64
+ # Only text
65
+ rb = ResponseBuilder()
66
+ mb = rb.create_message_builder(
67
+ role=role,
68
+ message_type=MessageType.MESSAGE,
69
+ )
70
+ # add meta field to store old id and name
71
+ mb.message.metadata = {
72
+ "original_id": msg.id,
73
+ "original_name": msg.name,
74
+ "metadata": msg.metadata,
75
+ }
76
+ cb = mb.create_content_builder(content_type="text")
77
+ cb.set_text(msg.content)
78
+ cb.complete()
79
+ mb.complete()
80
+ results.append(mb.get_message_data())
81
+ continue
82
+
83
+ # msg.content is a list of blocks
84
+ # We group blocks by high-level message type
85
+ current_mb = None
86
+ current_type = None
87
+
88
+ for block in msg.content:
89
+ if isinstance(block, dict):
90
+ btype = block.get("type", "text")
91
+ else:
92
+ continue
93
+
94
+ if btype == "text":
95
+ # Create/continue MESSAGE type
96
+ if current_type != MessageType.MESSAGE:
97
+ if current_mb:
98
+ current_mb.complete()
99
+ results.append(current_mb.get_message_data())
100
+ rb = ResponseBuilder()
101
+ current_mb = rb.create_message_builder(
102
+ role=role,
103
+ message_type=MessageType.MESSAGE,
104
+ )
105
+ # add meta field to store old id and name
106
+ current_mb.message.metadata = {
107
+ "original_id": msg.id,
108
+ "original_name": msg.name,
109
+ "metadata": msg.metadata,
110
+ }
111
+ current_type = MessageType.MESSAGE
112
+ cb = current_mb.create_content_builder(content_type="text")
113
+ cb.set_text(block.get("text", ""))
114
+ cb.complete()
115
+
116
+ elif btype == "thinking":
117
+ # Create/continue REASONING type
118
+ if current_type != MessageType.REASONING:
119
+ if current_mb:
120
+ current_mb.complete()
121
+ results.append(current_mb.get_message_data())
122
+ rb = ResponseBuilder()
123
+ current_mb = rb.create_message_builder(
124
+ role=role,
125
+ message_type=MessageType.REASONING,
126
+ )
127
+ # add meta field to store old id and name
128
+ current_mb.message.metadata = {
129
+ "original_id": msg.id,
130
+ "original_name": msg.name,
131
+ "metadata": msg.metadata,
132
+ }
133
+ current_type = MessageType.REASONING
134
+ cb = current_mb.create_content_builder(content_type="text")
135
+ cb.set_text(block.get("thinking", ""))
136
+ cb.complete()
137
+
138
+ elif btype == "tool_use":
139
+ # Always start a new PLUGIN_CALL message
140
+ if current_mb:
141
+ current_mb.complete()
142
+ results.append(current_mb.get_message_data())
143
+ rb = ResponseBuilder()
144
+ current_mb = rb.create_message_builder(
145
+ role=role,
146
+ message_type=MessageType.PLUGIN_CALL,
147
+ )
148
+ # add meta field to store old id and name
149
+ current_mb.message.metadata = {
150
+ "original_id": msg.id,
151
+ "original_name": msg.name,
152
+ "metadata": msg.metadata,
153
+ }
154
+ current_type = MessageType.PLUGIN_CALL
155
+ cb = current_mb.create_content_builder(content_type="data")
156
+
157
+ if isinstance(block.get("input"), (dict, list)):
158
+ arguments = json.dumps(block.get("input"))
159
+ else:
160
+ arguments = block.get("input")
161
+
162
+ call_data = FunctionCall(
163
+ call_id=block.get("id"),
164
+ name=block.get("name"),
165
+ arguments=arguments,
166
+ ).model_dump()
167
+ cb.set_data(call_data)
168
+ cb.complete()
169
+
170
+ elif btype == "tool_result":
171
+ # Always start a new PLUGIN_CALL_OUTPUT message
172
+ if current_mb:
173
+ current_mb.complete()
174
+ results.append(current_mb.get_message_data())
175
+ rb = ResponseBuilder()
176
+ current_mb = rb.create_message_builder(
177
+ role=role,
178
+ message_type=MessageType.PLUGIN_CALL_OUTPUT,
179
+ )
180
+ # add meta field to store old id and name
181
+ current_mb.message.metadata = {
182
+ "original_id": msg.id,
183
+ "original_name": msg.name,
184
+ "metadata": msg.metadata,
185
+ }
186
+ current_type = MessageType.PLUGIN_CALL_OUTPUT
187
+ cb = current_mb.create_content_builder(content_type="data")
188
+
189
+ if isinstance(block.get("output"), (dict, list)):
190
+ output = json.dumps(block.get("output"))
191
+ else:
192
+ output = block.get("output")
193
+
194
+ output_data = FunctionCallOutput(
195
+ call_id=block.get("id"),
196
+ name=block.get("name"),
197
+ output=output,
198
+ ).model_dump(exclude_none=True)
199
+ cb.set_data(output_data)
200
+ cb.complete()
201
+
202
+ elif btype == "image":
203
+ # Create/continue MESSAGE type with image
204
+ if current_type != MessageType.MESSAGE:
205
+ if current_mb:
206
+ current_mb.complete()
207
+ results.append(current_mb.get_message_data())
208
+ rb = ResponseBuilder()
209
+ current_mb = rb.create_message_builder(
210
+ role=role,
211
+ message_type=MessageType.MESSAGE,
212
+ )
213
+ # add meta field to store old id and name
214
+ current_mb.message.metadata = {
215
+ "original_id": msg.id,
216
+ "original_name": msg.name,
217
+ "metadata": msg.metadata,
218
+ }
219
+ current_type = MessageType.MESSAGE
220
+ cb = current_mb.create_content_builder(content_type="image")
221
+
222
+ if (
223
+ isinstance(block.get("source"), dict)
224
+ and block.get("source", {}).get("type") == "url"
225
+ ):
226
+ cb.set_image_url(block.get("source", {}).get("url"))
227
+
228
+ elif (
229
+ isinstance(block.get("source"), dict)
230
+ and block.get("source").get(
231
+ "type",
232
+ )
233
+ == "base64"
234
+ ):
235
+ media_type = block.get("source", {}).get(
236
+ "media_type",
237
+ "image/jpeg",
238
+ )
239
+ base64_data = block.get("source", {}).get("data", "")
240
+ url = f"data:{media_type};base64,{base64_data}"
241
+ cb.set_image_url(url)
242
+
243
+ cb.complete()
244
+
245
+ elif btype == "audio":
246
+ # Create/continue MESSAGE type with audio
247
+ if current_type != MessageType.MESSAGE:
248
+ if current_mb:
249
+ current_mb.complete()
250
+ results.append(current_mb.get_message_data())
251
+ rb = ResponseBuilder()
252
+ current_mb = rb.create_message_builder(
253
+ role=role,
254
+ message_type=MessageType.MESSAGE,
255
+ )
256
+ # add meta field to store old id and name
257
+ current_mb.message.metadata = {
258
+ "original_id": msg.id,
259
+ "original_name": msg.name,
260
+ "metadata": msg.metadata,
261
+ }
262
+ current_type = MessageType.MESSAGE
263
+ cb = current_mb.create_content_builder(content_type="audio")
264
+ # URLSource runtime check (dict with type == "url")
265
+ if (
266
+ isinstance(block.get("source"), dict)
267
+ and block.get("source", {}).get(
268
+ "type",
269
+ )
270
+ == "url"
271
+ ):
272
+ url = block.get("source", {}).get("url")
273
+ cb.content.data = url
274
+ try:
275
+ cb.content.format = urlparse(url).path.split(".")[-1]
276
+ except (AttributeError, IndexError, ValueError):
277
+ cb.content.format = None
278
+
279
+ # Base64Source runtime check (dict with type == "base64")
280
+ elif (
281
+ isinstance(block.get("source"), dict)
282
+ and block.get("source").get(
283
+ "type",
284
+ )
285
+ == "base64"
286
+ ):
287
+ media_type = block.get("source", {}).get(
288
+ "media_type",
289
+ )
290
+ base64_data = block.get("source", {}).get("data", "")
291
+ url = f"data:{media_type};base64,{base64_data}"
292
+
293
+ cb.content.data = url
294
+ cb.content.format = media_type
295
+
296
+ cb.complete()
297
+
298
+ else:
299
+ # Fallback to MESSAGE type
300
+ if current_type != MessageType.MESSAGE:
301
+ if current_mb:
302
+ current_mb.complete()
303
+ results.append(current_mb.get_message_data())
304
+ rb = ResponseBuilder()
305
+ current_mb = rb.create_message_builder(
306
+ role=role,
307
+ message_type=MessageType.MESSAGE,
308
+ )
309
+ # add meta field to store old id and name
310
+ current_mb.message.metadata = {
311
+ "original_id": msg.id,
312
+ "original_name": msg.name,
313
+ "metadata": msg.metadata,
314
+ }
315
+ current_type = MessageType.MESSAGE
316
+ cb = current_mb.create_content_builder(content_type="text")
317
+ cb.set_text(str(block))
318
+ cb.complete()
319
+
320
+ # finalize last open message builder
321
+ if current_mb:
322
+ current_mb.complete()
323
+ results.append(current_mb.get_message_data())
324
+
325
+ return results
326
+
327
+
328
+ def message_to_agentscope_msg(
329
+ messages: Union[Message, List[Message]],
330
+ ) -> Union[Msg, List[Msg]]:
331
+ """
332
+ Convert AgentScope runtime Message(s) to AgentScope Msg(s).
333
+
334
+ Args:
335
+ messages: A single AgentScope runtime Message or list of Messages.
336
+
337
+ Returns:
338
+ A single Msg object or a list of Msg objects.
339
+ """
340
+
341
+ def _convert_one(message: Message) -> Msg:
342
+ # Normalize role
343
+ if message.role == "tool":
344
+ role_label = "system" # AgentScope not support tool as role
345
+ else:
346
+ role_label = message.role or "assistant"
347
+
348
+ result = {
349
+ "name": getattr(message, "name", message.role),
350
+ "role": role_label,
351
+ }
352
+ _id = getattr(message, "id")
353
+
354
+ # if meta exists, prefer original id/name from meta
355
+ if hasattr(message, "metadata") and isinstance(message.metadata, dict):
356
+ if "original_id" in message.metadata:
357
+ _id = message.metadata["original_id"]
358
+ if "original_name" in message.metadata:
359
+ result["name"] = message.metadata["original_name"]
360
+ if "metadata" in message.metadata:
361
+ result["metadata"] = message.metadata["metadata"]
362
+
363
+ if message.type in (
364
+ MessageType.PLUGIN_CALL,
365
+ MessageType.FUNCTION_CALL,
366
+ ):
367
+ # convert PLUGIN_CALL, FUNCTION_CALL to ToolUseBlock
368
+ result["content"] = [
369
+ ToolUseBlock(
370
+ type="tool_use",
371
+ id=message.content[0].data["call_id"],
372
+ name=message.content[0].data.get("name"),
373
+ input=json.loads(message.content[0].data["arguments"]),
374
+ ),
375
+ ]
376
+ elif message.type in (
377
+ MessageType.PLUGIN_CALL_OUTPUT,
378
+ MessageType.FUNCTION_CALL_OUTPUT,
379
+ ):
380
+ # convert PLUGIN_CALL_OUTPUT, FUNCTION_CALL_OUTPUT to
381
+ # ToolResultBlock
382
+ blk = json.loads(message.content[0].data["output"])
383
+
384
+ def is_valid_block(obj):
385
+ return any(
386
+ matches_typed_dict_structure(obj, cls)
387
+ for cls in (TextBlock, ImageBlock, AudioBlock, VideoBlock)
388
+ )
389
+
390
+ if isinstance(blk, list):
391
+ if not all(is_valid_block(item) for item in blk):
392
+ blk = message.content[0].data["output"]
393
+ elif isinstance(blk, dict):
394
+ if not is_valid_block(blk):
395
+ blk = message.content[0].data["output"]
396
+ else:
397
+ blk = message.content[0].data["output"]
398
+
399
+ result["content"] = [
400
+ ToolResultBlock(
401
+ type="tool_result",
402
+ id=message.content[0].data["call_id"],
403
+ name=message.content[0].data.get("name"),
404
+ output=blk,
405
+ ),
406
+ ]
407
+ elif message.type in (MessageType.REASONING,):
408
+ result["content"] = [
409
+ ThinkingBlock(
410
+ type="thinking",
411
+ thinking=message.content[0].text,
412
+ ),
413
+ ]
414
+ else:
415
+ type_mapping = {
416
+ "text": (TextBlock, "text"),
417
+ "image": (ImageBlock, "image_url"),
418
+ "audio": (AudioBlock, "data"),
419
+ # "video": (VideoBlock, "video_url", True),
420
+ # TODO: support video
421
+ }
422
+
423
+ msg_content = []
424
+ for cnt in message.content:
425
+ cnt_type = cnt.type or "text"
426
+
427
+ if cnt_type not in type_mapping:
428
+ raise ValueError(f"Unsupported message type: {cnt_type}")
429
+
430
+ block_cls, attr_name = type_mapping[cnt_type]
431
+ value = getattr(cnt, attr_name)
432
+
433
+ if cnt_type == "image":
434
+ if value and value.startswith("data:"):
435
+ mediatype_part = value.split(";")[0].replace(
436
+ "data:",
437
+ "",
438
+ )
439
+ base64_data = value.split(",")[1]
440
+ base64_source = Base64Source(
441
+ type="base64",
442
+ media_type=mediatype_part,
443
+ data=base64_data,
444
+ )
445
+ msg_content.append(
446
+ block_cls(type=cnt_type, source=base64_source),
447
+ )
448
+ elif value:
449
+ url_source = URLSource(type="url", url=value)
450
+ msg_content.append(
451
+ block_cls(type=cnt_type, source=url_source),
452
+ )
453
+
454
+ elif cnt_type == "audio":
455
+ if (
456
+ value
457
+ and isinstance(value, str)
458
+ and value.startswith(
459
+ "data:",
460
+ )
461
+ ):
462
+ mediatype_part = value.split(";")[0].replace(
463
+ "data:",
464
+ "",
465
+ )
466
+ base64_data = value.split(",")[1]
467
+ base64_source = Base64Source(
468
+ type="base64",
469
+ media_type=mediatype_part,
470
+ data=base64_data,
471
+ )
472
+ msg_content.append(
473
+ block_cls(type=cnt_type, source=base64_source),
474
+ )
475
+ else:
476
+ parsed_url = urlparse(value)
477
+ if parsed_url.scheme and parsed_url.netloc:
478
+ url_source = URLSource(type="url", url=value)
479
+ msg_content.append(
480
+ block_cls(type=cnt_type, source=url_source),
481
+ )
482
+ else:
483
+ audio_extension = getattr(cnt, "format")
484
+ base64_source = Base64Source(
485
+ type="base64",
486
+ media_type=f"audio/{audio_extension}",
487
+ data=value,
488
+ )
489
+ msg_content.append(
490
+ block_cls(type=cnt_type, source=base64_source),
491
+ )
492
+ else:
493
+ msg_content.append(block_cls(type=cnt_type, text=value))
494
+
495
+ result["content"] = msg_content
496
+ _msg = Msg(**result)
497
+ _msg.id = _id
498
+ return _msg
499
+
500
+ # Handle single or list input
501
+ if isinstance(messages, Message):
502
+ return _convert_one(messages)
503
+ elif isinstance(messages, list):
504
+ converted_list = [_convert_one(m) for m in messages]
505
+
506
+ # Group by original_id
507
+ grouped = OrderedDict()
508
+ for msg, orig_msg in zip(messages, converted_list):
509
+ metadata = getattr(msg, "metadata")
510
+ if metadata:
511
+ orig_id = metadata.get(
512
+ "original_id",
513
+ orig_msg.id,
514
+ )
515
+ else:
516
+ # In case metadata is not provided, use the original id
517
+ orig_id = msg.id
518
+
519
+ if orig_id not in grouped:
520
+ agentscope_msg = Msg(
521
+ name=orig_msg.name,
522
+ role=orig_msg.role,
523
+ metadata=orig_msg.metadata,
524
+ content=list(orig_msg.content),
525
+ )
526
+ agentscope_msg.id = orig_id
527
+ grouped[orig_id] = agentscope_msg
528
+ else:
529
+ grouped[orig_id].content.extend(orig_msg.content)
530
+
531
+ return list(grouped.values())
532
+ else:
533
+ raise TypeError(
534
+ f"Expected Message or list[Message], got {type(messages)}",
535
+ )