feat: complete T-533 incremental generation
This commit is contained in:
@@ -230,18 +230,57 @@ def _task_attempt_count(task, field):
|
||||
return 0
|
||||
|
||||
|
||||
def is_generatable_task(task):
|
||||
"""判断任务是否能由② AI生成执行或重试。"""
|
||||
def _has_generated_title(task):
|
||||
return bool(str(getattr(task, "new_title", "") or "").strip())
|
||||
|
||||
|
||||
def _has_generated_cover(task):
|
||||
return bool(str(getattr(task, "new_cover_path", "") or "").strip())
|
||||
|
||||
|
||||
def generation_needs(task, generate_cover=False):
|
||||
"""Return which AI components still need generation for this task."""
|
||||
|
||||
stage = str(getattr(task, "stage", "") or "")
|
||||
status = str(getattr(task, "status", "") or "")
|
||||
if status in {"running", "skipped"}:
|
||||
return False
|
||||
if stage == "collected":
|
||||
return True
|
||||
if stage == "generated" and status == "failed":
|
||||
return _task_attempt_count(task, "apply_attempts") == 0
|
||||
return False
|
||||
return {"title": False, "cover": False}
|
||||
if stage not in {"collected", "generated"}:
|
||||
return {"title": False, "cover": False}
|
||||
if stage == "generated" and _task_attempt_count(task, "apply_attempts") > 0:
|
||||
return {"title": False, "cover": False}
|
||||
needs_title = not _has_generated_title(task)
|
||||
needs_cover = bool(generate_cover) and not _has_generated_cover(task)
|
||||
return {"title": needs_title, "cover": needs_cover}
|
||||
|
||||
|
||||
def is_generatable_task(task, generate_cover=False):
|
||||
"""判断任务是否能由② AI生成执行或重试。"""
|
||||
|
||||
needs = generation_needs(task, generate_cover=generate_cover)
|
||||
return bool(needs["title"] or needs["cover"])
|
||||
|
||||
|
||||
def generation_component_totals(tasks, generate_cover=False):
|
||||
"""Count task and component gaps for a generation run."""
|
||||
|
||||
eligible = [
|
||||
task for task in list(tasks)
|
||||
if is_generatable_task(task, generate_cover=generate_cover)
|
||||
]
|
||||
title_total = 0
|
||||
cover_total = 0
|
||||
for task in eligible:
|
||||
needs = generation_needs(task, generate_cover=generate_cover)
|
||||
if needs["title"]:
|
||||
title_total += 1
|
||||
if needs["cover"]:
|
||||
cover_total += 1
|
||||
return {
|
||||
"total": len(eligible),
|
||||
"title_total": title_total,
|
||||
"cover_total": cover_total,
|
||||
}
|
||||
|
||||
|
||||
def generate_batch(tasks, prompts, ai_cfg=None, on_progress=None, should_stop=None):
|
||||
@@ -278,14 +317,27 @@ def generate_batch(tasks, prompts, ai_cfg=None, on_progress=None, should_stop=No
|
||||
should_stop = should_stop or (lambda: False)
|
||||
eligible = [
|
||||
task for task in list(tasks)
|
||||
if is_generatable_task(task)
|
||||
if is_generatable_task(task, generate_cover=generate_cover)
|
||||
]
|
||||
needs_by_task = {
|
||||
getattr(task, "id", None): generation_needs(task, generate_cover=generate_cover)
|
||||
for task in eligible
|
||||
}
|
||||
title_tasks = [
|
||||
task for task in eligible
|
||||
if needs_by_task.get(getattr(task, "id", None), {}).get("title")
|
||||
]
|
||||
cover_candidates = [
|
||||
task for task in eligible
|
||||
if needs_by_task.get(getattr(task, "id", None), {}).get("cover")
|
||||
]
|
||||
summary = {
|
||||
"ok": True,
|
||||
"total": len(eligible),
|
||||
"title_total": len(title_tasks),
|
||||
"title_done": 0,
|
||||
"cover_done": 0,
|
||||
"cover_total": len(eligible) if generate_cover else 0,
|
||||
"cover_total": len(cover_candidates) if generate_cover else 0,
|
||||
"generated_done": 0,
|
||||
"failed": 0,
|
||||
"cancelled": False,
|
||||
@@ -331,7 +383,7 @@ def generate_batch(tasks, prompts, ai_cfg=None, on_progress=None, should_stop=No
|
||||
max_workers=max(1, int(generation_cfg.get("title_concurrency", 1)))
|
||||
) as executor:
|
||||
futures = {}
|
||||
for task in eligible:
|
||||
for task in title_tasks:
|
||||
if should_stop():
|
||||
summary["cancelled"] = True
|
||||
break
|
||||
@@ -361,29 +413,31 @@ def generate_batch(tasks, prompts, ai_cfg=None, on_progress=None, should_stop=No
|
||||
summary["title_done"] += 1
|
||||
set_step(task, "title_done")
|
||||
_emit_generation_event(on_event, task, "title", "title_done", "success")
|
||||
if not generate_cover:
|
||||
set_step(task, "db_write")
|
||||
_emit_generation_event(on_event, task, "title", "db_write", "start")
|
||||
db.set_generated(task.id, new_title, None, path=db_path)
|
||||
set_step(task, "db_write")
|
||||
_emit_generation_event(on_event, task, "title", "db_write", "start")
|
||||
existing_cover_path = getattr(task, "new_cover_path", None)
|
||||
db.set_generated(task.id, new_title, existing_cover_path, path=db_path)
|
||||
needs_cover = needs_by_task.get(getattr(task, "id", None), {}).get("cover")
|
||||
if not needs_cover:
|
||||
summary["generated_done"] += 1
|
||||
if on_task_update is not None:
|
||||
on_task_update(
|
||||
task.id,
|
||||
{
|
||||
"stage": "generated",
|
||||
"status": "success",
|
||||
"new_title": new_title,
|
||||
"new_cover_path": None,
|
||||
},
|
||||
)
|
||||
_emit_generation_event(
|
||||
on_event,
|
||||
task,
|
||||
"title",
|
||||
"db_write",
|
||||
"success",
|
||||
detail="仅生成标题",
|
||||
if on_task_update is not None:
|
||||
on_task_update(
|
||||
task.id,
|
||||
{
|
||||
"stage": "generated",
|
||||
"status": "success",
|
||||
"new_title": new_title,
|
||||
"new_cover_path": existing_cover_path,
|
||||
},
|
||||
)
|
||||
_emit_generation_event(
|
||||
on_event,
|
||||
task,
|
||||
"title",
|
||||
"db_write",
|
||||
"success",
|
||||
detail="标题已保存,等待封面" if needs_cover else "仅生成标题",
|
||||
)
|
||||
except CancelledError:
|
||||
summary["cancelled"] = True
|
||||
_emit_generation_event(on_event, task, "title", get_step(task, "title_request"), "cancelled", level="warning")
|
||||
@@ -401,9 +455,15 @@ def generate_batch(tasks, prompts, ai_cfg=None, on_progress=None, should_stop=No
|
||||
summary["ok"] = False
|
||||
return summary
|
||||
|
||||
for task in eligible:
|
||||
task_id = getattr(task, "id", None)
|
||||
if task_id not in title_results and _has_generated_title(task):
|
||||
title_results[task_id] = str(getattr(task, "new_title") or "")
|
||||
_emit_generation_event(on_event, task, "title", "title_submit", "skipped", detail="已有标题")
|
||||
|
||||
cover_tasks = [
|
||||
task for task in eligible
|
||||
if task.id in title_results
|
||||
task for task in cover_candidates
|
||||
if getattr(task, "id", None) in title_results
|
||||
]
|
||||
with ThreadPoolExecutor(
|
||||
max_workers=max(1, int(generation_cfg.get("image_concurrency", 1)))
|
||||
|
||||
Reference in New Issue
Block a user