diff --git a/CONTEXT.md b/CONTEXT.md index 1351b1033..b8e7a8793 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -44,6 +44,35 @@ _Avoid_: 把同一资源的全局元数据和某一次摆放坐标混在同一 由图片生成或图片修改流程产生的画布资源,必须记录来源资源、提示词、实际提示词、模型、provider、任务 ID 和生成时间;本期 `/editor` 的生成修改先允许 mock 生成资源,但仍按生成资源元数据形状保存。 _Avoid_: 无来源的静态素材、只显示在 UI 但不落工程资源记录的生成结果 +**图片模型历史值与使用端解析**: +图片资源中已持久化的 `gpt-image-2` 是历史业务事实,读回时保持原值;新任务使用业务模型值 `gpt-image-2.5`。当用户基于历史资源再次发起生成或编辑任务时,服务端只在新任务的使用端把历史值解析为当前业务模型,不改写历史资源。provider route 属于服务端执行与审计边界,前端不接收、不持久化、不展示,也不据此分支。 +_Avoid_: 读取数据库时改写历史模型值、把 provider route 暴露为前端模型选项或公开 DTO + +**图片 provider 显式路由**: +api-server 在任务入口按业务语义显式选择具体 provider model name(生成或编辑),并把同一具体名传给图片平台适配器和后台定价解析;图片平台适配器不从参考图数量或前端字段猜测任务。具体 provider model name 只存在于服务端调用、定价配置和审计边界。 +后台管理 Web/API 是明确例外,可以查看和编辑两个具体定价 key;主站普通前端与公开定价 API 不接收这些 key。 +_Avoid_: 让图片适配器隐式猜路由、让主站前端携带 provider model name + +**业务模型**: +面向任务与产品契约的稳定模型值;当前 GPT 图片新任务的业务模型是 `gpt-image-2.5`。业务模型不等同于 provider 的具体计费/请求 model,也不暴露 provider 凭证或 endpoint。 +_Avoid_: 把 provider concrete model 当作前端业务选项、用业务模型值直接推断 provider 凭证 + +**具体模型**: +服务端发送请求和定价使用的 concrete model name。GPT Image 2.5 生成与编辑分别是 `gpt-image-2.5-flare-c` 和 `gpt-image-2.5-sunburst-c`;nanobanana 仍使用 `gemini-3.1-flash-image-preview`。具体模型只在服务端执行、定价和审计边界出现。 +_Avoid_: 把具体模型写入普通前端 DTO、让未知字符串自动选择 provider + +**provider client**: +按具体模型选出的外部图片 provider 连接配置,包含 provider identity、base URL 和 API key;VectorEngine 与 Tiantoken client 共享图片协议执行器,不复制请求/响应业务逻辑。两套 required client 在 api-server 启动时构造。 +_Avoid_: 在首次请求时才创建 client、在 provider client 中复制尺寸/重试/审计逻辑、跨 provider credential fallback + +**历史模型值**: +已持久化的 `gpt-image-2` 或 `gpt-image-2-c` 字符串,只作为历史事实原样读取和审计;基于历史资源提交新任务时,在使用端解析为当前 GPT Image 2.5 业务任务,不回写历史记录,也不把旧值作为现役 provider route。 +_Avoid_: 数据库批量改写历史值、把历史值重新路由到 VectorEngine、把兼容解析扩散到普通前端 + +**GPT Image 2.5 新生成展示名**: +`GPT Image 2.5` 是新生成任务的产品展示名;历史资源与既有编辑上下文不因新模型上线而改写展示语义。 +_Avoid_: 把新生成展示名扩散到历史记录、历史生成器或旧编辑上下文 + **系列素材图集生成**: 一组同类素材的统一批量生成方式,采用批量规划、sheet 生图、后端切图、透明化、OSS 持久化和局部重生成的通用流水线。 _Avoid_: 为每个玩法单独发明素材流水线、把系列素材建模成任一玩法专属 DTO diff --git a/docs/README.md b/docs/README.md index 3de2b212c..dfa754898 100644 --- a/docs/README.md +++ b/docs/README.md @@ -70,6 +70,7 @@ - [画板音乐生成入口](./【编辑器】画板音乐生成入口设计-2026-06-18.md):BGM/SFX 共享视图、独立业务规则和当前发布门禁。 - [画布 Agent 对话面板](./【编辑器】画布Agent对话面板-2026-07-03.md) - [画布 Agent 会话消息存 OSS](./adr/【ADR】画布Agent会话消息存OSS-2026-07-03.md) +- [GPT Image 2.5 模型路由与历史值兼容](./adr/【ADR】GPT Image 2.5模型路由与历史值兼容-2026-09-18.md) - [编辑器模型定价配置](./【编辑器】模型定价配置管理方案-2026-06-22.md) ## 后端、运维与测试 diff --git a/docs/adr/【ADR】GPT Image 2.5模型路由与历史值兼容-2026-09-18.md b/docs/adr/【ADR】GPT Image 2.5模型路由与历史值兼容-2026-09-18.md new file mode 100644 index 000000000..e87f9338b --- /dev/null +++ b/docs/adr/【ADR】GPT Image 2.5模型路由与历史值兼容-2026-09-18.md @@ -0,0 +1,17 @@ +# GPT Image 2.5 模型路由与历史值兼容 + +状态:accepted + +新任务使用业务模型值 `gpt-image-2.5`,api-server 按任务显式选择具体 model:生成使用 `gpt-image-2.5-flare-c`,编辑使用 `gpt-image-2.5-sunburst-c`。这两个 GPT Image 2.5 model 必须通过启动时构造的 Tiantoken client 发送;Tiantoken client 只读取显式配置的 `TIANTOKEN_BASE_URL`(部署值由环境设置为 `https://api.tiantoken.com`)和独立 `TIANTOKEN_API_KEY`,缺失即阻止 api-server 启动,不得回退到 VectorEngine 或其 API key。 + +图片协议执行逻辑保持 provider-neutral:请求 body、multipart、尺寸约束、重试、响应解码和审计由共享 image executor 承担;VectorEngine 与 Tiantoken client 只提供相同协议所需的 base URL、API key 和 provider identity。platform-image 根据 concrete model 做严格白名单路由:`gpt-image-2.5-flare-c` 与 `gpt-image-2.5-sunburst-c` 走 Tiantoken,`gemini-3.1-flash-image-preview`(nanobanana)走 VectorEngine;未知 model 直接拒绝。已持久化的 `gpt-image-2` / `gpt-image-2-c` 只在新任务提交边界按兼容规则解析为当前 GPT Image 2.5 任务,不改写历史资源,也不进入旧 VectorEngine 图片路由。 + +普通主站前端只接触业务模型和新生成展示名 `GPT Image 2.5`,admin Web/API 可以查看和编辑两个具体定价 key;普通生成即使因参考图使用 edits multipart,仍按生成 concrete model。旧 `gpt-image-2-c` 审计记录原样保留,新代码不再跨模型或跨 provider fallback。 + +## Consequences + +- 定价配置的活动 key 是两个具体 provider model;旧单 key 配置只允许受控 backfill,并留下兼容 TODO。 +- 新任务的同模型重试固定使用 api-server dispatch 的具体 model,不切换到另一个 model。 +- 两套 provider client 在 api-server 启动阶段同时构造;任一 required provider 配置缺失,启动失败而不是延迟到首次图片请求。 +- provider routing 只依据 concrete model 的白名单;provider client 不复制共享协议执行逻辑。 +- 公开资源、`generationInputs` 和普通前端契约不包含具体 provider key;admin 定价管理是明确例外。 diff --git a/docs/project-memory/plans/【实施计划】GPT Image 2.5 provider边界重构-2026-09-18.md b/docs/project-memory/plans/【实施计划】GPT Image 2.5 provider边界重构-2026-09-18.md new file mode 100644 index 000000000..d342b69f5 --- /dev/null +++ b/docs/project-memory/plans/【实施计划】GPT Image 2.5 provider边界重构-2026-09-18.md @@ -0,0 +1,31 @@ +# GPT Image 2.5 provider 边界重构实施计划 + +- Version: 2 +- Status: active +- Date: 2026-09-18 +- Parent Milestone: `docs/project-memory/plans/【里程碑】GPT Image 2.5 provider边界重构-2026-09-18.md` + +## 实施边界 + +1. 先抽象 provider-neutral settings/client 与共享图片执行器接口;保留一套 body、multipart、尺寸、retry、响应和 audit 逻辑。 +2. 在 api-server 配置/state 初始化阶段分别构造 VectorEngine 与 Tiantoken client;删除 Tiantoken 对 VectorEngine URL/key 的任何 fallback。 +3. 在 platform-image 建立 concrete model 白名单路由:GPT Image 2.5 → Tiantoken,nanobanana → VectorEngine;legacy/unknown 按合同处理。 +4. 重命名 provider-specific client/build/transport 符号,避免共享逻辑继续伪装成 `vector_engine_*`;仅保留确有 VectorEngine 语义的名称。 +5. 迁移 api-server、Agent、raw edit、角色/图标/UI 入口和测试;核对 pricing/admin/public DTO 可见性。 +6. 删除跨模型/跨 provider fallback 分支,保留同 concrete model retry。 + +## 验证命令 + +- `cargo fmt --all --manifest-path server-rs/Cargo.toml -- --check` +- `cargo test -p platform-image` +- `cargo test -p platform-editor-agent` +- api-server 定向测试/`cargo check -p api-server` +- `npm run typecheck` +- `npm run check:doc-index` +- `npm run check:encoding` +- `git diff --check` + +## 风险与回滚 + +- 风险:启动阶段依赖变化、历史任务兼容解析遗漏、nanobanana 被误路由到 Tiantoken、provider key 泄露到公开 DTO。 +- 回滚:以 provider-neutral seam、启动配置、model route、调用方迁移四个局部提交边界回滚;不执行数据库历史迁移。 diff --git a/docs/project-memory/plans/【里程碑】GPT Image 2.5 provider边界重构-2026-09-18.md b/docs/project-memory/plans/【里程碑】GPT Image 2.5 provider边界重构-2026-09-18.md new file mode 100644 index 000000000..0e87c4e62 --- /dev/null +++ b/docs/project-memory/plans/【里程碑】GPT Image 2.5 provider边界重构-2026-09-18.md @@ -0,0 +1,52 @@ +# GPT Image 2.5 provider 边界重构 + +- Version: 2 +- Status: active +- Date: 2026-09-18 +- Parent Spec: `docs/adr/【ADR】GPT Image 2.5模型路由与历史值兼容-2026-09-18.md` + +## 目标 + +在保持图片协议执行逻辑共享的前提下,建立明确的 provider client 边界:GPT Image 2.5 通过 Tiantoken,nanobanana 通过 VectorEngine;路由依据 concrete model 严格白名单决定;两个 client 在 api-server 启动阶段构造。 + +## 范围 + +- `platform-image`:provider-neutral 图片执行器、provider client 注入 seam、concrete model 路由和错误/审计 provider 标识。 +- `api-server`:启动时构造 VectorEngine/Tiantoken 两个 client,分别读取各自环境变量;任务提交边界的历史模型兼容解析。 +- 共享请求、multipart、尺寸、retry、响应和 audit 逻辑保持单一实现。 +- Agent 与其它 server-side 图片调用方迁移到业务模型/concrete model 合同。 +- 定价、公开 DTO、admin DTO 与 provider model 可见性保持既定 ADR 约束。 + +## 现役路由 + +| Concrete model | Provider client | 业务用途 | +| --- | --- | --- | +| `gpt-image-2.5-flare-c` | Tiantoken | Generate,包括带参考图的普通生成 | +| `gpt-image-2.5-sunburst-c` | Tiantoken | Edit,包括快速编辑、原位修改、raw edit | +| `gemini-3.1-flash-image-preview` | VectorEngine | nanobanana 生成/编辑能力 | + +`gpt-image-2` 与 `gpt-image-2-c` 只保留为历史持久化/审计字符串;新任务不得 dispatch 到旧 GPT Image 2 路由。未知 model 直接拒绝。 + +## 必须成立的行为 + +1. api-server 启动时同时构造两个 required provider client;任一对应环境变量缺失,启动失败。 +2. Tiantoken 只读取 `TIANTOKEN_BASE_URL` / `TIANTOKEN_API_KEY`;VectorEngine 只读取 `VECTOR_ENGINE_BASE_URL` / `VECTOR_ENGINE_API_KEY`,互不回退。 +3. platform-image 根据 concrete model 选择已注入 client;共享执行器不复制 provider 协议逻辑。 +4. 同 concrete model 可以 retry,但永不跨 concrete model 或跨 provider fallback。 +5. 历史值读取不改写;新任务提交边界将旧值兼容为 GPT Image 2.5 业务任务。 +6. 普通前端不接收 concrete provider model;admin 定价界面可查看和编辑两个具体 pricing key。 + +## 非目标 + +- 不复制两套完整图片 client。 +- 不新增 GPT Image 2 现役 VectorEngine 路由。 +- 不修改历史数据库记录或旧审计字符串。 +- 不把 provider client 选择下沉给普通前端。 + +## 验收证据 + +- provider routing 单元测试覆盖 flare/sunburst/nanobanana/legacy/unknown。 +- 启动配置测试证明两套 client 独立读取环境变量,缺失任一配置即失败且无 VectorEngine/Tiantoken 回退。 +- 请求审计测试证明 provider 与 concrete model 正确记录。 +- platform-image 与 Agent 定向测试通过。 +- api-server 类型/编译检查、前端类型检查、编码/文档/diff 门禁通过。 diff --git a/docs/project-memory/shared-memory/decision-log.md b/docs/project-memory/shared-memory/decision-log.md index 01152bcba..0655b4a35 100644 --- a/docs/project-memory/shared-memory/decision-log.md +++ b/docs/project-memory/shared-memory/decision-log.md @@ -8840,3 +8840,20 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在 - 边界:Deploy 阶段在远端 dev / release agent 执行,不受该上限约束。调整只动这两处:`systemctl set-property / revert jenkins.service`、`docker update --cpus= gitea-runner` 加同步 compose(备份 `/opt/gitea-stack/compose.yml.bak-<时间戳>`)。 - 验证:限速后 `Genarrative-Full-Build-And-Deploy` #289 / #290 SUCCESS;采样期 Jenkins 峰值 10.2~10.5 核、限流不足 2s(可忽略),runner 峰值 12.07 核且持续出现 throttling,整机回落到 2.6%~19.8%。 - 关联文档:[开发运维](../../【开发运维】本地开发验证与生产运维-2026-05-15.md)。 + +## 2026-09-18 GPT Image 2.5 业务模型与具体 provider 定价路由 + +- **决策**:新任务使用业务模型值 `gpt-image-2.5`;api-server 按任务显式 dispatch 具体模型 `gpt-image-2.5-flare-c`(生成)或 `gpt-image-2.5-sunburst-c`(编辑),并把同一具体 key 交给 `platform-image` 与后台定价解析。普通生成即使因参考图使用 edits multipart,仍按生成 route;同模型重试不跨模型 fallback。 +- **历史兼容**:已持久化 `gpt-image-2` 读回原值不改写;基于旧资源发起新任务时,在提交边界解析为 `gpt-image-2.5`,新任务/新产物按新业务值和当前 task price 处理。旧 `gpt-image-2-c` 仅保留历史审计,不再作为 fallback 或业务模型。 +- **可见性**:普通主站前端和公开定价 API 不接收具体 provider key;新生成 UI label 为 `GPT Image 2.5`,历史资源/旧编辑上下文不扩散该 label。admin Web/API 是明确例外,可查看和编辑两个具体定价 key。旧单 key 定价配置允许受控 backfill,并加 compatibility TODO。 +- **关联 ADR**:[`docs/adr/【ADR】GPT Image 2.5模型路由与历史值兼容-2026-09-18.md`](../../adr/【ADR】GPT%20Image%202.5模型路由与历史值兼容-2026-09-18.md)。 + +- **补充**:GPT Image 2.5 的两个具体模型通过显式 `TIANTOKEN_BASE_URL` 与独立 `TIANTOKEN_API_KEY` 发送;环境变量缺失时必须失败,禁止使用 VectorEngine 配置或 API key 回退。 + +## 2026-09-18 GPT Image 2.5 provider-neutral 执行器与双 client 启动边界 + +- **决策**:图片协议执行逻辑保持单一共享实现;只抽出 provider client 的 identity、base URL、API key 和 client 构造,通过依赖注入复用请求 body、multipart、尺寸、retry、响应和 audit。 +- **路由**:`gpt-image-2.5-flare-c` / `gpt-image-2.5-sunburst-c` 走 Tiantoken;`gemini-3.1-flash-image-preview`(nanobanana)走 VectorEngine;`gpt-image-2` / `gpt-image-2-c` 仅是历史字符串,新任务不再进入旧 GPT Image 2 路由;未知 model 拒绝。 +- **启动**:api-server 启动时同时构造 VectorEngine 与 Tiantoken 两个 required client;各自只读取自己的环境变量,任一配置缺失即启动失败,不延迟到首次请求。 +- **重试**:只在同一个 concrete model 内 retry,禁止跨 model、跨 provider fallback。 +- **关联文档**:[`docs/adr/【ADR】GPT Image 2.5模型路由与历史值兼容-2026-09-18.md`](../../adr/【ADR】GPT%20Image%202.5模型路由与历史值兼容-2026-09-18.md)、[`docs/project-memory/plans/【里程碑】GPT Image 2.5 provider边界重构-2026-09-18.md`](../plans/【里程碑】GPT%20Image%202.5%20provider边界重构-2026-09-18.md)。 diff --git a/docs/【开发运维】本地开发验证与生产运维-2026-05-15.md b/docs/【开发运维】本地开发验证与生产运维-2026-05-15.md index 0d1af4ab8..e50e5c74c 100644 --- a/docs/【开发运维】本地开发验证与生产运维-2026-05-15.md +++ b/docs/【开发运维】本地开发验证与生产运维-2026-05-15.md @@ -226,7 +226,7 @@ spacetime sql "SELECT * FROM runtime_setting LIMIT 1" --server http:/ 本地 `spacetime` CLI / standalone 版本必须和 `server-rs/Cargo.toml` 里锁定的 `spacetimedb` 版本一致;当前统一版本为 `2.8.3`,CLI / standalone commit 固定核对为 `8e410d2842147bd8e5a32a9589cc00c19f7478e2`。若版本或 commit 错配,procedure 返回值可能在宿主侧触发 `Failed to BSATN deserialize procedure return value`,api-server 最终表现为现役 settings、editor project 或 profile procedure 超时。排障时先运行 `spacetime --version`,再对照 `server-rs/Cargo.toml` 的 `spacetimedb = "..."`;其它版本可执行 `spacetime version install && spacetime version use `,升级后重启 `npm run dev:spacetime` 再重试。当前 `scripts/dev.mjs` 会把 tool version 和 commit 一起写入 `dev-spacetime-tool-version`,启动新 standalone 与复用已有本地进程时都要求 `2.8.3 + 8e410d28...` 同时匹配;旧版本或旧单行版本记录会拒绝复用并要求重启。2.6.1 修复了 procedure context 中调用者 `Identity` / `ConnectionId` 始终为空的回归,依赖 `ctx.sender` 鉴权时必须同时确认宿主已升级。 -本地 `.env`、`.env.local` 或 `.env.secrets.local` 修改后必须重启 `api-server` 才会生效;若已经通过 `npm run dev` 启动完整联调,可在该终端输入 `rs api-server`。排查图片编辑器 Tiantoken 生成链路时,确认 `TIANTOKEN_BASE_URL`、`TIANTOKEN_API_KEY` 和 `TIANTOKEN_IMAGE_REQUEST_TIMEOUT_MS` 只在本地或服务器密钥文件中配置,不能写入 Git。VectorEngine 配置仅保留给 Suno 音乐任务。`TIANTOKEN_IMAGE_REQUEST_TIMEOUT_MS` 是单次 attempt 的配置上限,默认 `1000000`;配置加载层允许显式值低于该默认值,不再在读取环境变量时强制抬高。业务模型和 Tiantoken provider 首选请求都使用 `gpt-image-2`,符合条件时才回退到兜底模型 `gpt-image-2-c`;图片协议、URL / base64 响应解析、远端图片下载和 provider 侧结构化日志在 `server-rs/crates/platform-image`,`api-server` 只做编辑器请求编排、OSS / asset 持久化、计费和失败审计落库。`platform-image` 会在 JSON 生成和 multipart 编辑请求发送前按同一 GPT-image-2 family 规则归一显式像素尺寸;若请求发送失败,先按同一 `request_id` 查看 provider 日志与 `external_api_call_failure.metadata_json.errorSource`,当前 multipart `/v1/images/edits` 单独强制 HTTP/1.1。 +本地 `.env`、`.env.local` 或 `.env.secrets.local` 修改后必须重启 `api-server` 才会生效;若已经通过 `npm run dev` 启动完整联调,可在该终端输入 `rs api-server`。排查图片编辑器 Tiantoken 生成链路时,确认 `TIANTOKEN_BASE_URL`、`TIANTOKEN_API_KEY` 和 `TIANTOKEN_IMAGE_REQUEST_TIMEOUT_MS` 只在本地或服务器密钥文件中配置,不能写入 Git;同时确认 VectorEngine 的 `VECTOR_ENGINE_BASE_URL` / `VECTOR_ENGINE_API_KEY` 已配置,因为两个图片 client 都在 api-server 启动时构造,任一缺失都会阻止启动。VectorEngine 配置仍保留给 nanobanana 与 Suno 音乐任务。`TIANTOKEN_IMAGE_REQUEST_TIMEOUT_MS` 是单次 attempt 的配置上限,默认 `1000000`;配置加载层允许显式值低于该默认值,不再在读取环境变量时强制抬高。新生成任务使用业务模型 `gpt-image-2.5`,api-server 显式分派 `gpt-image-2.5-flare-c`(generate)或 `gpt-image-2.5-sunburst-c`(edit);已持久化的 `gpt-image-2` / `gpt-image-2-c` 只在提交边界按兼容规则解析,不改写历史值,也不回退到旧 GPT Image 2 路由。图片协议、URL / base64 响应解析、远端图片下载和 provider 侧结构化日志在 `server-rs/crates/platform-image`,`api-server` 只做编辑器请求编排、OSS / asset 持久化、计费和失败审计落库。`platform-image` 会在 JSON 生成和 multipart 编辑请求发送前按同一 GPT-image family 规则归一显式像素尺寸;若请求发送失败,先按同一 `request_id` 查看 provider 日志与 `external_api_call_failure.metadata_json.errorSource`,当前 multipart `/v1/images/edits` 单独强制 HTTP/1.1。 编辑器 ElevenLabs 音效生成只从服务端读取 `ELEVENLABS_BASE_URL`、`ELEVENLABS_API_KEY` 和 `ELEVENLABS_REQUEST_TIMEOUT_MS`,timeout 默认 `180000ms`;base URL 或 Key 缺失时失败关闭,不回退 Vidu。生产 API 与 external-generation worker 通过共享 API env 取得同一配置,模板见 `deploy/env/api-server.env.example`;Key 不得进入 Web/Vite 环境、命令参数、日志、fixture 或仓库。普通测试只使用 loopback mock,禁止把真实付费请求作为 T3 自动验收。