from __future__ import annotations from datetime import datetime from pydantic import BaseModel, Field class NovelCreate(BaseModel): title: str = Field(min_length=1, max_length=200) description: str = Field(default="", max_length=2000) class NovelUpdate(BaseModel): title: str | None = Field(default=None, min_length=1, max_length=200) description: str | None = Field(default=None, max_length=2000) class NovelRead(BaseModel): id: int title: str description: str created_at: datetime updated_at: datetime class NovelList(BaseModel): items: list[NovelRead] total: int # ── 大纲 ── class OutlineCreate(BaseModel): summary: str = Field(min_length=1, max_length=200) detail: str = Field(default="", max_length=1000) parent_id: int | None = None class OutlineUpdate(BaseModel): summary: str | None = Field(default=None, min_length=1, max_length=200) detail: str | None = Field(default=None, max_length=1000) sort_order: int | None = None class OutlineRead(BaseModel): id: int novel_id: int parent_id: int | None = None sort_order: int summary: str detail: str children: list[OutlineRead] = [] created_at: datetime updated_at: datetime class OutlineList(BaseModel): items: list[OutlineRead] total: int class OutlineReorder(BaseModel): """排序请求:按顺序传入大纲id列表""" ordered_ids: list[int] # ── 角色 ── class CharacterCreate(BaseModel): name: str = Field(min_length=1, max_length=100) description: str = Field(default="", max_length=1000) class CharacterUpdate(BaseModel): name: str | None = Field(default=None, min_length=1, max_length=100) description: str | None = Field(default=None, max_length=1000) class CharacterRead(BaseModel): id: int novel_id: int name: str description: str created_at: datetime updated_at: datetime class CharacterList(BaseModel): items: list[CharacterRead] total: int # ── 章节 ── class ChapterCreate(BaseModel): title: str = Field(min_length=1, max_length=200) content: str = Field(default="", max_length=5000) class ChapterUpdate(BaseModel): title: str | None = Field(default=None, min_length=1, max_length=200) content: str | None = Field(default=None, max_length=5000) sort_order: int | None = None class ChapterRead(BaseModel): id: int novel_id: int sort_order: int title: str content: str created_at: datetime updated_at: datetime class ChapterList(BaseModel): items: list[ChapterRead] total: int class ChapterReorder(BaseModel): """排序请求:按顺序传入章节id列表""" ordered_ids: list[int] # ── 世界观 ── class WorldSettingUpdate(BaseModel): content: str = Field(default="", max_length=5000) class WorldSettingRead(BaseModel): id: int novel_id: int content: str created_at: datetime updated_at: datetime # ── 文件 ── class FileRead(BaseModel): id: int novel_id: int filename: str file_size: int mime_type: str text_length: int created_at: datetime class FileList(BaseModel): items: list[FileRead] total: int # ── Skill ── class SkillCreate(BaseModel): name: str = Field(min_length=1, max_length=100) description: str = Field(default="", max_length=500) system_prompt: str = Field(default="", max_length=10000) allowed_tools: list[str] = Field(default_factory=list) novel_id: int | None = None class SkillUpdate(BaseModel): name: str | None = Field(default=None, min_length=1, max_length=100) description: str | None = Field(default=None, max_length=500) system_prompt: str | None = Field(default=None, max_length=10000) allowed_tools: list[str] | None = None class SkillRead(BaseModel): id: int name: str description: str system_prompt: str allowed_tools: list[str] is_builtin: bool novel_id: int | None created_at: datetime updated_at: datetime class SkillList(BaseModel): items: list[SkillRead] total: int class ToolInfo(BaseModel): name: str description: str # ── AI配置 ── class AIConfigSave(BaseModel): provider: str = Field(default="openai") base_url: str = Field(default="https://api.openai.com/v1") api_key: str = Field(min_length=1) model: str = Field(default="gpt-4o") class AIConfigRead(BaseModel): id: int provider: str base_url: str api_key_masked: str # 脱敏后的key model: str updated_at: datetime # ── 聊天 ── class ChatMessageInput(BaseModel): role: str content: str class PendingToolCall(BaseModel): """待审批的工具调用""" id: str name: str arguments: dict class ChatRequest(BaseModel): messages: list[ChatMessageInput] novel_id: int | None = None page_context: str | None = None # 用户当前所在页面,如 "大纲页" "世界观页" tools_enabled: bool = True # 是否启用工具调用 auto_approve_tools: bool = False # 是否自动执行工具(无需人工审批) skill_id: int | None = None # 激活的 Skill ID # 工具审批续传 pending_tool_calls: list[PendingToolCall] | None = None assistant_text: str | None = None # LLM 在工具调用前输出的文本 class ChatMessageRead(BaseModel): id: int novel_id: int | None role: str content: str created_at: datetime class ChatMessageList(BaseModel): items: list[ChatMessageRead] total: int # 解决 OutlineRead 自引用 OutlineRead.model_rebuild()