
Adapted from our opencode custom-provider tutorial on dev.to.
How do you use DeepSeek V4.1 Flash and Kimi K3 in opencode through a custom OpenAI-compatible provider?
You add a provider block to opencode.json that uses the @ai-sdk/openai-compatible package, set its baseURL to https://cloudgpu.app/v1, read the key from {env:CGW_API_KEY}, and list deepseek-v4.1-flash and kimi-k3 under models; opencode then offers them as cloudgpu/deepseek-v4.1-flash and cloudgpu/kimi-k3.
The rest of this post is the copy-paste version of that sentence, with each opencode behaviour traced to the opencode docs.
Disclosure: I work on CloudGPU.
What this is based on
We ran this provider block in opencode on 2026-10-26 and both headline models replied. Everything else below about opencode itself (the /models screen, /connect, the limit block, the default model) follows the opencode docs, so read "according to the opencode docs" literally.
The four values at a glance
| What | Value |
|---|---|
| npm package | @ai-sdk/openai-compatible |
| Base URL | https://cloudgpu.app/v1 (keep the /v1) |
| API key | a cgw-sk-... key, exported as CGW_API_KEY |
| Model IDs | deepseek-v4.1-flash, kimi-k3, kimi-k2.6, glm-5.3-flash |
The model IDs were checked against the model table on the live API page on 2026-10-27.
Step 1: Export the key
According to the opencode docs, config files support {env:VARIABLE_NAME} substitution, so the key can stay out of the JSON file:
export CGW_API_KEY="cgw-sk-..." # put this in ~/.bashrc or ~/.zshrc to keep it
Step 2: Add the custom provider to opencode.json
Put this in opencode.json in your project root. According to the opencode docs, the global file lives at ~/.config/opencode/opencode.json, the project file overrides it, and "configuration files are merged together, not replaced", so the same block also works globally.
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"cloudgpu": {
"npm": "@ai-sdk/openai-compatible",
"name": "CloudGPU",
"options": {
"baseURL": "https://cloudgpu.app/v1",
"apiKey": "{env:CGW_API_KEY}"
},
"models": {
"deepseek-v4.1-flash": { "name": "DeepSeek V4.1 Flash" },
"kimi-k3": { "name": "Kimi K3" },
"kimi-k2.6": { "name": "Kimi K2.6" },
"glm-5.3-flash": { "name": "GLM-5.3 Flash" }
}
}
}
}
Field guide, according to the opencode docs (Providers → Custom provider):
cloudgpuis the provider ID. You choose it, and it becomes the prefix incloudgpu/<model>.npm: "@ai-sdk/openai-compatiblefor OpenAI-compatible providers (for/v1/chat/completions)".options.baseURLis the API endpoint;options.apiKeyis optional "if not using auth".models: each key must be the exact ID the endpoint serves.nameis only the label in the UI.
Only want the two headline models? Delete the kimi-k2.6 and glm-5.3-flash lines.
Alternative: /connect instead of apiKey
According to the opencode docs, you can run /connect, scroll to Other, enter the provider ID (cloudgpu), and paste the key. The docs warn that this "only stores a credential", so you still need the provider block, just without the apiKey line.
Step 3: Choose a model
Check from the command line that opencode sees the provider. According to the opencode CLI docs, opencode models [provider] lists models in provider/model form:
opencode models cloudgpu
The output should look like this (illustrative, not a captured run):
cloudgpu/deepseek-v4.1-flash
cloudgpu/glm-5.3-flash
cloudgpu/kimi-k2.6
cloudgpu/kimi-k3
Then pick one with -m in provider/model form. The dot in v4.1 is part of the ID:
opencode -m cloudgpu/deepseek-v4.1-flash
opencode run -m cloudgpu/kimi-k3 "Explain what this repo's main entry point does"
Inside the TUI, according to the opencode docs, you "run the /models command and your custom provider and models will appear in the selection list". To make one the default, the opencode models docs use a top-level "model" key in provider_id/model_id form, here "model": "cloudgpu/deepseek-v4.1-flash".
Which one for what? There are no benchmarks here, so try them on your own repo. All four are in the config, so comparing them takes one switch.
Step 4: Sanity-check the endpoint outside opencode
If opencode misbehaves, take it out of the loop. First, list the IDs your key can use:
curl https://cloudgpu.app/v1/models \
-H "Authorization: Bearer $CGW_API_KEY"
Without the header this returns 401 with missing_authorization, which is expected. The API docs list it as "No Authorization header." (As of 2026-10-27, see /api/docs.)
Then send one chat request with the openai Python package:
# pip install openai
import os
from openai import OpenAI
client = OpenAI(
base_url="https://cloudgpu.app/v1",
api_key=os.environ["CGW_API_KEY"],
)
for model in ["deepseek-v4.1-flash", "kimi-k3"]:
resp = client.chat.completions.create(
model=model,
messages=[{"role": "user", "content": "Reply with: ok"}],
)
print(model, resp.choices[0].finish_reason, repr(resp.choices[0].message.content))
If a model comes back with empty or None content and finish_reason is "length", the reply ran out of output tokens: leave max_tokens unset or set it higher.
Troubleshooting
1. The model doesn't show up, or you get 400 model_not_found. The keys under models must match the IDs from GET /v1/models character for character. Watch for short aliases (deepseek-flash), a missing version segment (deepseek-v4-flash vs deepseek-v4.1-flash) and capital letters. Also check that the provider ID is the same in all three places: the provider key, the ID typed into /connect (if you used it), and the cloudgpu/ prefix. According to the opencode docs, opencode auth list shows which credentials are stored.
2. The base URL is wrong. The AI SDK docs describe baseURL as the URL prefix for API calls, and their example sends chat requests to <baseURL>/chat/completions:
https://cloudgpu.app/v1 correct
https://cloudgpu.app missing /v1
https://cloudgpu.app/v1/chat/completions too specific
3. An error asking for an Authorization: Bearer header. According to the opencode docs, an unset {env:...} variable "will be replaced with an empty string", so the request goes out without a key. Check the variable in the shell you launch opencode from:
echo "${CGW_API_KEY:+set}" # prints "set" if the variable is present
After editing ~/.bashrc or ~/.zshrc, open a new terminal or source the file. IDE terminals and desktop launchers may not read your shell profile.
4. Empty replies. Check finish_reason as in Step 4. In opencode, the per-model knob the docs describe is the optional limit block (limit.context = "maximum input tokens the model accepts", limit.output = "maximum tokens the model can generate"). If you add it, use real values from the model provider's docs, not guesses.
5. 401 invalid_api_key, 402 insufficient_balance or 429 rate_limit_exceeded. The CloudGPU API docs describe them like this (As of 2026-10-27, see /api/docs):
401invalid_api_key: "Key unknown or revoked."402insufficient_balance: "Top up."429rate_limit_exceeded: "Per-minute or per-day limit hit."
The documented rate limits are "Per API key, per minute: 100 requests" and "Per account, per day: 10,000 requests", and the docs add: "These defaults exist to stop abuse during the public beta. Contact us if you need them raised — happy to do it for any genuine workload, just need to know the use case."
Paying for it
Usage is billed per token from a prepaid balance (card, Alipay, Google Pay, Apple Pay or USDT). Keys, the model table and per-model rates are on the CloudGPU API page.
FAQ
Which npm package does an OpenAI-compatible custom provider use in opencode?
@ai-sdk/openai-compatible. According to the opencode docs, it's the package for providers that use /v1/chat/completions.
What base URL goes in options.baseURL?
https://cloudgpu.app/v1, with /v1 and nothing after it.
Can the model ID contain a dot, like deepseek-v4.1-flash?
Yes. Use it exactly as listed, in provider/model form: -m cloudgpu/deepseek-v4.1-flash.
Why does a model sometimes return an empty answer?
Check finish_reason. If it is "length", the reply ran out of output tokens; raise max_tokens or leave it unset.
Do I have to put the API key in opencode.json?
No. Use "apiKey": "{env:CGW_API_KEY}" so the file only holds a reference. According to the opencode docs, /connect can store the credential instead.
Are there rate limits? Yes. The API docs list "Per API key, per minute: 100 requests" and "Per account, per day: 10,000 requests", and say: "These defaults exist to stop abuse during the public beta. Contact us if you need them raised — happy to do it for any genuine workload, just need to know the use case." As of 2026-10-27, see /api/docs.
Comments
Join the discussionNo comments yet. Be the first to comment.



