---
name: opencode-flexinference
description: Point OpenCode at FlexInference with config only - a flexinference provider on @ai-sdk/openai-compatible, the OPENCODE_CONFIG search order, per-model start_within and reasoningEffort, model variants, the /flex session plugin, and agent keys that carry stored routing defaults. TRIGGER when wiring OpenCode to FlexInference, adding a FlexInference provider or model to an opencode.json, setting a flex deadline or thinking level for OpenCode, or debugging why an OpenCode request did not route through FlexInference. SKIP for FlexInference API work with no OpenCode in it, and for OpenCode questions that never touch model routing.
---

# OpenCode on FlexInference

Pointing OpenCode at FlexInference needs config only. No source change, no fork, no patch. A stock
OpenCode build reads a provider entry out of a JSON file and every request goes to the router.

## The provider entry

```json
{
  "$schema": "https://opencode.ai/config.json",
  "model": "flexinference/gpt-5.6-sol",
  "provider": {
    "flexinference": {
      "name": "FlexInference",
      "env": ["FLEXINFERENCE_API_KEY"],
      "npm": "@ai-sdk/openai-compatible",
      "options": {
        "apiKey": "{env:FLEXINFERENCE_API_KEY}",
        "baseURL": "https://api.flexinference.com/v1"
      },
      "models": {
        "gpt-5.6-sol": {
          "name": "GPT-5.6 Sol",
          "reasoning": true,
          "tool_call": true,
          "limit": { "context": 400000, "output": 128000 },
          "cost": { "input": 5, "output": 30 }
        }
      }
    }
  }
}
```

Four fields carry the whole integration. `npm` picks the OpenAI-compatible AI SDK package,
`options.baseURL` sends requests to the router, `options.apiKey` reads the key out of the
environment, and each key of `models` is a model slug the router serves. `env` lists the variable
so OpenCode marks the provider connected once it is set.

Add a model by adding a key under `models`. The slug is what goes on the wire, so it has to be a
slug the router prices. Everything else in the entry is display and limits.

## Where the file goes

OpenCode merges config from several places, and a later source wins over an earlier one:

- The global user config in OpenCode's config directory.
- `OPENCODE_CONFIG`, a path to one JSON or JSONC file.
- `opencode.json` and `opencode.jsonc` found walking up from the working directory to the worktree
  root, unless `OPENCODE_DISABLE_PROJECT_CONFIG` is set.
- `opencode.json` and `opencode.jsonc` inside each `.opencode` directory found on that walk and
  under your home directory, plus any directory named by `OPENCODE_CONFIG_DIR`.
- `OPENCODE_CONFIG_CONTENT`, holding the JSON inline.

For a bundle you want to opt into per run, use `OPENCODE_CONFIG` and leave the rest alone:

```bash
OPENCODE_CONFIG=/path/to/config/opencode.json opencode
```

## Routing per request, without code

`start_within` says how long a request may wait before it starts running. It is not a limit on how
long generation takes. Write a duration as `HHh-MMm-SSs` with two digits per field, from 5 seconds
to 10 minutes. Longer windows win the cheap tier more often. Send a tier name instead when you want
one tier and no race: `default` for the normal tier, `priority` for the fastest one, `auto` to let
the provider choose. Claude races a cheaper tier of its own, which a duration reaches on managed
keys, without streaming, inside a window of 3 to 10 minutes. Current constraints put that floor at
3 minutes. Outside those rules a duration on a `claude-*` model returns
`400 flex_unsupported_for_anthropic`.

Two config places put it on the wire, and both are plain JSON.

**Per model.** `options` on a model entry goes into the request body for that model:

```json
"gpt-5.6-sol": {
  "name": "GPT-5.6 Sol",
  "options": { "start_within": "00h-00m-30s", "reasoningEffort": "medium" }
}
```

**Per variant.** `variants` on a model entry declares named bodies you switch between without
changing model. Pick one with `--variant` on `opencode run`, or cycle them in the TUI with
`ctrl+t`:

```json
"gpt-5.6-sol": {
  "name": "GPT-5.6 Sol",
  "variants": {
    "patient": { "start_within": "00h-05m-00s", "reasoningEffort": "high" },
    "now": { "start_within": "priority", "reasoningEffort": "low" }
  }
}
```

## The /flex plugin, for per-session routing

A small plugin adds a `/flex` command so a session picks its own model, start window, and thinking
level. It is optional. Everything above works without it.

The plugin exports two hooks. `command.execute.before` parses the arguments and stores the
selection against the session. `chat.params` writes `start_within` and `reasoningEffort` onto every
FlexInference request in that session, and leaves other providers untouched. Load it from the same
config file:

```json
{ "plugin": ["./flex-plugin"] }
```

`/flex gpt-5.6-sol 2m high` selects a model, a two minute window, and high thinking.
`/flex gpt-5.6-sol off` keeps the model on standard routing. A session with no selection sends
`start_within: "default"`.

## Agent keys carry the defaults instead

A FlexInference agent key can hold routing defaults, so a config needs nothing beyond `baseURL` and
the key. Create one in the dashboard under API, then FlexInference keys, with Create agent key.
A key made with the plain create form carries no defaults.

A key stores three fields:

- `start_within`: a duration or a tier name, same values as the body field.
- `on_no_flex`: `default` or `priority`. Required exactly when `start_within` is a duration, and
  rejected otherwise. It is the tier a model with no cheap tier falls back to, so a duration on the
  key never fails the way a duration in the body would.
- `retry`: `count` from 1 to 5, optional `backoff` of `exponential` or `linear`, optional `jitter`.

The router fills a stored default into any request that arrives without that field, and stamps
`x-flexinference-defaults-applied` on the response. The request body always wins. A body value is
used even when it is wrong, so an explicit bad value still returns `400 invalid_start_within`. A key
default stands in for a field you did not send, never for one you sent wrong.

Edits to a key go live within seconds and the key stays the same. Revoking a key clears its defaults
with it.

An agent never creates or revokes a key. A person issues the key in the dashboard and exports it.
The agent only points config at it.

## What config cannot do

Reading the outcome needs a source change. Every successful response carries
`x-flexinference-flex-applied`, which is `true` only when the cheap tier served the request, plus
`x-flexinference-flex-reason` and `x-flexinference-cost`. OpenCode does not surface response headers
in the TUI. Showing a flex rate, a time to first token, or a side-by-side comparison against a
direct OpenAI call means patching the client.
