优化极速成品

This commit is contained in:
Azmat@qq.com
2026-08-26 09:00:00 +08:00
parent 2f70d3e8a0
commit 43d0b088e4
2 changed files with 157 additions and 16 deletions
@@ -8,6 +8,7 @@ from __future__ import annotations
import json
import logging
import threading
import time
from datetime import datetime, timedelta
from django.db import connections, transaction
@@ -72,6 +73,22 @@ def _is_finished(job: QuickCreateJob) -> bool:
return job.status in _FINISHED_STATUSES
def _latest_script_task(project: Project) -> AITask | None:
return (
AITask.objects.filter(project=project, task_type=AITask.Type.SCRIPT_GENERATION)
.order_by("-created_at")
.first()
)
def _script_generation_inflight(project: Project) -> bool:
return AITask.objects.filter(
project=project,
task_type=AITask.Type.SCRIPT_GENERATION,
status__in=_ACTIVE_TASK_STATUSES,
).exists()
def get_quick_script_model() -> ModelConfig | None:
"""极速成片固定复用专业创作的豆包 Seed 2.1 Pro 脚本模型。
@@ -145,6 +162,10 @@ def _is_retryable_exc(exc: Exception) -> bool:
"broken pipe",
"socket",
"temporarily unavailable",
"stream aborted",
"client disconnected",
"lost connection",
"operationalerror",
)
)
@@ -295,10 +316,23 @@ def _consume_script_agent(job: QuickCreateJob) -> None:
entry_source="ai",
persona="reviewer",
)
# 豆包思考模型会连续吐几百帧 reasoning。原先每帧 refresh_from_db,远程 MySQL
# 一抖动就会把生成器关掉,模型调用被误记成 stream aborted (client disconnected)。
frames = 0
last_cancel_check = time.monotonic()
for frame in stream:
job.refresh_from_db(fields=["status"])
if _is_finished(job):
return
frames += 1
now = time.monotonic()
if frames == 1 or frames % 40 == 0 or now - last_cancel_check >= 2:
last_cancel_check = now
try:
job.refresh_from_db(fields=["status"])
except Exception: # noqa: BLE001 — 查库失败不能中断正在跑的模型流
logger.exception("quick create script cancel check failed for job %s", job.id)
else:
if job.status == QuickCreateJob.Status.CANCELLED:
stream.close()
return
if not frame.startswith("data:"):
continue
try:
@@ -400,18 +434,20 @@ def _advance_script(job: QuickCreateJob) -> int | None:
run_quick_script_task.apply_async(args=[str(job.id)], queue="airshelf.quick")
return SCRIPT_POLL_SECONDS
failed = (
AITask.objects.filter(
project=job.project,
task_type=AITask.Type.SCRIPT_GENERATION,
status=AITask.Status.FAILED,
)
.order_by("-created_at")
.first()
)
if failed is not None:
fail_quick_create(job, _task_public_error(failed), internal_error=failed.error_message)
latest = _latest_script_task(job.project)
if latest is not None and latest.status in _ACTIVE_TASK_STATUSES:
started_at = _parse_iso(metadata.get("script_started_at"))
elapsed = int((timezone.now() - started_at).total_seconds()) if started_at else 0
_save_job(job, progress=min(44, 28 + elapsed // 8), message="正在生成分镜脚本…")
return SCRIPT_POLL_SECONDS
if latest is not None and latest.status == AITask.Status.FAILED:
fail_quick_create(job, _task_public_error(latest), internal_error=latest.error_message)
return None
if latest is not None and latest.status == AITask.Status.SUCCEEDED:
started_at = _parse_iso(metadata.get("script_started_at"))
elapsed = int((timezone.now() - started_at).total_seconds()) if started_at else 0
_save_job(job, progress=min(44, 28 + elapsed // 8), message="脚本已生成,正在写入分镜…")
return SCRIPT_POLL_SECONDS
started_at = _parse_iso(metadata.get("script_started_at"))
if started_at and timezone.now() - started_at > SCRIPT_TIMEOUT:
@@ -968,6 +1004,27 @@ def recover_quick_create(job: QuickCreateJob) -> None:
):
_save_job(job, status=QuickCreateJob.Status.RUNNING, error_message="", message="网络波动,正在继续生成…")
_enqueue_advance(job)
return
if (
job.status == QuickCreateJob.Status.FAILED
and job.phase == QuickCreateJob.Phase.SCRIPT
and _adopted_script(job.project) is None
and _transient_internal(job)
and retries < TRANSIENT_RETRY_LIMIT
):
metadata = dict(job.metadata or {})
metadata["transient_retries"] = retries + 1
metadata.pop("script_started", None)
metadata.pop("script_started_at", None)
metadata.pop("script_local", None)
_save_job(
job,
status=QuickCreateJob.Status.RUNNING,
error_message="",
message="脚本生成中断,正在重新生成…",
metadata=metadata,
)
_enqueue_advance(job)
return
if _is_finished(job):
return
@@ -983,6 +1040,7 @@ def recover_quick_create(job: QuickCreateJob) -> None:
and timezone.now() - started_at > SCRIPT_STOLEN_AFTER
and adopted is None
and not local_running
and not _script_generation_inflight(job.project)
)
if stolen:
metadata["script_local"] = True
@@ -990,7 +1048,7 @@ def recover_quick_create(job: QuickCreateJob) -> None:
_save_job(job, metadata=metadata, message="正在继续生成分镜脚本…")
_run_quick_script_in_thread(job_id)
return
if timed_out and adopted is None and not local_running:
if timed_out and adopted is None and not local_running and not _script_generation_inflight(job.project):
fail_quick_create(job, "脚本生成超时,请稍后重试或进入专业模式查看")
return
if timezone.now() - job.updated_at > STALE_AFTER and _claim_next_advance(str(job.id), SCRIPT_POLL_SECONDS):