Skip to content

Latest commit

 

History

History
352 lines (261 loc) · 13 KB

File metadata and controls

352 lines (261 loc) · 13 KB

Story2Script HTTP API

服务通过 FastAPI 暴露,默认监听 http://localhost:8000。所有业务端点以 /api/v1 为前缀;交互式 Swagger UI 在 /docs。

改编范式为 AI 主导,规则兜底:每个改编 stage 默认调 LLM,失败时该 stage 自动落到对应规则路径,响应的 warnings[] 会追加 STAGE_DEGRADED 条目。无 LLM key 时所有 stage 一并降级,YAML 仍是 schema 合法的;配置 MIMO_API_KEY 或 DEEPSEEK_API_KEY 后,quality_metrics.ai_stages_applied 字段告诉前端哪几个 stage 这次完整跑通了 AI 路径。字段语义见 YAML_SCHEMA.md。

通用约定

  • 请求与响应内容类型:除上传端点为 multipart/form-data 外均使用 application/json。

  • 字符编码:UTF-8。

  • 错误响应:状态码 ≥ 400 时返回结构化 detail:

    {
      "detail": {
        "code": "ERROR_CODE",
        "message": "对应的中文说明"
      }
    }

    常见 code:

    code 含义 出现端点
    EMPTY_TEXT text 字段缺失或全为空白 /adapt
    EMPTY_FILE 上传文件为空 /adapt/upload
    MISSING_FILENAME multipart 未带 filename /adapt/upload
    UNSUPPORTED_FORMAT 后缀不在白名单 /adapt/upload
    FILE_TOO_LARGE 上传超过 8 MiB /adapt/upload
    DECODE_FAILED 非 UTF-8 / 损坏 DOCX /adapt/upload

    注:FEW_CHAPTERS(章节不足 3 个)不再是 4xx 错误码,已降级为质量警告,出现在响应的 warnings[] 中。所有 /adapt 调用即便只识别到 1 个章节也会返回 200 + schema 合法 YAML。

服务端 LLM 配置

以下环境变量在 uvicorn 启动前设置(或写入 .env,应用启动时通过 python-dotenv 加载)。所有端点都从环境读 key,请求 body 无 LLM 字段。

env 取值 默认 说明
DEEPSEEK_API_KEY sk-... unset DeepSeek 默认 provider。OpenAI 兼容 /v1/chat/completions 协议。
MIMO_API_KEY sk-... / tp-... unset 小米 MiMo 备选 provider。tp- 前缀自动路由到 https://token-plan-cn.xiaomimimo.com/v1;sk- 走 https://api.xiaomimimo.com/v1。两者都是 OpenAI 兼容协议。
STORY2SCRIPT_LLM_PROVIDER deepseek / mimo 按 key 自动检测(两个 key 都配时选 deepseek) 显式覆盖自动检测。
STORY2SCRIPT_LLM_MODEL provider 支持的模型名 deepseek-v4-flash / mimo-v2.5-pro 覆盖默认模型。
STORY2SCRIPT_AI_LED_STAGES all / none / 逗号分隔子集 all 灰度控制哪些 stage 走 LLM。取值集:chapters, scenes, characters, beats, refine。未列出的 stage 强制走规则;不发警告(视作用户主动选择)。

stage 与失败行为对应表:

stage LLM 任务 失败时回退到 警告
chapters 让模型按段落编号返回章节边界 + 标题 detect_chapters(heading → regex → fallback) 一条 [stage:chapters]
scenes 每章一次调用,返回 scenes [{time, location, summary, 段落范围}] segment_scenes 时间/地点关键词切分 无 key 一条 summary;按章失败一条 per chapter + chapter_index
characters 整篇 digest 一次,返回 canonical + aliases + 首次出场 scene extract_characters 姓氏正则 + 别名合并 一条 [stage:characters]
beats 每场景一次,返回 beats [{kind, text, speaker}] extract_beats 引号扫描 + 说话动词窗口 无 key 一条 summary;多 scene 失败一条带总数
refine 按 mode = {fidelity, expansion} 精修已切好的 beats 无规则版精修;失败则保留原 beats 不动 默认 mode(medium/balanced)整 stage 短路,不发警告

GET /health

存活探针。

响应 200:

{ "status": "ok", "version": "0.1.0" }

POST /api/v1/adapt

JSON 提交原文,端到端跑改编流水线,返回 schema 合法 YAML + 质量报告。

请求体:

