Skip to content
Open
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,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
62 changes: 52 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,49 @@ 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`. Enable the Responses API for such a model on the
> LiteLLM side (`use_responses_api: true` in its `litellm_params`) and the
> plugin routes it 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` β€” usually after
you enable the Responses API for it in LiteLLM, e.g.
`use_responses_api: true` β€” 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.

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>`.

> **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 +561,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"` β€” for example after setting `use_responses_api: true` in its `litellm_params` β€” 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 `mode: "responses"` for it. The plugin acts only on that signal 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