Skip to content

Commit c61ead9

Browse files
committed
refactor(super_agent): Update config structure and parsing logic
This change updates the internal configuration structure to include nested `headers`, `body`, and `forwardedProps`. It also refactors the parsing logic in `_flatten_protocol_config()` to handle both old and new formats seamlessly. The unit tests have been updated accordingly. Co-developed-by: Aone Copilot <noreply@alibaba-inc.com> Signed-off-by: Sodawyx <sodawyx@126.com>
1 parent c8847e0 commit c61ead9

3 files changed

Lines changed: 178 additions & 45 deletions

File tree

agentrun/super_agent/api/control.py

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -337,9 +337,12 @@ def _business_fields_from_args(
337337
def _build_protocol_settings_config(
338338
*, name: str, business: Dict[str, Any]
339339
) -> str:
340-
"""构造 ``protocolSettings[0].config`` 的 JSON 字符串."""
341-
cfg_dict: Dict[str, Any] = {
342-
"path": SUPER_AGENT_INVOKE_PATH,
340+
"""构造 ``protocolSettings[0].config`` 的 JSON 字符串.
341+
342+
新结构: 顶层 ``path`` / ``headers`` / ``body``, 业务字段收拢到
343+
``body.forwardedProps`` (开放字典, 语义 "any, merge")。
344+
"""
345+
forwarded_props: Dict[str, Any] = {
343346
"prompt": business.get("prompt"),
344347
"agents": business.get("agents") or [],
345348
"tools": business.get("tools") or [],
@@ -350,6 +353,11 @@ def _build_protocol_settings_config(
350353
"modelName": business.get("modelName"),
351354
"metadata": {"agentRuntimeName": name},
352355
}
356+
cfg_dict: Dict[str, Any] = {
357+
"path": SUPER_AGENT_INVOKE_PATH,
358+
"headers": {},
359+
"body": {"forwardedProps": forwarded_props},
360+
}
353361
return json.dumps(cfg_dict, ensure_ascii=False)
354362

355363

@@ -483,10 +491,31 @@ def is_super_agent(rt: AgentRuntime) -> bool:
483491
return first.get("type") == SUPER_AGENT_PROTOCOL_TYPE
484492

485493

494+
def _flatten_protocol_config(cfg: Any) -> Dict[str, Any]:
495+
"""把 ``protocolSettings[0].config`` 解析结果压平为扁平业务字段 dict.
496+
497+
兼容两种物理布局:
498+
- 新: ``{"path": ..., "headers": ..., "body": {"forwardedProps": {...}}}``
499+
- 旧: 业务字段直接在根 (历史 AgentRuntime, 迁移前写入)
500+
501+
两种结构都返回扁平的业务字段 dict, 上游
502+
:func:`from_agent_runtime` 无需感知物理布局差异。
503+
"""
504+
if not isinstance(cfg, dict):
505+
return {}
506+
body = cfg.get("body")
507+
if isinstance(body, dict):
508+
forwarded = body.get("forwardedProps")
509+
if isinstance(forwarded, dict):
510+
return forwarded
511+
return cfg
512+
513+
486514
def parse_super_agent_config(rt: AgentRuntime) -> Dict[str, Any]:
487-
"""反解 ``protocolSettings[0].config`` 为业务字段 dict.
515+
"""反解 ``protocolSettings[0].config`` 为扁平业务字段 dict.
488516
489517
如果 config 缺失或非法 JSON, 返回空 dict (不抛异常)。
518+
新旧嵌套布局由 :func:`_flatten_protocol_config` 统一拍平。
490519
"""
491520
pc_dict = _extract_protocol_configuration(rt)
492521
if not pc_dict:
@@ -499,13 +528,13 @@ def parse_super_agent_config(rt: AgentRuntime) -> Dict[str, Any]:
499528
if not raw_config:
500529
return {}
501530
if isinstance(raw_config, dict):
502-
return raw_config
531+
return _flatten_protocol_config(raw_config)
503532
if isinstance(raw_config, str):
504533
try:
505534
parsed = json.loads(raw_config)
506-
return parsed if isinstance(parsed, dict) else {}
507535
except (TypeError, ValueError):
508536
return {}
537+
return _flatten_protocol_config(parsed)
509538
return {}
510539

511540

tests/unittests/super_agent/test_client.py

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -60,15 +60,20 @@ def _fake_rt(
6060
"""Build a minimal AgentRuntime-like object for ``from_agent_runtime``."""
6161
cfg_dict = {
6262
"path": "/invoke",
63-
"prompt": prompt,
64-
"agents": [],
65-
"tools": tools if tools is not None else [],
66-
"skills": [],
67-
"sandboxes": [],
68-
"workspaces": [],
69-
"modelServiceName": None,
70-
"modelName": None,
71-
"metadata": {"agentRuntimeName": name},
63+
"headers": {},
64+
"body": {
65+
"forwardedProps": {
66+
"prompt": prompt,
67+
"agents": [],
68+
"tools": tools if tools is not None else [],
69+
"skills": [],
70+
"sandboxes": [],
71+
"workspaces": [],
72+
"modelServiceName": None,
73+
"modelName": None,
74+
"metadata": {"agentRuntimeName": name},
75+
}
76+
},
7277
}
7378
pc = {
7479
"type": protocol_type,
@@ -124,7 +129,7 @@ async def _create_async(dara_input, config=None):
124129
first = pc.protocol_settings[0]
125130
assert first.type == SUPER_AGENT_PROTOCOL_TYPE
126131
cfg_json = json.loads(first.config)
127-
assert cfg_json["prompt"] == "new"
132+
assert cfg_json["body"]["forwardedProps"]["prompt"] == "new"
128133
assert agent.name == "alpha"
129134

130135

@@ -206,8 +211,9 @@ async def _update(agent_id, dara_input, config=None):
206211
cfg_json = json.loads(
207212
captured["dara"].protocol_configuration.protocol_settings[0].config
208213
)
209-
assert cfg_json["prompt"] == "new"
210-
assert cfg_json["tools"] == ["t1"]
214+
forwarded = cfg_json["body"]["forwardedProps"]
215+
assert forwarded["prompt"] == "new"
216+
assert forwarded["tools"] == ["t1"]
211217

212218

213219
async def test_update_async_explicit_none_clears_field():
@@ -238,7 +244,7 @@ async def _update(agent_id, dara_input, config=None):
238244
cfg_json = json.loads(
239245
captured["dara"].protocol_configuration.protocol_settings[0].config
240246
)
241-
assert cfg_json["prompt"] is None
247+
assert cfg_json["body"]["forwardedProps"]["prompt"] is None
242248

243249

244250
async def test_update_async_multiple_fields():
@@ -271,8 +277,9 @@ async def _update(agent_id, dara_input, config=None):
271277
cfg_json = json.loads(
272278
captured["dara"].protocol_configuration.protocol_settings[0].config
273279
)
274-
assert cfg_json["prompt"] == "p"
275-
assert cfg_json["tools"] == ["a", "b"]
280+
forwarded = cfg_json["body"]["forwardedProps"]
281+
assert forwarded["prompt"] == "p"
282+
assert forwarded["tools"] == ["a", "b"]
276283
assert captured["dara"].description == "d"
277284

278285

tests/unittests/super_agent/test_control.py

Lines changed: 121 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,10 @@ def test_to_create_input_minimal():
116116
assert len(settings) == 1
117117
cfg_dict = json.loads(settings[0]["config"])
118118
assert cfg_dict["path"] == "/invoke"
119-
assert cfg_dict["agents"] == []
120-
assert cfg_dict["metadata"] == {"agentRuntimeName": "alpha"}
119+
assert cfg_dict["headers"] == {}
120+
forwarded = cfg_dict["body"]["forwardedProps"]
121+
assert forwarded["agents"] == []
122+
assert forwarded["metadata"] == {"agentRuntimeName": "alpha"}
121123

122124

123125
def test_to_create_input_full():
@@ -136,14 +138,17 @@ def test_to_create_input_full():
136138
)
137139
pc_dict = inp.model_dump()["protocolConfiguration"]
138140
settings_cfg = json.loads(pc_dict["protocolSettings"][0]["config"])
139-
assert settings_cfg["prompt"] == "hello"
140-
assert settings_cfg["agents"] == ["a1"]
141-
assert settings_cfg["tools"] == ["t1", "t2"]
142-
assert settings_cfg["skills"] == ["s1"]
143-
assert settings_cfg["sandboxes"] == ["sb1"]
144-
assert settings_cfg["workspaces"] == ["ws1"]
145-
assert settings_cfg["modelServiceName"] == "foo"
146-
assert settings_cfg["modelName"] == "bar"
141+
assert settings_cfg["path"] == "/invoke"
142+
assert settings_cfg["headers"] == {}
143+
forwarded = settings_cfg["body"]["forwardedProps"]
144+
assert forwarded["prompt"] == "hello"
145+
assert forwarded["agents"] == ["a1"]
146+
assert forwarded["tools"] == ["t1", "t2"]
147+
assert forwarded["skills"] == ["s1"]
148+
assert forwarded["sandboxes"] == ["sb1"]
149+
assert forwarded["workspaces"] == ["ws1"]
150+
assert forwarded["modelServiceName"] == "foo"
151+
assert forwarded["modelName"] == "bar"
147152

148153

149154
def test_to_create_input_tags_fixed():
@@ -158,7 +163,9 @@ def test_to_create_input_metadata_only_agent_runtime_name():
158163
settings_cfg = json.loads(
159164
inp.protocol_configuration.protocol_settings[0]["config"]
160165
)
161-
assert settings_cfg["metadata"] == {"agentRuntimeName": "d"}
166+
assert settings_cfg["body"]["forwardedProps"]["metadata"] == {
167+
"agentRuntimeName": "d"
168+
}
162169

163170

164171
def test_to_create_input_uses_pre_environment_endpoint():
@@ -194,15 +201,21 @@ def _make_rt(**kwargs):
194201

195202
def test_from_agent_runtime():
196203
config_json = json.dumps({
197-
"prompt": "hi",
198-
"agents": ["a"],
199-
"tools": ["t"],
200-
"skills": [],
201-
"sandboxes": [],
202-
"workspaces": [],
203-
"modelServiceName": "svc",
204-
"modelName": "mod",
205-
"metadata": {"agentRuntimeName": "foo"},
204+
"path": "/invoke",
205+
"headers": {},
206+
"body": {
207+
"forwardedProps": {
208+
"prompt": "hi",
209+
"agents": ["a"],
210+
"tools": ["t"],
211+
"skills": [],
212+
"sandboxes": [],
213+
"workspaces": [],
214+
"modelServiceName": "svc",
215+
"modelName": "mod",
216+
"metadata": {"agentRuntimeName": "foo"},
217+
}
218+
},
206219
})
207220
pc = {
208221
"type": SUPER_AGENT_PROTOCOL_TYPE,
@@ -227,6 +240,89 @@ def test_from_agent_runtime():
227240
)
228241

229242

243+
def test_from_agent_runtime_legacy_flat_config():
244+
"""旧结构兼容: config 是扁平 dict, 业务字段直接在根 (历史 AgentRuntime)."""
245+
config_json = json.dumps({
246+
"prompt": "legacy",
247+
"agents": ["la"],
248+
"tools": ["lt"],
249+
"skills": [],
250+
"sandboxes": [],
251+
"workspaces": [],
252+
"modelServiceName": "legacy-svc",
253+
"modelName": "legacy-mod",
254+
"metadata": {"agentRuntimeName": "legacy"},
255+
})
256+
pc = {
257+
"type": SUPER_AGENT_PROTOCOL_TYPE,
258+
"protocolSettings": [{
259+
"type": SUPER_AGENT_PROTOCOL_TYPE,
260+
"config": config_json,
261+
"name": "legacy",
262+
"path": "/invoke",
263+
}],
264+
"externalEndpoint": "https://x.com/super-agents/__SUPER_AGENT__",
265+
}
266+
rt = _make_rt(agent_runtime_name="legacy", protocol_configuration=pc)
267+
agent = from_agent_runtime(rt)
268+
assert agent.prompt == "legacy"
269+
assert agent.agents == ["la"]
270+
assert agent.model_service_name == "legacy-svc"
271+
272+
273+
def test_parse_super_agent_config_dict_config_new_structure():
274+
"""config 已经是 dict (非字符串) 时也能拍平."""
275+
pc = {
276+
"type": SUPER_AGENT_PROTOCOL_TYPE,
277+
"protocolSettings": [{
278+
"type": SUPER_AGENT_PROTOCOL_TYPE,
279+
"config": {
280+
"path": "/invoke",
281+
"headers": {},
282+
"body": {
283+
"forwardedProps": {
284+
"prompt": "p",
285+
"agents": [],
286+
}
287+
},
288+
},
289+
}],
290+
}
291+
business = parse_super_agent_config(_make_rt(protocol_configuration=pc))
292+
assert business["prompt"] == "p"
293+
assert business["agents"] == []
294+
295+
296+
def test_parse_super_agent_config_dict_config_legacy_flat():
297+
"""config 是 dict + 旧扁平结构时走 fallback, 原样返回."""
298+
pc = {
299+
"type": SUPER_AGENT_PROTOCOL_TYPE,
300+
"protocolSettings": [{
301+
"type": SUPER_AGENT_PROTOCOL_TYPE,
302+
"config": {"prompt": "legacy-dict", "agents": ["la"]},
303+
}],
304+
}
305+
business = parse_super_agent_config(_make_rt(protocol_configuration=pc))
306+
assert business == {"prompt": "legacy-dict", "agents": ["la"]}
307+
308+
309+
def test_flatten_protocol_config_non_dict_returns_empty():
310+
"""非 dict 输入 (防御分支) 返回空 dict."""
311+
from agentrun.super_agent.api.control import _flatten_protocol_config
312+
313+
assert _flatten_protocol_config(None) == {}
314+
assert _flatten_protocol_config("not-a-dict") == {}
315+
assert _flatten_protocol_config([1, 2, 3]) == {}
316+
317+
318+
def test_flatten_protocol_config_body_without_forwarded_props():
319+
"""body 存在但缺 forwardedProps → fallback 到整个 cfg (旧结构)."""
320+
from agentrun.super_agent.api.control import _flatten_protocol_config
321+
322+
cfg = {"body": {"other": "x"}, "prompt": "flat"}
323+
assert _flatten_protocol_config(cfg) == cfg
324+
325+
230326
def test_is_super_agent_true():
231327
pc = {
232328
"type": SUPER_AGENT_PROTOCOL_TYPE,
@@ -280,10 +376,11 @@ def test_to_update_input_full_protocol_replace():
280376
assert inp.description == "new"
281377
settings = inp.protocol_configuration.protocol_settings
282378
assert len(settings) == 1
283-
assert (
284-
json.loads(settings[0]["config"])["metadata"]["agentRuntimeName"]
285-
== "alpha"
286-
)
379+
cfg_json = json.loads(settings[0]["config"])
380+
forwarded = cfg_json["body"]["forwardedProps"]
381+
assert forwarded["metadata"]["agentRuntimeName"] == "alpha"
382+
assert forwarded["prompt"] == "p"
383+
assert forwarded["tools"] == ["t"]
287384

288385

289386
# ─── Dara ListAgentRuntimesRequest tags 补丁 ──────────────────

0 commit comments

Comments
 (0)