from pydantic import BaseModel, ConfigDict, Field from app.agents.state_schema import MemoirStateSchema as MemoirStateResponse __all__ = [ "BookResponse", "ChapterDetailResponse", "ChapterListItemResponse", "CoverGenerationResponse", "ExportPdfRequest", "ExportPdfResponse", "ImageAssetResponse", "MemoirStateResponse", "NextQuestionContextResponse", "ReadingSegmentResponse", "SetChapterStoryOrderRequest", "StatusMessageResponse", "StoryOrderResponse", "UpdateBookRequest", ] class ImageAssetResponse(BaseModel): placeholder: str = "" description: str = "" index: int = 0 status: str = "pending" prompt: str | None = None url: str | None = None provider: str | None = None style: str | None = None size: str | None = None error: str | None = None retryable: bool | None = None created_at: str | None = None updated_at: str | None = None class ReadingSegmentResponse(BaseModel): story_id: str body_markdown: str = "" cover_asset: ImageAssetResponse | None = None class UpdateBookRequest(BaseModel): title: str subtitle: str | None = None class ExportPdfRequest(BaseModel): book_id: str class SetChapterStoryOrderRequest(BaseModel): """按顺序绑定本章节要收录的 stories(覆盖原有 chapter_story_links)。""" story_ids: list[str] class BookResponse(BaseModel): id: str | None = None title: str | None = None total_pages: int | None = None total_words: int | None = None cover_image_url: str | None = None has_update: bool | None = None last_update_chapter_id: str | None = None message: str | None = None class StatusMessageResponse(BaseModel): status: str message: str | None = None class ExportPdfResponse(BaseModel): pdf_base64: str filename: str class ChapterListItemResponse(BaseModel): model_config = ConfigDict(extra="ignore") id: str title: str | None = None category: str | None = None order_index: int | None = None status: str = "draft" summary: str = "" canonical_markdown: str = "" cover_asset: ImageAssetResponse | None = None images: list[ImageAssetResponse] = Field(default_factory=list) word_count: int = 0 updated_at: str | None = None is_new: bool = False source_segments: list[str] = Field(default_factory=list) class ChapterDetailResponse(BaseModel): model_config = ConfigDict(extra="ignore") id: str title: str | None = None canonical_markdown: str = "" order_index: int | None = None status: str | None = None category: str | None = None images: list[ImageAssetResponse] = Field(default_factory=list) cover_asset: ImageAssetResponse | None = None reading_segments: list[ReadingSegmentResponse] = Field(default_factory=list) updated_at: str | None = None is_new: bool = False source_segments: list[str] = Field(default_factory=list) class CoverGenerationResponse(BaseModel): triggered: list[str] = Field(default_factory=list) class StoryOrderResponse(BaseModel): status: str chapter_id: str story_count: int class NextQuestionContextResponse(BaseModel): current_stage: str | None = None empty_slots: list[str] = Field(default_factory=list) covered_stages: list[str] = Field(default_factory=list)