# Coding agents & orchestrators

Agentic coding tools — OpenCode, Cline, Roo Code, Continue, Goose, Kilo Code, Zed, Aider, Crush — run on DirectInference the same way your application does: one base URL, one `llm_live_…` key, any model id. The difference from a plain SDK swap is that these tools don't model an endpoint as "a base URL and a key." They model it as a **provider with per-model settings**, and they decide what a model is allowed to do — call tools, stream reasoning, accept images — from the model id. The id `di-fusion` is new to them, so you turn those capabilities on yourself. This page has the exact block to paste for each tool.

:::note[Simpler cases live elsewhere]
A CLI or agent that just reads `OPENAI_BASE_URL` / `ANTHROPIC_BASE_URL` (Cursor, Claude Code) needs nothing on this page — see [AI coding agents](https://docs.directinference.com/agents/). A settings screen with a generic "custom provider" form is covered field-by-field in [Custom providers](https://docs.directinference.com/custom-providers/). This page is for tools whose provider is a structured config file or a multi-field model form.
:::

## Why these tools need a little more

A tool's "does this model support tools / reasoning / vision?" answer normally comes from a built-in catalog keyed by model id (models.dev, a bundled allowlist, or name-matching). DirectInference exposes one model — `di-fusion` — and it is on no tool's catalog, so for an unknown id those capabilities can default off. With DirectInference, **capability is a property of the request shape, not the model id**: send tools and they are served; send an image and it is served as a vision request. So the rule for every tool below is the same — when it offers a tool / reasoning / vision switch or a per-model capability field, turn it on, and give it a context-window number so its prompt budgeting works.

:::caution[Two things to get right]
**1 · AI-SDK tools: the OpenAI-*compatible* provider is the simplest path.** Tools built on the Vercel AI SDK (OpenCode, and others) work well with the `@ai-sdk/openai-compatible` package, which calls `/v1/chat/completions`. The plain `@ai-sdk/openai` package defaults to the OpenAI **Responses API** (`/v1/responses`), which DirectInference now serves statelessly — so it works too; use `provider.chat("id")` if you'd rather pin Chat Completions. Base URL is `https://api.directinference.com/di/v1` (the openai-compatible package appends `/chat/completions`).

**2 · Declare a context window.** Most tools size their prompt and compaction from the model's context limit. For an unknown id a missing limit falls back to zero and breaks context tracking — always set the context/output fields shown below. These are the tool's local prompt budget, not a DirectInference cap; pick a value that suits your work and lower it if you ever see a context-length error.
:::

## Which id to use

There is one model; the id is a label that is read as intent and echoed back unchanged. Use whichever fits the slot:

| Id | Use it for |
| --- | --- |
| `di-fusion` | The default. The single model, effort chosen automatically per request. |
| `di-saver` | A tool's *small* / *background* / *weak* slot (titles, commit messages, summaries) — effort pinned low. |
| `di-max` | A tool's *large* / *main* slot for hard work — effort pinned high. |

Tools with two model slots (a fast background model and a main model) map cleanly to `di-saver` and `di-max`; single-slot tools should use `di-fusion`.

## Per-tool setup

Add a provider to `opencode.json` (project root) or `~/.config/opencode/opencode.json`. OpenCode auto-installs the `@ai-sdk/openai-compatible` package referenced by `npm`; you do not add it to a `package.json`.

```json
{
  "$schema": "https://opencode.ai/config.json",
  "model": "directinference/di-fusion",
  "small_model": "directinference/di-saver",
  "provider": {
    "directinference": {
      "npm": "@ai-sdk/openai-compatible",
      "name": "DirectInference",
      "options": {
        "baseURL": "https://api.directinference.com/di/v1",
        "apiKey": "{env:DIRECTINFERENCE_API_KEY}",
        "headers": { "X-Title": "opencode" }
      },
      "models": {
        "di-fusion": { "name": "DirectInference",         "tool_call": true, "reasoning": true,  "attachment": true, "limit": { "context": 200000, "output": 32000 } },
        "di-saver":  { "name": "DirectInference (saver)", "tool_call": true, "reasoning": false, "attachment": true, "limit": { "context": 200000, "output": 16000 } },
        "di-max":    { "name": "DirectInference (max)",   "tool_call": true, "reasoning": true,  "attachment": true, "limit": { "context": 200000, "output": 64000 } }
      }
    }
  }
}
```

Then `export DIRECTINFERENCE_API_KEY=llm_live_…` (or run `opencode auth login`). **Enable:** `tool_call`, `reasoning`, and `attachment` per model, plus `limit` — for an unknown id OpenCode otherwise leaves reasoning/vision off and the context at zero. **Gotcha:** keep `npm` as `@ai-sdk/openai-compatible` (not `@ai-sdk/openai`) so requests go to `/v1/chat/completions`.

Cline is configured in its panel (settings are stored by the extension, not in a file). **Settings → API Configuration → API Provider → `OpenAI Compatible`**, then fill:

- **Base URL** — `https://api.directinference.com/di/v1`
- **API Key** — `llm_live_…`
- **Model ID** — type `di-fusion` (free-text; use a separate config for `di-saver` / `di-max`)
- Expand **Model Configuration**:
  - **Supports Images** — on, if you want vision routed through `di-fusion`
  - **Context Window Size** — e.g. `200000`  ·  **Max Output Tokens** — e.g. `8192` (or `-1` to leave it uncapped)
  - **Enable R1 messages format** — leave **unchecked** (it merges messages for DeepSeek-R1-style models and would distort normal requests)

**Tools are not gated by the model id:** for an id it doesn't recognize, Cline drives edits and commands through its own structured (XML) tool protocol embedded in the system prompt, so `di-fusion` works as a full agent without native function calling being involved. **Gotcha:** the Base URL must end in `/di/v1` (Cline appends `/chat/completions`); model id + capabilities are stored per mode, so set the model in both **Plan** and **Act** if one looks unconfigured.

Roo Code (a Cline fork) is configured in a provider profile. **Settings → Providers → +** (new profile) → **API Provider → `OpenAI Compatible`**:

- **Base URL** — `https://api.directinference.com/di/v1`
- **API Key** — `llm_live_…`
- **Model** — type `di-fusion` in the search box, then pick the **"Use custom: di-fusion"** entry (do the same in separate profiles for `di-saver` / `di-max`)
- **Model Capabilities** — **Image Support** on for vision; **Context Window Size** e.g. `200000`; **Max Output Tokens** e.g. `8192` (or `-1`)
- Leave **Enable streaming** on and **Enable R1 model parameters** off

**Capability is on your declared fields, not the id:** vision and context window come from the toggles above. Tool use is the one place current Roo Code (3.37+) differs from Cline — it uses **native OpenAI tool calling exclusively** (the XML fallback was removed), which DirectInference serves (a tool-bearing request returns usable `tool_calls` or a clear error, never a bad 200), so `di-fusion` is fully agentic. **Gotcha:** Base URL ends in `/di/v1`; on Roo **3.36** you may instead see a Native/XML protocol selector — either works with `di-fusion`.

Add a model to your `config.yaml` (`~/.continue/config.yaml`, or a workspace `.continue/config.yaml`):

```yaml
name: DirectInference
version: 0.0.1
schema: v1
models:
  - name: DirectInference
    provider: openai
    model: di-fusion
    apiBase: https://api.directinference.com/di/v1
    apiKey: llm_live_...          # or ${{ secrets.DIRECTINFERENCE_API_KEY }}
    roles: [chat, edit, apply]
    capabilities:
      - tool_use                  # required for Agent mode on an unknown id
      - image_input               # vision
    defaultCompletionOptions:
      maxTokens: 4096
  # Optional second entry for the background/heavy slot: model: di-saver / di-max
```

**Enable:** the explicit `capabilities: [tool_use, image_input]` — Continue only auto-detects these for ids it recognizes, so Agent mode tools stay dark on `di-fusion` without it. **Gotcha:** `apiBase` must include `/di/v1`; don't give `di-fusion` the `autocomplete` role — it is a chat model, not a fill-in-the-middle completion model, so point autocomplete at a dedicated endpoint instead.

The quickest path uses Goose's built-in OpenAI provider via environment variables. Goose builds the request URL as `OPENAI_HOST` + `/` + `OPENAI_BASE_PATH`, so split DirectInference's path across the two:

```bash
export OPENAI_API_KEY=llm_live_...
export OPENAI_HOST=https://api.directinference.com
export OPENAI_BASE_PATH=di/v1/chat/completions
# then: goose configure  →  Configure Providers  →  OpenAI  →  model: di-fusion
```

For a named entry instead, drop `~/.config/goose/custom_providers/directinference.json`:

```json
{
  "name": "directinference",
  "engine": "openai",
  "display_name": "DirectInference",
  "api_key_env": "DIRECTINFERENCE_API_KEY",
  "base_url": "https://api.directinference.com/di/v1",
  "models": [
    { "name": "di-fusion", "context_limit": 200000 },
    { "name": "di-saver",  "context_limit": 200000 },
    { "name": "di-max",    "context_limit": 200000 }
  ],
  "requires_auth": true,
  "supports_streaming": true
}
```

**Enable:** nothing special — Goose inherits tool calling from the OpenAI engine for any id; just set `context_limit`. **Gotcha:** the host/path split is the trap — don't put `/di/v1` on `OPENAI_HOST` and leave the default `OPENAI_BASE_PATH`, or the version segment doubles.

In the Kilo Code sidebar: **Settings → Providers → API Provider → `OpenAI Compatible`**:

- **Base URL** — `https://api.directinference.com/di/v1`
- **API Key** — `llm_live_…`
- **Model ID** — type `di-fusion`, then **Use custom** (also `di-saver` / `di-max`)
- **Model Configuration** — **Context Window** (e.g. `200000`), **Max Output Tokens** (e.g. `8192`), **Image Support** on for vision, **Computer Use** off

**Enable:** tool calling is not gated by the id, so `di-fusion` is agentic immediately; set Image Support for vision and the context/output numbers so prompts are sized correctly. **Gotcha:** the Base URL is the `/di/v1` root; keep **Computer Use** off.

Open the Zed settings file (`zed: open settings`) and add an `openai_compatible` provider — note `openai_compatible`, not `openai`:

```json
{
  "language_models": {
    "openai_compatible": {
      "DirectInference": {
        "api_url": "https://api.directinference.com/di/v1",
        "available_models": [
          {
            "name": "di-fusion",
            "display_name": "DirectInference",
            "max_tokens": 200000,
            "max_completion_tokens": 8192,
            "capabilities": { "tools": true, "images": true }
          }
        ]
      }
    }
  }
}
```

Add `di-saver` / `di-max` as more `available_models` entries if you want them. The key does **not** go in settings — set the env var `DIRECT_INFERENCE_API_KEY=llm_live_…` (Zed upper-snake-cases the provider name) or paste it into the Agent Panel settings (stored in the OS keychain). **Enable:** a model must be listed in `available_models`, and `capabilities.tools` / `capabilities.images` gate tool use and vision — set both true. **Gotcha:** `api_url` ends in `/di/v1`; use the `openai_compatible` node so the custom `api_url` is honored.

Aider talks to DirectInference through its OpenAI-compatible path; the model id carries an `openai/` prefix.

```bash
export OPENAI_API_BASE=https://api.directinference.com/di/v1
export OPENAI_API_KEY=llm_live_...
aider --model openai/di-fusion   # openai/di-saver, openai/di-max also work
```

Aider warns on an unknown id until you describe it. Add `.aider.model.metadata.json` (home dir or repo root), keyed by the **full** prefixed id:

```json
{
  "openai/di-fusion": {
    "max_input_tokens": 200000,
    "max_output_tokens": 8192,
    "input_cost_per_token": 0,
    "output_cost_per_token": 0,
    "litellm_provider": "openai",
    "mode": "chat",
    "supports_function_calling": true,
    "supports_vision": true
  }
}
```

**Enable:** Aider edits via text edit formats, so tool calling isn't load-bearing for the core loop — set a good `edit_format` (e.g. `diff`) and, optionally in `.aider.model.settings.yml`, `weak_model_name: openai/di-saver` for cheap commit messages. Vision turns on with `supports_vision: true` in the metadata. **Gotcha:** the metadata key must be the prefixed `openai/di-fusion`, and `litellm_provider` must be `openai`; costs are `0` here because billing is handled by DirectInference, not Aider.

Add a provider to `crush.json` / `.crush.json` (project root) or `~/.config/crush/crush.json`. Crush doesn't auto-discover models for a custom provider — a provider with no `models` is skipped — so declare each one:

```json
{
  "$schema": "https://charm.land/crush.json",
  "providers": {
    "directinference": {
      "name": "DirectInference",
      "type": "openai-compat",
      "base_url": "https://api.directinference.com/di/v1",
      "api_key": "$DIRECTINFERENCE_API_KEY",
      "models": [
        {
          "id": "di-fusion",
          "name": "DirectInference",
          "cost_per_1m_in": 0, "cost_per_1m_out": 0,
          "cost_per_1m_in_cached": 0, "cost_per_1m_out_cached": 0,
          "context_window": 200000, "default_max_tokens": 16000,
          "can_reason": true,
          "reasoning_levels": ["low", "medium", "high"],
          "default_reasoning_effort": "medium",
          "supports_attachments": true
        },
        {
          "id": "di-saver",
          "name": "DirectInference (saver)",
          "cost_per_1m_in": 0, "cost_per_1m_out": 0,
          "cost_per_1m_in_cached": 0, "cost_per_1m_out_cached": 0,
          "context_window": 200000, "default_max_tokens": 16000,
          "can_reason": true,
          "reasoning_levels": ["low", "medium", "high"],
          "default_reasoning_effort": "low",
          "supports_attachments": true
        },
        {
          "id": "di-max",
          "name": "DirectInference (max)",
          "cost_per_1m_in": 0, "cost_per_1m_out": 0,
          "cost_per_1m_in_cached": 0, "cost_per_1m_out_cached": 0,
          "context_window": 200000, "default_max_tokens": 16000,
          "can_reason": true,
          "reasoning_levels": ["low", "medium", "high"],
          "default_reasoning_effort": "high",
          "supports_attachments": true
        }
      ]
    }
  },
  "models": {
    "large": { "model": "di-max",   "provider": "directinference" },
    "small": { "model": "di-saver", "provider": "directinference" }
  }
}
```

Then `export DIRECTINFERENCE_API_KEY=llm_live_…`. **Enable:** use `type: "openai-compat"` (the most-tested path; `"openai"` targets the Responses API, which DirectInference now serves statelessly, so it also works); set `can_reason: true` plus `reasoning_levels` so reasoning shows, and `supports_attachments: true` for vision. The `cost_*` fields are required by the schema — `0` is valid (billing is handled by DirectInference). Tool calls are served for any id with no extra field. **Gotcha:** the `base_url` ends in `/di/v1`; `api_key` takes a `$ENV` reference (it is expanded at load, as are `${VAR}` and `$(cmd)` — keep `crush.json` trusted).

## Confirm it's working

Two signals prove the tool is actually reaching DirectInference and not silently falling back to a previous provider:

1. **The model echo** — responses carry back the exact id you configured (`di-fusion`).
2. **The header** — every DirectInference response carries `X-DI-Request-Type` (`pro`, `code`, `vision`, `document`, `long`, `json`, `reason`, or `flash`). It is the one customer-visible signal and only DirectInference sets it. Prove the base URL and key independently of any tool:

```bash
curl -s -o /dev/null -D - https://api.directinference.com/di/v1/chat/completions \
  -H "Authorization: Bearer llm_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "model": "di-fusion", "messages": [{ "role": "user", "content": "ping" }] }' \
  | grep -i x-di-request-type
# x-di-request-type: flash
```

The request also appears in [Traces](https://app.directinference.com/traces) and [Usage](https://docs.directinference.com/usage/); set `X-Title` (where a tool exposes custom headers) to give it its own line in [per-application usage](https://docs.directinference.com/usage/). To bias every request the tool sends toward cost or quality, send `X-DI-Effort` (`fast` … `max`) — details in [Effort](https://docs.directinference.com/effort/).

## Known limitations

- **Computer-use tools are not served.** Tools that expose a "Computer Use" toggle (Kilo Code, others) drive a provider's hosted computer-use tool, which DirectInference does not serve — leave it off. Client-defined function tools and the tools' own edit/command protocols are fully served.
- **`di-fusion` is a chat model, not a tab-autocomplete (FIM) model.** Where a tool has a separate inline-completion / autocomplete slot, point it at a dedicated completion endpoint rather than `di-fusion`.
- **Provider server-side tools stay on their origin.** Web search / grounding, code execution, and URL context are rejected with a descriptive `400`; keep those call sites on their original provider. The full served / not-served list is in [Capabilities](https://docs.directinference.com/capabilities/).

For the field-by-field reference behind any "custom provider" form, see [Custom providers](https://docs.directinference.com/custom-providers/); for the underlying SDK swaps these tools are built on, see [Frameworks](https://docs.directinference.com/frameworks/).