feat: persist story generation states and cache audio
Some checks failed
Build and Push Docker Images / changes (push) Has been cancelled
Build and Push Docker Images / build-backend (push) Has been cancelled
Build and Push Docker Images / build-frontend (push) Has been cancelled
Build and Push Docker Images / build-admin-frontend (push) Has been cancelled

This commit is contained in:
2026-04-17 17:14:09 +08:00
parent 145be0e67b
commit a97a2fe005
17 changed files with 2045 additions and 849 deletions

View File

@@ -1,4 +1,4 @@
"""故事相关 Pydantic 模型。"""
"""Story-related Pydantic schemas."""
from datetime import datetime
from typing import Literal
@@ -11,7 +11,13 @@ MAX_EDU_THEME_LENGTH = 200
MAX_TTS_LENGTH = 4000
# ==================== 故事模型 ====================
class StoryStatusMixin(BaseModel):
"""Shared generation status fields returned by story APIs."""
generation_status: str
image_status: str
audio_status: str
last_error: str | None = None
class GenerateRequest(BaseModel):
@@ -24,8 +30,8 @@ class GenerateRequest(BaseModel):
universe_id: str | None = None
class StoryResponse(BaseModel):
"""Story response."""
class StoryResponse(StoryStatusMixin):
"""Story generation response."""
id: int
title: str
@@ -37,7 +43,7 @@ class StoryResponse(BaseModel):
universe_id: str | None = None
class StoryListItem(BaseModel):
class StoryListItem(StoryStatusMixin):
"""Story list item."""
id: int
@@ -47,8 +53,8 @@ class StoryListItem(BaseModel):
mode: str
class FullStoryResponse(BaseModel):
"""完整故事响应(含图片和音频状态)。"""
class FullStoryResponse(StoryStatusMixin):
"""Full story response with asset status."""
id: int
title: str
@@ -62,22 +68,19 @@ class FullStoryResponse(BaseModel):
universe_id: str | None = None
# ==================== 绘本模型 ====================
class StorybookRequest(BaseModel):
"""Storybook 生成请求。"""
"""Storybook generation request."""
keywords: str = Field(..., min_length=1, max_length=200)
page_count: int = Field(default=6, ge=4, le=12)
education_theme: str | None = Field(default=None, max_length=MAX_EDU_THEME_LENGTH)
generate_images: bool = Field(default=False, description="是否同时生成插图")
generate_images: bool = Field(default=False, description="Whether to generate images too.")
child_profile_id: str | None = None
universe_id: str | None = None
class StorybookPageResponse(BaseModel):
"""故事书单页响应。"""
"""One storybook page."""
page_number: int
text: str
@@ -85,8 +88,8 @@ class StorybookPageResponse(BaseModel):
image_url: str | None = None
class StorybookResponse(BaseModel):
"""故事书响应。"""
class StorybookResponse(StoryStatusMixin):
"""Storybook generation response."""
id: int | None = None
title: str
@@ -97,10 +100,29 @@ class StorybookResponse(BaseModel):
cover_url: str | None = None
# ==================== 成就模型 ====================
class StoryDetailResponse(StoryStatusMixin):
"""Story detail response for both stories and storybooks."""
id: int
title: str
story_text: str | None = None
pages: list[StorybookPageResponse] | None = None
cover_prompt: str | None
image_url: str | None
mode: str
child_profile_id: str | None = None
universe_id: str | None = None
class StoryImageResponse(StoryStatusMixin):
"""Cover image generation response."""
image_url: str | None
class AchievementItem(BaseModel):
"""Achievement item returned for a story."""
type: str
description: str
obtained_at: str | None = None