Repository navigation
fix(opencode): serve the gated free tier from /inference/v1 and give placeholder tools non-empty schemas - #16826
Merged
diegosouzapw merged 1 commit intoOct 16, 2026
Conversation
…placeholder tools non-empty schemas
velizarofff
marked this pull request as ready for review
October 16, 2026 09:10
diegosouzapw
merged commit Oct 16, 2026
3a2a3ba
into
cheaper-inference:release/v3.8.52
3 checks passed
This was referenced Oct 18, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Since 2026-10-14, standalone requests to OpenCode free-tier models (the ones
isGatedFreeTierRequestgates) fail with403 FreeTierError(#16328), while the official OpenCode CLI keeps working.Two things changed upstream at once. Both were measured by capturing the official CLI 1.19.0 request and replaying it with exactly one element changed at a time (2026-10-15, anonymous lane, 5–10 s between requests).
1. The free tier moved off /zen/v1
The client now takes its base URL from the models.dev catalog (
https://opencode.ai/inference/v1). The captured request on/inference/v1/chat/completionsanswers 200; byte-for-byte the same request on/zen/v1/chat/completionsanswers 403 FreeTierError.2. The tools check now inspects parameter schemas
/zen/v1readwith the client's real schemagrep; onlybash,edit,glob; any subset withoutreadreadwith any non-emptyproperties(filePath,file_path,x), with or withoutrequiredparameterson every toolread+bashwithproperties: {}read+bash{command}read+webfetch{}/read+noop_tool{}foowith a non-empty schemaclient: desktop, no session id)So: a property-bearing
readis required, an emptybashbreaks the request, and empty schemas on names outside the quartet still pass. Descriptions, system prompt and headers do not matter.What OmniRoute does today (release/v3.8.52)
buildUrlresolvesthis.config.baseUrlfor every request format — the zen registry base — and the request contract injects placeholder tools (applyFreeTierRequestContract,mergeClientToolsWithObserved) withparameters: { type: "object", properties: {} }. The surface machinery added in 3.8.52 (surfaceFromBaseUrl) already tells the gated zen surface apart, but nothing uses it to change the base URL, so a standalone request violates both conditions of the new gate.Fix
FREE_TIER_INFERENCE_BASE_URL(https://opencode.ai/inference/v1) toopencodeFreeTierContract.ts, next to the surface constants;buildUrlsends a gated free-tier request (zen surface, non-premium model) to that base for every request format. Paid models andopencode-gokeep the registry base, which still serves them;placeholderToolParameters(name): injected quartet members get minimal schemas mirroring the client's argument names —bash{command},glob{pattern},grep{pattern},read{filePath}, each withrequired— while names outside the quartet keep the empty schema. Used by bothapplyFreeTierRequestContractandmergeClientToolsWithObserved.With an operator configuration like
OPENCODE_FREE_TIER_PLACEHOLDER_TOOLS=bash,glob,grep,read, a standalone request carrying no tools of its own now satisfies the tools half of the 2026-10-14 gate. The session half is still open: measured 2026-10-16, the same request with a session id in the pre-#16727 shape (12 arbitrary hex digits) is refused 403 on/inference/v1as well, so a standalone client that relies on OmniRoute's synthesizedx-opencode-sessionstill needs the encoding half of #16727. A client that supplies its own valid session id (the official CLI does) is unaffected. I have that half ready on a second branch of the same fork and will keep it out of this PR unless maintainers want both here.The same two changes (endpoint + non-empty placeholder schemas) are what unblocked a live self-hosted instance on 2026-10-15, in a build that also carries #15322; this PR is the minimal subset against the current default branch.
Out of scope
/inference/v1: measured 2026-10-16, a request whose only tools are capitalizedRead/Grep/Globwith non-empty schemas is refused 403. Covering such clients is the concealment/rename machinery of fix(opencode): declare the free-tier tool fingerprint the upstream requires #15322, not touched here./inference/v1too.apifield, and fix(opencode): route each key-based model to the endpoint the published catalog declares #16798 routes each key-based model to the endpoint the published catalog declares. The constant keeps this patch minimal and self-contained; happy to rebase onto the catalog field if maintainers prefer that direction.Tests
/inference/v1/...; paid (gpt-5-nano), unknown models and the Go tier keep their bases;placeholderToolParametersand for an appended quartet member inmergeClientToolsWithObserved;tests/unit/opencode-*.test.tssuite (92 files, node 26) againstrelease/v3.8.52+ this patch: 855 tests, 0 failures. The four baseline failures of v3.8.51 (accounts-per-request,geo-block-rotation,responses-first-byte-stall,transient-retry-delay) no longer reproduce on clean v3.8.52.Addresses the endpoint and placeholder-schema halves of #16328.