diff --git a/docs/technical/【技术方案】Raw GPT Image 2图片编辑代理-2026-09-07.md b/docs/technical/【技术方案】Raw GPT Image 2图片编辑代理-2026-09-07.md new file mode 100644 index 000000000..ae006ffa7 --- /dev/null +++ b/docs/technical/【技术方案】Raw GPT Image 2图片编辑代理-2026-09-07.md @@ -0,0 +1,103 @@ +# Raw GPT Image 2 图片编辑代理 + +更新时间:`2026-09-07` + +## 目标 + +提供一个由主站客户端调用的独立同步图片编辑代理: + +```text +POST /api/raw/v1/images/edit +``` + +该入口使用登录态 Bearer access token,不进入 External v1 / MCP OpenAPI,不读取或写入画布、项目资源、素材库、OSS 结果或 `external_generation_job`。 + +## 请求合同 + +请求使用 `application/json`。图片字段只使用原始图片的 base64 数据和 MIME 类型,不接受 object key、URL、Data URL 或 Blob URL。 + +```json +{ + "images": [ + { + "data": "", + "mimeType": "image/png" + } + ], + "mask": { + "data": "", + "mimeType": "image/png" + }, + "prompt": "修改图片", + "quality": "auto", + "background": "auto", + "output_format": "png", + "width": 1536, + "height": 1024 +} +``` + +`images` 是必填数组,数组成员结构固定为 `{ data, mimeType }`;`mask` 可选并使用相同结构。输入格式由成员的 MIME 类型和解码后的图片字节共同确定,服务端不把输入格式另建成请求参数。`prompt` 必填。`quality`、`background` 和 `output_format` 采用 GPT Image 模型支持的值。`width`、`height` 为整数,组成发送给 provider 的输出尺寸;不把尺寸改写成业务字符串字段。 + +服务端发送给 `platform-image` 时固定注入: + +```text +model = gpt-image-2 +n = 1 +``` + +请求不暴露 `model`、`n`、`response_format`、`style`、`user` 或 `output_compression`。 + +## 成功响应合同 + +响应始终为 JSON,响应只保留 `data` 字段,图片内容只以 base64 返回: + +```json +{ + "data": [ + { + "b64_json": "" + } + ] +} +``` + +`data` 保持数组形状,即使服务端固定 `n=1`。响应不重复返回请求参数,不返回 URL、资源 ID、任务 ID、provider 原始 JSON 或 editor 字段。 + +## 预检查与计费事务 + +所有请求、JSON、base64、图片结构和 provider 参数检查必须在扣费前完成。预检查失败直接返回 4xx,不产生钱包流水,也不调用 provider。 + +检查通过后,api-server 调用一个 raw 图片操作的 SpacetimeDB 事务 procedure,在同一事务内完成: + +1. 以认证后的用户和请求 ID 建立 raw 操作幂等事实; +2. 按现有图片编辑算法解析价格:GPT Image 2 长边不超过 1536 使用 1K 价格,否则使用 2K 价格;当前默认价格为 3 / 5 泥点; +3. 原子扣除用户泥点并写入 `asset_operation_consume` 流水; +4. 持久化操作状态,供 provider 返回后成功或失败收口。 + +provider 调用在 SpacetimeDB 事务之外执行。成功后调用同一 raw 操作的完成 procedure;失败后调用失败 procedure,由数据库事务写入退款 outbox / settlement 事实。进程崩溃时不依赖 Rust future `Drop` 才能发现需要退款;恢复处理根据持久化的 raw 操作状态完成退款。 + +raw 操作使用独立的 operation / ledger 命名空间,例如 `raw-image-edit`,不能复用编辑器资源 ID、编辑器任务 ID 或 `external_generation_job`。 + +## Provider 边界 + +`platform-image` 保留 VectorEngine 协议细节。raw handler 只负责:认证、JSON DTO、base64 解码、预检查、计费编排和响应映射。provider 请求仍由 `platform-image` 统一构造,并携带 `model`、`n`、`quality`、`background`、`output_format`、尺寸及图片参考字节。 + +provider 返回的原始 `size` 必须沿 `GeneratedImages` 结果传回 api-server;raw handler 从该字段解析响应尺寸,再编码 `data[].b64_json`。 + +## 代码拆分 + +- `server-rs/crates/api-server/src/raw_image.rs`:独立路由 handler、请求/响应 DTO、base64 输入校验、预检查和 raw billing 编排。 +- `server-rs/crates/platform-image/src/vector_engine/raw_edit.rs`:raw 编辑选项、provider 请求映射和原始响应解码;现有编辑器调用通过默认选项复用,不在业务 handler 复制 provider 协议。 +- `server-rs/crates/api-server/src/modules/raw.rs`:只注册 `/api/raw/v1/images/edit` 并挂载 Bearer middleware。 + +不修改 External v1 OpenAPI;不在 `external_editor_api.rs`、编辑器项目模块或外部生成 worker 中增加 raw 分支。 + +## 验收 + +- 未认证请求被 Bearer middleware 拒绝。 +- 预检查失败时钱包无扣费、provider 无请求。 +- 成功响应严格只包含 `data[].b64_json`。 +- provider 失败时 raw 操作失败事务产生可恢复退款事实。 +- raw 请求不创建 `external_generation_job`,不写 editor project/resource/asset/OSS。 +- 运行 api-server 与 platform-image 定向测试、`npm run check:encoding` 和 `git diff --check`。