Skip to content

fix(vision): sync captions into Responses passthrough - #1047

Draft
baileyh8 wants to merge 2 commits into
lidge-jun:devfrom
baileyh8:agent/fix-responses-vision-passthrough
Draft

fix(vision): sync captions into Responses passthrough#1047
baileyh8 wants to merge 2 commits into
lidge-jun:devfrom
baileyh8:agent/fix-responses-vision-passthrough

Conversation

@baileyh8

@baileyh8 baileyh8 commented Aug 5, 2026

Copy link
Copy Markdown

Summary

  • keep the native Responses passthrough body aligned with vision-sidecar image replacements
  • replace images in user/developer messages and client tool outputs with generated captions
  • fail closed by stripping raw images when the vision sidecar is unavailable

Root cause

The vision fallback rewrote the parsed context.messages, but the OpenAI Responses passthrough adapter serializes _rawBody. Text-only Responses providers therefore still received the original input_image even after the sidecar produced a caption.

Impact

Text-only models using the Responses wire now receive the vision caption instead of an unsupported raw image. Native vision-capable models remain unchanged.

Validation

  • bun run prepush
    • 8465 passed
    • 8 skipped
    • 0 failed
  • focused vision regression suite: 5 passed, 0 failed
  • manual Codex -> OpenCodex -> DeepSeek V4 Flash image-description request completed successfully

Summary by CodeRabbit

  • Bug Fixes

    • Improved image handling for Responses API requests by keeping native request data synchronized after images are described or removed.
    • Ensured images in user messages, developer messages, agent messages, and tool outputs are consistently replaced with descriptions or text-only placeholders.
    • Added fallback behavior that cleanly strips images when vision support is unavailable.
  • Tests

    • Added end-to-end coverage for image captions, tool-returned images, and vision-disabled scenarios.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The vision pipeline now synchronizes rewritten image content with native Responses request bodies. End-to-end tests cover captions for standard and tool-returned images, cache isolation, and placeholder fallback behavior.

Changes

Responses vision synchronization

Layer / File(s) Summary
Synchronize native Responses input
src/vision/index.ts
The implementation recursively rewrites raw input_image parts with generated descriptions or omission markers. describeImagesInPlace and stripImagesInPlace synchronize the native request body after parsed message updates.
Validate Responses forwarding
tests/vision-sidecar-e2e.test.ts
Tests reset the vision cache, emulate a Responses upstream, and verify caption replacement for standard and tool-output images. They also verify text placeholders when no vision sidecar or ChatGPT login is available.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

Suggested reviewers: lidge-jun, ingwannu

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: synchronizing vision-generated captions in Responses passthrough.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added the bug Something isn't working label Aug 5, 2026
@baileyh8
baileyh8 marked this pull request as ready for review August 5, 2026 06:56

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0f46fdca55

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/vision/index.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/vision/index.ts`:
- Around line 308-311: Update the input-image normalization logic in the visible
transformation function so empty image_url values are omitted or replaced with
the existing omission marker instead of returning the original input_image part;
apply the same behavior when descriptions.length is zero. Add an end-to-end
assertion verifying the forwarded input contains no input_image parts for this
case, and flag the provider/adapter contract drift under src/**.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 72f00de6-a5b6-42df-a4e9-c97936338e89

📥 Commits

Reviewing files that changed from the base of the PR and between 0f46fdc and 18784cd.

📒 Files selected for processing (2)
  • src/vision/index.ts
  • tests/vision-sidecar-e2e.test.ts

Comment thread src/vision/index.ts
Comment on lines +308 to +311
if (value.type === "input_image" && typeof value.image_url === "string") {
if (nonEmptyImageUrlsOnly && value.image_url.length === 0) return value;
const description = descriptions[nextDescription++];
return description === undefined ? value : { type: "input_text", text: description };

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Remove empty input_image parts from _rawBody.

On Line 309, an empty image_url returns the original input_image part. A text-only Responses provider can still reject this unsupported part.

Remove the empty part or replace it with the omission marker. Apply the same result when descriptions.length === 0. Add an end-to-end assertion that forwarded input contains no input_image parts for this case.

As per path instructions, flag provider/adapter contract drift in src/**.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/vision/index.ts` around lines 308 - 311, Update the input-image
normalization logic in the visible transformation function so empty image_url
values are omitted or replaced with the existing omission marker instead of
returning the original input_image part; apply the same behavior when
descriptions.length is zero. Add an end-to-end assertion verifying the forwarded
input contains no input_image parts for this case, and flag the provider/adapter
contract drift under src/**.

Source: Path instructions

lidge-jun added a commit that referenced this pull request Aug 5, 2026
…edger

A fourth audit round found #812 -> #572 and #1047/#1002 -> #1024 still sitting
in the literal-extraction table. Those three PR bodies contain no issue
reference at all, so the table was attributing claims the authors never made.

The associations are still probably correct - #812 does add an Apertis preset,
#1047 and #1002 do edit the vision sidecar - but a ledger that mixes what was
written with what was inferred cannot be acted on without re-deriving it, which
defeats the point of writing it down.
@Wibias
Wibias marked this pull request as draft August 5, 2026 19:14
@lidge-jun

Copy link
Copy Markdown
Owner

Reviewed in a sweep of the bug-labelled backlog. The bug is real and subtle, and you found the right layer.

The vision sidecar rewrites parsed.context.messages when it describes or strips an image (src/vision/index.ts), but the Responses passthrough serializes parsed._rawBody (src/adapters/openai-responses.ts:1144-1188). So on that path the sidecar does its work and the original image is forwarded anyway — the user sees a 400 from a text-only model and no indication that a description was ever produced. Syncing the replacements into _rawBody fixes it where the two representations diverge, rather than papering over it downstream.

The test earns its place: it asserts against the recorded upstream body over actual HTTP passthrough, and covers tool-output images and the no-sidecar stripping case. It would fail before the fix, because the recorded body still contains the original image. Non-image raw items are preserved, which is the part I checked hardest — a _rawBody rewrite is exactly where unrelated content tends to get dropped.

Your remaining blocker is verification, not design. The PR is a draft, the head is 341 commits behind dev, and only the lightweight checks have run on it — so the full-suite claim is not backed by anything on this head. src/vision/index.ts and the Responses passthrough have both moved since your base, so please rebase onto current dev (rather than merging it in), re-run the suite, then tick the readiness boxes. The gate verifies the "on latest dev" and "ci green" claims itself and will not accept a completion at this distance.

Worth flagging the neighbourhood: #1002 makes sidecar reasoning configurable and touches adjacent code. They are not in conflict as written, but whichever lands second should re-run the other's tests rather than assuming.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants