feat: add generate progress and readable logs
This commit is contained in:
@@ -62,6 +62,14 @@ def gen_title(
|
||||
cfg,
|
||||
attempts,
|
||||
request_kind="json",
|
||||
on_retry=lambda attempt, total_attempts, exc: _notify_retry(
|
||||
on_step,
|
||||
"title_request",
|
||||
attempt,
|
||||
total_attempts,
|
||||
exc,
|
||||
model,
|
||||
),
|
||||
)
|
||||
_notify_step(on_step, "title_parse_response")
|
||||
text = _extract_text(data).strip()
|
||||
@@ -110,11 +118,33 @@ def gen_cover(
|
||||
attempts,
|
||||
request_kind="multipart",
|
||||
content_type=content_type,
|
||||
on_retry=lambda attempt, total_attempts, exc: _notify_retry(
|
||||
on_step,
|
||||
"cover_request",
|
||||
attempt,
|
||||
total_attempts,
|
||||
exc,
|
||||
model,
|
||||
),
|
||||
)
|
||||
else:
|
||||
payload = _image_chat_payload(model, cover_prompt, old_cover_path, resolution)
|
||||
_notify_step(on_step, "cover_request")
|
||||
data = _call_with_retry(model, payload, cfg, attempts, request_kind="json")
|
||||
data = _call_with_retry(
|
||||
model,
|
||||
payload,
|
||||
cfg,
|
||||
attempts,
|
||||
request_kind="json",
|
||||
on_retry=lambda attempt, total_attempts, exc: _notify_retry(
|
||||
on_step,
|
||||
"cover_request",
|
||||
attempt,
|
||||
total_attempts,
|
||||
exc,
|
||||
model,
|
||||
),
|
||||
)
|
||||
|
||||
_notify_step(on_step, "cover_parse_response")
|
||||
image_bytes = _extract_image_bytes(data, model, cfg)
|
||||
@@ -131,6 +161,23 @@ def _notify_step(callback, step):
|
||||
pass
|
||||
|
||||
|
||||
|
||||
def _notify_retry(callback, step, attempt, attempts, exc, model):
|
||||
if callback is None:
|
||||
return
|
||||
try:
|
||||
callback(
|
||||
{
|
||||
"step": step,
|
||||
"result": "retry",
|
||||
"attempt": attempt,
|
||||
"attempts": attempts,
|
||||
"detail": _redact(str(exc), model),
|
||||
}
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def generate_batch(tasks, prompts, ai_cfg=None, on_progress=None, should_stop=None):
|
||||
"""Generate titles first, then covers, and persist each successful task."""
|
||||
|
||||
@@ -186,9 +233,25 @@ def generate_batch(tasks, prompts, ai_cfg=None, on_progress=None, should_stop=No
|
||||
return step_by_task.get(getattr(task, "id", None), fallback)
|
||||
|
||||
def step_callback(task, phase):
|
||||
def callback(step):
|
||||
set_step(task, step)
|
||||
_emit_generation_event(on_event, task, phase, step, "start")
|
||||
def callback(event):
|
||||
if isinstance(event, dict):
|
||||
step = event.get("step") or "unknown"
|
||||
result = event.get("result") or "start"
|
||||
set_step(task, step)
|
||||
_emit_generation_event(
|
||||
on_event,
|
||||
task,
|
||||
phase,
|
||||
step,
|
||||
result,
|
||||
detail=event.get("detail"),
|
||||
level=event.get("level") or ("warning" if result == "retry" else "info"),
|
||||
attempt=event.get("attempt"),
|
||||
attempts=event.get("attempts"),
|
||||
)
|
||||
return
|
||||
set_step(task, event)
|
||||
_emit_generation_event(on_event, task, phase, event, "start")
|
||||
return callback
|
||||
|
||||
with ThreadPoolExecutor(
|
||||
@@ -300,7 +363,7 @@ def generate_batch(tasks, prompts, ai_cfg=None, on_progress=None, should_stop=No
|
||||
"new_cover_path": new_cover_path,
|
||||
},
|
||||
)
|
||||
_emit_generation_event(on_event, task, "cover", "db_write", "success")
|
||||
_emit_generation_event(on_event, task, "cover", "db_write", "success", detail=new_cover_path)
|
||||
except CancelledError:
|
||||
summary["cancelled"] = True
|
||||
_emit_generation_event(on_event, task, "cover", get_step(task, "cover_request"), "cancelled", level="warning")
|
||||
@@ -401,7 +464,17 @@ def _mark_generate_failed(task, exc, db_path, on_task_update):
|
||||
return error
|
||||
|
||||
|
||||
def _emit_generation_event(callback, task, phase, step, result, detail=None, level="info"):
|
||||
def _emit_generation_event(
|
||||
callback,
|
||||
task,
|
||||
phase,
|
||||
step,
|
||||
result,
|
||||
detail=None,
|
||||
level="info",
|
||||
attempt=None,
|
||||
attempts=None,
|
||||
):
|
||||
if callback is None:
|
||||
return
|
||||
payload = {
|
||||
@@ -413,6 +486,10 @@ def _emit_generation_event(callback, task, phase, step, result, detail=None, lev
|
||||
}
|
||||
if detail is not None:
|
||||
payload["detail"] = diagnostics.redact_log_text(detail)
|
||||
if attempt is not None:
|
||||
payload["attempt"] = attempt
|
||||
if attempts is not None:
|
||||
payload["attempts"] = attempts
|
||||
try:
|
||||
callback(payload)
|
||||
except Exception:
|
||||
@@ -475,7 +552,15 @@ def _headers(model, content_type):
|
||||
}
|
||||
|
||||
|
||||
def _call_with_retry(model, body, config, attempts, request_kind, content_type=None):
|
||||
def _call_with_retry(
|
||||
model,
|
||||
body,
|
||||
config,
|
||||
attempts,
|
||||
request_kind,
|
||||
content_type=None,
|
||||
on_retry=None,
|
||||
):
|
||||
last_exc = None
|
||||
for index in range(attempts):
|
||||
try:
|
||||
@@ -484,6 +569,11 @@ def _call_with_retry(model, body, config, attempts, request_kind, content_type=N
|
||||
last_exc = exc
|
||||
if index + 1 >= attempts:
|
||||
break
|
||||
if on_retry is not None:
|
||||
try:
|
||||
on_retry(index + 1, attempts, exc)
|
||||
except Exception:
|
||||
pass
|
||||
time.sleep(min(2.0, 0.4 * (index + 1)))
|
||||
raise AIError(
|
||||
"AI 调用失败(已尝试 %s 次): %s"
|
||||
|
||||
Reference in New Issue
Block a user