genmux API is now generally available. Read the docs →
genmux API

Blog

OpenCode with DeepSeek V4 through one genmux key

6 min readWritten by genmux API Team


OpenCode is a terminal coding agent that talks to any OpenAI-compatible backend, and genmux now serves the one thing an agent cannot live without: function calling. Since v0.4.0, POST /v1/chat/completions accepts tools, returns tool_calls, and streams delta.tool_calls the way OpenAI does — so OpenCode reads files, edits them and runs your tests through DeepSeek V4 the same way it would through any hosted model. This post is the setup, the model choice, and an honest cost estimate for a session.

The config block

Drop this into opencode.json in your project (or ~/.config/opencode/opencode.json for every project):

json
{
  "$schema": "https://opencode.ai/config.json",
  "provider": {
    "genmux": {
      "npm": "@ai-sdk/openai-compatible",
      "name": "genmux",
      "options": { "baseURL": "https://api.genmux.tech/v1", "apiKey": "{env:GENMUX_API_KEY}" },
      "models": {
        "deepseek-v4-flash": { "name": "DeepSeek V4 Flash" },
        "deepseek-v4-pro": { "name": "DeepSeek V4 Pro" },
        "qwen3-max": { "name": "Qwen3-Max" }
      }
    }
  }
}

Then export the key and start:

bash
export GENMUX_API_KEY=nb_live_...
opencode --model genmux/deepseek-v4-flash
# or one-shot, for scripts and CI:
opencode run --model genmux/deepseek-v4-flash "add a --json flag to cmd/report and update the README"

{env:GENMUX_API_KEY} keeps the key out of the file, which matters if opencode.json is committed. The models map is only what shows up in OpenCode's picker — any of the seven tool-capable ids works as genmux/<id> once listed there.

Flash or Pro

Both V4 models take tools, both have a 1M-token context and both can think (reasoning_effort maps to thinking on and off). The difference is price and depth:

ModelUse it forPrice per 1M tokens (in / out)
deepseek-v4-flashEveryday agent loops: reading code, small edits, running tests, commit messages$0.198 / $0.594
deepseek-v4-proHard refactors, cross-file changes, "why does this test fail" investigations$0.594 / $1.782

A practical pattern: run Flash as the default and switch to Pro with /model when the agent starts going in circles. Flash scored 19/20 and Pro 20/20 on our agent-style tool eval (details in the emulated function calling post), so the default is not a compromise on reliability — it is a compromise on how far the model reasons before acting. qwen3-max is in the block above because it adds vision, which is useful when a task starts with a screenshot.

What tool calling looks like from OpenCode's side

OpenCode ships around twenty tools — read, write, edit, bash, glob, grep, list, webfetch, a todo list and a few more — and sends every one of them as a JSON schema in the tools array on every request. genmux renders those definitions into the system prompt, streams the model's reply back, and turns the model's call block into a proper delta.tool_calls chunk with finish_reason: "tool_calls". OpenCode never sees the difference: it gets a tool call, runs it locally, appends a role: tool message with the result, and asks again.

A single turn on the wire, abbreviated:

→ POST /v1/chat/completions  {"model":"deepseek-v4-flash","stream":true,"tools":[…20 tools…],"messages":[…]}
← data: {"choices":[{"delta":{"tool_calls":[{"index":0,"id":"call_7f…","type":"function","function":{"name":"read","arguments":"{\"filePath\":\"cmd/report/main.go\"}"}}]}}]}
← data: {"choices":[{"delta":{},"finish_reason":"tool_calls"}],"usage":{"prompt_tokens":6814,"completion_tokens":31,"total_tokens":6845,"cost":0.109,"cost_source":"estimated"}}
← data: [DONE]
→ POST … messages now end with {"role":"tool","tool_call_id":"call_7f…","content":"package main\n…"}

Two properties are worth knowing. Models usually emit one call per turn rather than a parallel batch; OpenCode loops, so a five-file change is five short round trips rather than one long one. And the text a model writes before a call ("Let me read the file first") is streamed as ordinary content, so the TUI keeps narrating while the call is being assembled.

What a session costs

The tool definitions are not free: they are rendered as text, so they count as prompt tokens on every request — about 40 tokens per tool plus its schema, roughly 1.5K tokens per request for OpenCode's set. The bigger driver, though, is conversation history: every request resends everything read so far.

Take a realistic session — 40 requests, prompts growing from 3K to 25K tokens as files get read (average 12K), replies averaging 400 tokens. That is 480K prompt tokens and 16K completion tokens:

ModelPrompt (480K)Completion (16K)SessionOf which tools (60K)
deepseek-v4-flash$0.095$0.010≈ $0.105$0.012
deepseek-v4-pro$0.285$0.029≈ $0.314$0.036
qwen3-max$0.518$0.086≈ $0.605$0.065

In credits that is 8.4 credits≈ $0.10Flash, 40 requests versus 25.1 credits≈ $0.31Pro, 40 requests at the Scale pack rate ($0.0125 per credit). The tool overhead lands at roughly a cent on Flash — it is the history that dominates, which is why /compact (or starting a fresh session per task) saves more money than trimming tools ever would.

Troubleshooting

400 tools_unsupported — the model id is one of the three that do not take tools (qwen3-6-flash, doubao-seed-2-lite, doubao-seed-2-mini). Function calling is emulated, and it is switched on only where the eval passed: the two V4 models, deepseek-v3, qwen3-7-max, qwen3-max, doubao-seed-2-pro and doubao-seed-2-code. GET /v1/models shows the flag as params_schema.tools.

The model answered in prose instead of calling a tool. tool_choice: required and a named function are instructions the model can ignore — small models sometimes do. OpenCode uses auto, so in practice this shows up as an extra "please go ahead" from you, not as a broken loop. Pro ignored it 0 times in 20 in our run.

max_tokens complaints. OpenCode sends max_tokens of 32K and more for every model. genmux clamps it to the model's max_output_tokens instead of rejecting the request, so there is nothing to configure. The default reply budget on tool models is 8192 tokens, which covers a whole-file rewrite.

A call arrived as text. If a reply contains a raw <function_call> block instead of a tool card, the parser could not decode it — open an issue with the model id and the reply; we track a malformed counter per model and extend the parser when a model drifts.

Getting started

Grab a key from the dashboard, paste the block above, and opencode --model genmux/deepseek-v4-flash is the whole migration. Aider, Cline, Roo Code and Continue work the same way — Aider is covered in its own post, and the short version for every client is on the coding agents page. The full parameter reference for tools, tool_choice and response_format is in the docs.

About the author

Written by genmux API Team — genmux API.

More about us