Allow custom (non-ChatGPT) model providers to opt into the built-in `image_generation` hosted tool
What variant of Codex are you using?
Codex CLI (any 0.12x–0.13x). Applies to anyone running codex with a non-ChatGPT auth, including:
- bare
OPENAI_API_KEYagainst the OpenAI Responses endpoint - custom
[model_providers.X]withwire_api = "responses"— Azure OpenAI, OneAPI-style proxies, self-hosted Responses-compatible gateways, GitHub Copilot proxies, etc.
What feature would you like to see?
A per-provider opt-in to expose the built-in image_generation (a.k.a. image_gen) tool, e.g.:
[model_providers.example]
name = "Custom"
base_url = "https://example.com/v1"
wire_api = "responses"
[model_providers.example.capabilities]
image_generation = true # default: false for non-OpenAI custom providers
namespace_tools = true
web_search = true
This is the natural dual of the disabled_capabilities block proposed in #21952 — same mechanism, opposite default. Both can coexist: disabled_capabilities clamps the upper bound, capabilities.* raises it.
Why this matters
The gate at codex-rs/core/src/tools/spec_plan.rs:317 ANDs four conditions before adding image_generation to tools[]:
fn image_generation_tool_enabled(turn_context: &TurnContext) -> bool {
turn_context.auth_manager.as_deref()
.is_some_and(AuthManager::current_auth_uses_codex_backend) // ① auth must be ChatGPT
&& turn_context.provider.capabilities().image_generation // ② provider upper bound
&& turn_context.features.get().enabled(Feature::ImageGeneration) // ③ user toggle
&& turn_context.model_info.input_modalities.contains(&InputModality::Image) // ④ modality
}
Gate ① is:
// codex-rs/login/src/auth/manager.rs
pub fn current_auth_uses_codex_backend(&self) -> bool {
matches!(self.auth_mode(),
Some(AuthMode::Chatgpt | AuthMode::ChatgptAuthTokens | AuthMode::AgentIdentity))
}
i.e. AuthMode::ApiKey is hard-coded false, regardless of:
[features].image_generation = truein~/.codex/config.toml(overridden by AND)--enable image_generation(same)- the provider actually being able to fulfill image-generation requests through the Responses API
This is asserted by Codex's own unit tests at codex-rs/core/src/tools/spec_plan_tests.rs:920:
let api_key_auth = probe(|turn| {
set_feature(turn, Feature::ImageGeneration, /*enabled*/ true);
turn.model_info.input_modalities = vec![InputModality::Image];
// no use_chatgpt_auth() call → AuthMode::ApiKey
}).await;
api_key_auth.assert_visible_lacks(&["image_generation"]); // explicitly NOT visible
The user-visible consequence (already documented in #19133, #21640, etc.): Codex loads the bundled imagegen skill (codex-rs/skills/src/assets/samples/imagegen/SKILL.md), the skill tells the model to "use the built-in image_gen tool by default," and the model then can't find the tool — it was never put in tools[]. The model either errors with built-in image_gen tool is not available in this session or falls back to a deterministic placeholder PNG.
Why a server-side advertisement can't fix this
This is a client-side gate that runs before the request is built. The proxy/provider has no way to influence it — no header, no /v1/models capability flag, no response shape can convince Codex to put image_generation in tools[]. Only a config-level opt-in on the Codex side works.
Proposed change
- Add an optional
capabilitiesblock toModelProviderInfodeserialization:
``toml``
[model_providers.example.capabilities]
image_generation = true
namespace_tools = true
web_search = true
- When
provider_info.capabilitiesis set, haveConfiguredModelProvider::capabilities()return those values instead of the trait default — exactly the seamAmazonBedrockModelProvideralready uses (PR #19442) to clamp capabilities downward. - In
image_generation_tool_enabled(and the parallelweb_search/namespace_toolschecks), replace thecurrent_auth_uses_codex_backendshort-circuit with:
``rust`
let auth_or_explicit_optin =
turn_context.auth_manager.as_deref()
.is_some_and(AuthManager::current_auth_uses_codex_backend)
|| provider_explicitly_opted_in; // ← user-asserted capability overrides auth gate
[model_providers.X.capabilities].image_generation = true` is asserting "I know my provider supports this" — the same contract a first-party provider entry implicitly carries.
The intent of gate ① is "don't expose tools the OpenAI-hosted backend implicitly provides when we aren't talking to it." A user explicitly setting
A more conservative version (no behavior change for current configs, only adds the opt-in path) is to leave gate ① alone for the default case and only OR in the explicit per-provider capability flag.
Related
- #19133 — symptom report (Azure OpenAI custom provider, Windows/WSL)
- #21640 — same symptom from a ChatGPT Plus user via custom config
- #21952 — exact dual:
app-serverover-emitsimage_generationto providers that don't support it; the proposeddisabled_capabilitiesslots in here trivially - PR #19442 — introduced
ProviderCapabilities/ToolCapabilityBounds; this issue asks to expose that abstraction to user config - #18307 — OpenRouter rejects Responses payloads with
image_generation(same architectural divergence between built-in providers and custom providers)
Workarounds today
- Sign in with ChatGPT (not viable for users without a ChatGPT plan, or for self-hosted/proxied providers)
- Use the bundled
scripts/image_gen.pyCLI fallback (requiresOPENAI_API_KEY, can't share auth with the proxy, can't reuse the model's conversation context) - Fork Codex and patch the gate
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