Skip to content

fix(opencode): advertise wrapped source capabilities on router aliases - #82

Merged
robertn702 merged 1 commit into
mainfrom
robertn702/fix-pdf-capabilities
Oct 14, 2026
Merged

robertn702 merged 1 commit into
mainfrom
robertn702/fix-pdf-capabilities

Conversation

@robertn702

@robertn702 robertn702 commented Oct 14, 2026 •

Copy link
Copy Markdown
Owner

Fixes #81

What changed

  • Conservative fallback. ctx.provider.transform placeholders now advertise input: ["text"], output: ["text"] instead of the hard-coded ["text", "image"]. An alias keeps this until ctx.model.transform resolves its source, or forever if the source never resolves. Such an alias rejects every request anyway. This is documented in docs/behavior.md.
  • Source capabilities. ctx.model.transform now copies capabilities (tools, input, output) from editor.get(providerID, modelID) explicitly, with fresh arrays. An alias therefore accepts PDFs or images exactly when its source does.
  • Changeset: patch for @reasoning-router/opencode.

Root-cause note

Before this change, the ...source spread in ctx.model.transform already copied the source's capabilities, but only implicitly. The hard-coded list applied only to placeholders and to aliases whose source failed to resolve. The new PDF-capable and text-only tests pass on main too. Only the placeholder test fails without this change.

For the setup in the issue, the wrapped sources are config-defined (anthropic-proxy/*). They declare no capabilities, so OpenCode gives them its default ["text", "image"], and the source models themselves also reject PDFs. In OpenCode 2.0.28, opencode api GET /api/model reports ["text","image"] for both anthropic-proxy/claude-opus-5-5 and reasoning-router/claude-opus-5-5.

To get PDFs through, a config-defined source must declare them, for example:

"capabilities": { "input": ["text", "image", "pdf"] }

Sources from the models.dev catalog (for example openai/gpt-6-astra, which reports ["text","image","pdf"]) already declare PDF support.

Wire path

PDF parts are forwarded unchanged. The Anthropic rewrite only pins model, output_config, and thinking and inserts effort updates; it never rewrites user content blocks. New tests:

  • core: a document block (base64 PDF with cache_control) is unchanged through ResponsesRouter.prepare, including on a lineage-replayed second turn.
  • opencode: an Anthropic document block and a Responses input_file reach the upstream fetch unchanged.
  • proxy: an Anthropic document block reaches a real HTTP upstream unchanged.

Verification

  • npm run check: typecheck, lint, and tests pass (36 files, 476 tests). One lint warning is pre-existing, in packages/classifiers/test/jev.test.ts.
  • With the plugin-v2.ts change reverted, only "registers text-only placeholders and keeps them for an unresolved source" fails.

Summary by CodeRabbit

  • New Features
    • Router aliases now inherit their source model’s input and output capabilities. Unresolved sources advertise text-only capabilities.
  • Bug Fixes
    • PDF attachments and document details are preserved when messages are forwarded to upstream models, including during conversation replay.

Router aliases registered placeholder capabilities of text and image
input. Once the source resolved, the `...source` spread already copied
its capabilities, but only implicitly. An alias whose source never
resolved kept advertising image input it could not verify.

- Placeholders now advertise text input and output only. This is the
  documented conservative fallback for aliases whose source has not
  resolved, or never does. Those aliases reject every request anyway.
- ctx.model.transform now copies the source's capabilities explicitly
  (and copies the arrays), so an alias accepts PDFs or images only if
  its source does.
- Tests: a PDF-capable source advertises pdf; a text-only source
  advertises neither image nor pdf; placeholders are text-only. PDF
  parts are forwarded unchanged through the core Anthropic rewrite
  (including lineage replay), the OpenCode plugin (Anthropic document
  and Responses input_file), and the standalone proxy.

Decision (reversible): the fallback is text-only rather than a guess
from the profile, because OpenCode replaces an unsupported attachment
with an error message rather than sending input the source may reject.

Fixes #81
@coderabbitai

coderabbitai Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: ae67f1fb-47c5-463c-a668-f6cdbbc17f34
📥 Commits

Reviewing files that changed from the base of the PR and between defe328 and 3519281.

📒 Files selected for processing: 6
  • .changeset/alias-source-capabilities.md
  • docs/behavior.md
  • packages/core/test/wire-anthropic.test.ts
  • packages/opencode/src/plugin-v2.ts
  • packages/opencode/test/plugin-v2.test.ts
  • packages/proxy/test/server.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

📝 Walkthrough

Router aliases now copy input and output capabilities from resolved source models and use text-only capabilities before resolution. Tests cover capability selection and PDF forwarding through Anthropic and OpenAI paths, including Anthropic lineage replay.

Changes: Alias capabilities and PDF forwarding

Layer / File(s) Summary
Propagate source capabilities
packages/opencode/src/plugin-v2.ts, packages/opencode/test/plugin-v2.test.ts, docs/behavior.md, .changeset/alias-source-capabilities.md
Resolved aliases copy the source model’s input and output capabilities. Tests cover PDF-capable, text-only, and unresolved sources. The behavior documentation and changeset describe the capability behavior.
Test PDF forwarding and replay
packages/opencode/test/plugin-v2.test.ts, packages/core/test/wire-anthropic.test.ts, packages/proxy/test/server.test.ts
Tests check that PDF document and file parts reach provider requests unchanged. The Anthropic wire test also checks that lineage replay preserves the original document block.

Priority: ➖ Normal

Estimated code review effort: 2 (Simple) | ~15 minutes

Change: Bug fix · Severity of issue fixed: Medium

Merge Risk: ⚪ Minimal · up to 35192

No actionable issue remains in the supplied evidence; this change is mergeable after normal checks.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 4 files. (2 skipped: 2 … Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 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: OpenCode router aliases now advertise capabilities inherited from wrapped source models.
Linked Issues check Passed Issue #81 coding requirements are met. packages/opencode/src/plugin-v2.ts registers text-only placeholders, then copies source capabilities with fresh input and output arrays when the source r…
Out of Scope Changes check Passed The reviewed changes stay within issue #81. The plugin change implements capability propagation and the conservative fallback. The OpenCode, core, and proxy tests verify capability advertising and unc…
Full details: Docstring Coverage

Explanation

Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 4 files. (2 skipped: 2 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches: 💡 1
📝 Generate docstrings: 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@robertn702
robertn702 merged commit 693909a into main Oct 14, 2026
4 checks passed
@robertn702
robertn702 deleted the robertn702/fix-pdf-capabilities branch October 14, 2026 18:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OpenCode v2: router models drop PDF attachments because input capabilities are hard-coded

1 participant