Infer Route Note · 003
OpenCode 1.18.16 against a custom Responses provider.
A local capture of the request OpenCode made, followed by the narrower result from Infer’s bridge and SSE serializer. Client wire behavior, provider-facing relay behavior, and live availability are separate here.
- Published
- August 11, 2026
- Client
- OpenCode 1.18.16
- Scope
- Isolated local capture
- Public Infer route state
- Unavailable at publication
The captured request.
OpenCode’s title request and main request both used POST /v1/responses. Neither capture used /v1/chat/completions. The selected model reached the body as infer/auto/code:balanced, preserving the complete Infer selector rather than shortening it to the model key from the configuration object.
| Wire element | Observed in the local capture |
|---|---|
| Path | POST /v1/responses for title and main requests |
| Body | input, model, stream, tools, tool choice, and prompt_cache_key |
| Session headers | x-session-affinity and X-Session-Id |
| Custom relay option | No relay object reached the OpenCode request body |
The body record is intentionally a field list, not a reconstructed payload. It establishes which request components appeared without inventing values that were not retained in the evidence. OpenCode adds prompt_cache_key unless its setCacheKey option is false, and the pinned AI SDK serializes that field. Its two session headers carried the actual OpenCode session ID for this non-OpenCode provider.
The request contained a stream field, and the isolated client run consumed Infer SSE. This did not establish token-by-token upstream delivery, cancellation, or interruption behavior.
Why the base URL stops at /v1.
OpenCode’s versioned provider documentation assigns @ai-sdk/openai to custom providers that implement /v1/responses. The separate @ai-sdk/openai-compatible package is documented for /v1/chat/completions. OpenCode 1.18.16 pins @ai-sdk/openai 3.0.84; that adapter appends /responses to the configured base URL.
{
"$schema": "https://opencode.ai/config.json",
"model": "infer/auto/code:balanced",
"provider": {
"infer": {
"npm": "@ai-sdk/openai",
"name": "Infer by Flow7",
"options": {
"baseURL": "https://infer.flow7.org/v1",
"apiKey": "{env:INFER_API_KEY}"
},
"models": {
"auto/code:balanced": {
"id": "infer/auto/code:balanced",
"name": "Auto Code · Balanced"
}
}
}
}
}
The first infer in the selected model is OpenCode’s provider ID. The model entry’s id controls the selector placed on the wire. The API key remains in INFER_API_KEY. The same block can be generated at the Infer OpenCode integration page, but configuration alone does not activate an account or make the endpoint callable.
Session affinity arrives as a header, not custom JSON.
OpenCode model options are parsed against a finite Responses option schema. Adding model.options.relay.session_id to the checked configuration did not serialize a relay object. The recognized options and body construction are visible in the pinned AI SDK option schema and request construction.
OpenCode already supplies the usable identity in x-session-affinity and X-Session-Id. Infer’s bridge reads x-session-affinity only when neither body relay.session_id nor top-level session_id supplies a value. Explicit body input wins. The bridge trims the header, limits it to 200 characters, rejects control characters, leaves authentication mandatory, and does not log the header.
A second stripping boundary occurs after Infer accepts a customer request. Infer consumes its own relay control object for routing and privacy decisions, then removes that object before the provider-facing Responses request. A focused adapter test also confirms that Infer changes the provider-facing model to the selected route model and currently sends stream=false on that hop. Downstream SSE therefore does not by itself prove upstream streaming.
The local bridge completed two bounded runs.
In the first isolated run, OpenCode posted directly to the Responses path, parsed text and usage from an Infer-formatted SSE response, and exited with code 0. The app was an isolated Infer demo. It did not use a production Infer key, a public Infer route, or a completed production route.
The second run used Infer’s actual response_to_sse serializer. It emitted a function_call; OpenCode executed a safe, read-only glob; OpenCode returned a function_call_output; and a final text response parsed before exit 0. That is a mocked protocol round trip, not evidence for arbitrary tools or a live model deciding when to call them.
The capture included tools and tool choice, and the mocked loop completed one read-only operation. It did not cover every tool schema, tool failure, parallel calls, or a production tool loop.
What remains open.
At publication, the dated compatibility record separated eight checks instead of collapsing them into one label. Local configuration parsing, the direct Responses path, SSE text with usage, the mocked tool round trip, and session affinity were Pass; structured output was Not tested; a live routed-model run and public availability were Pending. The current matrix now includes a bounded live OpenCode run while preserving those untested boundaries.
Retries, cancellation, long streams, every tool schema, and future OpenCode releases also remain outside this record. response.model would be provider self-report, not proof of weights, snapshot, supplier, or route origin. Infer’s completed-request record was not displayed in the OpenCode terminal during these checks.
Public Infer routes were unavailable at publication. This note documents a client and local bridge shape; it does not claim a callable service or a completed production request.
Reproduce the same configuration boundary.
Start with opencode --version and keep the result with the capture. Generate the versioned provider block at /integrations/opencode, store the key only in INFER_API_KEY, and compare current check states at /compatibility. opencode --pure debug config can show the resolved configuration, but it is not a strict offline validator: the parser ignores excess keys, and normal loading may inspect remote organization configuration or install plugin dependencies.
Public paid access is now available. Create an account, verify its email address, fund its wallet, create an API key, and check current status before dispatch.