Files

228 lines
9.7 KiB
Markdown

## Context
The repository now has three distinct realities for GPT Image requests:
- official OpenAI GPT Image requests already route cleanly through dedicated schemas and the `gpt-image-adapter`
- Tuzi GPT compatibility is still encoded as `openai.image.basic-json` and ultimately handled by the generic default image adapter
- generic OpenAI-compatible gateways also use the same basic adapter path
That means `resolvedImageApiCompatibility` is richer than the actual dispatch boundary. The metadata can say `tuzi-compatible`, but the adapter selection still collapses it into the same generic route as every other basic gateway. This makes the current system work, but it keeps provider-specific GPT behavior in the wrong module.
## Goals / Non-Goals
- Goals:
- keep compatibility decisions profile-scoped
- keep `auto` as a resolver
- give Tuzi GPT compatibility a real adapter boundary
- preserve the current official GPT adapter and schema split
- preserve `openai-compatible-basic` as the generic fallback
- separate official GPT quality semantics from legacy basic resolution semantics
- keep the UI simpler than the internal routing model
- Non-Goals:
- no full `image.generate` / `image.edit` abstraction yet
- no new task type
- no forced removal of existing fallback behavior during rollout
- no commitment that Tuzi edit uses the same transport shape as official GPT edit
## Decisions
- Decision: Keep image contract selection on `ProviderProfile`.
The compatibility difference is caused by the selected profile and API key, not by the model ID alone. The contract selector therefore remains a profile-level field.
- Decision: Refine the internal compatibility enum.
```ts
type ImageApiCompatibility =
| 'auto'
| 'openai-gpt-image'
| 'tuzi-gpt-image'
| 'openai-compatible-basic';
```
Legacy value `tuzi-compatible` is accepted as an alias and normalized to `tuzi-gpt-image`.
- Decision: `auto` is a resolver, not a wire protocol.
`auto` stays stored on profiles. Runtime resolution maps it to a concrete internal mode based on profile metadata and selected model.
- Decision: `requestSchema` is the adapter dispatch boundary.
`metadata.image.resolvedImageApiCompatibility` is observability context. It is not enough on its own. Real ownership changes only when routing emits a distinct request schema that resolves to a distinct adapter.
- Decision: Introduce a dedicated Tuzi GPT adapter.
Tuzi GPT compatibility should no longer be implemented as GPT-specific special cases inside `default-adapters.ts`. The generic default/basic adapter remains the fallback for broad OpenAI-compatible gateways.
- Decision: Keep the main settings UI simpler than the internal state model.
The UI should primarily steer users toward explicit override choices for:
- `OpenAI GPT Image`
- `Tuzi GPT 兼容`
Profiles that still store `auto` or `openai-compatible-basic` should remain supported, but those modes are treated as internal or migration-oriented rather than first-class marketing options.
- Decision: Default profile creation to explicit official GPT mode.
New provider profiles should start with `imageApiCompatibility = openai-gpt-image` so the common path is explicit rather than hidden behind `auto`.
- Decision: Managed profile rebuilds must preserve stored overrides.
Built-in managed profiles such as the legacy default and Tuzi presets may be reconstructed by settings compatibility helpers. Their rebuild path must preserve any stored `imageApiCompatibility` override instead of resetting the field to `auto`.
- Decision: Only migrate missing defaults, not explicit `auto`.
Historical profiles that explicitly store `auto` may reflect deliberate operator intent. We should migrate missing compatibility fields and managed-profile defaults to `openai-gpt-image`, but avoid silently rewriting explicit `auto` on custom profiles.
## Internal Compatibility Model
### Stored Values
- `auto`
- `openai-gpt-image`
- `tuzi-gpt-image`
- `openai-compatible-basic`
### Accepted Legacy Alias
- `tuzi-compatible` -> normalize to `tuzi-gpt-image`
### Auto Resolution Rules
`auto` resolves only for GPT Image models. Non-GPT image models continue using their existing model-specific routing rules.
| Profile / model condition | Resolved mode |
| --- | --- |
| `api.openai.com` + GPT Image model | `openai-gpt-image` |
| `api.tu-zi.com` + GPT Image model | `tuzi-gpt-image` |
| other profile + GPT Image model | `openai-compatible-basic` |
Manual non-`auto` values always win.
## Request Schema Mapping
The routing layer should produce a schema that matches the resolved compatibility mode.
| Resolved mode | Operation | Request schema | Adapter |
| --- | --- | --- | --- |
| `openai-gpt-image` | generation | `openai.image.gpt-generation-json` | `gpt-image-adapter` |
| `openai-gpt-image` | edit | `openai.image.gpt-edit-form` | `gpt-image-adapter` |
| `tuzi-gpt-image` | generation | `tuzi.image.gpt-generation-json` | `tuzi-gpt-image-adapter` |
| `tuzi-gpt-image` | edit (phase 2) | dedicated Tuzi edit schema | `tuzi-gpt-image-adapter` |
| `openai-compatible-basic` | generation / compatibility fallback | `openai.image.basic-json` | default/basic adapter |
The key architectural change is that `tuzi-gpt-image` stops being metadata-only and becomes a real dispatch target.
## Adapter Responsibilities
### `gpt-image-adapter`
- own official GPT Image generation and edit
- own official size and quality semantics
- own official `/images/edits` multipart transport
- remain selected only by the official GPT request schemas
### `tuzi-gpt-image-adapter`
- own Tuzi GPT-specific request serialization
- translate project-level image parameters into the Tuzi GPT contract
- own the current `resolution -> legacy quality(1k|2k|4k)` folding behavior
- keep official GPT-only fields internal unless Tuzi later exposes matching support
- become the future home for Tuzi edit transport once phase 2 begins
### default/basic image adapter
- remain the generic fallback for `openai-compatible-basic`
- keep serving non-GPT OpenAI-compatible image providers
- stop owning GPT-specific Tuzi translation rules after migration
## Contract Semantics
### Official GPT Image Contract
- `quality`: official values `auto | low | medium | high`
- `size`: official GPT size value
- `resolution`: internal convenience only; it may help the UI choose a concrete size, but it is not the outbound official quality field
- edit transport: multipart/form-data for `/images/edits`
### Tuzi GPT Contract
- `size`: outbound value stays compatible with Tuzi expectations
- `resolution`: compatibility concept used to derive the legacy quality tier when needed
- outbound `quality`: legacy compatibility value such as `1k | 2k | 4k`
- official GPT `quality` remains an internal setting unless and until Tuzi explicitly supports the same meaning
- phase 2 decides the final Tuzi edit transport contract independently from official multipart semantics
### Generic OpenAI-Compatible Basic Contract
- remains a compatibility fallback
- does not become the primary home for GPT-specific translation logic
## UI Model
Primary user-facing override options:
- `OpenAI GPT Image`
- `Tuzi GPT 兼容`
Support behavior:
- new profiles default to `OpenAI GPT Image`
- built-in managed profiles default to `OpenAI GPT Image` when they have no stored override
- profiles with no explicit stored compatibility from older data are upgraded to `OpenAI GPT Image`
- profiles that already store `auto` remain on `auto`
- the settings UI can show a resolved summary for `auto`, for example “自动判断,当前解析为 OpenAI GPT Image”
- `openai-compatible-basic` remains supported for migration, diagnostics, and rollback, but does not need to be a prominent everyday choice
## Migration
- Preserve explicit stored `auto` values on custom profiles.
- Do not reinterpret existing `auto` on official OpenAI profiles as basic fallback.
- Normalize legacy `tuzi-compatible` values to `tuzi-gpt-image`.
- Keep `openai-compatible-basic` valid for existing profiles and rollback scenarios.
- Default newly created profiles to `openai-gpt-image`.
- Default managed profiles to `openai-gpt-image` when they do not already store a compatibility override.
- Upgrade profiles that are missing the compatibility field to `openai-gpt-image`.
- When copying or exporting profiles, preserve the stored compatibility mode exactly.
- Managed profile rebuild helpers must preserve explicit stored compatibility values rather than resetting them to `auto`.
## Rollout Phases
### Phase 1
- add enum and alias normalization
- add `tuzi-gpt-image` resolver branch
- add `tuzi.image.gpt-generation-json`
- add `tuzi-gpt-image-adapter`
- move Tuzi GPT generation translation logic out of `default-adapters.ts`
- keep official GPT generation/edit unchanged
### Phase 2
- define Tuzi edit request schema and transport
- route Tuzi GPT image-edit requests through `tuzi-gpt-image-adapter`
- extend planner, persistence, and tests for the dedicated Tuzi edit path
## Observability
Diagnostics should expose:
- stored `imageApiCompatibility`
- resolved compatibility mode
- `requestSchema`
- adapter ID
- submit path
- whether the request used generation or edit semantics
API keys and authorization headers must never be logged.
## Risks
- aggressive alias migration could accidentally rewrite user intent
- default migration could accidentally rewrite explicit `auto` if missing-vs-explicit state is not distinguished carefully
- `auto` could still mis-resolve for unusual custom gateways
- moving GPT translation out of the default adapter could regress current Tuzi behavior if request parity tests are weak
- UI simplification could hide recovery options unless diagnostics remain visible
- phase 2 could be delayed, leaving generation cleanly separated while Tuzi edit still relies on fallback behavior