Skip to content

Latest commit

 

History

History
317 lines (253 loc) · 9.88 KB

File metadata and controls

317 lines (253 loc) · 9.88 KB

API 文档(NovelToScript 后端)

backend/internal/api 实现一致。所有接口前缀 /api/v1,默认监听 :8080cmd/server)。 单进程:API + 进程内队列 + Worker 合一,不依赖 Redis(见 DESIGN §2.1)。

0. 通用约定

说明
Base URL http://localhost:8080/api/v1
请求体 JSON(Content-Type: application/json
成功响应 JSON 或 YAML(取剧本/设定集为 YAML 文本)
错误响应 { "error": "原因" },配合对应 HTTP 状态码
CORS 允许任意来源(Access-Control-Allow-Origin: *,方法 GET/POST/PUT/OPTIONS),前端开发可直连
鉴权 无(单用户本地工具)

作业状态机

queued → analyzing(Phase0) → converting(Phase1: i/N) → assembling(Phase2) → done
   ▲                              │  └──────────(任一阶段出错)──────────┘→ failed
   │ resume                       │ pause
   └──────── paused ◀─────────────┘   (paused / failed 都可 resume,从断点章续跑)
状态 含义
queued 已入队,待处理
analyzing Phase 0:全局预分析(StoryBible)
converting Phase 1:逐章转换(看 doneChapters/totalChapters
assembling Phase 2:汇总成完整剧本
done 完成,可取 screenplay
failed 失败,error 字段有原因(可 resume 从断点续跑)
paused 已暂停(pause 置位,已完成章节保留,可 resume

1. 健康检查

GET /api/v1/health

200

{ "status": "ok" }

2. 提交转换作业

POST /api/v1/conversions

把整本小说原文提交转换。异步:立即返回 jobId,转换在后台跑。

请求体

字段 类型 必填 说明
text string 小说原文(≥3 章效果最佳;服务端做章节切分)
title string 作品标题(用于剧本 title
{ "title": "兄妹", "text": "第一章 归来\n林夜推门而入……\n\n第二章 重逢\n……" }

响应

含义
202 Accepted 已入队 { "jobId": "uuid" }
400 请求体非法(如缺 text { "error": "..." }
500 建作业/存原文失败 { "error": "..." }
503 队列已满 { "error": "..." }
curl -XPOST localhost:8080/api/v1/conversions \
  -H 'Content-Type: application/json' \
  -d '{"title":"兄妹","text":"第一章 ...\n\n第二章 ..."}'
# => {"jobId":"9fd61ae3-45ac-4cd4-ba0c-dd06cf9fa9b9"}

2.1 上传文件提交

POST /api/v1/conversions/upload      # multipart/form-data

与 §2 等价,但用文件上传代替 JSON 正文。后端解析文件 → 文本后建作业。

表单字段

字段 类型 必填 说明
file file 小说文件,支持 .txt / .md / .epub(≤32MB)
title string 标题(缺省取文件名去扩展名)
  • 编码.txt/.md 自动识别 UTF-8 / GB18030 / GBK(中文小说常见),去 BOM。
  • epub:按 OPF spine 阅读顺序解包提取正文(标准库 archive/zip)。

响应:同 §2(202 {jobId} / 400 文件缺失或解析失败 / 413 过大 / 503 队列满)。

curl -XPOST localhost:8080/api/v1/conversions/upload \
  -F 'file=@骆驼祥子.txt' -F 'title=骆驼祥子'
# => {"jobId":"..."}

3. 查作业状态/进度

GET /api/v1/conversions/{id}

200

{
  "id": "9fd61ae3-...",
  "title": "兄妹",
  "status": "converting",
  "totalChapters": 12,
  "doneChapters": 3,
  "error": "",
  "createdAt": 1749200000,
  "updatedAt": 1749200200
}
  • 404 { "error": "作业不存在" }
  • 前端可每 1~2s 轮询此接口作为 SSE 的兜底(权威进度在此)。

3.1 任务列表(历史)

GET /api/v1/conversions

返回全部作业,按 createdAt 倒序,元素同 §3。供前端做历史/续跑入口。

200 —— JSON 数组(无作业时 [])。


3.2 暂停 / 继续

POST /api/v1/conversions/{id}/pause     # 暂停进行中的作业
POST /api/v1/conversions/{id}/resume    # 继续(暂停或失败的作业,从断点章续跑)
  • pause:真正取消正在跑的作业(停止 LLM 调用),已完成章节保留,置 paused
  • resume:置回 queued 重新入队,从 doneChapters 的下一章续跑;pausedfailed 都可 resume
含义
200 成功,返回最新状态(同 §3)
404 作业不存在
409 状态不允许(如对已 done 的作业 pause,或对未 paused/failed 的作业 resume
curl -XPOST localhost:8080/api/v1/conversions/$ID/pause
curl -XPOST localhost:8080/api/v1/conversions/$ID/resume

4. 实时进度(SSE)

GET /api/v1/conversions/{id}/events

text/event-stream。连接后先推一次当前 status,随后持续推送进度事件,直到 done/failed 或客户端断开。

事件类型(SSE event: 字段)

event data 说明
status 状态字符串 连接时的当前状态;暂停/继续时也会推一次(如 paused/queued
progress "i/N" 第 i 章完成(共 N 章)
delta 文本分片 模型流式吐字(打字机效果,本章生成中)
done 作业完成
failed 错误信息 作业失败

SSE 是"临时态",事件可能丢;权威进度始终以 §3 轮询为准

浏览器用法

const es = new EventSource(`/api/v1/conversions/${id}/events`);
es.addEventListener("delta",    e => append(e.data));   // 打字机
es.addEventListener("progress", e => setProgress(e.data));
es.addEventListener("done",     () => { es.close(); fetchScreenplay(); });
es.addEventListener("failed",   e => { es.close(); showError(e.data); });

5. 取剧本

GET /api/v1/conversions/{id}/screenplay          # YAML(默认)
GET /api/v1/conversions/{id}/screenplay?format=json   # JSON

按需汇总:从已生成的场景实时汇总成完整剧本(合并各章 + 角色名→ID 消解 + 校验)。

含义
200 YAML(application/x-yaml)或 JSON
404 作业不存在,或尚无可汇总场景(未就绪)

剧本结构(YAML,详见 docs/SCHEMA.md

schema_version: "1.0"
title: 兄妹
logline: (待补)
meta:
  source_chapters: [1, 2]
  chapter_count: 2
  generated_by: NovelToScript
  generated_at: "2026-06-06T14:28:35Z"
characters:
  - id: char_001
    name: 林夜
    aliases: [小夜]
    role: protagonist      # protagonist/antagonist/supporting/minor(可空)
    description: 归来的空宅主人
scenes:
  - id: s1_1
    chapter: 1             # 来源章节(溯源)
    order: 1               # 全局顺序(严格递增)
    heading: { setting: INT, location: 林家老宅, time: 夜 }   # setting: INT/EXT/INT-EXT
    synopsis: ""           # 可空;自动兜底场景会标 "[自动兜底…请人工整理]"
    characters: [char_001] # 出场角色 ID
    elements:
      - { type: action,    text: "林夜推门而入。" }
      - { type: dialogue,  character: char_001, line: "谁在那儿?", parenthetical: "压低声音" }
      - { type: narration, text: "风从窗缝钻入。" }
    transition: "切至:"     # 可空

6. 取角色库

GET /api/v1/conversions/{id}/characters

返回 CharacterKB 全量角色(比剧本里的 characters 更详细,含逐章累积的状态)。

200 —— JSON 数组,每个元素:

{
  "id": "char_001",
  "name": "林夜",
  "aliases": ["小夜"],
  "one_line": "归来的空宅主人",
  "personality": ["念旧", "沉稳", "警觉"],
  "appearance": "",
  "abilities": [{ "name": "御剑术", "since_chapter": 2 }],
  "key_actions": [{ "chapter": 1, "event": "点亮油灯" }],
  "relationships": { "林溪": { "relation": "妹妹", "status": "重逢" } },
  "arc_state": "",
  "emotional_state": "激动",
  "first_seen": 1,
  "last_seen": 3
}

未生成时返回 []


7. 取全局设定集

GET /api/v1/conversions/{id}/story-bible          # YAML(默认)
GET /api/v1/conversions/{id}/story-bible?format=json   # JSON(结构化,供前端可视化)

200 —— Phase 0 产出的 StoryBible。?format=json 输出同结构的 JSON(键为 snake_case);默认输出 YAML 原样:

synopsis: 一个关于兄妹乱世重逢的故事
theme: 亲情
tone: 沉郁
genre: 古装
characters:
  - { name: 林夜, aliases: [小夜], role: 主角, description: ... }
timeline: ["林夜归来", "兄妹重逢"]
glossary:
  - { term: 御剑术, definition: ... }
  • 404 { "error": "设定集尚未就绪" }(Phase 0 未完成)。

8. 保存编辑后的剧本(规划中,未实现)

PUT /api/v1/conversions/{id}/screenplay

当前未实现。规划:接收前端编辑后的 screenplay.yaml,校验后落库覆盖。需先给 store 增加剧本槽。前端可先做只读预览。


9. 端到端时序

前端                          后端
 │ POST /conversions {text} ──►│ 建作业(queued)+存原文+入队 → 202 {jobId}
 │◄────────── {jobId} ─────────│
 │ open SSE /{id}/events ─────►│ 推 status/progress/delta…
 │ (并行)轮询 GET /{id} ─────►│ 返回 status/i/N(兜底)
 │                             │ Worker: Phase0→Phase1(逐章)→Phase2 → done
 │◄──── event: done ───────────│
 │ GET /{id}/screenplay ──────►│ 按需汇总 → YAML
 │◄──────── screenplay.yaml ───│