From d9fc3418a11ce85a2994f0e7c1ca786d084ef80e Mon Sep 17 00:00:00 2001 From: ArthurErlich Date: Mon, 5 Oct 2026 21:32:58 +0200 Subject: [PATCH] docs(opencode): document v2 config, mark v1 deprecated Co-Authored-By: Claude Sonnet 5.5 --- docs/coding-cli-setup/opencode.md | 162 ++++++++++++++++++++++-------- 1 file changed, 119 insertions(+), 43 deletions(-) diff --git a/docs/coding-cli-setup/opencode.md b/docs/coding-cli-setup/opencode.md index 41a94ed..a99d3f3 100644 --- a/docs/coding-cli-setup/opencode.md +++ b/docs/coding-cli-setup/opencode.md @@ -2,62 +2,138 @@ [← back to overview](index.md) -> **Source of truth:** [OpenCode's docs](https://opencode.ai/docs/) for config keys; the [overview](index.md) for the gateway endpoint and context size. If this page disagrees with them, they win. +> **Source of truth:** [OpenCode v2 docs](https://opencode.ai/v2/docs) for config keys; the [overview](index.md) for the gateway endpoint and context size. If this page disagrees with them, they win. The v1 config is [deprecated](#v1-config-deprecated) but kept for installs that haven't migrated. -Confirmed project: **`anomalyco/opencode`** (renamed from `sst/opencode` — don't confuse with the unrelated `opencode-ai/opencode` Go TUI). Docs: https://opencode.ai/docs/ +OpenCode is **`anomalyco/opencode`** (renamed from `sst/opencode`). Don't confuse it with the unrelated `opencode-ai/opencode` Go TUI. It speaks plain **OpenAI Chat Completions**, so it talks to the gateway like any other OpenAI-compatible client. + +## Install -**Install**: ```bash -curl -fsSL https://opencode.ai/install | bash +curl -fsSL https://opencode.ai/v2/install | bash ``` -**Config** (`opencode.json`, project root or `~/.config/opencode/opencode.json`): +Homebrew, npm, bun, pnpm, yarn and AUR also work. See the [v2 docs](https://opencode.ai/v2/docs). -```json +## Config (v2) + +`opencode.json` or `opencode.jsonc`, in the project root (or `.opencode/`) or globally at `~/.config/opencode/opencode.json`. OpenCode searches upward from the current directory and merges, closest file wins. + +```jsonc { - "$schema": "https://opencode.ai/config.json", - "model": "omniroute/qwen3.8-27b-local//models/Qwen3.8-27B-UD-Q4_K_XL.gguf", - "provider": { - "omniroute": { - "npm": "@ai-sdk/openai-compatible", - "name": "OmniRoute", - "options": { - "baseURL": "https:///v1" - }, - "models": { - "qwen3.8-27b-local//models/Qwen3.8-27B-UD-Q4_K_XL.gguf": { - "name": "Qwen3.8-27B", - "reasoning": true, - "interleaved": { - "field": "reasoning_content" - }, - "limit": { - "context": 131000, - "output": 65536 - }, - "cost": { - "input": 0.4, - "output": 3.0, - "cache": { - "read": 0.04, - "write": 0 - } - } - } + "$schema": "https://opencode.ai/config.json", + "providers": { + "omniroute": { + "name": "OmniRoute", + "package": "aisdk:@ai-sdk/openai-compatible", + "settings": { + "baseURL": "https:///v1", + "apiKey": "" + }, + "models": { + "qwen3.8-27b": { + "modelID": "qwen3.8-27b-local//models/Qwen3.8-27B-UD-Q4_K_XL.gguf", + "name": "Qwen3.8-27B", + "capabilities": { "tools": true, "input": ["text"], "output": ["text"] }, + "compatibility": { "reasoningField": "reasoning_content" }, + "limit": { "context": 260000, "output": 32000 }, + "cost": { "input": 0.4, "output": 3 }, + "body": { + "temperature": 1, + "top_p": 0.95, + "top_k": 20, + "min_p": 0, + "presence_penalty": 0, + "chat_template_kwargs": { "reasoning_effort": "xhigh" } + }, + "variants": [ + { "id": "medium", "body": { "chat_template_kwargs": { "reasoning_effort": "medium" } } }, + { "id": "fast", "body": { "chat_template_kwargs": { "reasoning_effort": "low" } } }, + { + "id": "instruct", + "body": { + "temperature": 0.7, + "top_p": 0.8, + "presence_penalty": 1.5, + "chat_template_kwargs": { "enable_thinking": false } + } } + ] } + } } + } } - ``` -`limit.context` is the per-slot context ([overview](index.md)). OpenCode uses it for its own context bookkeeping; the server doesn't read it. +### What each part does -Select the model with `llm-server/qwen3.8-27b-local`. +| Key | Purpose | +|---|---| +| `providers` | Plural in v2 (v1: `provider`). The key (`omniroute`) is the prefix in model references. | +| `package` | The adapter for the API shape. This stack uses an OpenAI-compatible one. | +| `settings.baseURL` | The gateway URL, up to and including `/v1`. | +| `settings.apiKey` | The gateway API key issued to OpenCode ([gateway → API keys](../gateway.md#api-keys)). The v2 docs' own examples use an `env` list instead, to keep the key out of the file. | +| `models.` | The short name you select. `modelID` is the id the gateway exposes, which the key doesn't have to match. | +| `capabilities` | Declares tool calling and input/output modalities. | +| `compatibility.reasoningField` | Where the backend returns reasoning text. llama.cpp uses `reasoning_content`. | +| `limit` | OpenCode's own context bookkeeping. The server doesn't read it. See [overview](index.md) for the number to use. | +| `body` | Extra request fields, sent on every call. Sampling values and `chat_template_kwargs` go here. | +| `variants` | Named overrides on top of `body`. | -**OpenCode-specific risks** (on top of the shared Qwen3.8-27B tool-calling risk — see [overview](index.md)): -- Relies on the backend's `--jinja` flag ([architecture → llama-server](../architecture.md#llama-server)); without it, OpenCode's unconditional tool-call scaffolding gets a 500. -- [anomalyco/opencode#20669](https://github.com/anomalyco/opencode/issues/20669) (closed as "not planned" — a live, unfixed risk): OpenCode's `bash` tool crashes if the model omits the optional `description` field on a tool call; some local backends return `finish_reason: tool_calls` with an empty array, which can hang the agent loop instead of stopping cleanly. -- Thinking-mode handling (`options.reasoningEffort`) is undocumented for models that emit inline `` tags rather than a native reasoning API field — expect no effect from that config on this model; untested. +### Selecting a model and variant -Further reading: [`research/opencode-cli-setup.md`](../research/opencode-cli-setup.md) (its base URL example predates the gateway), [`research/opencode-auto-compact.md`](../research/opencode-auto-compact.md). +Format: `provider/model#variant`. The provider ends at the first `/`. + +```bash +opencode run --model omniroute/qwen3.8-27b # default: xhigh reasoning +opencode run --model omniroute/qwen3.8-27b#medium +opencode run --model omniroute/qwen3.8-27b#fast # low reasoning effort +opencode run --model omniroute/qwen3.8-27b#instruct # thinking off, instruct sampling +``` + +### Notes + +- **Unverified keys:** the v2 docs show `"package": "@opencode/ai/providers/openai-compatible"` for custom providers, while this config uses the `aisdk:@ai-sdk/openai-compatible` form. The docs pages I could read don't confirm that prefix. If OpenCode rejects the provider, switch `package` to the documented value. +- **Variants:** the v2 docs' own variant example puts settings under `settings`. This config uses `body`, which the models page lists as valid for provider-specific request fields. +- **`limit.context`:** [overview](index.md) explains how per-slot context is derived. The example above uses a larger value than a plain per-slot division gives. + +## OpenCode-specific risks + +On top of the shared Qwen3.8-27B tool-calling risk (see [overview](index.md)): + +- Relies on the backend's `--jinja` flag ([architecture → llama-server](../architecture.md#llama-server)). Without it, OpenCode's unconditional tool-call scaffolding gets a 500. +- [anomalyco/opencode#20669](https://github.com/anomalyco/opencode/issues/20669) (closed as "not planned", so a live, unfixed risk): OpenCode's `bash` tool crashes if the model omits the optional `description` field on a tool call. Some local backends return `finish_reason: tool_calls` with an empty array, which can hang the agent loop instead of stopping it. +- Reasoning control goes through `chat_template_kwargs` in `body`, not OpenCode's native `reasoningEffort`. The native key has no effect against llama.cpp. + +## V1 config (deprecated) + +Use this only on OpenCode v1. It is no longer maintained here. Differences from v2: `provider` (singular), `npm` instead of `package`, `options` instead of `settings`, and a flat `reasoning` / `interleaved` block instead of `capabilities` / `compatibility`, and no `variants` or `body` in this config. + +```jsonc +{ + "$schema": "https://opencode.ai/config.json", + "model": "omniroute/qwen3.8-27b-local//models/Qwen3.8-27B-UD-Q4_K_XL.gguf", + "provider": { + "omniroute": { + "npm": "@ai-sdk/openai-compatible", + "name": "OmniRoute", + "options": { "baseURL": "https:///v1" }, + "models": { + "qwen3.8-27b-local//models/Qwen3.8-27B-UD-Q4_K_XL.gguf": { + "name": "Qwen3.8-27B", + "reasoning": true, + "interleaved": { "field": "reasoning_content" }, + "limit": { "context": 131000, "output": 65536 }, + "cost": { "input": 0.4, "output": 3.0, "cache": { "read": 0.04, "write": 0 } } + } + } + } + } +} +``` + +Select the model with `omniroute/`. The v1 install script is `curl -fsSL https://opencode.ai/install | bash`. + +## Further reading + +[`research/opencode-cli-setup.md`](../research/opencode-cli-setup.md) (v1-era, base URL predates the gateway), [`research/opencode-auto-compact.md`](../research/opencode-auto-compact.md).