This commit is contained in:
2026-05-09 17:15:23 +08:00
parent 80a4183b45
commit a0ed128bde
43 changed files with 2573 additions and 381 deletions

View File

@@ -100,3 +100,13 @@
3. 发现
创作 Tab 必须位于中间,并使用原推荐 Tab 的星光图标,保持几何和视觉上的主行动入口。推荐 Tab 改用游戏手柄图标,避免与创作图标重复。
## 9. 2026-05-08 新用户默认发现与推荐门禁补充
未登录新用户首次进入平台时默认落在“发现”Tab不再直接进入“推荐”Tab 的内嵌运行态。
- 未登录用户点击底部或侧边栏“推荐”Tab 时,页面可切到推荐封面预览态,同时打开登录弹窗。
- 未登录状态下推荐页只展示当前推荐作品封面,不启动作品运行态,不展示推荐作品信息区。
- 未登录用户点击推荐页封面时,再次打开同一个登录弹窗;登录成功后由既有受保护动作继续进入作品详情或玩法入口。
- 未登录状态下点击“下一个”只切换下一张推荐封面,不触发登录弹窗,也不启动玩法。
- 已登录用户继续沿用推荐页内嵌运行态、上下滑切换和底部“下一个”行为。

View File

@@ -34,16 +34,21 @@ GENARRATIVE_LLM_BASE_URL=
GENARRATIVE_LLM_API_KEY=
GENARRATIVE_LLM_MODEL=
# APIMart / OpenAI 兼容图片网关
# APIMart / OpenAI 兼容 Responses 文本网关
APIMART_BASE_URL=
APIMART_API_KEY=
APIMART_IMAGE_REQUEST_TIMEOUT_MS=180000
# VectorEngine / Suno / Vidu 音频生成网关
VECTOR_ENGINE_BASE_URL=
# VectorEngine / GPT-image-2 / Suno / Vidu 生成网关
VECTOR_ENGINE_BASE_URL=https://api.vectorengine.ai
VECTOR_ENGINE_API_KEY=
VECTOR_ENGINE_IMAGE_REQUEST_TIMEOUT_MS=180000
VECTOR_ENGINE_AUDIO_REQUEST_TIMEOUT_MS=180000
# Hyper3D Rodin Gen-2 3D 模型生成
HYPER3D_BASE_URL=https://api.hyper3d.com/api/v2
HYPER3D_API_KEY=
HYPER3D_MODEL_REQUEST_TIMEOUT_MS=180000
# 火山引擎豆包语音 ASR / TTS
VOLCENGINE_SPEECH_API_KEY=
VOLCENGINE_SPEECH_APP_ID=
@@ -84,6 +89,9 @@ ARK_CHARACTER_VIDEO_MODEL / DASHSCOPE_CHARACTER_VIDEO_MODEL
VOLCENGINE_SPEECH_API_KEY / VOLCENGINE_API_KEY
VOLCENGINE_SPEECH_APP_ID / VOLCENGINE_ACCESS_KEY_ID
VOLCENGINE_SPEECH_ACCESS_KEY / VOLCENGINE_SECRET_ACCESS_KEY
HYPER3D_BASE_URL / RODIN_BASE_URL
HYPER3D_API_KEY / RODIN_API_KEY
HYPER3D_MODEL_REQUEST_TIMEOUT_MS / RODIN_MODEL_REQUEST_TIMEOUT_MS
```
## 运行时行为
@@ -93,8 +101,10 @@ VOLCENGINE_SPEECH_ACCESS_KEY / VOLCENGINE_SECRET_ACCESS_KEY
3. 文本 LLM provider 为 `ark` 且未配置 `GENARRATIVE_LLM_BASE_URL` 时,仍回退到 Ark 公开基础 URL。
4. 角色视频 provider 复用 Ark 且未配置 `ARK_CHARACTER_VIDEO_BASE_URL` 时,仍回退到 Ark 公开基础 URL。
5. 具体模型名缺失时不在配置层伪造默认模型,调用到对应能力时由下游配置校验返回缺配置错误。
6. VectorEngine 音频生成只读取 `VECTOR_ENGINE_BASE_URL` / `VECTOR_ENGINE_API_KEY`,不复用 `APIMART_*``GENARRATIVE_LLM_*` 或前端变量。
6. VectorEngine 图片与音频生成只读取 `VECTOR_ENGINE_BASE_URL` / `VECTOR_ENGINE_API_KEY`其中 GPT-image-2 图片生成额外读取 `VECTOR_ENGINE_IMAGE_REQUEST_TIMEOUT_MS`不复用 `APIMART_*``GENARRATIVE_LLM_*` 或前端变量。
7. 火山引擎语音能力由 `platform-speech` 收口协议帧与上游鉴权,`api-server` 只暴露平台鉴权后的代理路由,不向前端返回任何密钥字段。
8. Hyper3D Rodin Gen-2 使用公开默认 `https://api.hyper3d.com/api/v2`API Key 只读取 `HYPER3D_API_KEY` / `RODIN_API_KEY`,不复用文本 LLM、图片或音频网关密钥。
9. APIMart 当前只保留给创意 Agent 的 `gpt-5` Responses 文本/多模态理解链路GPT-image-2 图片生成不得再读取 APIMart 配置。
## 示例文件

