Repository navigation
fix(opencode): advertise wrapped source capabilities on router aliases - #82
Conversation
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
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
📝 Walkthrough
Merge Risk: ⚪ Minimal · up to
|
Fixes #81
What changed
ctx.provider.transformplaceholders now advertiseinput: ["text"], output: ["text"]instead of the hard-coded["text", "image"]. An alias keeps this untilctx.model.transformresolves its source, or forever if the source never resolves. Such an alias rejects every request anyway. This is documented indocs/behavior.md.ctx.model.transformnow copiescapabilities(tools, input, output) fromeditor.get(providerID, modelID)explicitly, with fresh arrays. An alias therefore accepts PDFs or images exactly when its source does.@reasoning-router/opencode.Root-cause note
Before this change, the
...sourcespread inctx.model.transformalready 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 onmaintoo. Only the placeholder test fails without this change.For the setup in the issue, the wrapped sources are config-defined (
anthropic-proxy/*). They declare nocapabilities, 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/modelreports["text","image"]for bothanthropic-proxy/claude-opus-5-5andreasoning-router/claude-opus-5-5.To get PDFs through, a config-defined source must declare them, for example:
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, andthinkingand inserts effort updates; it never rewrites user content blocks. New tests:core: adocumentblock (base64 PDF withcache_control) is unchanged throughResponsesRouter.prepare, including on a lineage-replayed second turn.opencode: an Anthropicdocumentblock and a Responsesinput_filereach the upstream fetch unchanged.proxy: an Anthropicdocumentblock 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, inpackages/classifiers/test/jev.test.ts.plugin-v2.tschange reverted, only "registers text-only placeholders and keeps them for an unresolved source" fails.Summary by CodeRabbit