fix(#2183): 信任链真·现场t2i+preheat接线+portrait_intercept降级纯t2v+timeout调大
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 1s
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 8s
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 20s
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 1m20s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m44s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 5m35s
AI Code Review / AI Code Review (pull_request) Successful in 10m19s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 9m26s
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 1s
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 8s
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 20s
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 1m20s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m44s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 5m35s
AI Code Review / AI Code Review (pull_request) Successful in 10m19s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 9m26s
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
- 信任链3处断链修复: 1. run_viral_video_analyze VLM完成后立即启动_start_trust_chain_preheat(后台daemon与文案阶段并行) 2. _step_render 预热不可用时真·同步调用Seedream t2i(而非打假日志直接传原图),120s超时 3. ai_client.video_generation遇到portrait_intercept 400时自动移除所有参考图降级纯t2v重试一次 - LLM timeout调大(实测pro 1500tok输出需75.8s): - _step_script_generation: fast-first/fast-retry 45→90s, pro-fallback 60→120s - _step_intent_parsing: 45→60s - _step_review: 30→45s - generate_copy celery时限放宽:soft=360→600s, hard=420→660s(三级重试最坏~405s) - 三层兜底:后台预热→现场t2i→400纯t2v,保证人像链路必出片
This commit is contained in:
@@ -598,7 +598,7 @@ def _step_intent_parsing(job: ViralVideoJob, image_analysis: dict) -> dict:
|
||||
for _m, _lbl in [(_fast, "fast"), (_pro, "pro-fallback")]:
|
||||
try:
|
||||
logger.info("[爆款视频] 意图解析 model=%s label=%s", _m, _lbl)
|
||||
result = call_llm(prompt, temperature=0.4, max_tokens=800, model=_m, timeout=45)
|
||||
result = call_llm(prompt, temperature=0.4, max_tokens=800, model=_m, timeout=60) # #2183: pro 实测800tok约40s,给60s
|
||||
return (
|
||||
result
|
||||
if isinstance(result, dict)
|
||||
@@ -1033,16 +1033,16 @@ def _step_script_generation(job: ViralVideoJob, intent: dict, image_analysis: di
|
||||
|
||||
try:
|
||||
# 第一次:快模型 25s
|
||||
normalized = _try_gen(_fast, 0.8, 2500, "fast-first", tmo=45)
|
||||
normalized = _try_gen(_fast, 0.8, 2500, "fast-first", tmo=90)
|
||||
if normalized is not None:
|
||||
return normalized
|
||||
# 第二次:快模型降温度+加大 max_tokens,25s
|
||||
normalized = _try_gen(_fast, 0.6, 3200, "fast-retry", tmo=45)
|
||||
# #2183: 实测pro 1500tok输出需75.8s,单次timeout提到90s
|
||||
normalized = _try_gen(_fast, 0.6, 3200, "fast-retry", tmo=90)
|
||||
if normalized is not None:
|
||||
return normalized
|
||||
# 第三次:用主力模型兜底,给 40s
|
||||
# 第三次:用主力模型兜底,给 120s
|
||||
if _pro and _pro != _fast:
|
||||
normalized = _try_gen(_pro, 0.7, 3500, "pro-fallback", tmo=60)
|
||||
normalized = _try_gen(_pro, 0.7, 3500, "pro-fallback", tmo=120)
|
||||
if normalized is not None:
|
||||
return normalized
|
||||
logger.warning("[爆款视频] 编导脚本三次都未生成合格结果,使用兜底脚本")
|
||||
@@ -1079,7 +1079,7 @@ def _step_review(job: ViralVideoJob, copy_result: dict) -> dict:
|
||||
for _m, _lbl in [(_fast, "fast"), (_pro, "pro-fallback")]:
|
||||
try:
|
||||
logger.info("[爆款视频] 合规审核 model=%s label=%s", _m, _lbl)
|
||||
result = call_llm(prompt, temperature=0.1, max_tokens=500, model=_m, timeout=30)
|
||||
result = call_llm(prompt, temperature=0.1, max_tokens=500, model=_m, timeout=45) # #2183: 对齐pro响应
|
||||
return result if isinstance(result, dict) else {"passed": True, "score": 80, "details": {}}
|
||||
except Exception as e:
|
||||
logger.warning("[爆款视频] 合规审核失败 label=%s err=%s", _lbl, e)
|
||||
@@ -1251,13 +1251,44 @@ def _step_render(job: ViralVideoJob, copy_result: dict, tts_audio_url: str | Non
|
||||
pre_trusted = list(pti)
|
||||
logger.info("[爆款视频] 使用信任链预热结果 n=%d,跳过现场 Seedream AI 化", len(pre_trusted))
|
||||
elif all_portrait_urls and _mcfg.get("provider", "doubao") == "doubao":
|
||||
# #2183: 真·现场跑信任链——同步调用 Seedream t2i,拿到 AI 人像 URL 后再传 Seedance
|
||||
logger.info(
|
||||
"[爆款视频] 预热结果不可用(%s/%d张),将现场跑信任链",
|
||||
"[爆款视频] 预热结果不可用(%s/%d张),现场同步跑信任链Seedream t2i",
|
||||
"缺失" if not pti else f"{len(pti)}/{len(all_portrait_urls)}",
|
||||
len(all_portrait_urls),
|
||||
)
|
||||
try:
|
||||
from packages.shared.ai_service import preheat_trust_chain
|
||||
_ia = getattr(job, "image_analysis", None) or {}
|
||||
_prods = (_ia.get("products") if isinstance(_ia, dict) else None) or []
|
||||
_pdescs = []
|
||||
if _prods:
|
||||
_pdescs = [(pp.get("portrait_prompt") or "无人像") for pp in _prods]
|
||||
elif isinstance(_ia, dict):
|
||||
_pp0 = _ia.get("portrait_prompt") or "无人像"
|
||||
if _pp0 and _pp0 != "无人像":
|
||||
_pdescs = [_pp0]
|
||||
_valid = [d for d in _pdescs if d and isinstance(d, str) and "无人像" not in d and len(d) >= 10]
|
||||
if _valid:
|
||||
_t0 = time.time()
|
||||
_live_urls = preheat_trust_chain(_valid, timeout=120)
|
||||
if _live_urls and len(_live_urls) == len(all_portrait_urls):
|
||||
pre_trusted = list(_live_urls)
|
||||
logger.info(
|
||||
"[爆款视频] 现场信任链t2i完成 %d张 耗时%.1fs,将用AI人像传Seedance",
|
||||
len(pre_trusted), time.time()-_t0,
|
||||
)
|
||||
else:
|
||||
logger.warning(
|
||||
"[爆款视频] 现场信任链t2i返回不匹配 urls=%s n_portraits=%d,回退原图+400降级纯t2v",
|
||||
_live_urls, len(all_portrait_urls),
|
||||
)
|
||||
else:
|
||||
logger.info("[爆款视频] 无有效人物描述(可能是商品图),无需现场跑信任链")
|
||||
except Exception as _te:
|
||||
logger.warning("[爆款视频] 现场跑信任链异常: %s,回退原图+400降级纯t2v", _te, exc_info=True)
|
||||
|
||||
# 第一次调用:带参考图/首帧/音频/参考视频
|
||||
# 第一次调用:带参考图/首帧/音频/参考视频(pre_trusted有值→走信任链;无值→原图;若400 ai_client内部自动降级纯t2v)
|
||||
result = call_video_generation(
|
||||
prompt=prompt,
|
||||
image_url=first_image,
|
||||
@@ -1550,8 +1581,20 @@ def run_viral_video_analyze(self: Task, job_id: str) -> dict:
|
||||
|
||||
image_analysis = _step_image_analysis(job)
|
||||
job.image_analysis = image_analysis
|
||||
# #2183/#2174: VLM完成后立即启动信任链t2i预热(后台daemon线程,与后续阶段并行)
|
||||
if job.images:
|
||||
try:
|
||||
_products = (image_analysis or {}).get("products", []) or []
|
||||
_portrait_descs = [(p.get("portrait_prompt") or "无人像") for p in _products] if _products else []
|
||||
if not _portrait_descs and isinstance(image_analysis, dict):
|
||||
_pp = image_analysis.get("portrait_prompt") or "无人像"
|
||||
if _pp and _pp != "无人像":
|
||||
_portrait_descs = [_pp]
|
||||
_start_trust_chain_preheat(job.id, _portrait_descs)
|
||||
except Exception as _e:
|
||||
logger.warning("[爆款视频][阶段1] 启动信任链t2i预热失败: %s", _e)
|
||||
_save_job(repo, job, session)
|
||||
_emit_progress(job_id, ViralVideoStage.IMAGE_ANALYSIS, 60.0, "图片分析完成", {"result": image_analysis})
|
||||
_emit_progress(job_id, ViralVideoStage.IMAGE_ANALYSIS, 60.0, "图片分析完成,AI人像预热中...", {"result": image_analysis, "trust_chain_preheating": True})
|
||||
|
||||
style_guide = None
|
||||
if job.reference_video_url or job.style_template_id:
|
||||
@@ -1598,8 +1641,8 @@ def run_viral_video_analyze(self: Task, job_id: str) -> dict:
|
||||
bind=True,
|
||||
max_retries=1,
|
||||
name="worker.run_viral_video_generate_copy",
|
||||
soft_time_limit=360, # #2173: 6min(编导脚本含意图+三级重试+审核,fast超时转pro)
|
||||
time_limit=420, # #2173: 7min hard limit
|
||||
soft_time_limit=600, # #2183: pro长脚本~120s+三级重试最坏300s+intent/review ~105s,给到10min
|
||||
time_limit=660, # #2183: 11min hard limit
|
||||
)
|
||||
def run_viral_video_generate_copy(self: Task, job_id: str) -> dict:
|
||||
"""v1.6 阶段2(v1.6.1 提速版):意图解析 → 编导分镜脚本生成 → 直接返回,合规审核后置到出片前。
|
||||
|
||||
@@ -585,7 +585,7 @@ class DoubaoClient:
|
||||
# #2172/#2174: 信任链——使用预热好的 Seedream t2i 文生图(纯模型生成人像,是方舟信任产物,
|
||||
# 不会触发肖像审核)。预热在 VLM 分析后由 daemon 线程后台完成,结果通过 pre_trusted_images 传入。
|
||||
# - 预热结果有效 → 替换原参考图,走 omni_ref 模式
|
||||
# - 预热结果不可用 → 直接用原图(若被400肖像拦截,#2166自动降级纯t2v),避免现场跑t2i阻塞渲染
|
||||
# - 预热结果不可用 → 直接用原图(上层 _step_render 已现场同步跑 Seedream t2i 兜底;若再被400拦截,下方自动降级纯t2v)
|
||||
# 信任链只作用于 doubao provider;DashScope(Wan) 保持原行为。
|
||||
trust_chain_applied = False
|
||||
if provider == "doubao" and getattr(self, "trust_chain_enabled", True) and pre_trusted_images:
|
||||
@@ -763,6 +763,28 @@ class DoubaoClient:
|
||||
# 保留第二次的错误信息
|
||||
sc, body = sc2, body2
|
||||
|
||||
# #2183: portrait_intercept / 真人肖像审核拦截 → 去掉所有参考图(含image_url首帧),纯 t2v 重试一次
|
||||
# 信任链预热或现场 t2i 都失败时的最后兜底,保证能出片
|
||||
if (
|
||||
not task_id
|
||||
and sc == 400
|
||||
):
|
||||
_err_code_for_400, _ = _classify_video_error(sc, body, last_err)
|
||||
if _err_code_for_400 == "portrait_intercept" and (image_url or ref_imgs):
|
||||
logger.warning(
|
||||
"Seedance 创建因 portrait_intercept 失败,降级纯 t2v(移除所有参考图)重试: img=%d ref=%d",
|
||||
1 if image_url else 0, len(ref_imgs),
|
||||
)
|
||||
_t2v_payload = dict(create_payload)
|
||||
_t2v_payload["content"] = [{"type": "text", "text": prompt.strip()}]
|
||||
_t2v_payload["ratio"] = ratio or "9:16"
|
||||
task_id, last_err, sc3, body3 = _do_create(_t2v_payload)
|
||||
if task_id:
|
||||
sc, body = sc3, body3
|
||||
logger.info("[trust-chain] portrait_intercept 降级纯 t2v 成功 task_id=%s", task_id)
|
||||
else:
|
||||
sc, body = sc3, body3
|
||||
|
||||
if not task_id:
|
||||
err_code, user_msg = _classify_video_error(sc, body, last_err)
|
||||
self.last_video_error = {
|
||||
|
||||
Reference in New Issue
Block a user