View File

@@ -135,5 +135,5 @@
2. 再请求当前 Rust API 目标,例如 `http://127.0.0.1:3100/api/auth/login-options``http://127.0.0.1:8082/api/auth/login-options`
3. 若直连 API 成功而 3000 返回 `500`,检查 `RUST_SERVER_TARGET``GENARRATIVE_API_TARGET``GENARRATIVE_RUNTIME_SERVER_TARGET` 是否指向仍在监听的 API 端口。
4. `npm run dev` / `npm run dev:rust` 完整栈默认由脚本计算 API 端口;加载 `.env.local` 给后端使用后,脚本必须重新固定 `RUST_SERVER_TARGET`,避免 `.env.local` 中的旧代理目标覆盖本次启动的实际 API 端口。
5. `npm run dev:web` 只启动前端,不会自动拉起 Rust API如果单独使用它,脚本会先探测 `.env.local` / 当前环境里声明的目标,再回退到本机常见端口,最终只会接入一个真实可用的 `api-server`
5. `npm run dev:web` 只启动前端,不会自动拉起 Rust API如果 `.env.local` / 当前环境已经显式声明 `GENARRATIVE_RUNTIME_SERVER_TARGET``RUST_SERVER_TARGET``GENARRATIVE_API_TARGET``GENARRATIVE_API_PORT`,脚本必须固定使用该目标。目标当下不可用时只打印警告,不自动切到另一个端口,避免前端进程长时间绑定到随后会停掉的临时 API
6. 如果 `3000` 仍然返回 `500`,先确认浏览器是不是还开着旧的前端进程。当前脚本如果因为端口占用漂移到 `3001` / `3002`,应直接关掉旧进程后重启,而不是继续用旧的 3000 页面判断登录入口状态。

View File

@@ -0,0 +1,128 @@
# Hyper3D Rodin Gen-2 3D 模型生成接入方案 2026-05-08
## 1. 范围
本方案用于接入 Hyper3D Rodin Gen-2 的文生 3D 模型与图生 3D 模型能力。
本次只做后端安全代理与前端可复用 client不新增 SpacetimeDB 表,不落正式资产对象,不把 Hyper3D API Key 下发到前端。生成结果仍由调用方在拿到下载链接后决定是否进入 OSS / `asset_object` 的正式资产链。
## 2. 参考接口
参考文档:
- `https://developer.hyper3d.ai/api-specification/rodin-generation-gen2`
- `https://developer.hyper3d.ai/api-specification/check-status`
- `https://developer.hyper3d.ai/api-specification/download-results`
上游接口:
```text
POST https://api.hyper3d.com/api/v2/rodin
POST https://api.hyper3d.com/api/v2/status
POST https://api.hyper3d.com/api/v2/download
```
Rodin Gen-2 提交接口必须使用 `multipart/form-data`。文本生成时提交 `prompt`;图片生成时提交一个或多个 `images` 文件,可选 `prompt` 作为辅助描述。两种模式均固定提交 `tier=Gen-2`
## 3. 环境变量
```text
HYPER3D_BASE_URL=https://api.hyper3d.com/api/v2
HYPER3D_API_KEY=
HYPER3D_MODEL_REQUEST_TIMEOUT_MS=180000
```
兼容变量:
```text
RODIN_BASE_URL
RODIN_API_KEY
RODIN_MODEL_REQUEST_TIMEOUT_MS
```
说明:
1. `HYPER3D_API_KEY` / `RODIN_API_KEY` 只允许写入本地或生产私密环境,不提交到 Git。
2. 缺少 API Key 时,后端返回 `503 SERVICE_UNAVAILABLE`
3. `HYPER3D_BASE_URL` 默认使用公开 API 基础地址;如果团队后续改用代理网关,可通过环境变量覆盖。
## 4. 后端路由
新增 4 个鉴权路由:
| 方法 | 路由 | 用途 |
| --- | --- | --- |
| `POST` | `/api/assets/hyper3d/text-to-model` | 提交 Rodin Gen-2 文生模型任务 |
| `POST` | `/api/assets/hyper3d/image-to-model` | 提交 Rodin Gen-2 图生模型任务 |
| `POST` | `/api/assets/hyper3d/status` | 使用 `subscriptionKey` 查询任务状态 |
| `POST` | `/api/assets/hyper3d/download` | 使用 `taskUuid` 获取模型下载列表 |
文生模型请求最小体:
```json
{
"prompt": "一只低多边形宝箱,适合 RPG 游戏资产",
"geometryFileFormat": "glb",
"material": "PBR",
"quality": "medium",
"meshMode": "Quad",
"previewRender": true
}
```
图生模型请求最小体:
```json
{
"imageDataUrls": ["data:image/png;base64,..."],
"prompt": "保留主体轮廓,生成游戏可用 3D 模型",
"conditionMode": "concat",
"geometryFileFormat": "glb"
}
```
## 5. 约束
1. 图片只接受 `data:image/png|jpeg|webp;base64,...`,最多 5 张。
2. 单张图片解码后不超过 10MB。
3. `geometryFileFormat` 限定为 `glb/usdz/fbx/obj/stl`,默认 `glb`
4. `material` 限定为 `PBR/Shaded/All`,默认 `PBR`
5. `quality` 限定为 `high/medium/low/extra-low`,默认 `medium`
6. `meshMode` 限定为 `Quad/Raw`,默认 `Quad`
7. `addons` 首版只允许 `HighPack`
8. `bboxCondition` 必须为 3 个正数,按上游要求序列化为 JSON 字符串。
## 6. 返回语义
提交任务成功后返回:
```json
{
"ok": true,
"provider": "hyper3d-rodin",
"mode": "text-to-model",
"taskUuid": "task-uuid",
"subscriptionKey": "subscription-key",
"jobUuids": ["job-uuid"],
"message": "Submitted.",
"tier": "Gen-2"
}
```
状态查询会把上游 `Waiting / Generating / Done / Failed` 归一化为 `waiting / generating / done / failed / unknown`。下载接口只返回上游 `list.name``list.url`,不在后端转存文件。
## 7. 验收
建议执行:
```bash
npm run check:encoding
npm run typecheck
cd server-rs
cargo test -p shared-contracts hyper3d
cargo test -p api-server hyper3d
cargo check -p api-server
```
真实 API smoke 只在本地私密环境设置 `HYPER3D_API_KEY` 后执行。提交生成任务会消耗 Hyper3D Credit默认验证不自动调用真实生成接口。

