feat(api): C2 多章链 服务+端点+schema+checkpointer 接线
承 C1 链图(build_chain_graph),落地多章工作流链的 apps/api 壳:
- 3 端点 routers/chain.py:POST .../chains/{key}/run→202 ChainRunAccepted;
POST .../chains/runs/{job_id}/resume→202;GET /jobs/{id} 复用。校验:
count 1..50→422、未知 chain_key→404、resume 非 awaiting→409、无凭据→503。
- schemas/chain.py:ChainRunRequest/ChainRunAccepted/ChainResumeRequest
(ConflictDecision 复用 schemas/projects)。
- services/chain_runner.py:run_chain_job 仿 run_job 壳自建独立 session 驱动链图
(set_running→ainvoke→据 __interrupt__ 置 awaiting_input/done/failed);
build_accept_op 在 apps/api 装配验收事务闭包注入图节点(守 #3/#4);
token 不入 result/日志。
- services/chain_deps.py:get_checkpointer_factory(运行时 AsyncPostgresSaver
上下文 / 测试 MemorySaver)。
- 零迁移(设计 §7):复用 jobs,新增 status="awaiting_input" + JobRepo.set_awaiting,
awaiting 章经 result.awaiting_chapter;新错误码 ErrorCode.CONFLICT(409)。
- project_deps:build_chain_gateway/get_chain_gateway(按请求 tier writer/analyst/light
分派——单档网关恒返该档会错路由 review/digest)+ get_digest_gateway_builder。
单测 apps/api/tests/test_chain.py 12 用例(mock 网关 + MemorySaver + fake session/
accept_op,无 DB/无网络/无真 LLM):run/resume→202、未知 key 404、count 越界 422、
resume 非 awaiting 409、run_chain_job 无冲突→done、冲突→awaiting→resume→done、
错误脱敏、accept_op 冲突缺判→CONFLICT_UNRESOLVED。
门禁绿:ruff/format 干净 · mypy 193 Success · alembic 无漂移 · pytest 600 passed。
守不变量 #1/#3/#4/#5/#9。唯一新增 DDL(langgraph 检查点表)= C3 迁移。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -84,3 +84,6 @@
|
||||
- [2026-06-19] @llm **Kimi Code coding API 伪造头真源 = `ooojustin/opencode-kimi`(`src/headers.ts`+`src/constants.ts`,1:1 镜像 kimi-cli v1.37.0),发完整 7 头、UA `KimiCLI/1.37.0`**(K1.2 校正,推翻上方 pi-kimi-coder UA-only 那条):`picassio/pi-kimi-coder` 是分歧/错误参考(只发 UA),照它裁成 UA-only 是误修,已回退。`kimi_code_headers()` 发 `User-Agent=KimiCLI/1.37.0` + `X-Msh-Platform=kimi_cli`(字面常量,非 OS 名)+ `X-Msh-Version=1.37.0`(==UA 版本)+ `X-Msh-Device-Name`(`socket.gethostname()` ASCII 化)+ `X-Msh-Device-Model`(macOS=`f"macOS {platform.mac_ver()[0]} {platform.machine()}"`,`machine()` 原样 `arm64`/`x86_64` 不归一化)+ `X-Msh-Os-Version`(`platform.version()`)+ `X-Msh-Device-Id`(稳定 32 位无连字符 `uuid4().hex`:env `KIMI_DEVICE_ID` 优先→否则读/建 `~/.kimi/device_id` 复用,**绝不每次随机**,否则 Kimi 侧每次当新设备)。源码若与此有出入以源码为准(本次核对 master 一致)。⚠️ HTTP 头值含非 ASCII 会被底层 fetch/httpx 拒,host 派生值(Device-Name/Model/Os-Version)须 ASCII 化(`asciiHeaderValue`:裁 `\x20-\x7e` 之外 + trim,空回退 `unknown`)。单测用 env `KIMI_DEVICE_ID` 注入固定 id 保 CI 确定(不触真 ~/.kimi)。ruff E501 按显示宽度算(中文占 2 列),含中文 docstring/注释的行别贴边 100。
|
||||
- [2026-06-19] @backend `assemble`(C5)此前只从 world_entities/characters/style/rules + cards/foreshadow/digests/outline.beats 组装,**从不含项目 premise/logline/theme/title,也无「写章」指令** → 全新项目(只有 premise、无世界观/角色/大纲)产出 `stable_core=""`+`volatile=""`,writer 的 user message 为空,LLM 回 `400 "the message at position 0 with role 'user' must not be empty"`。修复:`MemoryRepos` 加第 8 个 repo `project:ProjectSpecRepo`(`spec(project_id)->ProjectSpecView{title,logline?,premise?,theme?}`,按 project_id 读 `projects`,无 owner 过滤——owner 隔离归路由层);`assemble` 把「作品蓝本」放进 `stable_core` 首段(书级 spec=定型→缓存前缀,不违 #9),并让 `volatile` 始终含 `请创作第 {chapter_no} 章的正文。`(易变指令)。⚠️ 任何手搓 `MemoryRepos(...)` 的地方(含测试 fake)现在**必须**补 `project=` 字段,否则 dataclass 缺参报错。无 DDL 变更(只读既有 projects 列),无迁移。
|
||||
- [2026-06-22] @frontend/@orchestrator T6 创作工具箱通用端点的两处契约边界(加新生成器时注意):① `ToolGenerateRequest` 是**固定字段并集** `{brief, chapter_no?, count?, kind?}`——`GeneratorRunner` 只映射落在此集合内的 `input_field` 名;新生成器若声明了集合外的 `input_field`,前端会**静默忽略**该字段。新工具的 `input_fields` 须落在 `brief/chapter_no/count/kind` 内,否则需先扩 `ToolGenerateRequest` 契约(+`pnpm gen:api`)。② `ToolInputFieldView.type` 是自由字符串,后端可声明 `"select"` 但 descriptor **无 `options` 字段** → 前端把 select 当 text 输入渲染。若某工具需要真下拉选项,须先给 `InputField`/`ToolInputFieldView` 加 `options` 再改前端 `GeneratorRunner`。
|
||||
- [2026-06-23] @backend 多章链网关(C2):**单档 `build_gateway_for_tier(tier)` 网关不能驱动跨档链**。其 `chain_resolver`/`_resolver` 闭包恒返该 tier 的链、**忽略入参 `req.tier`**,故拿它跑 review(analyst)/digest(light) 会全部错路由到 writer 档的 provider/model。链/任何跨档编排须用 `build_chain_gateway`(union 三档适配器 + `chain_resolver` 据 `req.tier` 分派)。测试用 mock 网关按 `req.output_schema` 路由(无 schema=write,有=review),不受此影响。
|
||||
- [2026-06-23] @backend langgraph `CompiledStateGraph.ainvoke` 的 mypy 重载:`config` 必须是 `RunnableConfig`(`from langchain_core.runnables import RunnableConfig`),裸 `dict[str,dict[str,str]]` 不匹配任何重载 → call-overload 错。返回值是 `dict[str,Any] | Any`,传给取 `__interrupt__`/`written` 的 helper 前先 `dict(raw_final)` 收敛为 `dict[str,Any]`。interrupt 命中时返回值含 `__interrupt__`(Interrupt 对象列表),载荷读 `getattr(first,"value",first)`(兼容 dict)。
|
||||
- [2026-06-23] @backend 改 `JobRepo` Protocol 加方法(如 `set_awaiting`)= 契约变更:**所有 fake 实现都要同步补**,否则 mypy 报 Protocol 缺成员(本次漏了 `packages/core/tests/test_job_repo.py::FakeJobRepo` + `apps/api/tests/fakes_projects.py::FakeJobRepo`)。grep `class Fake.*JobRepo` 全仓再补。
|
||||
|
||||
Reference in New Issue
Block a user