M4(文风): style-auditor 双轨(提取指纹/漂移第四审)+ jobs 长任务框架(zombie reaper) + 回炉 refine + GET /style read-back。 M5(生成+扩展): worldbuilder/character-gen(入库 continuity 409 gate + partition_writes 白名单 + schema→JSONB 形变); 网关多 provider 回退链/熔断/能力降级(Anthropic/Gemini 适配器);Skill registry + 表权限沙箱 + 规则; 前端 角色生成器/世界观/Codex/规则页/技能库/⌘K 命令面板。 K1(Kimi Code 订阅接入): OAuth device-flow(kimi-code)+ 静态 Console key(kimi-code-key)两路径; coding 端点 KimiCLI 伪造头(实测 UA allow-list 门禁,缺则 403)+ JSON 模式结构化(thinking ⊥ tool_choice)。 本地联调修复: CORS 中间件;assemble 注入 premise+「写第N章」指令(修空 prompt 400); GET /outline·/draft read-back + 大纲/工作台/审稿页重载;写页 client/server 常量边界 + notFound 健壮化; 字数 toLocaleString locale 水合;审稿页终稿从已存草稿 seed(修 accept 422)。 门禁: backend ruff/mypy(157)/alembic 无漂移/pytest 451 · frontend lint/tsc/vitest/build。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
143 lines
4.4 KiB
Python
143 lines
4.4 KiB
Python
"""项目(立项)与章节草稿的请求/响应 schema(C3 / ARCH §7.2)。
|
||
|
||
snake_case;前端经 OpenAPI 生成 TS 类型消费。改字段 → 前端必须 `pnpm gen:api`。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import uuid
|
||
from typing import Any, Literal
|
||
|
||
from pydantic import BaseModel, Field
|
||
|
||
|
||
class ProjectCreateRequest(BaseModel):
|
||
"""POST /projects:立项向导字段(owner_id 由后端补 stub,不入参)。"""
|
||
|
||
title: str = Field(min_length=1)
|
||
genre: str | None = None
|
||
logline: str | None = None
|
||
premise: str | None = None
|
||
theme: str | None = None
|
||
selling_points: list[Any] = Field(default_factory=list)
|
||
structure: str | None = None
|
||
|
||
|
||
class ProjectResponse(BaseModel):
|
||
"""项目视图(创建/列表/详情共用)。"""
|
||
|
||
id: uuid.UUID
|
||
title: str
|
||
genre: str | None = None
|
||
logline: str | None = None
|
||
premise: str | None = None
|
||
theme: str | None = None
|
||
selling_points: list[Any] = Field(default_factory=list)
|
||
structure: str | None = None
|
||
|
||
|
||
class ProjectListResponse(BaseModel):
|
||
"""GET /projects:项目列表。"""
|
||
|
||
projects: list[ProjectResponse] = Field(default_factory=list)
|
||
|
||
|
||
class DraftSaveRequest(BaseModel):
|
||
"""PUT /projects/:id/chapters/:no/draft:自动保存草稿正文。"""
|
||
|
||
text: str
|
||
|
||
|
||
class DraftResponse(BaseModel):
|
||
"""草稿保存结果(脱敏:只回元信息 + 长度,不回灌正文以外的衍生)。"""
|
||
|
||
project_id: uuid.UUID
|
||
chapter_no: int
|
||
volume: int
|
||
status: str
|
||
version: int
|
||
length: int
|
||
|
||
|
||
class DraftView(BaseModel):
|
||
"""GET .../draft:回灌已保存草稿(含正文,供工作台重载编辑器)。
|
||
|
||
与 PUT 的 `DraftResponse` 同元信息,**额外带 `content`**——读侧需要正文以重建编辑器,
|
||
保存侧不回灌正文故不带。`length` 仍按 content 字符数派生。
|
||
"""
|
||
|
||
project_id: uuid.UUID
|
||
chapter_no: int
|
||
volume: int
|
||
status: str
|
||
version: int
|
||
content: str
|
||
length: int
|
||
|
||
|
||
# ---- 审稿(T2.5)----
|
||
|
||
|
||
class ReviewRequest(BaseModel):
|
||
"""POST /projects/:id/chapters/:no/review:可选携带待审草稿正文。
|
||
|
||
不传 `draft` 时端点回退到已保存的草稿(chapter_repo)。
|
||
"""
|
||
|
||
draft: str | None = None
|
||
|
||
|
||
class ReviewHistoryItem(BaseModel):
|
||
"""单条审稿留痕(GET .../reviews 历史项;snake_case)。"""
|
||
|
||
id: uuid.UUID
|
||
project_id: uuid.UUID
|
||
chapter_no: int
|
||
chapter_version: int | None = None
|
||
conflicts: list[dict[str, Any]] = Field(default_factory=list)
|
||
foreshadow_sug: list[dict[str, Any]] = Field(default_factory=list)
|
||
style: dict[str, Any] | None = None
|
||
pace: dict[str, Any] | None = None
|
||
health_score: int | None = None
|
||
decisions: dict[str, Any] | None = None
|
||
|
||
|
||
class ReviewHistoryResponse(BaseModel):
|
||
"""GET /projects/:id/chapters/:no/reviews:审稿历史(新→旧)。"""
|
||
|
||
reviews: list[ReviewHistoryItem] = Field(default_factory=list)
|
||
|
||
|
||
# ---- 验收(T2.4)----
|
||
|
||
# 每个冲突的裁决:采纳改法 / 忽略 / 手改(R5)。
|
||
Verdict = Literal["accept", "ignore", "manual"]
|
||
|
||
|
||
class ConflictDecision(BaseModel):
|
||
"""对最近一次审稿留痕里**某个冲突**(按其在 conflicts 列表的下标定位)的裁决。"""
|
||
|
||
conflict_index: int = Field(ge=0, description="冲突在最近审稿 conflicts 列表中的下标")
|
||
verdict: Verdict = Field(description="采纳改法 / 忽略 / 手改")
|
||
note: str | None = Field(default=None, description="可选裁决备注(如手改说明)")
|
||
|
||
|
||
class AcceptRequest(BaseModel):
|
||
"""POST /projects/:id/chapters/:no/accept:裁决清单 + 可能改过的终稿。"""
|
||
|
||
final_text: str = Field(min_length=1, description="作者裁决/改稿后的最终验收文本")
|
||
decisions: list[ConflictDecision] = Field(
|
||
default_factory=list, description="对最近审稿每个冲突的裁决(每冲突必有其一,R5)"
|
||
)
|
||
|
||
|
||
class AcceptResponse(BaseModel):
|
||
"""验收「本次将更新」清单(ARCH §7.2 写回结果)。"""
|
||
|
||
project_id: uuid.UUID
|
||
chapter_no: int
|
||
accepted_version: int = Field(description="晋升到的 accepted 版次(max+1)")
|
||
digest_added: bool = Field(description="是否新增了一行 chapter_digests")
|
||
decisions_recorded: int = Field(description="本次写回的裁决条数")
|
||
review_id: uuid.UUID | None = Field(default=None, description="写回裁决的审稿留痕行 id")
|