fix(platform): DashScope同步生成模板生成器(2026-09-08 qwen-image-plus真图验证后根治)
数据手工修通后回填生成器,让后续DashScope同步模型自动配对不用手改:
- _dashscope_envelope:SDK kwargs形态({model,messages,size...})→原生HTTP信封
({model,input:{messages},parameters:{...}}),平铺体提交必400 InvalidParameter
(实测"messages parameter invalid");代码级确定性归一不靠提取LLM自觉
- _gen_templates对dashscope_sync/async协议先过信封归一
- _resp_tpl_for协议感知:dashscope_sync产物路径output.choices[0].message.content[0]
.{image|video|audio}(实测形态),文档response示例给output.*路径则优先;同步命名
空间无task_id/status只引用output/usage/request(StrictUndefined防崩)
- 信封归一纯函数单测4例PASS
This commit is contained in:
parent
1bc7413c1f
commit
a9cac99c52
@ -1338,6 +1338,33 @@ def _media_array_expr(elements):
|
||||
return ''.join(segs), params
|
||||
|
||||
|
||||
def _dashscope_envelope(example):
|
||||
"""DashScope SDK kwargs 形态 → 原生 HTTP 信封归一(2026-09-08 实测根因)。
|
||||
|
||||
文档常只给 Python SDK 示例(MultiModalConversation.call(messages=...,
|
||||
size=...)),提取 LLM 照抄成平铺 {model, messages, size, watermark...};
|
||||
但原生 HTTP 端点要求 {"model", "input": {"messages": [...]},
|
||||
"parameters": {...}}——平铺体提交必 400 InvalidParameter
|
||||
(qwen-image-plus 实测「messages parameter invalid」)。
|
||||
代码级确定性归一(不靠提取 LLM 自觉):顶层有 messages 且无 input 信封
|
||||
时,messages 进 input,除 model/input/parameters 外的业务键全进 parameters。
|
||||
"""
|
||||
if not isinstance(example, dict) or not example:
|
||||
return example
|
||||
if 'input' in example or 'parameters' in example:
|
||||
return example # 已是信封形态(curl 示例),不动
|
||||
msgs = example.get('messages')
|
||||
if not isinstance(msgs, list):
|
||||
return example # 非 SDK kwargs 形态,不动
|
||||
out = {'model': example.get('model', '')}
|
||||
out['input'] = {'messages': msgs}
|
||||
params = {k: v for k, v in example.items()
|
||||
if k not in ('model', 'messages')}
|
||||
if params:
|
||||
out['parameters'] = params
|
||||
return out
|
||||
|
||||
|
||||
def _tpl_from_example(example, capability):
|
||||
"""文档请求示例 → Jinja2 请求体模板(结构逐字保留,值换成运行时变量)。
|
||||
|
||||
@ -1493,6 +1520,25 @@ def _resp_tpl_for(capability, spec):
|
||||
if not field and hits:
|
||||
field = hits[0]
|
||||
note = ''
|
||||
proto = str(spec.get('protocol') or '').strip()
|
||||
if proto == 'dashscope_sync':
|
||||
# DashScope 同步生成实测响应形态(2026-09-08 qwen-image-plus 真图验证):
|
||||
# output.choices[0].message.content[0].{image|video|audio}
|
||||
# 同步路径渲染命名空间无 task_id/status(StrictUndefined 会崩),
|
||||
# 模板只引用 output/usage/request 三个必有变量。
|
||||
# 优先级:文档响应示例给出的 output.* 产物路径 > 实测兜底形态。
|
||||
if field and field.startswith('output.'):
|
||||
path_expr = field
|
||||
note = '' # 文档确证,无需兜底声明
|
||||
else:
|
||||
seg_key = {'image': 'image', 'video': 'video', 'audio': 'audio'}.get(outkey, outkey)
|
||||
path_expr = 'output.choices[0].message.content[0].%s' % seg_key
|
||||
if not field:
|
||||
note = ('产物路径按 DashScope 同步生成实测形态生成(%s),'
|
||||
'文档未提供响应示例,如上游形态不同需修正' % path_expr)
|
||||
resp = ('{"%s": "{{ downloadfile2url(request, %s) }}", '
|
||||
'"usage": {{ json.dumps(usage) }}}' % (outkey, path_expr))
|
||||
return resp, note
|
||||
if not field:
|
||||
field = _MEDIA_OUT_FIELD.get(outkey, 'result_url')
|
||||
note = ('产物字段名「%s」按供应商惯例兜底(文档未提供响应示例或示例中无 URL 字段),'
|
||||
@ -1563,6 +1609,10 @@ def _gen_templates(protocol: str, capability: str, spec: dict) -> dict:
|
||||
example = _example_to_nested(spec.get("request_fields"))
|
||||
notes.append("文档未提供完整请求示例,模板按 request_fields 字段路径生成,"
|
||||
"业务参数无默认值(调用时必须显式传入),建议人工核对")
|
||||
# DashScope SDK kwargs → 原生 HTTP 信封归一(2026-09-08 qwen-image-plus 实测:
|
||||
# 平铺体提交 400 InvalidParameter,必须 input.messages + parameters 信封)
|
||||
if protocol in ('dashscope_sync', 'dashscope_async'):
|
||||
example = _dashscope_envelope(example)
|
||||
req, media_params, biz_params, _used = _tpl_from_example(example, capability)
|
||||
if capability in _MEDIA_CAPS:
|
||||
resp, rnote = _resp_tpl_for(capability, spec)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user