feat(provider-opencode-go): add OpenCode Go Chat Completions provider worker - #1
Open
faramirezs wants to merge 7 commits into
Open
feat(provider-opencode-go): add OpenCode Go Chat Completions provider worker#1faramirezs wants to merge 7 commits into
faramirezs wants to merge 7 commits into
Conversation
skill-check — worker0 verified, 54 skipped (no docs/).
Three for three. Nicely done. |
OpenCode Go Chat Completions provider behind llm-router. Implements the provider protocol: stream (SSE chunks to AssistantMessageEvent frames), abort, refresh_models (live GET /v1/models enriched with models.dev metadata: context window, reasoning efforts, tool/structured-output capability), and re-declaration on router::ready. Chat Completions wire format only, max_completion_tokens, strict json_schema structured output, reasoning_effort low/medium/high for deepseek-/kimi-k2.7- families. Wired into create-tag/release workflows and the harness worker deps.
README to the provider family structure (Behavior/Tests/Running), manifest tags+description to the canonical form, release wiring in alpha-release.yml and discover_changed_workers.py, llm-router README reference note.
Upstream MOT-4335 rewrote all provider identity prompts to teach the live
surface (register_trigger, harness::spawn, orchestrator: true) and stripped
orchestration-process doctrine. The fork-PR merge with the new main runs the
harness prompts sweep over every shipped prompt, which failed on our pre-
rewrite copy ("delegation is one-way" etc.). Absorb the rewritten prompt,
update the register.rs identity assertions, and apply the iii-state -> state
rename.
faramirezs
force-pushed
the
feat/provider-opencode-go
branch
from
August 4, 2026 11:10
099ac93 to
20201db
Compare
- iii-permissions.yaml: deny provider::opencode_go::abort (agents must not cancel router-owned streams; matches provider-claude-code) - sse.rs: relay delta.reasoning_content as thinking blocks (the OpenCode Go wire emits it, live-verified); bound tool-call index to 64 (malformed upstream could grow the vec unboundedly) - upstream.rs: data_line per SSE spec — accept data: without a space and join repeated data: lines instead of silently dropping output - curated.rs: per-model max_output_tokens from models.dev limit.output; 4096 stays the unknown-id fallback - reasoning.rs: Minimal maps to minimal then none (gpt-5.6-luna floor) - register.rs: log router::ready trigger registration failures - router_client.rs: narrow module doc claim - README: repair split provider-openai table row; III_WS_URL -> III_URL (code + engine convention); thinking-delta relay note
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.
What
New Rust worker
provider-opencode-go: an LLM provider worker behindllm-routerspeaking the OpenCode Go API — Chat Completions(
https://opencode.ai/zen/go/v1/chat/completions), SSE streaming, live modeldiscovery, auth/error taxonomy, reasoning-effort mapping, tool calling, and
structured output. Registers
provider::opencode_go::stream/refresh_models/
abortwith the router, binds identity via registration token (state scopeprovider-opencode-go), and readsOPENCODE_GO_API_KEYas credential.Why
The stack has no way to route chat completions to an OpenCode Go subscription.
The OpenCode Go API is Chat Completions compatible, so the existing
provider-openaiworker ports directly — same protocol, same relay/pumpscaffold, same error taxonomy, with the OpenAI-only surfaces (embeddings,
Responses API) dropped as dead code.
How it works
router::provider::resolve(config slice →
OPENCODE_GO_API_KEYenv on the router → none); sent asAuthorization: Bearer.AssistantMessageEventframesinto a router-owned channel;
ping≥ every 30s of silence; a failed channelwrite (
router::abort/ caller gone) drops the SSE receiver and aborts thein-flight HTTP request. Stream-path
tracing::debugfor provider-sideobservability.
GET /v1/modelssupplies bare ids; each isenriched from a hardcoded curated metadata table (
src/curated.rs) preparedfrom models.dev (2026-08-03) — context window, reasoning support/effort
levels, tool-call and structured-output capability for the maintainer's
curated model set (24 models.dev entries +
hy3-preview). Ids outside thetable keep conservative defaults (128K, no thinking, tools on) — same
pattern as provider-openai's
curated.rs.thinking_levelmaps to the upstreamreasoning_effortonlywhen the model's curated effort list accepts the level (e.g.
grok-4.5accepts
low/medium/high;deepseek-v4-flashandglm-5.2accepthigh/max;hy3acceptsnone/low/high); toggle-only models andunknown ids stream without the field.
router::provider::registerwithbackoff until acked, re-declares on
router::ready;registration_tokenpersisted in iii-state (scope
provider-opencode-go).Scope / caveat
provider-openai: mechanical renames only in most files; theOpenAI-only surface (ApiMode, embeddings, Responses-API event handlers and
thinking deltas, curated reasoning-fallback ladder, luna guard) is dropped —
OpenCode Go has no such surface.
opencode/worker —different role (CLI wrapper vs provider), no overlap; both install side by
side.
hy3-previewis listed by the liveGET /v1/modelsbut the chat endpointcurrently returns
ModelNotFound— an upstream inconsistency; the curatedrow keeps conservative defaults and the provider surfaces the upstream error
cleanly.
no-ticketlabel applied.Repo wiring
README.mdModules row added forprovider-opencode-go(alphabetical, between
provider-openaiandprovider-xai)create-tag.yml,release.yml,alpha-release.yml, and.github/scripts/discover_changed_workers.pyllm-router/README.mdreference note (same structure as provider-openai)Verification
cargo fmt --checkandcargo clippy --all-targets --all-features -- -D warningscleancargo test --all-features: 67 pass — 57 lib unit + 2 bin unit + 4schema/golden + 4 integration
stubbed upstream: chat stream end-to-end (incl.
cache_readusage),401 →
auth_expirederror frame,refresh_models→ catalog from thecurated table, re-declare on
router::readycargo build --releaseOKrefresh_models→ 25 models;router::models::list→ 25 curated entrieswith metadata;
router::completeserved onglm-5,deepseek-v4-flash(incl.
thinking_level: high→reasoning_effort), andhy3Test plan
cargo fmt --checkcargo clippy --all-targets --all-features -- -D warningscargo test— 67 passIII_ENGINE_BIN=$(which iii) cargo test --test integration -- --test-threads=1iii worker add provider-opencode-go→refresh_models→router::models::list→router::complete