View File

@@ -14,12 +14,12 @@
### 1. 图片生成
1. 拼图默认使用 APIMart `gpt-image-2` 生成图,外部请求尺寸固定为 `1:1``nanobanana2` 仍映射为 `gemini-3.1-flash-image-preview`
1. 拼图默认使用 VectorEngine `gpt-image-2-all` 生成图,外部请求尺寸固定为 `1024x1024`;前端历史 `nanobanana2` 选项只保留兼容展示,后端同样回落到 VectorEngine GPT-image-2-all不再调用 APIMart 图片网关
2. 历史 `original` 或空模型值只做兼容输入,不再进入 DashScope 原模型链路,统一按 `gpt-image-2` 路由。
3. 文生图和参考图生图共用同一个正方形尺寸口径,禁止一条链路仍生成竖屏或横版图。
4. 拼图图片提示词明确写入 `1:1 正方形画布`,继续保留适配 `3x3 / 4x4 / 5x5 / 6x6 / 7x7` 拼图切块、主体清晰、层次明确、无文字水印等约束。
5. 文生图正向 prompt 必须由后端压缩到 `500` 字符以内,优先保留玩家画面描述开头与固定拼图约束,避免上游把超长 prompt 判为“请求参数不合法”。
6. APIMart 上游失败时api-server 必须在错误 details 中保留业务 message、`upstreamStatus` 和截断后的 `rawExcerpt`,日志也要记录同样的摘要,避免生成进度页只能看到通用 HTTP 文案。
6. VectorEngine 上游失败时api-server 必须在错误 details 中保留业务 message、`upstreamStatus` 和截断后的 `rawExcerpt`,日志也要记录同样的摘要,避免生成进度页只能看到通用 HTTP 文案。
7. 图片生成仍由 `api-server` 执行。SpacetimeDB reducer 不做网络 I/O。
8. 光点预扣失败属于钱包或 SpacetimeDB 服务链路错误,不得映射成 `400 BAD_REQUEST`。除余额不足返回 `409 CONFLICT` 外,其余预扣异常统一按上游/服务错误暴露,避免生成页误提示“请求参数不合法”。
@@ -47,10 +47,10 @@
## 验收
1. 点击拼图草稿生成或重新生成画面时,后端请求 APIMart`size``1:1`默认模型为 `gpt-image-2`
1. 点击拼图草稿生成或重新生成画面时,后端请求 VectorEngine`size``1024x1024`上游模型为 `gpt-image-2-all`
2. 图片提示词包含 `1:1 正方形拼图关卡`
3. 图片提示词长度不超过 `500` 字符,超长画面描述会被截断,但适配 `3x3 / 4x4 / 5x5 / 6x6 / 7x7` 拼图切块、`避免文字、水印、边框和 UI 元素` 等玩法约束不能丢。
4. APIMart 返回参数错误、任务失败或非 2xx 时,前端错误优先展示后端 details.message后端日志能看到 `upstreamStatus``rawExcerpt`
4. VectorEngine 返回参数错误、任务失败或非 2xx 时,前端错误优先展示后端 details.message后端日志能看到 `upstreamStatus``rawExcerpt`
5. 正式拼图 run 中拖动拼块后,前端立即更新棋盘、合并块和通关状态,不再等待 `/drag`
6. 移动端运行时棋盘为正方形,并尽量贴近屏幕两侧边缘。
7. 基础单块和合并块都能看到圆角,合并块的外凸角与内凹角都不是直角,且图片不会溢出圆角裁剪。

View File

