1414from __future__ import annotations
1515
1616import json
17- from typing import Any , Dict , List , Optional
17+ from typing import Any , Dict , List , Optional , TYPE_CHECKING
1818from urllib .parse import urlparse , urlunparse
1919
20+ if TYPE_CHECKING :
21+ from agentrun .super_agent .agent import SuperAgent
22+
2023from alibabacloud_agentrun20250910 .client import Client as _DaraClient
2124from alibabacloud_agentrun20250910 .models import (
2225 CreateAgentRuntimeInput as _DaraCreateAgentRuntimeInput ,
6063
6164_RAM_DATA_DOMAINS = ("agentrun-data" , "funagent-data-pre" )
6265
66+ # SUPER_AGENT 不跑用户 container/code, 但服务端强制要求 artifact/container_configuration 非空,
67+ # 这里给一个占位镜像地址即可。region 取杭州仅为了格式合法, 服务端不会实际 pull。
68+ _PLACEHOLDER_IMAGE = (
69+ "registry.cn-hangzhou.aliyuncs.com/agentrun/super-agent-placeholder:v1"
70+ )
71+
6372
6473# ─── URL 工具 ──────────────────────────────────────────
6574
@@ -143,14 +152,23 @@ def model_dump(self, **kwargs: Any) -> Dict[str, Any]:
143152 return super ().model_dump (** kwargs )
144153
145154
146- # ─── Dara 模型猴补丁 ──────────────────────────────────────
147- # Dara 的 ``ProtocolConfiguration`` 当前版本没有 ``externalEndpoint`` 字段;
148- # ``AgentRuntimeClient.create_async/update_async`` 内部做
149- # ``CreateAgentRuntimeInput().from_map(pydantic.model_dump())`` 的 roundtrip,
150- # 会在 Dara 层丢失此字段。这里做一次加性 patch: 仅追加读写 ``externalEndpoint``,
151- # 不改变任何现有字段行为, 用模块级哨兵属性保证幂等。
155+ # ─── Dara 模型/客户端猴补丁 ──────────────────────────────────────
156+ # 当前版 Dara SDK 缺 ``ProtocolConfiguration.externalEndpoint`` 和
157+ # ``CreateAgentRuntimeInput/UpdateAgentRuntimeInput/ListAgentRuntimesRequest.tags``
158+ # 字段, 会在 Pydantic ↔ Dara ``from_map / to_map`` roundtrip 中静默丢失; 且
159+ # ``Client.list_agent_runtimes_with_options{,_async}`` 不会把 ``tags`` 写到 query。
160+ #
161+ # 所有补丁都延迟到 ``SuperAgentClient`` 实例化时 (见
162+ # ``ensure_super_agent_patches_applied``) 才触发, 避免仅 import 本模块的调用方
163+ # 被动承担全局副作用。补丁本身用哨兵属性保证幂等, 重复调用安全。
164+ # TODO: 等 Dara SDK 原生支持后删除。
165+
166+
167+ def _patch_dara_protocol_configuration () -> None :
168+ """补齐 ``ProtocolConfiguration.externalEndpoint`` 的 from_map/to_map 读写."""
169+ if getattr (_DaraProtocolConfiguration , "_super_agent_patched" , False ):
170+ return
152171
153- if not getattr (_DaraProtocolConfiguration , "_super_agent_patched" , False ):
154172 _orig_to_map = _DaraProtocolConfiguration .to_map
155173 _orig_from_map = _DaraProtocolConfiguration .from_map
156174
@@ -174,10 +192,8 @@ def _patched_from_map(
174192 _DaraProtocolConfiguration ._super_agent_patched = True # type: ignore[attr-defined]
175193
176194
177- # Dara 的 ``CreateAgentRuntimeInput`` / ``UpdateAgentRuntimeInput`` 当前版本没有
178- # ``tags`` 字段, 与 ``ProtocolConfiguration`` 同理会在 Pydantic → Dara 的 roundtrip
179- # 中被静默丢弃. 这里沿用同款加性 patch, 只补齐 ``tags`` 字段的读写.
180195def _patch_dara_tags (cls : Any ) -> None :
196+ """给 Dara model 补齐 ``tags`` 字段的 from_map/to_map 读写."""
181197 if getattr (cls , "_super_agent_tags_patched" , False ):
182198 return
183199 _orig_to_map = cls .to_map
@@ -201,22 +217,6 @@ def _patched_from_map(self: Any, m: Optional[Dict[str, Any]] = None) -> Any:
201217 cls ._super_agent_tags_patched = True # type: ignore[attr-defined]
202218
203219
204- _patch_dara_tags (_DaraCreateAgentRuntimeInput )
205- _patch_dara_tags (_DaraUpdateAgentRuntimeInput )
206- # ``ListAgentRuntimesRequest`` 同样没有 ``tags`` 字段: 补上 from_map/to_map 以保留
207- # 属性; 真正让服务端生效的查询参数注入由下面的 client 级补丁完成。
208- _patch_dara_tags (_DaraListAgentRuntimesRequest )
209-
210-
211- # ─── Dara 客户端猴补丁: list 请求 query 注入 tags ───────────────
212- # 现版 Dara ``Client.list_agent_runtimes_with_options`` 不读 ``request.tags``
213- # 构造 query, 导致即便 Pydantic 侧把 tags 传下来, 服务端也收不到。这里一次性
214- # 包裹同步 / 异步两个方法: 若 request 带有 ``tags`` 就在底层 ``call_api`` 调用
215- # 前把 ``tags`` (列表 → 逗号分隔) 追加到 ``req.query``。
216- # 每个 API 调用都会 ``_get_client()`` 新建 ``Client`` 实例, 实例属性级别的替换
217- # 在并发下是安全的。
218-
219-
220220def _tags_query_value (tags : Any ) -> Optional [str ]:
221221 if tags is None :
222222 return None
@@ -228,6 +228,13 @@ def _tags_query_value(tags: Any) -> Optional[str]:
228228
229229
230230def _patch_dara_client_list_tags () -> None :
231+ """包裹 ``Client.list_agent_runtimes_with_options{,_async}``: 若 request 带
232+ ``tags`` 就在底层 ``call_api`` 调用前把 ``tags`` (列表 → 逗号分隔) 追加到
233+ ``req.query``。
234+
235+ 每次 API 调用由 ``_get_client()`` 新建 ``Client`` 实例, 实例属性级别的
236+ ``self.call_api = _injecting`` 替换在并发下是安全的。
237+ """
231238 if getattr (_DaraClient , "_super_agent_list_tags_patched" , False ):
232239 return
233240
@@ -285,7 +292,20 @@ async def _injecting(params: Any, req: Any, rt: Any) -> Any:
285292 _DaraClient ._super_agent_list_tags_patched = True # type: ignore[attr-defined]
286293
287294
288- _patch_dara_client_list_tags ()
295+ def ensure_super_agent_patches_applied () -> None :
296+ """按需应用全部 Dara SDK 兼容补丁 (幂等)。
297+
298+ 由 ``SuperAgentClient.__init__`` 调用。如果调用方直接使用
299+ ``to_create_input`` / ``to_update_input`` 并自己构造 ``CreateAgentRuntimeInput``
300+ / ``ListAgentRuntimesRequest``, 也应在 Pydantic → Dara 转换前调用一次本函数。
301+ """
302+ _patch_dara_protocol_configuration ()
303+ _patch_dara_tags (_DaraCreateAgentRuntimeInput )
304+ _patch_dara_tags (_DaraUpdateAgentRuntimeInput )
305+ # ``ListAgentRuntimesRequest`` 补齐 from_map/to_map 保留属性; 真正让服务端
306+ # 生效的 query 注入由 ``_patch_dara_client_list_tags`` 完成。
307+ _patch_dara_tags (_DaraListAgentRuntimesRequest )
308+ _patch_dara_client_list_tags ()
289309
290310
291311# ─── AgentRuntime ↔ SuperAgent 转换 ────────────────────────
@@ -392,9 +412,7 @@ def to_create_input(
392412 external_agent_endpoint_url = build_super_agent_endpoint (cfg ),
393413 # 占位 artifact: SUPER_AGENT 不跑用户 container/code, 但服务端要求非空。
394414 artifact_type = AgentRuntimeArtifact .CONTAINER ,
395- container_configuration = AgentRuntimeContainer (
396- image = "registry.cn-hangzhou.aliyuncs.com/agentrun/super-agent-placeholder:v1"
397- ),
415+ container_configuration = AgentRuntimeContainer (image = _PLACEHOLDER_IMAGE ),
398416 network_configuration = NetworkConfig (network_mode = NetworkMode .PUBLIC ),
399417 )
400418
@@ -425,9 +443,7 @@ def to_update_input(
425443 external_agent_endpoint_url = build_super_agent_endpoint (cfg ),
426444 # 占位 artifact: SUPER_AGENT 不跑用户 container/code, 但服务端要求非空。
427445 artifact_type = AgentRuntimeArtifact .CONTAINER ,
428- container_configuration = AgentRuntimeContainer (
429- image = "registry.cn-hangzhou.aliyuncs.com/agentrun/super-agent-placeholder:v1"
430- ),
446+ container_configuration = AgentRuntimeContainer (image = _PLACEHOLDER_IMAGE ),
431447 network_configuration = NetworkConfig (network_mode = NetworkMode .PUBLIC ),
432448 )
433449
@@ -502,7 +518,7 @@ def _get_external_endpoint(rt: AgentRuntime) -> str:
502518 )
503519
504520
505- def from_agent_runtime (rt : AgentRuntime ) -> "SuperAgent" : # noqa: F821
521+ def from_agent_runtime (rt : AgentRuntime ) -> "SuperAgent" :
506522 """反解 AgentRuntime → SuperAgent 实例 (不注入 ``_client``)."""
507523 # 延迟导入避免循环
508524 from agentrun .super_agent .agent import SuperAgent
@@ -547,4 +563,5 @@ def from_agent_runtime(rt: AgentRuntime) -> "SuperAgent": # noqa: F821
547563 "from_agent_runtime" ,
548564 "is_super_agent" ,
549565 "parse_super_agent_config" ,
566+ "ensure_super_agent_patches_applied" ,
550567]
0 commit comments