feat: 初始提交

This commit is contained in:
anonymous
2025-10-21 09:38:26 +08:00
committed by t59688
parent 2965b8e28f
commit c9fc816fab
175 changed files with 23968 additions and 87 deletions

View File

View File

@@ -0,0 +1,49 @@
from datetime import datetime
from typing import Optional
from pydantic import BaseModel, Field
class Statistics(BaseModel):
novel_count: int
user_count: int
api_request_count: int
class DailyRequestLimit(BaseModel):
limit: int = Field(..., ge=0, description="匿名用户每日可用次数")
class UpdateLogRead(BaseModel):
id: int
content: str
created_at: datetime
created_by: Optional[str] = None
is_pinned: bool
class Config:
from_attributes = True
class UpdateLogBase(BaseModel):
content: Optional[str] = None
is_pinned: Optional[bool] = None
class UpdateLogCreate(UpdateLogBase):
content: str
class UpdateLogUpdate(UpdateLogBase):
pass
class AdminNovelSummary(BaseModel):
id: str
title: str
owner_id: int
owner_username: str
genre: str
last_edited: str
completed_chapters: int
total_chapters: int

View File

@@ -0,0 +1,23 @@
from typing import Optional
from pydantic import BaseModel, Field
class SystemConfigBase(BaseModel):
key: str = Field(..., description="配置键,需全局唯一")
value: str = Field(..., description="配置值,统一存储为字符串")
description: Optional[str] = Field(default=None, description="配置用途说明")
class SystemConfigCreate(SystemConfigBase):
pass
class SystemConfigUpdate(BaseModel):
value: Optional[str] = Field(default=None)
description: Optional[str] = Field(default=None)
class SystemConfigRead(SystemConfigBase):
class Config:
from_attributes = True

View File

@@ -0,0 +1,20 @@
from typing import Optional
from pydantic import BaseModel, HttpUrl, Field
class LLMConfigBase(BaseModel):
llm_provider_url: Optional[HttpUrl] = Field(default=None, description="自定义 LLM 服务地址")
llm_provider_api_key: Optional[str] = Field(default=None, description="自定义 LLM API Key")
llm_provider_model: Optional[str] = Field(default=None, description="自定义模型名称")
class LLMConfigCreate(LLMConfigBase):
pass
class LLMConfigRead(LLMConfigBase):
user_id: int
class Config:
from_attributes = True

View File

@@ -0,0 +1,170 @@
from enum import Enum
from typing import Any, Dict, List, Optional
from pydantic import BaseModel, Field
class ChoiceOption(BaseModel):
"""前端选择项描述,用于动态 UI 控件。"""
id: str
label: str
class UIControl(BaseModel):
"""描述前端应渲染的组件类型与配置。"""
type: str = Field(..., description="控件类型,如 single_choice/text_input")
options: Optional[List[ChoiceOption]] = Field(default=None, description="可选项列表")
placeholder: Optional[str] = Field(default=None, description="输入提示文案")
class ConverseResponse(BaseModel):
"""概念对话接口的统一返回体。"""
ai_message: str
ui_control: UIControl
conversation_state: Dict[str, Any]
is_complete: bool = False
ready_for_blueprint: Optional[bool] = None
class ConverseRequest(BaseModel):
"""概念对话接口的请求体。"""
user_input: Dict[str, Any]
conversation_state: Dict[str, Any]
class ChapterGenerationStatus(str, Enum):
NOT_GENERATED = "not_generated"
GENERATING = "generating"
EVALUATING = "evaluating"
SELECTING = "selecting"
FAILED = "failed"
EVALUATION_FAILED = "evaluation_failed"
WAITING_FOR_CONFIRM = "waiting_for_confirm"
SUCCESSFUL = "successful"
class ChapterOutline(BaseModel):
chapter_number: int
title: str
summary: str
class Chapter(ChapterOutline):
real_summary: Optional[str] = None
content: Optional[str] = None
versions: Optional[List[str]] = None
evaluation: Optional[str] = None
generation_status: ChapterGenerationStatus = ChapterGenerationStatus.NOT_GENERATED
class Relationship(BaseModel):
character_from: str
character_to: str
description: str
class Blueprint(BaseModel):
title: str
target_audience: str = ""
genre: str = ""
style: str = ""
tone: str = ""
one_sentence_summary: str = ""
full_synopsis: str = ""
world_setting: Dict[str, Any] = {}
characters: List[Dict[str, Any]] = []
relationships: List[Relationship] = []
chapter_outline: List[ChapterOutline] = []
class NovelProject(BaseModel):
id: str
user_id: int
title: str
initial_prompt: str
conversation_history: List[Dict[str, Any]] = []
blueprint: Optional[Blueprint] = None
chapters: List[Chapter] = []
class Config:
from_attributes = True
class NovelProjectSummary(BaseModel):
id: str
title: str
genre: str
last_edited: str
completed_chapters: int
total_chapters: int
class BlueprintGenerationResponse(BaseModel):
blueprint: Blueprint
ai_message: str
class ChapterGenerationResponse(BaseModel):
ai_message: str
chapter_versions: List[Dict[str, Any]]
class NovelSectionType(str, Enum):
OVERVIEW = "overview"
WORLD_SETTING = "world_setting"
CHARACTERS = "characters"
RELATIONSHIPS = "relationships"
CHAPTER_OUTLINE = "chapter_outline"
CHAPTERS = "chapters"
class NovelSectionResponse(BaseModel):
section: NovelSectionType
data: Dict[str, Any]
class GenerateChapterRequest(BaseModel):
chapter_number: int
writing_notes: Optional[str] = Field(default=None, description="章节额外写作指令")
class SelectVersionRequest(BaseModel):
chapter_number: int
version_index: int
class EvaluateChapterRequest(BaseModel):
chapter_number: int
class UpdateChapterOutlineRequest(BaseModel):
chapter_number: int
title: str
summary: str
class DeleteChapterRequest(BaseModel):
chapter_numbers: List[int]
class GenerateOutlineRequest(BaseModel):
start_chapter: int
num_chapters: int
class BlueprintPatch(BaseModel):
one_sentence_summary: Optional[str] = None
full_synopsis: Optional[str] = None
world_setting: Optional[Dict[str, Any]] = None
characters: Optional[List[Dict[str, Any]]] = None
relationships: Optional[List[Relationship]] = None
chapter_outline: Optional[List[ChapterOutline]] = None
class EditChapterRequest(BaseModel):
chapter_number: int
content: str

