Initial TrueGrowth source import

This commit is contained in:
2026-07-07 09:36:36 +08:00
commit 3b6781d695
2283 changed files with 691996 additions and 0 deletions

View File

@@ -0,0 +1,262 @@
## 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.
```ts
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:
```text
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:
```ts
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:
```text
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?

View File

@@ -0,0 +1,48 @@
# Change: Add GPT Image profile compatibility routing
## Why
The same `gpt-image-*` model ID can require different image API request formats depending on the selected provider profile and API key. Some keys need the existing Tuzi/basic image compatibility path, while others need the official OpenAI GPT Image format.
Today the system mostly routes image models through model ID, protocol, and broad request schema inference. That makes it hard to support official GPT Image behavior without breaking the current default adapter compatibility path.
## What Changes
- Add a profile-level image API compatibility setting that describes the image request contract for that provider/key.
- Resolve image compatibility from `ProviderProfile + modelId`, with `auto` inference and manual override.
- Split GPT Image routing into distinct request schemas:
- existing basic compatibility path: `openai.image.basic-json`
- official GPT Image generation path: `openai.image.gpt-generation-json`
- Add a dedicated GPT Image adapter for official generation requests.
- Keep the current default image adapter compatibility path for Tuzi/basic and generic OpenAI-compatible image requests.
- Add settings UI for selecting the profile's image interface format.
- Add tests proving the same `gpt-image-*` model can route differently under different provider profiles.
## Non-Goals
- Do not add `TaskType.IMAGE_EDIT`.
- Do not split the MCP/tool surface into `generate_image` and `edit_image`.
- Do not introduce the full `image.generate` / `image.edit` operation abstraction in this change.
- Do not migrate all providers to a unified image edit schema.
- Do not automatically retry failed paid image requests through another compatibility mode.
## Impact
- Affected specs:
- `provider-routing`
- `image-generation`
- Affected code:
- `packages/drawnix/src/utils/settings-manager.ts`
- `packages/drawnix/src/components/settings-dialog/settings-dialog.tsx`
- `packages/drawnix/src/services/provider-routing/types.ts`
- `packages/drawnix/src/services/provider-routing/settings-repository.ts`
- `packages/drawnix/src/services/provider-routing/binding-inference.ts`
- `packages/drawnix/src/services/model-adapters/registry.ts`
- `packages/drawnix/src/services/model-adapters/default-adapters.ts`
- `packages/drawnix/src/services/model-adapters/gpt-image-adapter.ts`
## Relationship To Existing Changes
- Builds on `add-multi-provider-profiles`, especially the `ModelRef(profileId + modelId)` routing model.
- Narrows and extends `add-provider-protocol-routing` by adding a concrete profile-level image request contract for GPT Image compatibility.
- Preserves the existing default adapter behavior as a compatibility path rather than replacing it with model-ID-only GPT routing.

View File

@@ -0,0 +1,59 @@
## ADDED Requirements
### Requirement: Preserve Generic GPT Image Basic Compatibility
The system SHALL preserve the existing default image adapter path only for GPT Image requests that resolve to the generic basic image compatibility mode.
#### Scenario: Generic GPT Image generation stays on default adapter
- **GIVEN** a provider profile resolves image API compatibility to `openai-compatible-basic`
- **AND** the selected model is `gpt-image-2`
- **WHEN** the user submits a text-to-image request
- **THEN** the request SHALL use the `openai.image.basic-json` schema
- **AND** SHALL continue to use the existing default image adapter compatibility behavior
#### Scenario: Tuzi GPT Image uses dedicated adapter ownership
- **GIVEN** a provider profile resolves image API compatibility to `tuzi-gpt-image`
- **AND** the user submits an image generation request with reference images
- **WHEN** the request is serialized
- **THEN** the system SHALL route through the dedicated Tuzi GPT Image adapter boundary
- **AND** SHALL NOT hide Tuzi GPT-specific request translation inside the generic default adapter
### Requirement: Support Official GPT Image Generation Adapter
The system SHALL provide a dedicated GPT Image adapter for provider profiles that resolve to the official GPT Image format.
#### Scenario: Official GPT Image text-to-image request
- **GIVEN** a provider profile resolves image API compatibility to `openai-gpt-image`
- **AND** the selected image model is a GPT Image model
- **WHEN** the user submits a text-to-image request
- **THEN** the system SHALL route the request to the GPT Image adapter
- **AND** SHALL send a generation request to `/images/generations`
- **AND** SHALL NOT default `response_format` to `url`
#### Scenario: Official GPT Image response parsing
- **GIVEN** the GPT Image adapter receives a response containing `data[].b64_json`
- **WHEN** the response is parsed
- **THEN** the system SHALL normalize the image into the existing image result shape
- **AND** SHALL support URL-based gateway variants without failing valid responses
### Requirement: Keep Image Generation Task Surface Stable
The system SHALL keep the current image task and tool surface stable for this change.
#### Scenario: GPT Image compatibility does not create a new task type
- **GIVEN** a user submits a GPT Image generation request
- **WHEN** the task is created
- **THEN** it SHALL remain a `TaskType.IMAGE` task
- **AND** SHALL continue through the existing task queue, media library, and canvas insertion flows
#### Scenario: GPT Image compatibility does not require a new tool name
- **GIVEN** an agent or workflow invokes image generation
- **WHEN** GPT Image compatibility routing is applied
- **THEN** the invocation SHALL continue to use the existing image generation tool surface
- **AND** SHALL NOT require a new public `edit_image` tool for this change