@@ -56,3 +56,9 @@
3. 标签少于 `3` 个时,发布弹窗明确提示“正式标签数量必须在 3 到 6 之间”。
4. 标签补到 `3~6` 个后,无需刷新页面即可通过前端发布校验。
5. 结果页顶部能看到轻量自动保存状态,不额外堆叠说明文案。
## 2026-05-09 发布失败提示补充
`publish_puzzle_work` 属于资产操作发布入口,按 `ASSET_GENERATION_POINTS_CONSUMPTION_2026-04-27.md` 会在发布 mutation 前预扣 `1` 枚光点。余额不足时后端返回 `409 CONFLICT`,响应 `details.message``光点余额不足`,这属于业务拒绝,不是拼图发布接口不可用。
结果页发布弹窗必须在用户点击发布后继续展示后端错误原因,不能只把错误写到弹窗背后的页面 banner。这样余额不足、SpacetimeDB 发布门禁或其他后端业务错误都会在当前独立发布面板中直接可见。

View File

@@ -5,8 +5,11 @@
## 文档列表
- [RUST_WORKSPACE_DEPENDENCY_CONSOLIDATION_2026-05-07.md](./RUST_WORKSPACE_DEPENDENCY_CONSOLIDATION_2026-05-07.md):记录 `server-rs` Cargo 依赖集中配置口径,第三方版本和 workspace 内部 crate path 统一维护在根 `server-rs/Cargo.toml`,成员 crate 只保留 feature/optional 差异。
- [RECOMMEND_RUNTIME_AUTH_FAILURE_ISOLATION_FIX_2026-05-09.md](./RECOMMEND_RUNTIME_AUTH_FAILURE_ISOLATION_FIX_2026-05-09.md):记录平台推荐页自动加载作品后局部运行态请求 `401` 不应扩散成全局登出的修复,覆盖请求层局部鉴权失败隔离、推荐页 embedded 运行态启动和回归测试
- [VECTOR_ENGINE_GPT_IMAGE_2_GENERATION_2026-05-09.md](./VECTOR_ENGINE_GPT_IMAGE_2_GENERATION_2026-05-09.md):记录 GPT-image-2 图片生成从 APIMart 迁移到 VectorEngine `gpt-image-2-all` 的接口、环境变量、尺寸映射、错误口径和验收命令
- [SPACETIMEDB_PUBLISH_SCCACHE_FALLBACK_2026-05-09.md](./SPACETIMEDB_PUBLISH_SCCACHE_FALLBACK_2026-05-09.md):记录本地 `spacetime publish` 被 sccache wrapper 通信异常阻断时的根因、`dev-rust-stack` 自动降级策略和手动排障命令。
- [RECOMMEND_RUNTIME_AUTH_FAILURE_ISOLATION_FIX_2026-05-09.md](./RECOMMEND_RUNTIME_AUTH_FAILURE_ISOLATION_FIX_2026-05-09.md):记录平台推荐页自动加载作品、公开拼图作品完整运行态和展示层图片换签的局部请求 `401` 不应扩散成全局登出的修复,覆盖请求层局部鉴权失败隔离、推荐页 embedded 运行态启动、拼图开局/排行榜/下一关和回归测试。
- [AUTH_GATE_LOGIN_RACE_GUARD_FIX_2026-05-09.md](./AUTH_GATE_LOGIN_RACE_GUARD_FIX_2026-05-09.md):记录 `AuthGate` 登录成功后又被旧 hydrate 覆盖回未登录态的竞态根因、版本号保护修复与回归测试。
- [HYPER3D_RODIN_GEN2_MODEL_GENERATION_2026-05-08.md](./HYPER3D_RODIN_GEN2_MODEL_GENERATION_2026-05-08.md):记录 Hyper3D Rodin Gen-2 文生 3D 模型、图生 3D 模型、状态查询和下载列表的后端代理、环境变量、请求约束与验收边界。
- [VOLCENGINE_SPEECH_STREAMING_INTEGRATION_2026-05-08.md](./VOLCENGINE_SPEECH_STREAMING_INTEGRATION_2026-05-08.md):记录火山引擎大模型 ASR 双向流式、TTS WebSocket 双向流式和 TTS HTTP SSE 单向流式的后端代理、环境变量、协议帧和验收边界。
- [VECTOR_ENGINE_AUDIO_GENERATION_SUNO_VIDU_2026-05-08.md](./VECTOR_ENGINE_AUDIO_GENERATION_SUNO_VIDU_2026-05-08.md):记录视觉小说结果页接入 VectorEngine Suno 文生背景音乐与 Vidu 文生音效的接口、环境变量、后端路由、OSS 资产回写和前端弹层交互边界。
- [PROFILE_FEEDBACK_BACKEND_INTEGRATION_2026-05-08.md](./PROFILE_FEEDBACK_BACKEND_INTEGRATION_2026-05-08.md):冻结“我的”页签帮助与反馈入口的后端接入方案,覆盖 `POST /api/profile/feedback``profile_feedback_submission`、凭证图片 Data URL 校验和前端预览/提交边界。
@@ -20,7 +23,7 @@
- [PRODUCTION_DEPLOYMENT_PLAN_2026-05-02.md](./PRODUCTION_DEPLOYMENT_PLAN_2026-05-02.md):冻结单机生产部署目标,从旧一体化启动脚本切到 Nginx、systemd 托管 SpacetimeDB 与 Rust `api-server`,并记录生产 Jenkins 流水线拆分计划和首批部署骨架。
- [PUZZLE_RUNTIME_FRONTEND_LOGIC_REHOME_2026-05-02.md](./PUZZLE_RUNTIME_FRONTEND_LOGIC_REHOME_2026-05-02.md):记录拼图正式平台入口移动、交换、合并、拆分和通关裁决收回前端即时运行态,排行榜、下一关和游玩记录继续由后端持久化处理。
- [RPG_FOUNDATION_DRAFT_ROLE_DOSSIER_TIMEOUT_FALLBACK_2026-05-02.md](./RPG_FOUNDATION_DRAFT_ROLE_DOSSIER_TIMEOUT_FALLBACK_2026-05-02.md):记录 `agent-foundation-*-dossier-batch-*` 无搜索 Responses 请求超时后的本地养成档案兜底,避免底稿主链被尾部角色润色阶段阻断。
- [RPG_IMAGE_GENERATION_GPT_IMAGE_2_MIGRATION_2026-05-02.md](./RPG_IMAGE_GENERATION_GPT_IMAGE_2_MIGRATION_2026-05-02.md):记录 RPG 角色主图与场景幕背景图统一迁移到 APIMart OpenAI 兼容 `gpt-image-2` 生图入口的边界、配置和验收口径。
- [RPG_IMAGE_GENERATION_GPT_IMAGE_2_MIGRATION_2026-05-02.md](./RPG_IMAGE_GENERATION_GPT_IMAGE_2_MIGRATION_2026-05-02.md):记录 RPG 角色主图与场景幕背景图统一迁移到 `gpt-image-2` 生图入口的边界、配置和验收口径2026-05-09 起实际上游以 VectorEngine 迁移文档为准
- [RPG_FOUNDATION_DRAFT_LANDMARK_SEED_BATCH_TIMEOUT_FIX_2026-05-02.md](./RPG_FOUNDATION_DRAFT_LANDMARK_SEED_BATCH_TIMEOUT_FIX_2026-05-02.md):记录 `agent-foundation-landmark-seed-batch-1` 无搜索 Responses 请求超时的根因,并将场景骨架批次收敛为单场景生成。
- [PROFILE_MAIN_ROUTE_VITE_PROXY_FIX_2026-05-02.md](./PROFILE_MAIN_ROUTE_VITE_PROXY_FIX_2026-05-02.md):记录“我的”和“存档”页面在本地把 `/api/profile/*` 请求落到 Vite SPA fallback、导致 HTML 被当 JSON 解析的根因,以及 `/api/profile` 代理补齐与回归测试。
- [SERVER_RS_DDD_WP_DEL_CLEANUP_2026-05-01.md](./SERVER_RS_DDD_WP_DEL_CLEANUP_2026-05-01.md):记录 `WP-DEL 删除旧层与命名收口`,物理删除旧 runtime story HTTP DTO、前端 `Rpg*` alias、旧 `/api/custom-world/*` 非 runtime 前缀、Puzzle `local-next-level` 入口和 `/generated-*` 资产直读代理;生成资产读取统一走 OSS read-url 链路。

