286 lines
6.9 KiB
Python
286 lines
6.9 KiB
Python
"""
|
||
爽文(Fiction / Novel)数据模型。
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
from typing import Dict, List, Optional
|
||
|
||
from pydantic import BaseModel, Field
|
||
|
||
|
||
class EmotionFlowStep(BaseModel):
|
||
key: str
|
||
text: str
|
||
|
||
|
||
class EmotionFlow(BaseModel):
|
||
id: str
|
||
intro: str
|
||
tags: List[str] = Field(default_factory=list)
|
||
steps: List[EmotionFlowStep] = Field(default_factory=list)
|
||
|
||
|
||
class EmotionFlowCatalog(BaseModel):
|
||
flows: List[EmotionFlow] = Field(default_factory=list)
|
||
|
||
|
||
class GuideGlobalEntry(BaseModel):
|
||
layer: str
|
||
title: str
|
||
content: str
|
||
|
||
|
||
class GuideGlobalEntries(BaseModel):
|
||
entries: List[GuideGlobalEntry] = Field(default_factory=list)
|
||
|
||
|
||
class FictionGuideWorldbook(BaseModel):
|
||
"""Book-local guide 世界书:本书具体人设 / 爽点 / 用户体验 / 禁区。"""
|
||
|
||
persona: str = ""
|
||
highlight: str = ""
|
||
experience: str = ""
|
||
forbiddenZones: str = ""
|
||
|
||
|
||
class FictionBookMeta(BaseModel):
|
||
id: str
|
||
title: str
|
||
allowedFlowIds: List[str] = Field(default_factory=list)
|
||
createdAt: str = ""
|
||
updatedAt: str = ""
|
||
|
||
|
||
class FictionBookSummary(BaseModel):
|
||
id: str
|
||
title: str
|
||
allowedFlowIds: List[str] = Field(default_factory=list)
|
||
updatedAt: str = ""
|
||
|
||
|
||
class FictionPrompts(BaseModel):
|
||
openBook: str = ""
|
||
coarseOutline: str = ""
|
||
eventPlan: str = ""
|
||
chapter: str = ""
|
||
nudge: str = ""
|
||
|
||
|
||
class FictionReaderSettings(BaseModel):
|
||
contextWindowChars: int = 2000
|
||
prefetchRemainingWords: int = 300
|
||
|
||
|
||
class FictionPipelineSettings(BaseModel):
|
||
"""半自动流水线:各层 ON=自动,OFF=需手动触发。"""
|
||
|
||
semiAuto: bool = False
|
||
autoCoarse: bool = True
|
||
autoEventPlan: bool = True
|
||
autoChapter: bool = True
|
||
|
||
|
||
class FictionBookSettings(BaseModel):
|
||
prompts: FictionPrompts = Field(default_factory=FictionPrompts)
|
||
reader: FictionReaderSettings = Field(default_factory=FictionReaderSettings)
|
||
pipeline: FictionPipelineSettings = Field(default_factory=FictionPipelineSettings)
|
||
|
||
|
||
class CreateFictionBookRequest(BaseModel):
|
||
title: str
|
||
inspiration: str = ""
|
||
guide: FictionGuideWorldbook
|
||
allowedFlowIds: List[str] = Field(default_factory=list)
|
||
|
||
|
||
class OpenBookRequest(BaseModel):
|
||
inspiration: str
|
||
profile_id: Optional[str] = None
|
||
api_config: Optional[Dict[str, str]] = None
|
||
|
||
|
||
class OpenBookResult(BaseModel):
|
||
title: str
|
||
optimizedIntro: str
|
||
guide: FictionGuideWorldbook
|
||
allowedFlowIds: List[str] = Field(default_factory=list)
|
||
|
||
|
||
class UpdateFictionBookSettingsRequest(BaseModel):
|
||
prompts: Optional[FictionPrompts] = None
|
||
reader: Optional[FictionReaderSettings] = None
|
||
pipeline: Optional[FictionPipelineSettings] = None
|
||
|
||
|
||
class FictionPipelineTickResult(BaseModel):
|
||
run: FictionRunState
|
||
started: bool = False
|
||
pendingStages: List[str] = Field(default_factory=list)
|
||
|
||
|
||
class VolumeOutline(BaseModel):
|
||
"""卷纲:当前 10~30 章的阶段规划,不是整本书全局大纲。"""
|
||
|
||
id: str
|
||
order: int = 1
|
||
title: str = ""
|
||
goal: str = ""
|
||
coreConflict: str = ""
|
||
powerProgression: str = ""
|
||
emotionalPromise: str = ""
|
||
endingHook: str = ""
|
||
targetChapterCount: int = 20
|
||
primaryEmotionFlowId: str = ""
|
||
status: str = "active"
|
||
|
||
|
||
class CoarseOutlineEvent(BaseModel):
|
||
"""兼容旧 coarseOutline.events,同时作为新版事件串条目的轻量视图。"""
|
||
|
||
id: str
|
||
title: str
|
||
summary: str = ""
|
||
order: int = 1
|
||
volumeId: str = ""
|
||
|
||
|
||
class CoarseOutline(BaseModel):
|
||
events: List[CoarseOutlineEvent] = Field(default_factory=list)
|
||
version: int = 1
|
||
|
||
|
||
class EventChainItem(BaseModel):
|
||
"""事件串:卷纲 + 情感链在剧情层的落地。"""
|
||
|
||
id: str
|
||
volumeId: str = ""
|
||
order: int = 1
|
||
title: str = ""
|
||
summary: str = ""
|
||
purpose: str = ""
|
||
conflict: str = ""
|
||
turningPoint: str = ""
|
||
expectedPayoff: str = ""
|
||
targetChapterCount: int = 3
|
||
emotionFlowId: str = ""
|
||
emotionStepKey: str = ""
|
||
emotionStepText: str = ""
|
||
status: str = "planned"
|
||
|
||
|
||
class FlowStepsPlan(BaseModel):
|
||
起: str = ""
|
||
承: str = ""
|
||
转: str = ""
|
||
合: str = ""
|
||
|
||
|
||
class ChapterPlanItem(BaseModel):
|
||
seq: int
|
||
|
||
# 旧字段:保留以兼容现有 metadata.events[eventId].chapterPlan。
|
||
phaseKey: str = ""
|
||
phaseSlice: str = ""
|
||
brief: str = ""
|
||
|
||
# 新字段:章纲是写章前最具体的规划层。
|
||
eventId: str = ""
|
||
title: str = ""
|
||
goal: str = ""
|
||
opening: str = ""
|
||
mainConflict: str = ""
|
||
emotionalTurn: str = ""
|
||
emotionStepKey: str = ""
|
||
emotionGoal: str = ""
|
||
payoff: str = ""
|
||
endingHook: str = ""
|
||
forbidden: str = ""
|
||
targetWords: int = 2000
|
||
|
||
status: str = "planned"
|
||
|
||
|
||
class EventPlanEntry(BaseModel):
|
||
emotionFlowId: str = ""
|
||
flowStepsPlan: FlowStepsPlan = Field(default_factory=FlowStepsPlan)
|
||
chapterPlan: List[ChapterPlanItem] = Field(default_factory=list)
|
||
|
||
|
||
class FictionProgress(BaseModel):
|
||
currentChapterSeq: int = 0
|
||
charOffset: int = 0
|
||
ttsPaused: bool = False
|
||
genPaused: bool = False
|
||
|
||
|
||
class FictionChapter(BaseModel):
|
||
seq: int
|
||
title: str = ""
|
||
body: str = ""
|
||
charCount: int = 0
|
||
status: str = "written"
|
||
eventId: str = ""
|
||
phaseKey: str = ""
|
||
createdAt: str = ""
|
||
|
||
|
||
class FictionChapterSummary(BaseModel):
|
||
seq: int
|
||
title: str = ""
|
||
charCount: int = 0
|
||
eventId: str = ""
|
||
phaseKey: str = ""
|
||
|
||
|
||
class FictionBookMetadata(BaseModel):
|
||
# v2 新规划结构:不再保留旧 coarseOutline/events 作为持久化主结构。
|
||
version: int = 2
|
||
volumes: List[VolumeOutline] = Field(default_factory=list)
|
||
eventChains: Dict[str, List[EventChainItem]] = Field(default_factory=dict)
|
||
chapterPlans: Dict[str, List[ChapterPlanItem]] = Field(default_factory=dict)
|
||
progress: FictionProgress = Field(default_factory=FictionProgress)
|
||
|
||
|
||
class FictionRunState(BaseModel):
|
||
status: str = "idle"
|
||
pipelineStage: Optional[str] = None
|
||
stage: str = "idle"
|
||
message: Optional[str] = None
|
||
progress: Optional[Dict[str, int]] = None
|
||
updatedAt: str = ""
|
||
|
||
|
||
class FictionStartReadingResult(BaseModel):
|
||
run: FictionRunState
|
||
started: bool = False
|
||
|
||
|
||
class FictionGenerationRequest(BaseModel):
|
||
profile_id: Optional[str] = None
|
||
api_config: Optional[Dict[str, str]] = None
|
||
event_id: Optional[str] = None
|
||
seq: Optional[int] = None
|
||
stream: bool = False
|
||
|
||
|
||
class UpdateFictionProgressRequest(BaseModel):
|
||
currentChapterSeq: Optional[int] = None
|
||
charOffset: Optional[int] = None
|
||
ttsPaused: Optional[bool] = None
|
||
genPaused: Optional[bool] = None
|
||
|
||
|
||
class FictionPrefetchRequest(BaseModel):
|
||
profile_id: Optional[str] = None
|
||
api_config: Optional[Dict[str, str]] = None
|
||
currentChapterSeq: int
|
||
charOffset: int = 0
|
||
remainingWords: Optional[int] = None
|
||
|
||
|
||
class FictionPrefetchResult(BaseModel):
|
||
run: FictionRunState
|
||
started: bool = False
|
||
targetSeq: Optional[int] = None
|
||
skippedReason: Optional[str] = None
|