codex - 💡(How to fix) Fix Allow custom (non-ChatGPT) model providers to opt into the built-in `image_generation` hosted tool

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Root Cause

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 = true in ~/.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.

Fix Action

Fix / Workaround

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.py CLI fallback (requires OPENAI_API_KEY, can't share auth with the proxy, can't reuse the model's conversation context)
  • Fork Codex and patch the gate

Code Example

[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

---

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
}

---

// 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))
}

---

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

---

[model_providers.example.capabilities]
   image_generation = true
   namespace_tools  = true
   web_search       = true

---

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
RAW_BUFFERClick to expand / collapse

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_KEY against the OpenAI Responses endpoint
  • custom [model_providers.X] with wire_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 = true in ~/.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

  1. Add an optional capabilities block to ModelProviderInfo deserialization:
    [model_providers.example.capabilities]
    image_generation = true
    namespace_tools  = true
    web_search       = true
  2. When provider_info.capabilities is set, have ConfiguredModelProvider::capabilities() return those values instead of the trait default — exactly the seam AmazonBedrockModelProvider already uses (PR #19442) to clamp capabilities downward.
  3. In image_generation_tool_enabled (and the parallel web_search / namespace_tools checks), replace the current_auth_uses_codex_backend short-circuit with:
    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
    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 [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.

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-server over-emits image_generation to providers that don't support it; the proposed disabled_capabilities slots 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.py CLI fallback (requires OPENAI_API_KEY, can't share auth with the proxy, can't reuse the model's conversation context)
  • Fork Codex and patch the gate

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING