添加seedance2.5
This commit is contained in:
@@ -38,11 +38,36 @@ from .video_pricing import get_resolution
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
FREE_VIDEO_MODELS = {
|
||||
"doubao-seedance-2-5-260628",
|
||||
"doubao-seedance-2-0-260128",
|
||||
"doubao-seedance-2-0-fast-260128",
|
||||
"doubao-seedance-2-0-mini-260615",
|
||||
}
|
||||
HIGH_RES_MODEL = "doubao-seedance-2-0-260128" # 1080p/4k 仅标准档(火山限制)
|
||||
# 视频复刻固定用这一档:只有它支持 30 秒单次出片
|
||||
REPLACE_MODEL = "doubao-seedance-2-5-260628"
|
||||
# 出片时长的兜底上限。真实上限按 ModelConfig.metadata.durations 取(见 model_duration_range),
|
||||
# 元数据缺失时回落这里 —— 别写死 15,否则新模型上来就被老常量卡住。
|
||||
DEFAULT_MAX_DURATION = 15
|
||||
MIN_DURATION = 4
|
||||
|
||||
|
||||
def model_duration_range(model_config) -> tuple[int, int]:
|
||||
"""模型支持的出片时长区间。取 metadata.durations(catalog / 后台可配),缺失回落 4–15。"""
|
||||
meta = (model_config.metadata or {}) if model_config is not None else {}
|
||||
durations = (meta.get("capabilities") or {}).get("durations") or meta.get("durations") or []
|
||||
values = [int(v) for v in durations if str(v).isdigit()]
|
||||
if not values:
|
||||
return MIN_DURATION, DEFAULT_MAX_DURATION
|
||||
return min(values), max(values)
|
||||
|
||||
|
||||
def model_resolutions(model_config) -> set[str]:
|
||||
"""模型支持的分辨率档。缺失回落全集,交给火山自己拒 —— 总比把能用的档误拒好。"""
|
||||
meta = (model_config.metadata or {}) if model_config is not None else {}
|
||||
listed = (meta.get("capabilities") or {}).get("resolutions") or meta.get("resolutions") or []
|
||||
values = {str(v) for v in listed if str(v) in RESOLUTIONS}
|
||||
return values or set(RESOLUTIONS)
|
||||
RATIOS = {"21:9", "16:9", "4:3", "1:1", "3:4", "9:16"}
|
||||
RESOLUTIONS = {"480p", "720p", "1080p", "4k"}
|
||||
MODES = {"universal", "keyframe"}
|
||||
@@ -126,7 +151,9 @@ def _guard_asset_reference(asset: Asset, label: str) -> None:
|
||||
raise ValueError(f"素材「{name}」是上传素材,已提交审核,通过后即可引用")
|
||||
|
||||
|
||||
def build_content_items(*, team, prompt: str, mode: str, references: list) -> dict:
|
||||
def build_content_items(
|
||||
*, team, prompt: str, mode: str, references: list, max_ref_seconds: float = REF_DURATION_MAX
|
||||
) -> dict:
|
||||
"""references → 火山 content_items + api_prompt(@label 已替换)。
|
||||
|
||||
移植 jimeng views.py:369-567:URL 去重、blob: 拦截、素材库引用(FreeAsset → asset://)、
|
||||
@@ -322,8 +349,9 @@ def build_content_items(*, team, prompt: str, mode: str, references: list) -> di
|
||||
raise ValueError(f"参考音频最多 3 条,当前 {audio_n} 条,请减少后重试")
|
||||
if audio_n > 0 and image_n + video_n == 0:
|
||||
raise ValueError("音频不能单独作为参考素材,请同时提供参考图片或视频")
|
||||
if video_duration_total > REF_DURATION_MAX:
|
||||
raise ValueError("参考视频总时长不能超过 15 秒,请缩短后重试")
|
||||
if video_duration_total > max_ref_seconds:
|
||||
# 上限跟着模型走:自由创作是 Seedance 2.0(15 秒),视频复刻走 2.5(30 秒)。
|
||||
raise ValueError(f"参考视频总时长不能超过 {int(max_ref_seconds)} 秒,请缩短后重试")
|
||||
|
||||
# @label 替换:按 label 长度降序,防子串吞噬
|
||||
ordered = sorted(label_to_placeholder.items(), key=lambda kv: len(kv[0]), reverse=True)
|
||||
@@ -444,10 +472,6 @@ def submit_free_video(*, team, user, params: dict) -> AITask:
|
||||
raise ValueError("画面比例无效")
|
||||
if resolution not in RESOLUTIONS:
|
||||
raise ValueError("分辨率无效")
|
||||
if not 4 <= duration <= 15:
|
||||
raise ValueError("视频时长需在 4-15 秒之间")
|
||||
if resolution in ("1080p", "4k") and model_name != HIGH_RES_MODEL:
|
||||
raise ValueError(f"{resolution} 仅标准档模型支持,请切换模型或降低分辨率")
|
||||
get_resolution(aspect_ratio, resolution) # 组合合法性 fail loud
|
||||
|
||||
orphan = find_orphan_material_mention(prompt, references)
|
||||
@@ -471,6 +495,17 @@ def submit_free_video(*, team, user, params: dict) -> AITask:
|
||||
if model_config is None:
|
||||
raise ValueError("视频模型未配置,请联系管理员")
|
||||
|
||||
# 分辨率与时长都按所选模型自己的能力判,不再写死「1080p/4k 仅 2.0 标准档」——
|
||||
# Seedance 2.5 同样支持 1080p/4k,写死会把它误拒。能力表见 ModelConfig.metadata。
|
||||
allowed_resolutions = model_resolutions(model_config)
|
||||
if resolution not in allowed_resolutions:
|
||||
raise ValueError(
|
||||
f"{model_config.display_name or model_name} 不支持 {resolution},请切换模型或降低分辨率"
|
||||
)
|
||||
min_seconds, max_seconds = model_duration_range(model_config)
|
||||
if not min_seconds <= duration <= max_seconds:
|
||||
raise ValueError(f"视频时长需在 {min_seconds}-{max_seconds} 秒之间")
|
||||
|
||||
_reap_stale_free_video_tasks(team=team)
|
||||
|
||||
# 团队并发闸(移植 jimeng Layer2.6):视频是长时高价任务,必须限并发
|
||||
@@ -481,7 +516,10 @@ def submit_free_video(*, team, user, params: dict) -> AITask:
|
||||
if in_flight >= max_concurrent:
|
||||
raise ValueError(f"当前有 {in_flight} 个视频任务进行中(上限 {max_concurrent}),请等待完成后再提交")
|
||||
|
||||
built = build_content_items(team=team, prompt=prompt, mode=mode, references=references)
|
||||
built = build_content_items(
|
||||
team=team, prompt=prompt, mode=mode, references=references,
|
||||
max_ref_seconds=max_seconds,
|
||||
)
|
||||
|
||||
# 统一计价引擎:¥成本 × 毛利系数 → 积分;预留 = 积分 × buffer(均为 BillingConfig 可配)
|
||||
tokens, quote = quote_video_estimate(
|
||||
@@ -583,7 +621,10 @@ def start_pending_free_video(task: AITask) -> AITask:
|
||||
seed = -1
|
||||
references = payload.get("references") or []
|
||||
try:
|
||||
built = build_content_items(team=task.team, prompt=prompt, mode=mode, references=references)
|
||||
built = build_content_items(
|
||||
team=task.team, prompt=prompt, mode=mode, references=references,
|
||||
max_ref_seconds=model_duration_range(task.model_config)[1],
|
||||
)
|
||||
except ValueError as exc:
|
||||
message = str(exc)
|
||||
if "正在审核" in message or "已提交审核" in message or "尚未完成合规审核" in message:
|
||||
|
||||
Reference in New Issue
Block a user