{
  "text": "第一章 启程\n\n张三离开了小镇。\n\n...",
  "source_format": "txt",
  "title": "送别",
  "mode": {
    "fidelity": "medium",
    "expansion": "balanced"
  }
}
字段 类型 默认 说明
text string (≥ 1) — 原文。
source_format "txt"|"markdown"|"docx" "txt" 仅决定段落规范化策略;DOCX 走 TXT 退化解析。真 DOCX 请用上传端点。
title string (1-200) "未命名作品" 写入 YAML metadata.title。
mode.fidelity "high"|"medium"|"low" "medium" 保真度。
mode.expansion "minimal"|"balanced"|"rich" "balanced" 扩写量。

响应 200:

{
  "screenplay_yaml": "schema_version: 0.1.0\n...",
  "schema_validation": { "valid": true, "errors": [] },
  "quality_metrics": {
    "chapter_count": 3,
    "scene_count": 4,
    "character_count": 2,
    "detection_method": "llm",
    "mean_chapter_confidence": 0.88,
    "ai_stages_applied": ["chapters", "scenes", "characters", "beats"],
    "llm_provider": "mimo"
  },
  "warnings": [
    { "code": "SHORT_CHAPTER", "message": "...", "scene_id": "scene_1" }
  ]
}
quality_metrics 字段 类型 说明
chapter_count / scene_count / character_count int 输出 YAML 中三个集合的元素数
detection_method string chapters stage 的检测策略:headings / regex / fallback / llm
mean_chapter_confidence number 全部 chapter confidence 的算术平均,[0, 1]
ai_stages_applied string[] 端到端无任何 per-item 回退的 stage 名子集;空列表表示无 key 或全部降级
llm_provider string | null provider 名("mimo" / "deepseek");无 key 时 null

典型错误:

  • 400 EMPTY_TEXT:空文本。
  • 章节不足 3 个不再 4xx;通过 warnings[].code == "FEW_CHAPTERS" 提示。

POST /api/v1/adapt/upload

multipart 上传,DOCX 二进制必走此端点。

Form 字段:

name required 默认 说明
file 是 — .txt / .text / .md / .markdown / .docx,≤ 8 MiB。
title 否 文件名 stem 若空白将自动回落到文件名(去掉后缀)。
fidelity 否 "medium" 同上。
expansion 否 "balanced" 同上。

响应:同 /adapt。

典型错误:

  • 415 UNSUPPORTED_FORMAT:detail 含 suffix。
  • 413 FILE_TOO_LARGE。
  • 400 EMPTY_FILE / MISSING_FILENAME / DECODE_FAILED。

POST /api/v1/validate

把作者改过的 YAML 回传服务端做 JSON Schema 全量校验。永不 5xx:malformed YAML 也只返回 valid: false + 路径化错误,前端可原地高亮字段。

请求体:

{ "screenplay_yaml": "schema_version: 0.1.0\n..." }

响应 200:

{
  "valid": false,
  "errors": [
    { "path": "/scenes/0/beats/2", "message": "'speaker' is a required property" }
  ]
}

path 是 JSON Pointer 风格,从根 / 开始。valid: true 时 errors 必为空数组。


POST /api/v1/refine/scene-summary

LLM 重写一段场景摘要,用于 /app YAML 编辑器下方的"AI 优化场景摘要"面板。需配 LLM key。

请求体:

{ "original_text": "张三准备离开小镇,李四前来送别。夕阳把候车厅染成暖橙色。" }
字段 类型 约束 说明
original_text string 1 ≤ len ≤ 4000 待精修的原文段落或现有摘要

响应 200:

{
  "refined": "张三在车站告别李四,独自登车而去。",
  "model": "mimo-v2.5-pro",
  "confidence": 0.75
}

错误:

  • 503 LLM_NOT_CONFIGURED:未设 MIMO_API_KEY / DEEPSEEK_API_KEY。
  • 502 LLM_REFINE_FAILED:LLM 调用失败或返回为空。
  • 422:original_text 为空或超过 4000 字符。

POST /api/v1/refine/inline-edit

LLM 重写作者在 /editor 选中的一段文本,可携带场景上下文做语义对齐。需配 LLM key。

请求体:

{
  "text": "天亮就走。",
  "instruction": "改得更口语化",
  "context": "张三和李四在候车厅道别,气氛沉重。"
}
字段 类型 约束 说明
text string 1 ≤ len ≤ 2000 待精修的选中文本,进 <input> tag
instruction string 1 ≤ len ≤ 500 自然语言指令,例如"改得更书面化" / "换成李四的语气"
context string ≤ 2000,可省 可选场景上下文,进 <context> tag;LLM 只读不改

响应 200:

{
  "refined": "明儿一早我就走。",
  "model": "mimo-v2.5-pro",
  "confidence": 0.75
}

错误:与 /refine/scene-summary 一致(503 / 502 / 422)。

两个 refine 端点均不受 STORY2SCRIPT_AI_LED_STAGES 控制——那个 flag 管的是 /adapt 流水线 5 个 stage 的灰度,refine 是作者主动触发的编辑动作,点了就跑。


POST /api/v1/refine/scene

整场景 AI 润色:用指定 mode = {fidelity, expansion} 重写一个 scene 的全部 beats。需配 LLM key。

请求体:

{
  "screenplay_yaml": "schema_version: ...",
  "scene_id": "scene_3",
  "fidelity": "low",
  "expansion": "rich"
}
字段 类型 约束 说明
screenplay_yaml string len ≥ 1 当前完整 YAML(编辑器的 sessionStorage 草稿)
scene_id string len ≥ 1 目标场景 id(YAML 里 scenes[].id)
fidelity high / medium / low enum 保真度
expansion minimal / balanced / rich enum 扩写量

响应 200:

{
  "scene_id": "scene_3",
  "beats": [
    { "kind": "stage_direction", "text": "INT. 候车厅 - 凌晨", "speaker": null, "source_type": "generated", "confidence": 0.85, "needs_review": false },
    { "kind": "dialogue", "text": "到了打电话。", "speaker": "char_001", "source_type": "generated", "confidence": 0.85, "needs_review": false }
  ],
  "model": "deepseek-v4-flash"
}

错误:

状态 code 说明
503 NO_LLM_PROVIDER 未配 LLM key
400 INVALID_YAML YAML 解析失败
404 SCENE_NOT_FOUND scene_id 在 YAML 里找不到
422 EMPTY_SCENE 场景 beats 为空,无可润色内容
422 INVALID_SCENE 场景字段不合法(不满足 Scene pydantic 校验)
502 REFINE_FAILED LLM 跑完但返回不可用;详情在 logs/story2script.log stage=refine 行

{medium, balanced} 是 no-op 模式,前端按钮在该模式禁用;如绕过约束硬调,结果与 REFINE_FAILED 等价。


POST /api/v1/export

把当前 YAML 渲染成作者可读的剧本格式。不需要 LLM。

请求体:

{
  "screenplay_yaml": "schema_version: ...",
  "format": "fountain"
}
字段 类型 约束 说明
screenplay_yaml string len ≥ 1 待导出 YAML(编辑器的当前草稿)
format fountain / txt / md enum 输出格式;PDF 由前端用浏览器打印生成,不在服务端

响应 200:

{
  "content": "Title: 离镇\nNotes: AI mode = ...\n\n...",
  "format": "fountain",
  "mime_type": "text/x-fountain; charset=utf-8",
  "suggested_filename": "离镇.fountain"
}
格式 MIME 后缀 用途
fountain text/x-fountain; charset=utf-8 .fountain 行业标准纯文本,Final Draft / WriterDuet / Highland 可直接读
txt text/plain; charset=utf-8 .txt 居中标题 + 缩进对白 + 章节横线的可读版
md text/markdown; charset=utf-8 .md GitHub / Notion 预览友好

错误:

状态 code 说明
400 INVALID_YAML YAML 解析失败
422 INVALID_SCREENPLAY YAML 解析成功但不通过 schema 校验,detail.errors 列前 5 条
422 (Pydantic) format 不在枚举内

部署注意

  • CORS:当前仅放行 http://localhost:{5173,5174,5175}(Vite 三档端口);生产部署请按需收紧或开放。
  • 超时:FastAPI 默认 worker timeout 由部署侧决定;规则路径 3 章短稿处理在毫秒级;AI 主导模式下 /adapt 是 5 stage 串行调用 LLM(每 stage 1–N 次),3 章短稿 + 默认 mode 通常 5–15 s,长稿或 mode != {medium, balanced} 触发 refine stage 后会更慢。建议反代 timeout ≥ 60 s。
  • 缓存:LLM 响应缓存写入 .cache/llm_responses.db(SQLite),按 (provider, model, messages, params) 哈希命中;同输入永不二次计费,跨重启保留。无 TTL 淘汰策略,需要时手动删库。

版本与兼容

  • 当前 API 版本 v1,schema 0.1.0。
  • 字段策略:只增不删;新字段在 schema 中通过 additionalProperties: false 的局部放宽引入。废弃字段保留 ≥ 1 个 minor 版本周期。
  • breaking change 必走 /api/v2。