View File

@@ -6,6 +6,8 @@
登录成功进入平台推荐页后,推荐页会自动加载一个公开作品并启动嵌入式运行态。实际联调中出现过:作品刚加载出来,前端又瞬间回到未登录状态;停留在其他页面,或推荐页没有成功加载出作品时不会复现。
后续复测又发现:登录成功后,从推荐页点进拼图公开作品详情并启动完整拼图运行态,也可能在开局或通关后瞬间退回未登录。两类现象的底层问题一致,都是玩法/展示层局部请求把 `401` 扩散成全局鉴权事件。
## 根因
推荐页首屏的作品运行态启动是后台自动副作用,不是用户主动点击的账号操作。它会触发多条受保护请求,例如:
@@ -16,6 +18,10 @@
这些请求一旦遇到本地代理错配、后端短暂不可用或 token 刷新失败,原请求层会按普通受保护请求处理 `401`,清空 access token 并广播全局鉴权变更。`AuthGate` 收到事件后重新 hydrate于是当前用户界面被切回未登录态。
推荐页里还有一类更隐蔽的触发点:`ResolvedAssetImage` / `useResolvedAssetReadUrl` 在挂载时会请求 `/api/assets/read-url` 给 generated 私有图片换签。它本质上也是展示层后台请求,若按普通受保护请求处理 `401`,同样会把一次图片换签失败放大成全局掉线。
公开拼图作品的完整运行态还会在用户进入作品后自动发起 `startPuzzleRun`,通关后自动 `submitPuzzleLeaderboard`,点击下一关时 `advancePuzzleNextLevel`。这些请求属于当前玩法的运行态同步,失败时应该落到当前拼图错误态;它们不能清空全局 access token也不能触发 `AuthGate` 重新 hydrate。
## 修复
本次把推荐页自动运行态请求定义为“卡片级后台请求”:
@@ -23,14 +29,18 @@
1. `apiClient` 增加 `clearAuthOnUnauthorized` 选项,允许局部请求在 `401` 时不清空全局 token。
2. 推荐页嵌入式运行态请求统一传入 `skipRefresh: true``notifyAuthStateChange: false``clearAuthOnUnauthorized: false`
3. 推荐页自动启动作品前必须满足 `canReadProtectedData`,避免 `AuthGate` 仍在恢复阶段就提前发起受保护写请求。
4. 普通用户主动点击“启动”、Remix、发布、点赞等路径继续保留默认全局鉴权处理。
4. generated 图片换签请求同样使用局部后台鉴权选项并跳过 refresh失败只让当前图片为空不触发全局登录态清理。
5. 公开拼图作品进入完整运行态后,把本次 run 标记为 `isolated` 鉴权模式;开局、重开、排行榜提交和下一关推进都沿用局部鉴权选项。
6. Remix、发布、点赞、账号设置、退出登录等真正账号动作继续保留默认全局鉴权处理。
## 验证
1. `npm run test -- src/services/apiClient.test.ts`
1. `npm run test -- src/services/apiClient.test.ts src/services/assetReadUrlService.test.ts`
2. `npm run test -- src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx -t "home recommendation starts embedded puzzle"`
3. `npm run typecheck`
4. `npm run check:encoding`
3. `npm run test -- src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx -t "formal puzzle runtime uses frontend move merge logic and backend leaderboard"`
4. `npm run test -- src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx -t "formal puzzle similar work keeps current run level progression"`
5. `npm run typecheck`
6. `npm run check:encoding`
## 关联文件

