Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@

## [Unreleased]

### Breaking Changes

* On OpenCode 1, models LiteLLM reports with `mode: "responses"` move to a `<provider>-responses` sibling provider, so their model id changes from `<provider>/<model>` to `<provider>-responses/<model>`. Update saved sessions, agents, and `opencode.json` references that hard-code the old id. OpenCode 2 is unaffected.

### Bug Fixes

* restore baseline reasoning-effort variants from sparse LiteLLM metadata (including GPT-6 Astra), respect explicit effort lists and disabled levels
Expand All @@ -36,6 +40,7 @@

### Features

* route models LiteLLM reports with `mode: "responses"` through the OpenAI Responses API (per-model runtime package on OpenCode 2; a `<provider>-responses` sibling on OpenCode 1)
* support the OpenCode 2 API with the OpenCode 1 server entrypoint
* add `formatModelNames` option to keep raw model ids in the picker

Expand Down
72 changes: 62 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ also shows how to configure a provider directly in `opencode.json`.
| 🧠 **Modality-aware** | Enriches `/v1/models` entries with `/v1/model/info` (`mode`, token limits, capability flags) and hides embedding / image / audio models from the picker. |
| 💵 **Real pricing** | Maps `input_cost_per_token` / `output_cost_per_token` (and cache read/write costs) from `/v1/model/info` into OpenCode's `cost` field, so the picker and `/cost` show what the proxy actually bills instead of `$0.00`. Models LiteLLM has no price for are left unpriced, not falsely marked free. |
| 🧩 **Reasoning-effort variants** | When LiteLLM reports per-model effort support (`supports_low_reasoning_effort`, …), the plugin surfaces each level as a picker variant automatically. |
| 🧭 **Responses-API routing** | Models LiteLLM reports with `mode: "responses"` are invoked through `/v1/responses` instead of `/v1/chat/completions`, with no extra configuration. OpenCode 2 overrides the runtime package per model; OpenCode 1 gets a `<provider>-responses` sibling. |
| 🔐 **Auth-aware** | Honours `LITELLM_API_KEY` / `LITELLM_MASTER_KEY`, `providers.litellm.settings.apiKey`, and OpenCode `/connect` credentials on the V1 server path. |
| 🌐 **Gateway-friendly** | Supports provider `headers` (and the legacy `customHeaders` option) for proxies behind Cloudflare Access or other API gateways. |
| 🧩 **Splittable catalog** | `includeModels` / `excludeModels` (glob patterns) let one LiteLLM proxy be divided into several OpenCode providers — e.g. by naming prefix — without hand-maintaining a model list. |
Expand Down Expand Up @@ -208,8 +209,11 @@ Here, `openai/gpt-4o` keeps your custom name; every other model from the proxy i

### Reasoning models and effort variants

All discovered models — reasoning-tier included — register under your single
LiteLLM provider and are invoked through `/v1/chat/completions`.
All discovered models register under your single LiteLLM provider, and models
LiteLLM reports with `mode: "responses"` are invoked through the OpenAI
Responses API (`/v1/responses`) instead of `/v1/chat/completions`. See
[Responses API models](#responses-api-models) for how that routing is applied
on each OpenCode version.

If LiteLLM reports per-model reasoning-effort support, the plugin automatically
surfaces it as OpenCode variants. Explicit `reasoning_effort_levels` (or
Expand All @@ -236,11 +240,59 @@ between effort levels from the model picker without hand-curating every entry.

> **Note**: OpenAI's reasoning-tier models (gpt-5, o1, o3, o4) reject
> requests that combine `reasoning_effort` with function tools on
> `/v1/chat/completions`. If you hit that error, fix it on the LiteLLM
> side — e.g. enable the Responses API for that model
> (`use_responses_api: true` in its `litellm_params`) — or leave
> `reasoningEffort` unset for that model in OpenCode. See the FAQ entry
> below.
> `/v1/chat/completions`. When LiteLLM reports such a model with
> `mode: "responses"` the plugin routes it to `/v1/responses` automatically;
> otherwise reach it without `reasoningEffort`. See the FAQ entry below.

### Responses API models

LiteLLM reports `mode` per model on `/v1/model/info` (the plugin fetches it
alongside `/v1/models`). When a model's `mode` is `responses`, the plugin routes
that model to `/v1/responses` instead of `/v1/chat/completions`. Only the
reported `mode` is used; the plugin does not guess from the model id, so an alias
that merely looks like a reasoning model stays on the transport LiteLLM actually
serves it from.

`mode` comes from LiteLLM's model cost map / the model's configured mode, not
from `use_responses_api`. If a model you expect to be routed isn't, check
`GET /v1/model/info` and confirm its `model_info.mode` is `responses` for the
deployment the proxy exposes under that id.

The routing is applied without any extra configuration, and it differs by
OpenCode version because the two versions pick a runtime package differently:

- **OpenCode 2** chooses the runtime per model. A Responses model keeps its
provider id and simply overrides the package to
`@opencode/ai/providers/openai/responses`. Chat models are untouched, and no
additional provider appears.
- **OpenCode 1** chooses the runtime per provider, so the plugin adds a sibling
provider named `<provider>-responses` (for the default `litellm` provider,
`litellm-responses`) using the `@ai-sdk/openai` package, which defaults to the
Responses API. It reuses the same endpoint, credentials, and custom headers,
and is created only when at least one model needs it.

Because the OpenCode 1 sibling occupies the `-responses` id suffix, the plugin
does not manage any provider whose id ends with `-responses`; pick a different
name if you want the plugin to discover models into a provider of your own.
Selecting a Responses model on OpenCode 1 therefore means picking
`<provider>-responses/<model>` rather than `<provider>/<model>`.

> **Upgrading on OpenCode 1 (model-id change).** Before this routing existed, a
> Responses-tier model was reachable as `<provider>/<model>` (over Chat
> Completions). It now lives under `<provider>-responses/<model>`. Any saved
> session, agent, or `opencode.json` reference that hard-codes the old
> `<provider>/<model>` id needs to be repointed, or it will fail to resolve.
> OpenCode 2 is unaffected — the model keeps its provider id.

> **Hand-curated models on OpenCode 1.** OpenCode 1 chooses the runtime per
> provider, so routing a model to the Responses API means putting it in the
> `<provider>-responses` provider. The plugin only relocates models it
> discovered itself; entries you hand-defined under the chat provider keep
> their place and stay on Chat Completions. To route a hand-defined model
> through the Responses API on OpenCode 1, define it under the
> `<provider>-responses` provider (or omit it and let discovery add it).
> OpenCode 2 needs no relocation because it overrides the runtime per model, so
> the same model keeps its provider id either way.

### Authentication

Expand Down Expand Up @@ -519,10 +571,10 @@ The `customHeaders` map works for any gateway that requires extra HTTP headers
<details>
<summary><b>I get <code>Function tools with reasoning_effort are not supported … in /v1/chat/completions</code> — what do I do?</b></summary>

This error comes from OpenAI: their reasoning-tier models (gpt-5, o1, o3, o4) refuse function-tool calls on `/v1/chat/completions` when `reasoning_effort` is set. The plugin registers every discovered model through the chat-completions path, so fix this on the LiteLLM side:
This error comes from OpenAI: their reasoning-tier models (gpt-5, o1, o3, o4) refuse function-tool calls on `/v1/chat/completions` when `reasoning_effort` is set. When LiteLLM reports such a model with `mode: "responses"`, the plugin sends that model to `/v1/responses` and the error goes away. See [Responses API models](#responses-api-models).

- Enable the Responses API for that model in your LiteLLM config (e.g. `use_responses_api: true` in its `litellm_params`), or
- Leave the model's `reasoningEffort` unset in OpenCode (don't pick a reasoning-effort variant for it).
- If the model isn't routed, confirm `/v1/model/info` reports `model_info.mode: "responses"` for it. `mode` comes from LiteLLM's model cost map / the model's configured mode — setting `use_responses_api: true` in `litellm_params` does not by itself change it. The plugin acts only on the reported `mode` and does not guess from the model id.
- If you'd rather stay on Chat Completions, leave the model's `reasoningEffort` unset in OpenCode (don't pick a reasoning-effort variant for it).

If your model id doesn't look like a reasoning model to LiteLLM (e.g. you renamed it), also check that its `model_info` in `litellm config.yaml` carries the right `supports_*` flags.
</details>
Expand Down
Loading
Loading