Files

9.2 KiB

Context

Superseded note: the later update-image-api-compatibility-modes change refines this design. In current implementation guidance, the legacy tuzi-compatible value is accepted only as an alias for tuzi-gpt-image, Tuzi GPT Image routes through the dedicated Tuzi GPT Image adapter, and openai-compatible-basic remains the generic fallback/default adapter path.

The project already has multi-provider concepts:

  • ProviderProfile stores the API key, base URL, provider type, and auth mode.
  • ModelRef stores profileId + modelId.
  • ProviderModelBinding stores profileId + modelId + operation + protocol + requestSchema.
  • The model adapter registry already prefers requestSchema matches over protocol and model matches.

This gives the system enough structure to solve the GPT Image compatibility issue without binding every gpt-image-* model directly to one adapter.

Goals / Non-Goals

  • Goals:
    • Allow the same GPT Image model ID to route differently by provider profile/key.
    • Preserve the current default adapter compatibility path.
    • Add a dedicated official GPT Image generation path.
    • Keep the user-facing configuration understandable.
    • Keep this change smaller than the full image operation abstraction.
  • Non-Goals:
    • No new image edit task type.
    • No new public edit_image tool.
    • No automatic paid request fallback/retry across compatibility modes.
    • No provider-wide rewrite of all image adapters.

Decisions

  • Decision: Store compatibility on ProviderProfile.

    The compatibility difference is caused by the API key/profile, not by the model ID alone. The profile is therefore the right place for this setting.

  • Decision: Name the field imageApiCompatibility.

    The field describes the upstream image API contract. It does not expose internal adapter or schema names to users.

  • Decision: Use user-facing values that map to internal schemas.

    type ImageApiCompatibility =
      | 'auto'
      | 'openai-gpt-image'
      | 'tuzi-compatible'
      | 'openai-compatible-basic';
    
  • Decision: Keep auto as a stored value.

    auto should not be rewritten into a concrete mode during normalization. Runtime inference can evolve later while existing profiles continue to benefit from better inference rules.

  • Decision: Resolve compatibility before binding inference.

    The binding layer maps the resolved compatibility to a requestSchema. The adapter registry then selects the implementation by schema.

Architecture Overview

This change adds a narrow compatibility-selection layer between provider profile resolution and image binding inference.

Runtime flow:

Image request
  -> ModelRef(profileId + modelId)
  -> ProviderProfile
  -> imageApiCompatibility
  -> resolved image compatibility
  -> ProviderModelBinding.requestSchema
  -> model adapter registry
  -> default image adapter OR GPT Image adapter
  -> ProviderTransport

The important architectural boundary is that modelId identifies what the user selected, while imageApiCompatibility describes the request contract exposed by the chosen API key/profile.

Data Model

Add a profile-level field:

type ImageApiCompatibility =
  | 'auto'
  | 'openai-gpt-image'
  | 'tuzi-compatible'
  | 'openai-compatible-basic';

interface ProviderProfile {
  imageApiCompatibility?: ImageApiCompatibility;
}

Propagate the same field into ProviderProfileSnapshot because binding inference operates on snapshots rather than full settings objects.

Request Schema Contract

The request schema is the stable handoff between routing and adapters:

openai.image.basic-json
  -> existing default image adapter

openai.image.gpt-generation-json
  -> new GPT Image adapter

The adapter registry already scores requestSchema matches above protocol and model matches. This change relies on that behavior so official GPT Image requests are not selected by bare model ID alone.

Module Responsibilities

  • settings-manager.ts
    • Owns the persisted profile field and normalization.
    • Keeps missing values as auto.
  • settings-dialog.tsx
    • Lets users choose the profile's image interface format.
    • Shows user-facing labels only.
  • provider-routing/types.ts
    • Adds the field to ProviderProfileSnapshot.
  • provider-routing/settings-repository.ts
    • Copies the field from settings profiles into snapshots.
  • provider-routing/binding-inference.ts
    • Resolves auto to a concrete compatibility mode.
    • Maps the resolved mode to requestSchema.
  • model-adapters/gpt-image-adapter.ts
    • Implements official GPT Image generation request and response handling.
  • model-adapters/default-adapters.ts
    • Continues to serve Tuzi/basic and generic OpenAI-compatible image formats.

