feat: track generation jobs

This commit is contained in:
2026-04-18 16:29:22 +08:00
parent 16fafe0fe0
commit 96dfc677e2
18 changed files with 709 additions and 71 deletions

View File

@@ -0,0 +1,133 @@
"""Lightweight generation job/event tracking."""
from __future__ import annotations
from typing import Any
from sqlalchemy.ext.asyncio import AsyncSession
from app.db.models import GenerationJob, GenerationJobEvent, Story
def _story_snapshot(story: Story | None) -> dict[str, Any]:
if story is None:
return {}
return {
"story_id": story.id,
"mode": story.mode,
"generation_status": story.generation_status,
"image_status": story.image_status,
"audio_status": story.audio_status,
"retryable_assets": story.retryable_assets,
"last_error": story.last_error,
}
def _job_status_from_story(story: Story) -> str:
if story.generation_status == "failed":
return "failed"
if story.generation_status == "degraded_completed":
return "degraded_completed"
return "completed"
async def create_generation_job(
db: AsyncSession,
*,
user_id: str,
output_mode: str,
input_type: str,
request_payload: dict[str, Any],
story_id: int | None = None,
) -> GenerationJob:
"""Create a generation job and record its first event."""
job = GenerationJob(
user_id=user_id,
story_id=story_id,
output_mode=output_mode,
input_type=input_type,
status="running",
current_step="request_accepted",
request_payload=request_payload,
result_snapshot={},
)
db.add(job)
await db.flush()
await record_generation_event(
db,
job=job,
story_id=story_id,
event_type="request_accepted",
status="succeeded",
message="Generation request accepted.",
metadata={"output_mode": output_mode, "input_type": input_type},
commit=False,
)
await db.commit()
await db.refresh(job)
return job
async def record_generation_event(
db: AsyncSession,
*,
job: GenerationJob,
event_type: str,
status: str,
story_id: int | None = None,
message: str | None = None,
metadata: dict[str, Any] | None = None,
commit: bool = True,
) -> GenerationJobEvent:
"""Append one event to an existing generation job."""
event = GenerationJobEvent(
job_id=job.id,
story_id=story_id if story_id is not None else job.story_id,
event_type=event_type,
status=status,
message=message,
event_metadata=metadata or {},
)
db.add(event)
if commit:
await db.commit()
return event
async def finish_generation_job(
db: AsyncSession,
*,
job: GenerationJob,
story: Story | None,
status: str | None = None,
current_step: str,
error_message: str | None = None,
message: str | None = None,
metadata: dict[str, Any] | None = None,
) -> GenerationJob:
"""Mark a generation job as completed/degraded/failed and append a final event."""
job.story_id = story.id if story is not None else job.story_id
job.status = status or (_job_status_from_story(story) if story is not None else "failed")
job.current_step = current_step
job.error_message = error_message
job.result_snapshot = _story_snapshot(story)
await record_generation_event(
db,
job=job,
story_id=job.story_id,
event_type=current_step,
status=job.status,
message=message,
metadata={
**(metadata or {}),
"result_snapshot": job.result_snapshot,
},
commit=False,
)
await db.commit()
await db.refresh(job)
return job