Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,11 @@ Thumbs.db
*.log
# Runtime log dir written by configure_logging (override via STORY2SCRIPT_LOG_DIR)
logs/

# Local manuscript files dropped at the repo root during testing
/novel.txt
/novel.md
/novel.docx
# Screenshots dropped at the repo root during smoke-testing
/image*.png
/image*.jpg
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co

**Story2Script** — AI-assisted Web API that converts ≥3 chapters of novel text (TXT / Markdown / DOCX) into a structured, editable screenplay YAML, accompanied by a stand-alone Schema document.

In scope: API + screenplay schema + validation + docs.
Out of scope: frontend editor, storyboard/audio export, custom model training.
In scope: API + screenplay schema + validation + docs + frontend editor + screenplay export (Fountain/txt/md/PDF).
Out of scope: storyboard/audio export, custom model training, real-time collaboration.

## Competition Constraints (READ FIRST)

Expand Down
49 changes: 35 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

AI 辅助小说改编剧本 Web 应用:输入 ≥3 章节的小说文本(TXT / Markdown / DOCX),输出可编辑的结构化剧本 YAML + 校验报告 + 质量警告 + 角色与对话归属。前端附带可视化编辑控制台与 AI 内联润色。

- 后端:FastAPI · Python 3.12+ · 227 个 pytest 全绿
- 后端:FastAPI · Python 3.12+ · 318 个 pytest 全绿
- 前端:Vite 8 · React 19 · TypeScript · Tailwind v4 · shadcn/ui · lucide-react
- LLM(可选,可插拔):默认 **小米 MiMo `mimo-v2.5-pro`**;亦支持 DeepSeek `deepseek-v4-flash`。统一 OpenAI 兼容协议 + SQLite 内容哈希缓存
- 改编范式:**AI 主导,规则兜底** —— 章节切分 / 场景切分 / 角色抽取 / 节拍提取 / mode 精修五个 stage 各自独立调用 LLM,失败时该 stage 自动落到确定性规则路径,无 LLM key 也能输出 schema 合法 YAML
Expand All @@ -17,14 +17,25 @@ AI 辅助小说改编剧本 Web 应用:输入 ≥3 章节的小说文本(TXT
> **TODO(提交前替换)**:上传至 bilibili / 云盘,将可播放链接粘贴于此。
>
> 推荐分镜(约 5 分钟):
> 1. 打开 `/` landing,介绍 Schema 优先 + AI 主导 + 规则兜底 + source_type 可追溯四个差异点。
> 2. 进入 `/app` 改编控制台,粘贴示例小说,按"生成草稿"。
> 3. 解释顶部 metrics 七格(chapters / scenes / characters / detection / mean conf / **改编路径徽标**(`AI · mimo · 4/5`)/ schema)。
> 4. 滚动 YAML 编辑器,指出 source_type 四态、AI 跑通时多为 `generated`、`confidence: 0.88`。
> 5. 改一个字段(例:删 `schema_version` 那行)→ 点"重新校验" → 展示路径化错误。
> 6. 切换 source format 为 DOCX,导入 `.docx` 文件 → multipart 上传跑通。
> 7. 跳 `/editor` 可视化视图,选一段对白 → "AI 改写"输入"改得更口语化" → 展示 inline-edit 端点。
> 8. 一句话强调:评分项里"过程"指标(持续 PR、commit 分布、原创/三方透明)都已上 GitHub。
> 1. 打开 `/` landing,介绍四个差异点:Schema 优先 / AI 主导 + 规则兜底 / source_type 可追溯 / 多格式导出。
> 2. 跳 `/app` —— **首次访问自动用内置《离镇》示例跑一次完整管道**,直接看到顶部 metrics + YAML。解释五个 stage(章节/场景/角色/节拍/精修)+ 改编路径徽标 `AI · deepseek · 5/5`。
> 3. 进入 `/editor` 可视化视图,按 `source_type` 颜色编码逐场展示。**重点演示场景级 AI 整体润色**:选一个场景 → 选 `保真度·低 / 扩写量·丰富` → 一键 `润色场景` → 整场 beats 用新 mode 重写 → `撤销` 可回退。
> 4. 演示**手动 + AI 双轨编辑**:点任一对白 → 切到 `手动改写` → 改 `说话人` 下拉 + 改文本 → 保存;或切 `AI 改写` 输入指令 → 接受 diff。
> 5. 演示 `导出剧本` 下拉:**Fountain / 纯文本 / Markdown / PDF (浏览器打印) / 原始 YAML** 五种格式;下载一份 Fountain 文件,可被 Final Draft 直接读取。
> 6. 切回 `/app`,演示 Editor → Adapt **数据回流**:刚改的内容立刻反映在 result 面板的 YAML / schema 校验中。
> 7. 一句话强调:本项目过程指标(持续 PR、commit 分布、原创/三方透明)全部上 GitHub。

---

## 亮点速览

- **AI 主导 + 规则兜底**:5 个 stage 全部跑 LLM,单 stage 失败自动回到确定性规则路径,无 LLM key 也输出 schema 合法 YAML。
- **首访即演示**:自带《离镇》5 章短篇,第一次进 `/app` 自动跑一次完整改编,评委零配置看到产出。
- **网络稳定性栈**:HTTP timeout 拆分(read 180s / connect 10s)、瞬态错误 1 次自动重试、429/503 遵守 `Retry-After`、SQLite 缓存 WAL 并发安全。
- **诊断日志**:`logs/story2script.log` 每条 LLM 调用 + 每次 stage 拒答都带 reason 短码,操作员可 grep。
- **多格式导出**:编辑器 `导出剧本` 下拉支持 Fountain / 纯文本 / Markdown / PDF(浏览器打印)/ 原始 YAML 五种。
- **可视化编辑器**:手动 + AI 双轨改写每条 beat;下拉切换 speaker;场景级 AI 整体润色(保真度 × 扩写量 6 模式)+ 一键撤销。
- **数据回流**:编辑器对 YAML 的修改实时同步到 `/app` 的 result 面板与 schema 校验。

---

Expand Down Expand Up @@ -192,17 +203,27 @@ src/story2script/
# / characters / beats / mode_refine)
refine_summary.py # /refine/scene-summary 后端
refine_inline.py # /refine/inline-edit 后端
cache.py # SQLite 内容哈希缓存
cache.py # SQLite 内容哈希缓存(WAL 模式 + 损坏 row 静默 miss)
transport.py # HTTP timeout 拆分 + 瞬态错误重试 (429/503/timeout)
parse_utils.py # LLM 响应截断 JSON 修复
logging.py # 结构化日志(chat / stage_reject / stage_ok)
stages_flag.py # STORY2SCRIPT_AI_LED_STAGES 解析
export/ # Fountain / TXT / Markdown 渲染器
pipeline/ # 改编流水线编排(按 stage 调 AI 或回退规则)
tests/ # 227 个 pytest 单元 + 集成测试
tests/ # 318 个 pytest 单元 + 集成测试
web/ # Vite + React + Tailwind v4 + shadcn 前端
src/routes/ # / (Landing) + /app (Adapt) + /editor (可视化)
src/components/ # Landing + ScreenplayView + ui/ shadcn 注册表
src/lib/api.ts # 类型化 fetch 客户端
src/lib/api.ts # 类型化 fetch 客户端(adapt / validate / export / refine/*)
src/lib/screenplay-patch.ts # YAML round-trip 编辑器补丁
src/data/sample-novel.ts # 自带《离镇》示例(首访 autorun 用)
docs/
API.md # 端点 + stage flag + 错误码
YAML_SCHEMA.md # 字段语义 + "为什么这么设计"
logs/ # 运行时日志(rotated,默认 5 MiB × 3)
examples/
sample_novel.md # 《离镇》canonical 版本(与 web/src/data 同步)
sample_output.yaml # Schema 锚定的最小样例
```

---
Expand Down Expand Up @@ -248,7 +269,7 @@ LLM 可选依赖:
## 测试

```powershell
# 后端单元 + 集成测试(227 通过)
# 后端单元 + 集成测试(318 通过)
py -m pytest -q

# 前端类型检查
Expand Down Expand Up @@ -291,5 +312,5 @@ npm run verify:libs

- **持续提交**:截至当前 36 个 PR 全部 merged,每个单一功能;commit 颗粒度细且时间分布合理。AI 主导流水线的 5 个 stage + foundation 分 6 个 PR 上线(#28–#33),后续优化又 3 个(#34 inline-edit / #35 ai_stages_applied / #36 死代码清理)。
- **PR 描述规范**:模板在 [`.github/pull_request_template.md`](./.github/pull_request_template.md),五段(标题 / 功能 / 思路 / 测试 / 复用声明)+ 校验清单全套。
- **主分支可运行**:每次 merge 后 `pytest`(227 全绿)、`tsc -b --noEmit`、`uvicorn ...`、`npm run dev` 四线全绿。
- **主分支可运行**:每次 merge 后 `pytest`(318 全绿)、`tsc -b --noEmit`、`uvicorn ...`、`npm run dev` 四线全绿。
- **架构与流程约束**:详见 [`CLAUDE.md`](./CLAUDE.md)。
95 changes: 95 additions & 0 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,101 @@ LLM 重写作者在 `/editor` 选中的一段文本,可携带场景上下文

---

## `POST /api/v1/refine/scene`

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

**请求体**:

```json
{
"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`:

```json
{
"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**。

**请求体**:

```json
{
"screenplay_yaml": "schema_version: ...",
"format": "fountain"
}
```

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

**响应** `200`:

```json
{
"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 三档端口);生产部署请按需收紧或开放。
Expand Down
Loading