View File

@@ -7,7 +7,7 @@ RPG 创作链路里有两类正式图片资产需要统一模型:
1. 角色主图候选生成。
2. 场景幕背景图生成。
旧实现中角色主图默认使用 `wan2.7-image-pro`,场景图根据是否有参考图分别使用 DashScope 文生图与图生图模型。拼图链路已经接入 APIMart 的 OpenAI 兼容 `/images/generations`,并以 `gpt-image-2` 作为默认图片模型,因此本次 RPG 图片迁移复用同一类服务端配置与请求口径
旧实现中角色主图默认使用 `wan2.7-image-pro`,场景图根据是否有参考图分别使用 DashScope 文生图与图生图模型。拼图链路已经接入 GPT-image-2 图片生成,因此本次 RPG 图片迁移复用同一类服务端配置与请求口径。2026-05-09 起GPT-image-2 图片生成上游统一迁移到 VectorEngine具体接口以 `VECTOR_ENGINE_GPT_IMAGE_2_GENERATION_2026-05-09.md` 为准
## 落地范围
@@ -26,9 +26,9 @@ RPG 创作链路里有两类正式图片资产需要统一模型:
服务端使用:
```text
POST {APIMART_BASE_URL}/images/generations
Authorization: Bearer {APIMART_API_KEY}
model = gpt-image-2
POST {VECTOR_ENGINE_BASE_URL}/v1/images/generations
Authorization: Bearer {VECTOR_ENGINE_API_KEY}
model = gpt-image-2-all
```
请求体统一包含:
@@ -36,16 +36,15 @@ model = gpt-image-2
1. `model`
2. `prompt`
3. `n`
4. `official_fallback = true`
5. `size`
6. 有参考图时增加 `image_urls`
4. `size`
5. 有参考图时增加 `image`
尺寸归一规则:
1. `1024*1024``1024x1024``1:1` -> `1:1`
2. `1280*720``1600*900``16:9` -> `16:9`
1. `1024*1024``1024x1024``1:1` -> `1024x1024`
2. `1280*720``1600*900``16:9` -> `1536x1024`
响应解析兼容同步 `data[].url``data[].b64_json` 异步 `task_id` / `GET /tasks/{task_id}` 结构。
响应解析同步 `data[].url``data[].b64_json`VectorEngine GPT-image-2-all 当前不再使用 APIMart 异步 `task_id` / `GET /tasks/{task_id}` 结构。
## 非范围
@@ -56,22 +55,22 @@ model = gpt-image-2
## 配置
本次复用已有 APIMart 配置:
本次复用 VectorEngine 图片配置:
```text
APIMART_BASE_URL=https://api.apimart.ai/v1
APIMART_API_KEY=...
APIMART_IMAGE_REQUEST_TIMEOUT_MS=180000
VECTOR_ENGINE_BASE_URL=https://api.vectorengine.ai
VECTOR_ENGINE_API_KEY=...
VECTOR_ENGINE_IMAGE_REQUEST_TIMEOUT_MS=180000
```
`APIMART_API_KEY` 缺失时,角色主图与场景图返回 `SERVICE_UNAVAILABLE``details.provider = "apimart"`
`VECTOR_ENGINE_API_KEY` 缺失时,角色主图与场景图返回 `SERVICE_UNAVAILABLE``details.provider = "vector-engine"`
## 验收
1. 角色主图生成请求上游 `model``gpt-image-2`,且携带 `official_fallback = true`
2. 场景图生成请求上游 `model``gpt-image-2`,且携带 `official_fallback = true`
1. 角色主图生成请求上游 `model``gpt-image-2-all`,且携带 `official_fallback`
2. 场景图生成请求上游 `model``gpt-image-2-all`,且携带 `official_fallback`
3. 旧前端或历史草稿传 `wan2.7-image-pro` 时不会回退旧模型。
4. 场景参考图生成仍能把参考图 Data URL 放入 `image_urls`
4. 场景参考图生成仍能把参考图 Data URL 放入 `image`
5. 角色主图生成后仍执行原有 PNG 透明背景处理与 OSS 写入。
6. `cargo test -p api-server character_visual --manifest-path server-rs/Cargo.toml` 通过。
7. `cargo test -p api-server custom_world_ai --manifest-path server-rs/Cargo.toml` 通过。