Compatibility Resolution

Manual values win over auto.

Recommended auto inference:

  • api.openai.com + gpt-image* -> openai-gpt-image
  • api.tu-zi.com -> tuzi-compatible
  • other openai-compatible or custom profiles -> openai-compatible-basic
  • gemini-compatible profiles do not use GPT Image routing

Request Schema Mapping

  • openai-gpt-image + GPT Image model:
    • requestSchema: openai.image.gpt-generation-json
    • adapter: gpt-image-adapter
  • tuzi-gpt-image:
    • requestSchema: tuzi.image.gpt-generation-json
    • adapter: tuzi-gpt-image-adapter
  • openai-compatible-basic:
    • requestSchema: openai.image.basic-json
    • adapter: existing default image adapter

Official edit support uses:

  • openai.image.gpt-edit-form

Adapter Boundaries

The existing default adapter remains the compatibility path for generic OpenAI-compatible image providers. Tuzi GPT Image-specific behavior belongs to the dedicated Tuzi GPT Image adapter.

The new GPT Image adapter only handles official GPT Image request schemas. It should not match every gpt-image-* model by model ID alone.

Initial GPT Image adapter scope:

  • POST /images/generations
  • Do not default response_format to url
  • Prefer b64_json parsing
  • Accept URL responses for gateway compatibility
  • Support official generation fields such as size, quality, n, output_format, output_compression, background, and moderation when present

GPT Image Adapter Request Policy

The adapter should only include optional fields when the caller provided them or when the project already has a safe default for that official field.

It should not:

  • inject response_format: url by default
  • add provider-specific prompt prefixes
  • use the basic compatibility image field for official edit semantics

It should:

  • POST official text-to-image requests to /images/generations
  • prefer b64_json response data
  • tolerate URL responses from gateway variants
  • normalize returned images to the existing ImageGenerationResult

Default Adapter Policy

The default image adapter remains the compatibility adapter for:

  • tuzi-compatible
  • openai-compatible-basic
  • unknown or broad OpenAI-compatible image gateways

Its existing GPT Image behavior is preserved to protect current users and API keys.

Settings UI

Add a profile-level field near provider type / API URL / API key:

Label: 图片接口格式

Options:

  • 自动
  • OpenAI GPT Image 格式
  • 兔子兼容格式
  • 通用 OpenAI 兼容格式

Suggested helper text:

同一个图片模型在不同 API Key 或网关下可能需要不同接口格式;不确定时使用自动。

The UI should not mention adapters, request schemas, or routing internals.

Observability

Generation diagnostics should expose enough non-secret context to debug misrouting:

  • profileId
  • modelId
  • stored imageApiCompatibility
  • resolved compatibility
  • requestSchema
  • adapter ID
  • submit path

API keys and full authorization headers must never be logged.

Migration

  • Old profiles without imageApiCompatibility normalize to auto.
  • Legacy default and managed Tuzi profiles should store auto.
  • At runtime, auto for api.tu-zi.com resolves to tuzi-compatible, preserving current behavior.
  • Copying a provider profile should copy the field.
  • Deleting or disabling profiles requires no special handling.

Risks

  • auto inference may choose the wrong compatibility mode for a key.
  • The adapter registry may accidentally select the default adapter if schema matching is incomplete.
  • Default adapter changes may break existing Tuzi/basic image keys.
  • Official GPT Image and gateway variants may return mixed response shapes.
  • The UI setting may be unclear without concise helper text.
  • Future /images/edits support may need a broader image operation abstraction.

Rollout Plan

  1. Add data model support and keep all existing profiles resolving to current behavior.
  2. Add routing/schema split while defaulting Tuzi/basic profiles to the existing adapter.
  3. Add GPT Image generation adapter and route only openai-gpt-image profiles to it.
  4. Add UI controls and observability.
  5. Add tests and manually verify two profiles with the same GPT model ID but different compatibility modes.

Open Questions For Approval

  • Should api.tu-zi.com always resolve to tuzi-compatible in auto mode?
  • Should the first implementation include only /images/generations, or also /images/edits?
  • Should openai-compatible-basic and tuzi-compatible remain separate user options even if they initially map to the same request schema?