feat(web): AI 立项方案生成+预填(种子门控 + 按字段接受)
向导阶段(project 未建)一键推演结构化书级蓝图预填立项向导。 - 新 SPEC project_plan_spec(analyst 档,纯预览 reads/writes=())+ schema ProjectPlanResult(书名候选 + 时空背景/叙事结构/故事核心/结局设计/基调, 全字段默认值守解析韧性)+ 注册 SCHEMA_CATALOG;计数三处 21→22 + regen golden。 - 端点 POST /skills/project-plan/generate(不带 project 前缀,绕开 toolbox 404): 请求体序列化向导草稿为 project_context,复用 build_brief_context + run_generator; 种子门控(缺 genre/logline→422);请求 schema 加 max_length 上界。 - run_generator/_build_request 的 project_id 放宽 uuid.UUID|None(向导无 project)。 - 前端 wizard.ts 加 mapPlanResultToWizardForm(错配字段折进 premise 不静默丢)+ applyPlanPatch(仅回填空字段、保护已编辑)+ useProjectPlan hook + PlanAssistant。
This commit is contained in:
@@ -52,6 +52,64 @@ class ProjectListResponse(BaseModel):
|
||||
projects: list[ProjectResponse] = Field(default_factory=list)
|
||||
|
||||
|
||||
# ---- AI 立项方案生成(灵感⑤;向导阶段 project 未建,纯预览不写库)----
|
||||
|
||||
# 请求字段长度上界(CR-H9:防超长向导草稿撑爆上下文/成本)。短字段=标签级,长字段=段落级。
|
||||
_PLAN_SHORT_MAX = 200
|
||||
_PLAN_LONG_MAX = 4000
|
||||
_PLAN_LIST_MAX = 20
|
||||
|
||||
|
||||
class ProjectPlanGenerateRequest(BaseModel):
|
||||
"""POST /skills/project-plan/generate:向导草稿种子(不带 project 前缀,立项前 project 未建)。
|
||||
|
||||
**种子门控**:`genre` + `logline` 为必给种子(端点校验非空,否则 422)——空向导不出方案,
|
||||
防 slop。其余字段有则贴合、无则不臆造(序列化进 project_context,仿 `_project_context`)。
|
||||
全字段带 `max_length` 上界(CR-H9),`brief` = 作者一句话方向(可空)。
|
||||
"""
|
||||
|
||||
genre: str | None = Field(default=None, max_length=_PLAN_SHORT_MAX, description="题材(种子)")
|
||||
logline: str | None = Field(
|
||||
default=None, max_length=_PLAN_LONG_MAX, description="一句话故事(种子)"
|
||||
)
|
||||
title: str | None = Field(
|
||||
default=None, max_length=_PLAN_SHORT_MAX, description="暂定书名(可空)"
|
||||
)
|
||||
premise: str | None = Field(default=None, max_length=_PLAN_LONG_MAX, description="立意(可空)")
|
||||
theme: str | None = Field(default=None, max_length=_PLAN_SHORT_MAX, description="主题(可空)")
|
||||
structure: str | None = Field(
|
||||
default=None, max_length=_PLAN_SHORT_MAX, description="故事结构(可空)"
|
||||
)
|
||||
tone: str | None = Field(default=None, max_length=_PLAN_SHORT_MAX, description="基调(可空)")
|
||||
ending_type: str | None = Field(
|
||||
default=None, max_length=_PLAN_SHORT_MAX, description="结局取向(可空)"
|
||||
)
|
||||
narrative_pov: str | None = Field(
|
||||
default=None, max_length=_PLAN_SHORT_MAX, description="叙事视角(可空)"
|
||||
)
|
||||
selling_points: list[str] = Field(
|
||||
default_factory=list, max_length=_PLAN_LIST_MAX, description="核心卖点(可空)"
|
||||
)
|
||||
# `str | None`(非 `str=""`):codegen 渲染为可选字段,前端种子请求可省略(brief 本就可空)。
|
||||
brief: str | None = Field(
|
||||
default=None, max_length=_PLAN_LONG_MAX, description="作者一句话方向/需求(可空)"
|
||||
)
|
||||
|
||||
|
||||
class ProjectPlanView(BaseModel):
|
||||
"""立项方案生成结果(贴 ww_agents.ProjectPlanResult;结构化预览,不入库)。
|
||||
|
||||
全字段有默认值(守解析韧性)——前端按字段回填立项向导,只填非空交集、不覆盖作者已编辑值。
|
||||
"""
|
||||
|
||||
title_candidates: list[str] = Field(default_factory=list, description="书名候选清单")
|
||||
setting: str = Field(default="", description="时空背景")
|
||||
narrative_structure: str = Field(default="", description="叙事结构")
|
||||
story_core: str = Field(default="", description="故事核心")
|
||||
ending_design: str = Field(default="", description="结局设计")
|
||||
tone: str = Field(default="", description="基调")
|
||||
|
||||
|
||||
class DraftStreamRequest(BaseModel):
|
||||
"""POST /projects/:id/chapters/:no/draft:本章生成的可选输入(T4-b)。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user