From 03d1e13ebd6e63ec1390010a4564000de359772f Mon Sep 17 00:00:00 2001 From: yumoqing Date: Sun, 6 Sep 2026 20:56:49 +0800 Subject: [PATCH] =?UTF-8?q?fix(platform):=20media=E9=94=AE=E6=9D=A1?= =?UTF-8?q?=E4=BB=B6=E6=B8=B2=E6=9F=93=E4=B8=8B=E6=B2=89=E5=88=B0=E7=9C=9F?= =?UTF-8?q?=E5=AE=9E=E7=A4=BA=E4=BE=8B=E8=B7=AF=E5=BE=84(=5Ftpl=5Ffrom=5Fe?= =?UTF-8?q?xample)=E2=80=94=E2=80=94=E5=9B=9B=E7=A7=8D=E9=94=AE=E4=BD=8D+?= =?UTF-8?q?=E5=B9=B2=E8=B7=91=E8=A6=86=E7=9B=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 上一提交只修了合成兜底路径;用权威文档示例重新apply会走真实示例路径, 该路径零媒体同样发出"media": []空数组。本次统一下沉: - 新增 _media_any_expr(mps):「任一媒体有值」Jinja布尔(字符串/数组归一), 合成/真实两路径共用 - _tpl_from_example: media数组token记录(键名,mps),替换阶段按json.dumps 逗号位置分三模式(首/中键带尾逗号,末键吃前逗号,独键直包)整键条件化 - 单测22项全绿:四种键位×(零媒体省略/三模态/字符串形态/干跑)+合成路径 +schema(媒体非必填/prompt必填/multiple/ratio六值枚举) --- pipeline_platform/platform_ability.py | 50 +++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/pipeline_platform/platform_ability.py b/pipeline_platform/platform_ability.py index f832eae..850797a 100644 --- a/pipeline_platform/platform_ability.py +++ b/pipeline_platform/platform_ability.py @@ -1032,6 +1032,20 @@ def _is_media_array(lst, capability): return True +def _media_any_expr(mps): + """「任一媒体参数有值」Jinja 布尔表达式(字符串/数组两形态归一)。 + + 2026-09-06 用户纠正后引入:全能参考模型(wan3.0 类 r2v)文档明示 + 「prompt 和 media 必填其一」「文生视频不传入任何媒体文件」——零媒体是 + 合法 t2v 形态且文档示例**不带 media 键**。无条件渲染会发出 "media": [] + 空数组(API 未承诺接受,不能赌),故 media 键必须条件渲染:有媒体才 + 输出键,零媒体整个省略。真实示例路径与合成兜底路径共用本表达式。 + """ + return ' or '.join( + '(params.%s if params.%s is not string and params.%s else ' + '([params.%s] if params.%s is string else []))' % ((mp,) * 5) for mp in mps) + + def _media_array_expr(elements): """媒体数组示例 → Jinja 动态组装表达式(三数组契约 image_files/ audio_files/video_files,值为字符串或数组均可)。 @@ -1094,6 +1108,7 @@ def _tpl_from_example(example, capability): 返回 (模板串, media_params, biz_params, used_example)。 """ tokens = {} + cond_tokens = {} media_params, biz_params = [], [] used_example = isinstance(example, dict) and bool(example) tree = example if used_example else _example_to_nested(None) @@ -1111,7 +1126,14 @@ def _tpl_from_example(example, capability): for mp in mps: if mp not in media_params: media_params.append(mp) - return _put(expr) + tk = _put(expr) + # media 键条件渲染(2026-09-06 用户纠正):零媒体=合法 t2v + # 形态且文档示例不带 media 键,无条件渲染发出 "media": [] + # 空数组(API 未承诺接受,不能赌)。记录 (token→键名/媒体参数) + # 供替换阶段整键包 Jinja 条件。仅 dict 下的数组键可条件化。 + if parent_key: + cond_tokens[tk] = (parent_key, mps) + return tk return [walk(v, parent_key, sib_type) for v in node] if not isinstance(node, dict): return node @@ -1150,6 +1172,25 @@ def _tpl_from_example(example, capability): body = walk(tree) s = json.dumps(body, ensure_ascii=False) for k, expr in tokens.items(): + if k in cond_tokens: + # media 键条件渲染:零媒体时整键省略(对齐文档 t2v 形态)。 + # json.dumps 分隔符固定 ", ",按键在对象中的位置分三模式吃逗号: + # 首/中键(后随 ", ")→ 条件段自带尾逗号 + # 末键(前缀 ", ") → 条件段吃掉前逗号 + # 独键 → 直接包 + keyname, mps = cond_tokens[k] + anyexpr = _media_any_expr(mps) + seg_body = '"%s": %s' % (keyname, expr) + pat_first = '"%s": "%s", ' % (keyname, k) + pat_last = ', "%s": "%s"' % (keyname, k) + pat_only = '"%s": "%s"' % (keyname, k) + if pat_first in s: + s = s.replace(pat_first, ' {%% if %s %%}%s, {%% endif %%}' % (anyexpr, seg_body)) + elif pat_last in s: + s = s.replace(pat_last, '{%% if %s %%}, %s{%% endif %%}' % (anyexpr, seg_body)) + elif pat_only in s: + s = s.replace(pat_only, ' {%% if %s %%}%s{%% endif %%}' % (anyexpr, seg_body)) + continue s = s.replace('"%s"' % k, expr) return s, media_params, biz_params, used_example @@ -1277,11 +1318,8 @@ def _gen_templates(protocol: str, capability: str, spec: dict) -> dict: # 文件」——零媒体是合法 t2v 形态,且文档 t2v 示例**不带 media # 键**;无条件渲染会发出 "media": [] 空数组(API 未承诺接受, # 不能赌)。有媒体才渲染 media 键,零媒体整个省略。 - anyexpr = ' or '.join( - '(params.%s if params.%s is not string and params.%s else ' - '([params.%s] if params.%s is string else []))' - % ((mp,) * 5) for mp in mps) - # 空格不可省:media_seg 以 {% 开头,紧跟 input 的 { 会拼成 + anyexpr = _media_any_expr(mps) + # 前导空格不可省:段以 {% 开头,紧跟 input 的 { 会拼成 # "{{%" —— Jinja 词法把 {{ 当变量表达式开始 → unexpected '%' # (2026-09-06 单测实抓)。JSON 空白无副作用。 media_seg = (' {%% if %s %%}"media": %s, {%% endif %%}'