Files
patbond-ai/app/providers/volcengine/capabilities.py
T
lixi d4213a6ece
CI / ai-gates (push) Successful in 1m16s
fix(ai): capabilities 嵌套 if 合并(SIM102)——修 8f1d1bc 的 lint 红
上一条提交时我先 commit 后看门禁输出,lint 已红却已入库。四道门禁现逐条复验
全绿(secret-scan / format / lint / pytest 退出码均 0,123 测试)。

教训与 db87d66 同源:门禁必须在 commit **之前**逐条读对退出码。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-20 15:13:23 +08:00

126 lines
4.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""各 Seedance 模型的参数限制。
**为什么需要它**:已接入的两个模型限制不同——2.5 时长上限 30 秒但没有 4k,
2.0 有 4k 但时长上限 15 秒;2.5 在图生视频时 `ratio` 只能是 `adaptive`。超范围的
请求会被厂商以 400 拒绝,那要花一次往返、计入 RPM,并表现为一个失败的任务。
在本地先拦下来,错误信息还能具体指出是哪个模型的哪条限制。
**未知模型一律放行**(只记一条日志):账号新开通一个模型时,不应该因为这张表
没更新就用不了——那会让「开通即可用」变成「开通还得改代码发版」。代价只是失去
本地预校验,厂商仍会兜底。
限制取自官方文档,取证见 patbond-doc
`docs/development/providers/volcengine-seedance.md` §5。
"""
from __future__ import annotations
import logging
from dataclasses import dataclass
log = logging.getLogger(__name__)
@dataclass(frozen=True)
class ModelLimits:
"""一个模型族的参数限制。
:param ratio_must_be_adaptive_with_first_frame: 2.5 在图生视频(首帧/首尾帧)
与视频编辑/延长任务中**只支持 `adaptive`**——模型自动保持输出宽高比与
首帧图片一致,不接受指定值。2.0 系列则可指定。
:param supports_output_format: `output_format`(mp4/mov)仅 2.5 支持。
"""
resolutions: frozenset[str]
min_duration_sec: int
max_duration_sec: int
ratio_must_be_adaptive_with_first_frame: bool
supports_output_format: bool
#: Seedance 2.5:时长可到 30 秒,但没有 4k;图生视频时宽高比锁 adaptive。
#: 1080p 输出为 10bit + H.265/HEVC,少数播放环境可能不兼容。
SEEDANCE_2_5 = ModelLimits(
resolutions=frozenset({"480p", "720p", "1080p"}),
min_duration_sec=4,
max_duration_sec=30,
ratio_must_be_adaptive_with_first_frame=True,
supports_output_format=True,
)
#: Seedance 2.0 系列:有 4k,但时长上限 15 秒。4k 输出为 10bit + H.265/HEVC。
SEEDANCE_2_0 = ModelLimits(
resolutions=frozenset({"480p", "720p", "1080p", "4k"}),
min_duration_sec=4,
max_duration_sec=15,
ratio_must_be_adaptive_with_first_frame=False,
supports_output_format=False,
)
#: 按模型族前缀匹配,而非精确匹配完整 model ID。
#: 完整 ID 形如 `doubao-seedance-2-5-260628`,尾部的日期版本会随厂商更新而变;
#: 按前缀匹配使一次模型小版本升级不需要改这张表。
_BY_PREFIX: tuple[tuple[str, ModelLimits], ...] = (
("doubao-seedance-2-5", SEEDANCE_2_5),
("doubao-seedance-2-0", SEEDANCE_2_0),
)
#: `duration` 的特殊值:交给模型在有效范围内自选。不参与范围校验。
AUTO_DURATION = -1
def limits_for(model: str) -> ModelLimits | None:
"""查模型限制;未知模型返回 None(表示不做本地预校验)。"""
for prefix, limits in _BY_PREFIX:
if model.startswith(prefix):
return limits
return None
def validate(
model: str,
*,
resolution: str | None = None,
duration_sec: int | None = None,
ratio: str | None = None,
has_first_frame: bool = False,
) -> None:
"""按模型限制校验参数,不合法则抛 `ValueError`。
未知模型直接放行——见模块 docstring。
:raises ValueError: 参数超出该模型的限制,消息里指明模型与具体限制
"""
limits = limits_for(model)
if limits is None:
log.info("模型 %s 无本地限制表,跳过预校验,由厂商兜底", model)
return
if resolution and resolution not in limits.resolutions:
raise ValueError(
f"模型 {model} 不支持分辨率 {resolution},"
f"可选:{sorted(limits.resolutions)}"
)
if (
duration_sec is not None
and duration_sec != AUTO_DURATION
and not limits.min_duration_sec <= duration_sec <= limits.max_duration_sec
):
raise ValueError(
f"模型 {model} 的时长需在 "
f"[{limits.min_duration_sec}, {limits.max_duration_sec}] 秒内"
f"(或 {AUTO_DURATION} 表示由模型自选),当前 {duration_sec}"
)
if (
has_first_frame
and limits.ratio_must_be_adaptive_with_first_frame
and ratio
and ratio != "adaptive"
):
raise ValueError(
f"模型 {model} 在图生视频时只支持 ratio=adaptive"
f"(输出宽高比自动与首帧图片一致),当前 {ratio}"
)