Skip to content

[bug] Hardcoded input_modalities: ["text"] makes Codex drop attached images #563

Description

@mrPronin

Symptom

Every route's GET /v1/models entry declares input_modalities: ["text"], hardcoded, regardless of the resolved target. Codex trusts that declaration and drops an attached image before sending, so routing a vision-capable model through Switchyard silently loses images.

Reproduction

One passthrough route to any OpenAI-Responses target, then a Codex turn with an image:

switchyard-server --config routes.toml --host 127.0.0.1 --port 4100
codex exec -p <profile-pointing-at-switchyard> -i diagram.png -- 'Describe the image.'

routes.toml:

schema_version = 1
[llm_clients.upstream]
format = "openai_responses"
base_url = "http://127.0.0.1:4999/v1"
[targets.t]
id = "some-vision-model"
llm_client = "upstream"
[routes.r]
id = "r"
type = "passthrough"
target = "t"

Expected vs. actual

  • Expected: the input_image item reaches the upstream, as it does with no proxy in the path.
  • Actual: Codex substitutes a placeholder before the request ever leaves the client:
{"type": "input_text", "text": "image content omitted because you do not support image input"}

Measured at the wire with a stand-in upstream logging the request body:

Run Outbound body Last user-message content
Codex → upstream, no proxy (control) 759,745 B input_image (543,102-char data URI)
Codex → Switchyard, input_modalities: ["text"] 248,385 B 60-char placeholder
Codex → Switchyard, input_modalities: ["text","image"] 739,155 B input_image, same as control

The response is 200 and no diagnostic is emitted anywhere; the only other signal is a correspondingly smaller prompt-token count.

⚠ Note the failure is entirely client-side, so it cannot be seen by inspecting Switchyard's request path — the proxy never receives an image. The placeholder string is in the codex binary (6 occurrences in 0.150.1) and not in Switchyard.

Cause

crates/switchyard-server/src/lib.rs, in codex_model_entry_json:

"input_modalities": ["text"],

The surrounding comment already anticipates this class of problem — "Every field below is either derived from the route's declared capabilities or a required ModelInfo field the server has no better value for", with a TODO about sourcing capabilities from the backend. input_modalities is neither derived nor declarable.

Related: supports_image_detail_original is hardcoded false on the same entry.

Environment

  • Commit: 7f3b2fe9 (main), also present at v0.2.0
  • Client: codex-cli 0.150.1, wire_api = "responses"

Suggested fix

Add a vision route capability beside the existing tool_calling and reasoning, failing closed. Failing closed matters here: a route may resolve to a target with no vision, so declaring image support unconditionally would send images where the backend cannot read them.

PR: see linked pull request.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions