Skip to content

feat: add complete OpenCode Go support - #763

Open
vcavichini wants to merge 3 commits into
huggingface:mainfrom
vcavichini:fix/opencode-go-session-header
Open

vcavichini wants to merge 3 commits into
huggingface:mainfrom
vcavichini:fix/opencode-go-session-header

Conversation

@vcavichini

@vcavichini vcavichini commented Oct 7, 2026 •

Copy link
Copy Markdown

OpenCode Go requires a stable x-opencode-session header, a client-specific User-Agent, and different API transports for different models. Tau previously sent every Go model through Chat Completions, which caused protocol errors for models such as Muse Spark 1.3.

This PR:

  • Sends session affinity and tau/<version> User-Agent on all three documented Go transports; leaves OpenCode Zen and other providers unchanged.
  • Adds model-specific routing for Chat Completions, OpenAI Responses, and Anthropic Messages. Anthropic-protocol Go models work with the Go API key (no OAuth requirement).
  • Refreshes OpenCode Go model IDs and generated metadata from models.dev, retaining the Kimi K2.6 entry still listed in Go docs.
  • Documents Muse Spark training/region restrictions and explains that Tau cannot display Go usage allowances or time-varying DeepSeek peak pricing.

Validation: uv run pytest tests/ -q (2,069 passed, 2 skipped), Ruff, and mypy. No live Go API request was made.

The Go model list and plan details can change; protocol mapping follows the current OpenCode Go endpoint table.}

@vcavichini vcavichini changed the title fix: send OpenCode Go session affinity header feat: add complete OpenCode Go support Oct 7, 2026

@mycr0ft mycr0ft left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I run opencode-go daily — the affinity design here is the right shape: catalog opt-in via compat, per-model transport metadata, and the api-key-without-OAuth fix for Anthropic-protocol Go models (unblocks the MiniMax/Qwen rows that currently error). The provider guide additions are honest about the usage/pricing limits Tau can't surface. A few concrete suggestions before this merges:

1. Unify the two affinity appliers before more formats appear. openai_compatible._apply_session_affinity_headers now understands openrouter/openai/opencode, while the new _apply_anthropic_session_affinity_headers understands only opencode. Two switches keyed by the same sessionAffinityFormat value will drift — the first new format added to one and not the other becomes a silent no-op on that transport. A single tau_ai-level helper (format → header mapping) called from both transports removes the failure mode.

2. Consider host-based detection for custom providers. The opt-in covers catalog providers, but a user-defined openai-compatible provider pointed at opencode.ai gets no affinity unless they know to set compat keys. Detecting the relay by hostname (as a fallback when no explicit affinity config exists) would cover that case without weakening the opt-in for unrelated gateways. I have a working implementation of this on my fork (opencode-affinity branch — provider-name prefix match plus opencode.ai host match, one merge point per transport) and am happy to extract it if useful.

3. Replace the opencode-go name check in provider_runtime with a compat field. if credential is None and provider.name != "opencode-go" hardcodes one provider into runtime logic. A catalog flag (e.g. anthropicAuth = "api-key", defaulting to current OAuth behavior) gives every key-based Anthropic-protocol gateway the same fix and keeps the config-driven philosophy from #241.

4. User-Agent scope. As written, tau/<version> is applied via _model_headers for opencode-go. Worth checking that the Responses-transport models (gpt-5.6-luna etc.) and the Anthropic-transport ones both end up sending it — the header takes a different path per transport. Longer-term, setting it as a default in create_async_client would make every request Tau emits self-identify (catalog refreshes, models.dev fetches), not just provider chat calls.

5. Question: should google/mistral transports learn sessionAffinityFormat too? Not needed for today's Go model list, but the config table in configuration.md currently implies the format is transport-independent while two transports ignore it.

Happy to test the branch against the relay (cache-warm behavior across turns is observable from here) if that's useful signal before merge.

Co-authored-by: vcavichini <vcavichini@users.noreply.github.com>
@vcavichini

Copy link
Copy Markdown
Author

Thanks @mycr0ft for the detailed review! Pushed 8eddbe5:

  1. Both transports now use one session affinity helper, so formats can't drift.
  2. The hardcoded opencode-go check is gone. A new anthropicAuth = "api-key" catalog field handles it. The default keeps OAuth.
  3. User-Agent was already sent on all three transports through _model_headers. Tests cover it.
  4. configuration.md now says google and mistral ignore sessionAffinityFormat.

For item 2, I'd be glad if you extract the host-based detection from your opencode-affinity branch as a follow-up. A relay test before merge would be great too.

@mycr0ft

mycr0ft commented Oct 10, 2026

Copy link
Copy Markdown

Confirmed against 8eddbe5: all three transports (completions, responses, Anthropic messages) now go through the single _apply_session_affinity_headers, anthropic_auth = "api-key" replaces the runtime name check (default OAuth preserved, catalog-validated), the UA tests cover all three transport paths, and the configuration.md wording now says what the code does. Nothing open from my earlier review.

For the host-based detection follow-up: the shape that works is a fallback in the header builders — try explicit compat config first, then detect the relay by target (provider name starting opencode or a base URL on opencode.ai), so user-defined custom providers pointed at the relay get affinity without knowing about compat keys, while unrelated gateways are untouched. I run this on my fork (opencode-affinity branch, src/tau_ai/opencode_affinity.py); happy to open it as a follow-up PR once this one merges, or send it here as a patch if the author prefers.

One relay-behavior datapoint I will try to gather before merge from my Go setup: whether an x-opencode-session value actually pins backend selection across turns (cache-warm confirmation). If I can observe it, I'll follow up here with the result or a no-op note if the relay doesn't currently surface enough signal to distinguish.

@vcavichini

Copy link
Copy Markdown
Author

@mycr0ft Thanks for confirming! A follow-up PR after this one merges sounds best, so this diff stays small. Looking forward to the relay result.

This branch has not been deployed

No deployments
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.

3 participants