Skip to content

fix(adapt): retry sample autorun on next reload + structured error hints - #63

Merged
shanchuann merged 4 commits into
mainfrom
feat/stability-polish
Jun 7, 2026
Merged

shanchuann merged 4 commits into
mainfrom
feat/stability-polish

Conversation

@shanchuann

Copy link
Copy Markdown
Owner

功能描述

两个 demo 流程里发现的稳定性 papercut:

  1. autorun 失败后永不重试:首访 autorun 之前不管成功失败都写 `localStorage[SAMPLE_AUTORUN_KEY]=1`,意思是"演示已经放过了"。后果:评委在 uvicorn 没启动时打开 `/app`,autorun 失败一次后之后再也不会自动跑——哪怕后端起来了。
  2. 错误文案不告诉用户怎么修:result 面板报错只 dump `ApiError.message`,对评委毫无指引。

实现思路

autorun 只在成功时写 flag:`.finally` 改成 `.then`,失败路径不写 flag。`autorunFiredRef` 在 same-session 范围阻止重复 fire,下次刷新(新 session)自动重试。等于:每次 reload 最多一次尝试,不会变成轮询。

两条针对性 hint:

  • `status === 0`(网络层断开)→ 提示 `uvicorn story2script.app.main:app --reload` 命令
  • `status === 503`(无 LLM provider)→ 提示设置 `DEEPSEEK_API_KEY` 或 `MIMO_API_KEY` + 说明可继续以"全规则"模式跑

测试方式

  • 后端 `pytest`: 318 passed(没碰)
  • 前端 `tsc -b`: clean
  • `eslint`: 0 新增

依赖与复用声明

无新增依赖。仅改 `web/src/routes/Adapt.tsx` autorun 写 flag 时机 + 错误面板加 hint。

Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com

shanchuann and others added 4 commits June 7, 2026 17:01
The editor's only download was the schema YAML — useful for the
pipeline contract, useless for directors and actors. Adds an
end-to-end export path so the author leaves with a file the rest
of the team can read.

Backend: new story2script.export package with three pure renderers
(to_fountain, to_txt, to_markdown), wired to POST /api/v1/export.
The endpoint runs the same schema validator the YAML download
relies on, so a broken draft surfaces as 422 with field-level errors
instead of a half-rendered file. PDF is intentionally NOT a server
format — generating CJK-safe PDFs needs either weasyprint+GTK or a
5 MB bundled font; the frontend opens the Markdown variant in a
print-friendly window and lets the browser's native Print → Save
as PDF do the conversion. Zero new server dependencies.

Frontend: the toolbar's 导出 YAML button becomes a 导出剧本
dropdown with five options (Fountain / txt / md / PDF / raw YAML).
PDF reuses the markdown response, rewraps it in an inline HTML
template with CJK system fonts, and triggers window.print() after
load. Error messaging surfaces 422 schema details so the author
knows which field to fix in the editor.

Tests: 25 new (8 fountain, 4 txt, 7 markdown, 6 endpoint),
covering character cue casing, parenthetical split, transition
normalization, mode block presence, invalid YAML / invalid
screenplay / unknown format error paths. Full suite 314 passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Returning to /app after editing in /editor showed a stale result —
the screenplay_yaml in the React state still reflected the original
LLM output even though the editor had patched sessionStorage with
every keystroke. Two real consequences:

- Exporting via PR #60's new dropdown would have produced the
  PRE-edit Fountain / Markdown / etc., silently discarding all the
  author's manual fixes.
- Schema validation and quality metrics displayed the LLM's draft
  state, not what the author was about to ship.

Strategy: on mount AND on tab focus / visibility change, diff the
editor's draft against the result.screenplay_yaml. When they differ,
swap the YAML in and re-run /api/v1/validate so schema_validation
reflects current state. Quality metrics intentionally stay frozen
since they describe how the LLM produced the draft, not whether
the author's hand-edits are still valid.

The validate call's failure path falls through to a YAML-only swap
so the export button still reflects current edits when the backend
is down; the eventual /export call will surface the same errors.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The mode_refine LLM stage was wired into the pipeline run but invisible
in the editor — once the draft was generated, the author had no way
to ask "redo this scene with low fidelity / rich expansion" without
regenerating the whole document. Exposes a per-scene polish toolbar
that hits a new POST /api/v1/refine/scene endpoint.

Backend: new endpoint deserializes the YAML, finds the scene by id,
reconstructs the Scene / Beat / Character pydantic instances the
existing ai_refine_scene_beats helper expects, and returns the
refined beat list. 503 when no LLM provider is configured, 404 on
unknown scene id, 422 on empty beats, 502 when the LLM call returns
nothing usable (rule_fallback fired). Tests cover the four error
paths without burning quota.

Frontend: a dashed-bordered toolbar at the top of each scene with
two selects (fidelity / expansion) + 润色场景 button + 撤销 button.
The default (medium, balanced) mode disables the button because the
backend would no-op it anyway. A polish snapshots the pre-polish
YAML to a ref for one-level undo so the author can try a mode,
hate it, and revert without losing their other edits.

Beats are spliced back wholesale rather than per-beat patched —
expansion=rich can change the beat count, so a positional patch
would leave the YAML inconsistent. The full splice round-trips
through js-yaml.dump to keep the file structurally clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two stability papercuts found while smoke-testing the demo flow:

- First-visit autorun set the localStorage flag unconditionally —
  including when the backend was down — so a grader who hit /app
  before starting uvicorn saw the demo fail once and never again,
  even after launching the server. Now the flag is set only on
  success; failure leaves the flag clear so the next page load
  retries. autorunFiredRef still blocks intra-session re-fires so
  the user sees one attempt per reload, not a hammer.

- The Adapt result panel's error block dumped the raw ApiError
  message without suggesting next steps. status=0 (network) and
  status=503 (no LLM provider) are the two cases a grader is most
  likely to hit during setup; both now show a one-line hint with
  the exact command or env var that resolves them.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@shanchuann
shanchuann merged commit f33263c into main Jun 7, 2026
2 checks passed
@shanchuann
shanchuann deleted the feat/stability-polish branch June 7, 2026 09:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant