This commit is contained in:
2026-05-09 18:24:08 +08:00
parent a0ed128bde
commit bc704d0c22
38 changed files with 481 additions and 378 deletions

View File

@@ -1,15 +1,16 @@
# 拼图 APIMart 图片模型路由接入 2026-05-01
# 拼图图片模型路由接入 2026-05-01
> 2026-05-09 更新GPT-image-2 图片生成已从 APIMart 迁移到 VectorEngine。本文保留前端模型选择和拼图扣费/持久化历史设计,图片上游接口、环境变量和请求体以 `VECTOR_ENGINE_GPT_IMAGE_2_GENERATION_2026-05-09.md` 为准。
## 背景
拼图创作已收口为填表式流程,首图生成和结果页关卡重新生成都由 `server-rs/crates/api-server/src/puzzle.rs` 执行外部图片 I/O再把正式图写入 OSS 与 SpacetimeDB。新的模型选择只影响图片生成上游不改变 SpacetimeDB 表结构、拼图草稿结构或前端运行时规则。
本轮参考 APIMart 文档:
历史版本曾参考 APIMart 文档;当前 GPT-image-2 图片生成参考 VectorEngine Apifox 文档:
1. `https://docs.apimart.ai/cn/api-reference/images/gpt-image-2/generation`
2. `https://docs.apimart.ai/cn/api-reference/images/gemini-3.1-flash/generation`
1. `https://vectorengine.apifox.cn/api-448710071`
两条文档均指向 OpenAI 兼容风格的图片生成入口:`POST https://api.apimart.ai/v1/images/generations`,头部使用 `Authorization: Bearer {APIMART_API_KEY}`。请求体至少包含 `model``prompt``n``official_fallback = true``size`。返回体按 OpenAI images 兼容格式优先读取 `data[].url`,若供应商返回异步任务结构,则继续按 `task_id` / `tasks/{task_id}` 轮询并提取图片 URL
当前图片生成入口:`POST {VECTOR_ENGINE_BASE_URL}/v1/images/generations`,头部使用 `Authorization: Bearer {VECTOR_ENGINE_API_KEY}`。请求体至少包含 `model = gpt-image-2-all``prompt``n``size`,参考图使用 `image` 数组。返回体按同步 OpenAI images 结构读取 `data[].url``data[].b64_json`,不再轮询 APIMart `tasks/{task_id}`
## 模型选项
@@ -17,8 +18,8 @@
| 前端显示 | 请求值 | 上游 |
| --- | --- | --- |
| `gpt-image-2` | `gpt-image-2` | APIMart `/v1/images/generations` |
| `nanobanana2` | `gemini-3.1-flash-image-preview` | APIMart `/v1/images/generations` |
| `gpt-image-2` | `gpt-image-2` | VectorEngine `/v1/images/generations`,上游模型 `gpt-image-2-all` |
| `nanobanana2` | `gemini-3.1-flash-image-preview` | 历史兼容选项,后端回落到 VectorEngine `gpt-image-2-all` |
默认值为 `gpt-image-2`。前端只负责展示和传递所选模型,不能把模型路由逻辑、上游请求体拼装或 API Key 暴露到浏览器。历史草稿或旧请求中的空值、`original`、未知值统一按 `gpt-image-2` 处理,不再把拼图生图路由回 DashScope 原模型。
@@ -40,16 +41,15 @@
2. `compile_puzzle_draft_with_initial_cover``generate_puzzle_image_candidates` 增加图片模型参数。
3. `imageModel` 归一化规则:
- 空值、`original` 或未知值统一回落为 `gpt-image-2`
- `gpt-image-2`APIMart
- `gemini-3.1-flash-image-preview` 走 APIMart前端显示名为 `nanobanana2`
4. APIMart 文生图和图生图共用 `POST /v1/images/generations`。有参考图时,后端将参考图 Data URL 作为 `image_urls` 数组传入;若上游不接受该字段,错误按上游失败返回,不在前端降级伪造结果。
5. APIMart 尺寸使用文档要求的比例写法 `1:1`,所有 APIMart 图片请求体固定携带 `official_fallback = true``gemini-3.1-flash-image-preview` 额外带 `resolution = "1K"`,对齐约 1024px 的拼图正方形素材
6. APIMart 生成成功后仍下载远程图片,沿用现有 OSS 私有对象、`asset_object``asset_entity_binding` 写入流程。若图片已成功上传 OSS Maincloud / SpacetimeDB 短暂返回 `503 Service Unavailable`,资产索引写入允许降级跳过,并返回本次生成图片;日志必须记录 `拼图图片资产索引写入因 SpacetimeDB 连接不可用而降级跳过`
7. `save_puzzle_generated_images` 写回草稿时若遇到 Maincloud 连接级 `503` 或断线API 层基于本次生成结果合成 session 快照返回给前端,避免 APIMart 已成功出图却被后置持久化误报成服务不可用。余额不足、参数错误、上游生图失败仍按原错误返回,不做伪成功。
8. 结果页 `generate_puzzle_images` 会携带当前作品信息和 `levelsJson`。当 Maincloud / SpacetimeDB 在读取 session 阶段就返回连接级 `503` 或断线时,后端必须先用这份结果页快照构造最小内存 session再继续调用 APIMart;外部图片已经生成后仍按第 6、7 条处理持久化降级。余额不足、参数错误、缺少草稿快照、关卡不存在等业务错误不走此降级。
9. APIMart 异步任务轮询按文档口径在提交后先等待 `10s`,再调用 `GET /v1/tasks/{task_id}`;图片地址提取同时支持 `url: "..."``url: ["..."]` 两种结构
10. APIMart 错误统一映射为 `502 UPSTREAM_ERROR``details.provider = "apimart"`,保留上游状态码、业务 message 和截断后的 raw excerpt
11. 拼图首图生成 `compile_puzzle_draft` 与关卡图片生成 `generate_puzzle_images` 每次预扣 `2` 光点;余额不足仍返回 `409 CONFLICT`Maincloud 连接级 503 仍按既有降级策略处理。
- `gpt-image-2`VectorEngine
- `gemini-3.1-flash-image-preview` 不再走 APIMart前端显示名`nanobanana2`,后端统一回落到 VectorEngine `gpt-image-2-all`
4. VectorEngine 文生图和图生图共用 `POST /v1/images/generations`。有参考图时,后端将参考图 Data URL 作为 `image` 数组传入;若上游不接受该字段,错误按上游失败返回,不在前端降级伪造结果。
5. VectorEngine 拼图尺寸使用 `1024x1024`请求体携带 `official_fallback`
6. VectorEngine 生成成功后仍下载远程图片,沿用现有 OSS 私有对象、`asset_object``asset_entity_binding` 写入流程。若图片已成功上传 OSS但 SpacetimeDB 短暂返回 `503 Service Unavailable`,资产索引写入允许降级跳过,并返回本次生成图片;日志必须记录 `拼图图片资产索引写入因 SpacetimeDB 连接不可用而降级跳过`
7. `save_puzzle_generated_images` 写回草稿时若遇到 SpacetimeDB 连接级 `503` 或断线API 层基于本次生成结果合成 session 快照返回给前端,避免 VectorEngine 已成功出图却被后置持久化误报成服务不可用。余额不足、参数错误、上游生图失败仍按原错误返回,不做伪成功。
8. 结果页 `generate_puzzle_images` 会携带当前作品信息和 `levelsJson`。当 SpacetimeDB 在读取 session 阶段就返回连接级 `503` 或断线时,后端必须先用这份结果页快照构造最小内存 session再继续调用 VectorEngine;外部图片已经生成后仍按第 6、7 条处理持久化降级。余额不足、参数错误、缺少草稿快照、关卡不存在等业务错误不走此降级。
9. VectorEngine 错误统一映射为 `502 UPSTREAM_ERROR``details.provider = "vector-engine"`,保留上游状态码、业务 message 和截断后的 raw excerpt
10. 拼图首图生成 `compile_puzzle_draft` 与关卡图片生成 `generate_puzzle_images` 每次预扣 `2` 光点;余额不足仍返回 `409 CONFLICT`SpacetimeDB 连接级 503 仍按既有降级策略处理
## 关卡名多模态生成
@@ -64,21 +64,21 @@
新增服务端环境变量:
```text
APIMART_BASE_URL="https://api.apimart.ai/v1"
APIMART_API_KEY="YOUR_APIMART_API_KEY"
APIMART_IMAGE_REQUEST_TIMEOUT_MS=180000
VECTOR_ENGINE_BASE_URL="https://api.vectorengine.ai"
VECTOR_ENGINE_API_KEY="YOUR_VECTOR_ENGINE_API_KEY"
VECTOR_ENGINE_IMAGE_REQUEST_TIMEOUT_MS=180000
```
`APIMART_API_KEY` 只能存在于本地或部署环境,不写入 Git 跟踪文件。若选择 APIMart 模型但缺少 key后端返回服务不可用错误前端展示现有错误面板。
`VECTOR_ENGINE_API_KEY` 只能存在于本地或部署环境,不写入 Git 跟踪文件。若缺少 key后端返回服务不可用错误前端展示现有错误面板。
## 验收
1. 创作表单和关卡详情的画面描述框左下角能切换 `gpt-image-2``nanobanana2`,默认显示 `gpt-image-2`
2. 点击“生成草稿”时,后端首图生成使用当前表单选择的模型。
3. 点击“生成画面 / 重新生成画面”时,后端当前关卡图片生成使用关卡详情选择的模型。
4. 历史 `original` 或空模型值不会再触发 DashScope统一按 `gpt-image-2` 请求 APIMart
5. 选择 APIMart 模型时,请求 `POST {APIMART_BASE_URL}/images/generations`,使用 `Authorization: Bearer {APIMART_API_KEY}``model` 等于请求值,`official_fallback = true``size = 1:1`
6. 首图和结果页关卡重新生图成功后Network 中应先完成图片生成,再调用 APIMart `POST {APIMART_BASE_URL}/chat/completions`,请求模型为 `gpt-4o-mini`,消息同时包含画面描述文本和正式图 `image_url` Data URL。
4. 历史 `original` 或空模型值不会再触发 DashScope统一按 `gpt-image-2` 请求 VectorEngine
5. 选择图片模型时,请求 `POST {VECTOR_ENGINE_BASE_URL}/v1/images/generations`,使用 `Authorization: Bearer {VECTOR_ENGINE_API_KEY}`上游 `model = gpt-image-2-all`,不携带 `official_fallback``size = 1024x1024`
6. 首图和结果页关卡重新生图成功后Network 中应先完成 VectorEngine 图片生成,再调用 APIMart `POST {APIMART_BASE_URL}/chat/completions`,请求模型为 `gpt-4o-mini`,消息同时包含画面描述文本和正式图 `image_url` Data URL。
7. “生成草稿”和关卡详情生图按钮展示 `消耗2光点`;关卡详情确认后展示 30 秒预计剩余进度条。
8. 不改 SpacetimeDB 表结构,因此无需更新 `migration.rs` 或重新生成 bindings。
9. 后端改动后运行对应 Rust 测试,并按项目约束用 `npm run api-server` 重启验证。