View File

@@ -0,0 +1,53 @@
# SpacetimeDB publish sccache 降级处理
## 背景
Windows 本地执行 `npm run dev:rust``spacetime publish` 时,`spacetime` 会在内部调用 Cargo 构建 `server-rs/crates/spacetime-module`。因为 `server-rs/.cargo/config.toml` 配置了 `rustc-wrapper = "sccache"`,即使当前 shell 没有设置 `RUSTC_WRAPPER`Cargo 仍会先执行 `sccache rustc -vV`
当本机 sccache server 状态损坏、client/server 通信异常或版本残留不一致时,可能出现:
```text
sccache: error: failed to execute compile
sccache: caused by: Failed to send data to or receive data from server
sccache: caused by: Failed to read response header
sccache: caused by: failed to fill whole buffer
```
这类错误发生在 rustc wrapper 层,不能说明 SpacetimeDB module 代码本身编译失败。
## 本地开发处理
`scripts/dev-rust-stack.sh` 的 publish 阶段保留首次正常 `sccache` 构建;如果 stderr 命中 sccache 通信或 wrapper 失败特征,则自动在同一 `--root-dir`、同一发布参数下清空本次子进程的 `RUSTC_WRAPPER``CARGO_BUILD_RUSTC_WRAPPER` 后重试。
该处理只影响本次 publish 子进程,不修改 `server-rs/.cargo/config.toml`,也不删除本地 target 缓存。
## 手动排障命令
优先确认 rustc 本身可用:
```bash
rustc -vV
```
如果只想绕过本次 Cargo 构建的 sccache wrapper可在 Git Bash 中执行:
```bash
cd server-rs/crates/spacetime-module
RUSTC_WRAPPER= CARGO_BUILD_RUSTC_WRAPPER= cargo build --target=wasm32-unknown-unknown --release
```
如果需要排查 sccache server 状态:
```bash
sccache --show-stats
sccache --stop-server
sccache --start-server
```
`sccache --stop-server` 本身也可能因为 server 通道已损坏而失败;此时不应阻断本地开发 publish先使用 wrapper 降级完成验证。
## 验证
1. `bash -n scripts/dev-rust-stack.sh`
2. `RUSTC_WRAPPER= CARGO_BUILD_RUSTC_WRAPPER= cargo check --target=wasm32-unknown-unknown --release`
3. 重新运行 `npm run dev:rust`,看到 sccache 通信失败时脚本应打印降级提示并继续真实构建。

View File

@@ -39,6 +39,8 @@ spacetime --root-dir="${GENARRATIVE_SPACETIME_ROOT_DIR}" ...
`spacetime start` 不再额外设置 `--data-dir`,启动前会先执行 Ubuntu 专用 `sync_ubuntu_spacetime_install`,优先从 `/usr/.local/share/spacetime/bin/<version>/spacetimedb-cli``$HOME/.local/share/spacetime/bin/<version>/spacetimedb-cli` 同步到 `.spacetimedb/bin/current/spacetimedb-cli`;当前线上 `spacetime` 入口为 `/usr/local/bin/spacetime`。启动参数、探活和 root-dir 占用判定都使用同一个 `.spacetimedb/`。这样可以把发布包与部署机全局 `~/.spacetime` 隔离,避免后续人工 `spacetime login` 影响本地发布包。但如果旧 `.spacetimedb/` 已经由另一个身份创建,仍需要按第 4 节处理。
本地 `npm run dev:rust` / `scripts/dev-rust-stack.sh` 也必须遵循同一条规则:`server ping``start``publish` 都显式使用 `server-rs/.spacetimedb/local` 作为 `--root-dir`。不要让发布命令回退到全局 CLI 登录态,否则会出现本地 root 已有目标库权限,但裸 `spacetime publish` 仍使用另一个身份发起预检查并返回 403。
## 4. 排查与处理
先在执行 `start.sh` 的同一台机器、同一用户下确认身份:
@@ -48,6 +50,16 @@ spacetime --root-dir ./.spacetimedb login show
spacetime --root-dir ./.spacetimedb list --server http://127.0.0.1:3101
```
本地开发栈排查时使用仓库本地 root
```bash
spacetime login show
spacetime --root-dir server-rs/.spacetimedb/local login show
spacetime --root-dir server-rs/.spacetimedb/local list --server http://127.0.0.1:3101
```
如果裸 `spacetime login show` 的身份与 `--root-dir server-rs/.spacetimedb/local login show` 不一致,而目标库只出现在本地 root 的 `list` 结果中,说明不能使用裸 `spacetime publish`。应通过 `npm run dev:rust` 或显式追加 `--root-dir=server-rs/.spacetimedb/local` 重新发布。
如果目标是本地部署库,且允许清空本地数据:
```bash

View File

