合并最新 master 分支

保留 master 的画布交互、Agent、VectorEngine 与原生 CI 更新。
解决角色动画测试和项目决策日志冲突,不恢复按帧数放大 BgFilter 超时的旧逻辑。
同步修正融合文档中的 BgFilter worker、N/Q、重试与父侧降级边界。
This commit is contained in:
2026-07-21 14:37:15 +00:00
49 changed files with 2411 additions and 366 deletions
@@ -4379,3 +4379,16 @@
## 2026-07-20 VectorEngine 图片任务预算收口到 worker deadline
- 决策:`editor_image_generation`、`editor_image_edit`、`editor_icon_spritesheet_generation` 和 `editor_ui_design_asset_extraction` 使用默认 `1800s` long job 预算。worker 从同一起点计算绝对 job deadline,并向 provider 提前保留 `min(60s, job 预算 / 2)` 作为审计、OSS 和终态写回窗口。deadline 只经进程内 `RequestContext` 传递;VectorEngine 单 attempt 取配置 timeout 与剩余预算的较小值,退避加下一次 attempt 无法落在同一 deadline 内时停止重试,参考图和响应图片下载也受同一 deadline 限制。普通 HTTP / `inline` 保持无 deadline 行为;`VECTOR_ENGINE_IMAGE_REQUEST_TIMEOUT_MS` 默认仍为 `1000000`,配置加载层允许显式值更低。lease 续租 / fencing、迟到写回仲裁、attempt 耗尽和原子退款语义不变。
## 2026-07-21 VectorEngine 图片首选 gpt-image-2 并以 gpt-image-2-c 兜底
- 决策:前端、DTO、计费配置、持久化和 `platform-image` 的 `/v1/images/generations` / `/v1/images/edits` provider 首选请求统一使用 `gpt-image-2`;符合条件时才回退到兜底模型 `gpt-image-2-c`。不在业务 handler、前端或价格表中新增平行模型。
- 回退边界:明确模型不存在 / 不支持、408、非内容拒绝类 429、5xx、响应解析失败或非拒绝类缺图可以切模型;401 / 403、普通参数 / 内容安全拒绝、本地配置与参考图错误、发送 / 连接错误、request budget 耗尽和已生成图片下载失败不切模型。一次业务请求总发送上限仍为 5 次,两个模型共享同一 worker provider deadline 和 attempt 预算。
- 观测边界:审计 `image_model` 记录实际 provider attempt;首选 `gpt-image-2` 失败但兜底 `gpt-image-2-c` 恢复成功时,首选失败仍写入 `external_api_call_failure`,最终成功运行摘要记录 `recoveredFailureCount`。日志用 `fallback_from_model` / `fallback_to_model` 标识切换,不改变业务模型、扣费、素材 metadata 或终态语义。
- 脚本边界:仓库 `gpt-image-2-apimart` skill 的现役生成脚本采用同一首选 / 回退顺序;认证、请求发送不确定错误和下载失败不重新生图,避免重复上游成本。
## 2026-07-21 图片画布滚轮与中键平移统一为二维视口移动
- 背景:画布中键拖拽的平移模型已同时计算 X / Y,但普通滚轮分支只消费 `deltaY`,横向滚轮或触控板的 `deltaX` 被丢弃,且缺少中键横向拖动的状态机回归覆盖。
- 决策:普通滚轮原样消费设备上报的 `deltaX / deltaY` 二维平移 viewport;当按住 Shift 且设备上报 `deltaX = 0` 时,输入适配层把 `deltaY` 映射为横向位移并将纵向位移置零,核心平移模型不感知修饰键。`Ctrl / Cmd + 滚轮` 继续只负责围绕指针缩放;中键和抓手拖拽继续同时更新 X / Y。
- 验证:交互模型单测覆盖原始 `deltaX / deltaY` 和缩放边界;viewport hook 单测覆盖二维滚轮、Shift 横向适配与 Ctrl 缩放;stage 状态机单测覆盖中键水平、垂直同时移动。
@@ -269,6 +269,15 @@ DDD 边界检查:
npm run check:server-rs-ddd
```
## Gitea CI 与 PR 检查
- 仓库 CI 入口是 `.gitea/workflows/project-ci.yml`,向 `master`、`codex/ai-game-creator-app` 推送和所有 PR 创建、更新时必须运行,也允许手工触发。
- CI 固定拆分为 `Repository checks`、`Frontend tests`、`Backend tests`、`Native shell tests` 四个 required job;对应 PR context 完整名称是 `Project CI / Repository checks (pull_request)`、`Project CI / Frontend tests (pull_request)`、`Project CI / Backend tests (pull_request)`、`Project CI / Native shell tests (pull_request)`,首次运行后仍须从 Gitea 最近一周 context 表复核。测试使用独立 job,不能只藏在综合检查 step 中;原生壳验收单独运行以便定位重型构建失败。
- 四个 job 共同覆盖 `npm run check`,并追加 `npm run check:server-rs-ddd`、`cargo test --locked --workspace --no-fail-fast --manifest-path server-rs/Cargo.toml`、`cargo check -p api-server --all-targets --manifest-path server-rs/Cargo.toml` 和 `cargo check -p spacetime-module --manifest-path server-rs/Cargo.toml`。后端 runner 安装 `ffmpeg`,避免视频抽帧测试因工具缺失提前返回。`codex/ai-game-creator-app` 分支的原生壳入口还必须覆盖 `npm run ai-game-creator-shell:check` 和 release build smoke。
- checkout 必须使用完整历史。PR 将 base SHA 写入 `SPACETIME_SCHEMA_BASE_REF`,直接推送 `master` 使用 before SHA;事件基线不可解析时直接失败。Gitea 检查的是 PR head 而非预合并 commit,workflow 必须拒绝不包含最新 base commit 的过期 PR,分支保护同时保持“PR 过期禁止合并”。
- 普通 PR job 不读取业务 secret,不运行真实 API/SpacetimeDB/OSS/支付/生成/live smoke,也不执行会修改外部状态的维护、迁移、发布或备份命令。
- Gitea 至少升级到 `1.26.4` 后才能注册执行 PR job 的 runner;`ubuntu-latest` 标签只映射到固定 digest 的 Ubuntu 24.04 级 Docker/临时隔离镜像,不使用浮动镜像 tag,不映射 host,不向 job 暴露 Docker socket、业务 secret 或不必要内网。runner 能访问 Gitea、GitHub Actions 与 `actions/node-versions`、nodejs.org、npm、Rust 分发和 crates.io;workflow 的官方 action 固定完整 commit,若内网禁用 GitHub,先在当前 Gitea 镜像对应 commit 并改用绝对 URL。受控镜像优先预装 rustup。Gitea 1.26 的任务超时由 runner 全局配置控制;首次运行成功后,`master` 分支保护必须要求上述四个 job 全部成功。
## 后端相关默认验证
后端修改后,按 DDD 文档中的验收命令执行。涉及 API smoke 时:
@@ -1510,7 +1510,7 @@
- 现象:配置了 `APIMART_BASE_URL` / `APIMART_API_KEY` 后,RPG、拼图或方洞的 GPT-image-2 生图仍返回缺配置,或请求体里还出现 `official_fallback` / `image_urls`。
- 原因:2026-05-21 后 GPT-image-2 图片生成按 VectorEngine 创建/编辑接口分流;2026-07-05 后创意 Agent 文本链路也改为 VectorEngine Chat Completions `gpt-5.4-mini`,APIMart 不再作为当前创意 Agent 来源。
- 处理:为图片生成配置 `VECTOR_ENGINE_BASE_URL=https://api.vectorengine.ai`、`VECTOR_ENGINE_API_KEY`、`VECTOR_ENGINE_IMAGE_REQUEST_TIMEOUT_MS`;排查请求体时确认无参考图路径为 `/v1/images/generations`、有参考图路径为 `/v1/images/edits`,模型为 `gpt-image-2`。
- 处理:为图片生成配置 `VECTOR_ENGINE_BASE_URL=https://api.vectorengine.ai`、`VECTOR_ENGINE_API_KEY`、`VECTOR_ENGINE_IMAGE_REQUEST_TIMEOUT_MS`;排查请求体时确认无参考图路径为 `/v1/images/generations`、有参考图路径为 `/v1/images/edits`,业务 / 计费与 provider 首发模型均为 `gpt-image-2`,仅在符合条件的 provider 失败后切到兜底模型 `gpt-image-2-c`。
- 验证:运行 `cargo test -p api-server openai_image --manifest-path server-rs/Cargo.toml` 和相关玩法图片生成测试;真实联调只在本地私密环境放置 VectorEngine key。
- 关联:`docs/technical/VECTOR_ENGINE_GPT_IMAGE_2_GENERATION_2026-05-09.md`、`server-rs/crates/api-server/src/openai_image_generation.rs`。
@@ -3234,6 +3234,14 @@
- 验证:runner 回归测试必须同时覆盖“待确认工具只调用一次 LLM 并成功结束”和“普通连续工具仍会触发 max-turn 门禁”。
- 关联:`server-rs/crates/platform-editor-agent/src/framework/run.rs`、`server-rs/crates/platform-editor-agent/src/framework/tool.rs`、`server-rs/crates/platform-editor-agent/src/agent/tools/`。
## 画布 Agent 的规划请求不能关闭瞬时失败重试
- 现象:美术 Agent 对话返回红色错误气泡 `completion error: LLM 请求超时,累计尝试 1 次`;HTTP 本身仍返回 200,前端 20 分钟 transport timeout 没有触发。
- 原因:规划请求虽然有 Agent 专用单次 timeout,但 `editor_agent_llm_client` 把 `max_retries` 硬编码为 0;VectorEngine `gpt-5.4-mini` 的偶发长尾、连接超时或可重试上游状态会在第一次失败后直接持久化成 system error。framework 的英文 `completion error` 前缀也被原样暴露给用户。
- 处理:120 秒改为前端软提示阈值:POST 仍 pending 时显示不入库的“仍在处理中,请耐心等待”;provider 明确断开/失败才写正式错误。专用 provider 单 attempt 使用 8 分钟 hard timeout,请求发起阶段读取 `GENARRATIVE_LLM_MAX_RETRIES`,但画布 Agent 最多重试 1 次且重试退避最多 60 秒。不要只计算单次 complete 的最坏时间:runner 还可因非法 JSON/工具校验失败进入后续轮次,必须从 handler 入口开始计算 18 分钟总 deadline,进入 `agent.prompt(...)` 时扣除会话锁/上下文准备已用时间,为持久化和前端 20 分钟 timeout 留出余量。响应头后的体读取/解析错误按明确失败收口,必须使用真实 attempt 计数;规划、配置和定价错误对用户统一为中文,原始诊断只记后端日志。重试发生在任何生成工具执行前,不会重复提交生成任务或扣费,不要通过提高前端 timeout 或 runner `max_turns` 掩盖 provider 重试缺失。
- 验证:`platform-editor-agent` 测试锁定 8 分钟 hard timeout 与中文错误;前端 fake timer 用例锁定 120 秒前只显示思考动画、到点后显示耐心等待、成功/失败后移除;`platform-llm` 回归用例锁定第二次 attempt 成功响应头后的 body timeout 仍报累计 2 次;`api-server` 测试锁定专用 client retry、18 分钟整体 deadline 与中文直达错误。运行态排障按同一 request id 对齐 `platform_llm` failure stage 与 `/messages` 总耗时,并确认仍 pending 的请求不再在 120 秒形成错误气泡。
- 关联:`server-rs/crates/platform-editor-agent/src/agent/agent.rs`、`server-rs/crates/platform-editor-agent/src/framework/error.rs`、`server-rs/crates/api-server/src/state.rs`、`src/components/image-editor/EditorAgentConversation/useEditorAgentConversation.ts`、`src/components/image-editor/EditorAgentConversation/MessageBubble.tsx`、`src/services/image-editor/editorAgentClient.ts`。
## 前端退役目录不能只靠扫描和 ignore 隔离
- 现象:Tailwind `@source`、TypeScript 根 `include`、ESLint ignore 和 Vitest include 都排除了旧创作目录,但干净打开新版页面时,Vite 仍转换 `services/rpg-entry/index.ts`,构建产物也包含旧作品库和旧 profile 逻辑。