feat: move unified generation to background worker

This commit is contained in:
2026-04-19 17:29:37 +08:00
parent 5318de670f
commit 6fb128955f
15 changed files with 632 additions and 285 deletions

View File

@@ -12,7 +12,7 @@ from sqlalchemy.orm import joinedload
from app.core.config import settings
from app.core.logging import get_logger
from app.db.models import ChildProfile, Story, StoryUniverse
from app.db.models import ChildProfile, GenerationJob, Story, StoryUniverse
from app.schemas.story_schemas import (
AchievementItem,
FullStoryResponse,
@@ -33,6 +33,7 @@ from app.services.audio_storage import (
write_story_audio_cache,
)
from app.services.generation_jobs import (
claim_generation_job_for_worker,
create_generation_job,
ensure_no_active_story_generation_job,
finish_generation_job,
@@ -1113,7 +1114,7 @@ async def generate_generation_service(
user_id: str,
db: AsyncSession,
) -> GenerationResponse:
"""Unified generation workflow entry point for stories and storybooks."""
"""Queue one unified generation workflow for background execution."""
job = await create_generation_job(
db,
@@ -1124,7 +1125,65 @@ async def generate_generation_service(
)
try:
response = await _generate_generation_service_with_job(request, user_id, db, job=job)
from app.tasks.generation_workflow import run_generation_workflow_task
run_generation_workflow_task.delay(job.id)
except Exception as exc:
await finish_generation_job(
db,
job=job,
story=None,
status="failed",
current_step="generation_failed",
error_message="Background generation dispatch failed.",
message="Generation failed before the worker could start processing the job.",
metadata={"dispatch_error": str(exc)},
)
raise HTTPException(
status_code=503,
detail="后台生成任务派发失败,请确认 worker 可用后重试。",
) from exc
return _build_queued_generation_response(request, job_id=job.id)
def _build_queued_generation_response(
request: GenerationRequest,
*,
job_id: str,
) -> GenerationResponse:
"""Build the immediate API response after a generation job is accepted."""
return GenerationResponse(
id=None,
generation_job_id=job_id,
title="生成任务已提交",
mode="storybook" if request.output_mode == "storybook" else "generated",
generation_status="queued",
text_status="generating",
image_status="not_requested",
audio_status="not_requested",
last_error=None,
retryable_assets=[],
child_profile_id=request.child_profile_id,
universe_id=request.universe_id,
)
async def execute_generation_job_service(
job: GenerationJob,
db: AsyncSession,
) -> GenerationResponse:
"""Execute one previously accepted generation job inside the worker."""
try:
request = GenerationRequest.model_validate(job.request_payload or {})
response = await _generate_generation_service_with_job(
request,
job.user_id,
db,
job=job,
)
except HTTPException as exc:
await finish_generation_job(
db,
@@ -1151,6 +1210,21 @@ async def generate_generation_service(
return response
async def run_generation_job_service(
job_id: str,
db: AsyncSession,
) -> GenerationJob | None:
"""Claim and execute one generation job from the background queue."""
job = await claim_generation_job_for_worker(db, job_id=job_id)
if job is None:
logger.info("generation_job_execution_skipped", job_id=job_id)
return None
await execute_generation_job_service(job, db)
return job
async def _generate_generation_service_with_job(
request: GenerationRequest,
user_id: str,