Files
thotamandClaude Opus 5 c2c5124a0c feat(image): add GPT Image 2.5 and default to flare (#1562)
OpenAI shipped GPT Image 2.5 on 2026-09-08 as two model IDs rather than one:
flare is tuned for speed, sunburst for editing precision. Both accept text and
image input and run on /images/generations, /images/edits and the Responses
API, which is the path the native image_generation tool already uses.

Flare replaces gpt-image-2 as the default because OpenAI reports higher quality
at roughly half the latency, which suits create_image's one-shot call. Both
gpt-image-2 and gpt-image-1.5 stay on the whitelist, so a saved image_model
keeps working untouched.

Also in this change:

- The rejection message is now built from the whitelist itself instead of a
  hand-written string that drifts whenever a model is added.
- codex_build.go uses the DefaultImageModel constant instead of hardcoding
  "gpt-image-2", so the inline chat path cannot drift from the shared default.
- Both new models join the isEditModel branch so reference images route to
  /images/edits.
- ParamField gains an optional labelKey, letting the dropdown read its label
  from i18n instead of showing English inside a translated UI. It passes a
  defaultValue, so a missing key degrades to the literal rather than breaking.
- Locale keys added to all five catalogs; ko was missing the whole
  mediaChain.imageModel* group.

Surface parity: the desktop UI is unchanged because sortable-provider-card
renders no param form and passes params through as Record<string, unknown>.
The CLI is unchanged because cmd/ exposes no image-model flag. The API contract
is unchanged because image_model lives in the free-form params map; only the
backend whitelist widened.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 10:31:48 +07:00

120 lines
4.1 KiB
Go

package providers
import (
"context"
"fmt"
"strings"
)
// NativeImageProvider is implemented by OAuth-backed providers whose upstream
// exposes an image_generation native tool (ChatGPT Responses API style).
// create_image routes through this interface when the chain resolves to such
// a provider, bypassing the credentialProvider (APIKey/APIBase) path.
type NativeImageProvider interface {
GenerateImage(ctx context.Context, req NativeImageRequest) (*NativeImageResult, error)
}
// DefaultImageModel is the image model used by the Responses API image_generation
// tool when the caller does not specify one. GPT Image 2.5 (2026-09-08) ships as
// two model IDs instead of one: "flare" tuned for speed, "sunburst" tuned for
// editing precision. Flare is the default because it beats gpt-image-2 on quality
// at roughly half the latency, which suits the one-shot create_image path.
const DefaultImageModel = "gpt-image-2.5-flare"
// allowedImageModels enumerates the image models the native ChatGPT Responses API
// image_generation tool will accept. Constraining to this whitelist prevents
// silent upstream rejections from arbitrary model names (e.g. "dall-e-3").
var allowedImageModels = map[string]bool{
"gpt-image-2.5-flare": true, // default - fastest at high quality
"gpt-image-2.5-sunburst": true, // most capable at editing precision
"gpt-image-2": true, // previous generation
"gpt-image-1.5": true, // legacy fallback
}
// allowedImageModelList is the whitelist rendered for error messages, ordered
// newest first so the suggestion a caller sees leads with the recommended model.
var allowedImageModelList = []string{
"gpt-image-2.5-flare (default)",
"gpt-image-2.5-sunburst",
"gpt-image-2",
"gpt-image-1.5 (legacy)",
}
// ValidateImageModel returns the model to use, or an error if the caller
// supplied an unsupported value. Empty input returns DefaultImageModel.
func ValidateImageModel(model string) (string, error) {
if model == "" {
return DefaultImageModel, nil
}
if !allowedImageModels[model] {
return "", fmt.Errorf("unsupported image model %q; allowed: %s", model, strings.Join(allowedImageModelList, ", "))
}
return model, nil
}
// NativeImageRequest describes a single image generation request.
type NativeImageRequest struct {
// Model is the parent LLM model for the Responses API call (e.g. "gpt-5.5").
// NOT the image model — see ImageModel below.
// If empty, the provider uses its own default LLM model.
Model string
// ImageModel is the image-generation model attached to the image_generation
// tool (e.g. "gpt-image-2.5-flare"). Must be a value accepted by ValidateImageModel;
// empty falls back to DefaultImageModel.
ImageModel string
// Prompt is the text description of the image.
Prompt string
// AspectRatio is the desired aspect ratio, e.g. "16:9", "1:1", "9:16".
// Converted to a concrete pixel size by the provider implementation.
// Defaults to "1:1" if empty.
AspectRatio string
// OutputFormat is the desired image format: "png" (default), "jpg", "webp".
OutputFormat string
// RefImages contains the list of reference images.
RefImages []RefImage
}
// RefImage represents a single reference image for image-to-image or styling tasks.
type RefImage struct {
Data []byte
Base64 string
MimeType string
URL string
Strength float64
}
// NativeImageResult holds the result of a native image generation call.
type NativeImageResult struct {
// MimeType is the detected MIME type of the generated image (e.g. "image/png").
MimeType string
// Data is the raw decoded image bytes (NOT base64).
Data []byte
// Usage is optional token usage if the provider reports it.
Usage *Usage
}
// SizeFromAspect converts a common aspect ratio string to a pixel dimension
// string expected by image generation APIs (e.g. "1792x1024").
// Falls back to "1024x1024" for unrecognised ratios.
func SizeFromAspect(aspectRatio string) string {
switch aspectRatio {
case "16:9":
return "1792x1024"
case "9:16":
return "1024x1792"
case "3:4":
return "1024x1365"
case "4:3":
return "1365x1024"
default:
return "1024x1024"
}
}