@@ -0,0 +1,116 @@
# VectorEngine GPT-image-2 图片生成迁移 2026-05-09
## 背景
GPT-image-2 图片生成此前通过 APIMart OpenAI 兼容入口执行。为统一供应商网关,本次参考 VectorEngine Apifox 文档 `https://vectorengine.apifox.cn/api-448710071`,把仓库内所有 GPT-image-2 生图调用迁移到 VectorEngine不再使用 APIMart 图片网关。
APIMart 仍只保留给创意 Agent 的 `gpt-5` Responses 文本/多模态理解链路;不要把该文本链路与 GPT-image-2 图片生成配置混用。
## 参考接口
VectorEngine 正式环境基础地址来自 Apifox 项目环境:
```text
https://api.vectorengine.ai
```
GPT-image-2-all 生图接口:
```text
POST /v1/images/generations
Content-Type: application/json
Accept: application/json
Authorization: Bearer {VECTOR_ENGINE_API_KEY}
```
请求体:
```json
{
"model": "gpt-image-2-all",
"size": "1024x1024",
"n": 1,
"prompt": "生成一只猫"
}
```
参考图场景可按文档字段追加:
```json
{
"image": ["data:image/png;base64,..."]
}
```
响应体按同步 OpenAI Images 结构读取:
```json
{
"created": 1776909189,
"data": [
{
"revised_prompt": "",
"url": "https://pro.filesystem.site/cdn/20260423/example.webp"
}
]
}
```
## 尺寸映射
VectorEngine 文档要求使用像素尺寸,不再使用 APIMart 的比例写法:
| 旧输入 | VectorEngine 请求值 | 用途 |
| --- | --- | --- |
| `1:1``1024*1024``1024x1024` | `1024x1024` | 拼图、方洞局部贴图、角色主形象 |
| `16:9``1280*720``1600*900``1536x1024` | `1536x1024` | 场景图、封面图、方洞横版背景 |
| `1024x1536` | `1024x1536` | 竖版图 |
| `2048x1152` | `1536x1024` | 开局 CG 故事板首版降为文档明确支持的横版尺寸 |
若调用方传入其它非空尺寸,后端先透传,方便后续跟随 VectorEngine 文档扩展;空值统一回落到 `1024x1024`
## 后端落点
1. `server-rs/crates/api-server/src/openai_image_generation.rs`
- 保留当前共享 helper 文件名与函数名,减少 RPG、方洞等调用方改动面。
- 内部配置改读 `VECTOR_ENGINE_BASE_URL` / `VECTOR_ENGINE_API_KEY` / `VECTOR_ENGINE_IMAGE_REQUEST_TIMEOUT_MS`
- 请求模型固定为 `gpt-image-2-all`,不再写 `official_fallback`
- 请求路径改为 `/v1/images/generations`,响应直接解析 `data[].url` / `data[].b64_json`,不再轮询 `/tasks/{task_id}`
2. `server-rs/crates/api-server/src/puzzle.rs`
- 拼图默认 `gpt-image-2` 前端值继续兼容,但上游请求模型统一映射到 `gpt-image-2-all`
- `nanobanana2` / `gemini-3.1-flash-image-preview` 不再走 APIMart当前阶段统一回落到 VectorEngine GPT-image-2-all避免保留旧图片网关。
- 错误 `details.provider` 改为 `vector-engine`
3. `.codex/skills/gpt-image-2-apimart/`
- 目录名暂不强制迁移避免本地插件索引漂移Skill 文案与脚本行为改为 VectorEngine。
## 环境变量
```text
VECTOR_ENGINE_BASE_URL=https://api.vectorengine.ai
VECTOR_ENGINE_API_KEY=
VECTOR_ENGINE_IMAGE_REQUEST_TIMEOUT_MS=180000
```
说明:
1. GPT-image-2 图片生成不读取 `APIMART_BASE_URL``APIMART_API_KEY``APIMART_IMAGE_REQUEST_TIMEOUT_MS`
2. `VECTOR_ENGINE_BASE_URL` 仍允许部署环境覆盖,不在代码中绑定私有网关。
3. `VECTOR_ENGINE_API_KEY` 只能进入本地或生产私密环境文件,不提交到 Git。
## 非范围
1. 不迁移创意 Agent 的 APIMart `gpt-5` Responses 链路。
2. 不改变 SpacetimeDB 表结构、migration 或 bindings。
3. 不改前端 UI 文案和模型选择控件展示。
4. 不新增新的图片资产表或图片代理路由。
## 验收
1. 所有 GPT-image-2 生图请求都走 `POST {VECTOR_ENGINE_BASE_URL}/v1/images/generations`
2. 请求体 `model = gpt-image-2-all`,尺寸为 VectorEngine 支持的像素尺寸。
3. 请求体不再包含 `official_fallback`
4. 参考图字段使用 `image`,不再使用 APIMart 的 `image_urls`
5. 缺少 `VECTOR_ENGINE_BASE_URL``VECTOR_ENGINE_API_KEY` 时返回 `503 SERVICE_UNAVAILABLE``details.provider = "vector-engine"`
6. 上游错误映射为 `502 UPSTREAM_ERROR`,保留 `upstreamStatus`、业务 message 和截断后的 raw excerpt。
7. 运行 `npm run check:encoding``cargo test -p api-server openai_image --manifest-path server-rs/Cargo.toml``cargo test -p api-server puzzle --manifest-path server-rs/Cargo.toml``cargo test -p api-server custom_world_ai --manifest-path server-rs/Cargo.toml``cargo test -p api-server character_visual --manifest-path server-rs/Cargo.toml`
8. 后端改动后使用 `npm run api-server` 重启,并确认 `/healthz`