View File

@@ -0,0 +1,56 @@
from typing import Any, List, Optional
from pydantic import BaseModel, Field
class PromptBase(BaseModel):
"""Prompt 基础模型。"""
name: str = Field(..., description="唯一标识,用于代码引用")
title: Optional[str] = Field(default=None, description="可读标题")
content: str = Field(..., description="提示词具体内容")
tags: Optional[List[str]] = Field(default=None, description="标签集合")
class PromptCreate(PromptBase):
"""创建 Prompt 时使用的模型。"""
pass
class PromptUpdate(BaseModel):
"""更新 Prompt 时使用的模型。"""
title: Optional[str] = Field(default=None)
content: Optional[str] = Field(default=None)
tags: Optional[List[str]] = Field(default=None)
class PromptRead(PromptBase):
"""对外暴露的 Prompt 数据结构。"""
id: int
class Config:
from_attributes = True
@classmethod
def model_validate(cls, obj: Any, *args: Any, **kwargs: Any) -> "PromptRead": # type: ignore[override]
"""在转换 ORM 模型时,将字符串标签拆分为列表。"""
if hasattr(obj, "id") and hasattr(obj, "name"):
raw_tags = getattr(obj, "tags", None)
if isinstance(raw_tags, str):
processed = [tag for tag in raw_tags.split(",") if tag]
elif isinstance(raw_tags, list):
processed = raw_tags
else:
processed = None
data = {
"id": getattr(obj, "id"),
"name": getattr(obj, "name"),
"title": getattr(obj, "title", None),
"content": getattr(obj, "content", None),
"tags": processed,
}
return super().model_validate(data, *args, **kwargs)
return super().model_validate(obj, *args, **kwargs)

View File

@@ -0,0 +1,74 @@
from pydantic import BaseModel, EmailStr, Field
from typing import Optional
class UserBase(BaseModel):
"""用户基础数据结构,供多处复用。"""
username: str = Field(..., description="用户名")
email: Optional[EmailStr] = Field(default=None, description="邮箱,可选")
class UserCreate(UserBase):
"""注册时使用的模型。"""
password: str = Field(..., min_length=6, description="明文密码")
class UserUpdate(BaseModel):
"""用户信息修改模型。"""
email: Optional[EmailStr] = Field(default=None, description="邮箱")
password: Optional[str] = Field(default=None, min_length=6, description="新密码")
class User(UserBase):
"""对外暴露的用户信息。"""
id: int = Field(..., description="用户主键")
is_admin: bool = Field(default=False, description="是否为管理员")
must_change_password: bool = Field(default=False, description="是否需要强制修改密码")
class Config:
from_attributes = True
class UserInDB(User):
"""数据库内部使用的模型,包含哈希后的密码。"""
hashed_password: str
class Token(BaseModel):
"""登录成功后返回的访问令牌。"""
access_token: str
token_type: str = "bearer"
must_change_password: bool = Field(default=False, description="是否需要强制修改密码")
class TokenPayload(BaseModel):
"""JWT 负载信息。"""
sub: str
is_admin: bool = False
class UserRegistration(UserCreate):
"""注册接口需要的字段,包含邮箱验证码。"""
verification_code: str = Field(..., min_length=4, max_length=10, description="邮箱验证码")
class PasswordChangeRequest(BaseModel):
"""管理员修改密码请求模型。"""
old_password: str = Field(..., min_length=6, description="当前密码")
new_password: str = Field(..., min_length=8, description="新密码")
class AuthOptions(BaseModel):
"""认证相关开关信息,供前端动态控制功能。"""
allow_registration: bool = Field(..., description="是否允许开放用户注册")
enable_linuxdo_login: bool = Field(..., description="是否启用 Linux.do 登录")