View File

@@ -0,0 +1,67 @@
## ADDED Requirements
### Requirement: Resolve Image API Compatibility Per Provider Profile
The system SHALL resolve image API compatibility from the selected provider profile before inferring an image request schema.
#### Scenario: Same GPT Image model uses different contracts by profile
- **GIVEN** two enabled provider profiles both expose `gpt-image-2`
- **AND** the first profile has image API compatibility `tuzi-gpt-image`
- **AND** the second profile has image API compatibility `openai-gpt-image`
- **WHEN** the user invokes image generation with a `ModelRef` pointing to either profile
- **THEN** the system SHALL resolve the image compatibility from that profile
- **AND** SHALL allow the same `modelId` to produce different request schemas by `profileId`
#### Scenario: Manual compatibility overrides automatic inference
- **GIVEN** a provider profile has image API compatibility set to a non-`auto` value
- **WHEN** image binding inference runs for that profile
- **THEN** the system SHALL use the manually selected compatibility mode
- **AND** SHALL NOT replace it with an automatically inferred mode
### Requirement: Infer Automatic Image Compatibility Conservatively
The system SHALL support an `auto` image API compatibility mode that infers a compatibility mode from provider profile metadata and the selected model.
#### Scenario: Official OpenAI GPT Image auto inference
- **GIVEN** a provider profile uses an `api.openai.com` base URL
- **AND** the selected image model ID starts with `gpt-image`
- **AND** the profile image API compatibility is `auto`
- **WHEN** the system infers image compatibility
- **THEN** it SHALL resolve to the OpenAI GPT Image format
#### Scenario: Tuzi auto inference selects dedicated Tuzi GPT mode
- **GIVEN** a provider profile uses an `api.tu-zi.com` base URL
- **AND** the profile image API compatibility is `auto`
- **WHEN** the system infers image compatibility
- **THEN** it SHALL resolve to the Tuzi GPT image format
- **AND** SHALL keep the request eligible for the dedicated Tuzi GPT Image adapter path
#### Scenario: Generic OpenAI-compatible auto inference
- **GIVEN** a provider profile is `openai-compatible` or `custom`
- **AND** it is not recognized as official OpenAI or Tuzi
- **AND** the profile image API compatibility is `auto`
- **WHEN** the system infers image compatibility
- **THEN** it SHALL resolve to the generic OpenAI-compatible image format
### Requirement: Map Image Compatibility To Request Schema
The system SHALL map the resolved image API compatibility mode to the request schema used by provider routing.
#### Scenario: OpenAI GPT Image compatibility selects GPT schema
- **GIVEN** a GPT Image model invocation resolves to OpenAI GPT Image compatibility
- **WHEN** the provider binding is inferred
- **THEN** the binding SHALL use `openai.image.gpt-generation-json`
- **AND** SHALL be eligible for the GPT Image adapter
#### Scenario: Basic compatibility selects existing schema
- **GIVEN** a GPT Image model invocation resolves to generic OpenAI-compatible image compatibility
- **WHEN** the provider binding is inferred
- **THEN** the binding SHALL use `openai.image.basic-json`
- **AND** SHALL be eligible for the existing default image adapter

View File

@@ -0,0 +1,71 @@
## 1. Proposal And Design
- [x] 1.1 Review existing provider routing and image generation specs/changes for conflicts.
- [ ] 1.2 Validate this change with `openspec validate add-gpt-image-profile-compatibility --strict`.
- [x] 1.3 Get approval before implementation.
Note: `openspec` CLI is not available in the current shell, so 1.2 remains unchecked.
## 2. Settings Data Model
- [x] 2.1 Add `ImageApiCompatibility` type and `ProviderProfile.imageApiCompatibility`.
- [x] 2.2 Add the field to `ProviderProfileSnapshot`.
- [x] 2.3 Update provider profile normalization to preserve valid values and coerce missing/invalid values to `auto`.
- [x] 2.4 Ensure legacy default and managed Tuzi profiles store `auto`.
- [x] 2.5 Ensure `auto` for Tuzi profiles resolves to the existing compatibility path at runtime.
- [x] 2.6 Ensure profile copy/update paths preserve the field.
## 3. Compatibility Resolution And Routing
- [x] 3.1 Add a resolver for profile image API compatibility, including `auto` inference.
- [x] 3.2 Add GPT Image model detection that does not force all GPT Image models to one adapter by model ID.
- [x] 3.3 Update image binding inference to emit `openai.image.gpt-generation-json` for official GPT Image generation.
- [x] 3.4 Keep `tuzi-compatible` and `openai-compatible-basic` on `openai.image.basic-json`.
- [x] 3.5 Ensure same `modelId` under different `profileId` can produce different request schemas.
- [x] 3.6 Keep non-GPT image model bindings unchanged.
## 4. GPT Image Adapter
- [x] 4.1 Add `gpt-image-adapter.ts`.
- [x] 4.2 Match only GPT Image official request schemas, not every GPT model ID.
- [x] 4.3 Build official `/images/generations` requests without defaulting `response_format` to `url`.
- [x] 4.4 Support official generation fields only when present or safely defaulted.
- [x] 4.5 Parse `b64_json`, `url`, data URL, and gateway base64 variants into the existing image result shape.
- [x] 4.6 Register the adapter without changing MJ, Flux, Seedream, or default adapter behavior.
## 5. Settings UI
- [x] 5.1 Add the `图片接口格式` select to the provider settings form.
- [x] 5.2 Place the field near provider type / API URL / API key.
- [x] 5.3 Use user-facing labels for automatic, OpenAI GPT Image, Tuzi-compatible, and generic OpenAI-compatible formats.
- [x] 5.4 Add concise helper text explaining why the setting is profile/key scoped.
- [x] 5.5 Avoid exposing adapter or request schema names in UI copy.
## 6. Observability
- [x] 6.1 Log or expose debug context for `profileId`, resolved image compatibility, `requestSchema`, adapter ID, and submit path.
- [x] 6.2 Include stored compatibility and resolved compatibility separately when possible.
- [x] 6.3 Ensure logs do not expose API keys.
## 7. Tests
- [x] 7.1 Add binding inference tests for the same `gpt-image-*` model under different profiles.
- [x] 7.2 Add adapter registry tests for GPT schema vs basic schema selection.
- [x] 7.3 Add default adapter regression tests for existing Tuzi/basic request bodies.
- [x] 7.4 Add GPT adapter request and response parsing tests.
- [x] 7.5 Add service-level integration tests from generation request to selected adapter/transport.
- [x] 7.6 Add minimal regression coverage for MJ, Flux, Seedream, and non-GPT OpenAI-compatible image models.
## 8. Manual Verification
- [ ] 8.1 Create one `tuzi-gpt-image` profile and one `openai-gpt-image` profile that both select `gpt-image-2`.
- [ ] 8.2 Verify text-to-image produces different schema/body/adapter paths for those profiles.
- [ ] 8.3 Verify generated results still enter task history, media library, and canvas insertion normally.
- [ ] 8.4 Verify switching an affected profile to `openai-compatible-basic` restores the default adapter path.
## 9. Rollout And Rollback
- [ ] 9.1 Release with existing profiles defaulting to `auto`.
- [ ] 9.2 Confirm Tuzi `auto` resolves to the dedicated Tuzi GPT Image adapter path.
- [ ] 9.3 Document that users can manually switch a profile back to `通用 OpenAI 兼容格式`.
- [ ] 9.4 Keep the default adapter path available as the first rollback option.