This fork documents how to use OpenCode Go models from the desktop model picker, exactly like switching models in the stock UI.
Two pieces are needed:
- The bundled
api-key-model-visibilityLinux feature, which lets the desktop model picker show every model reported by an API-key authenticated provider. - A local LiteLLM bridge. Current Codex CLI
builds only speak the OpenAI Responses API (
wire_api = "responses";"chat"was removed, see openai/codex#7782), while most OpenCode Go models only accept/chat/completions. The bridge translates Responses to Chat Completions locally.
Create a key in the OpenCode console and verify it:
curl -s -H "Authorization: Bearer $OPENCODE_API_KEY" \
https://opencode.ai/zen/go/v1/modelsStore it where GUI apps can see it (systemd-based desktops), then log out and back in once:
mkdir -p ~/.config/environment.d
printf 'OPENCODE_API_KEY=<your key>\n' > ~/.config/environment.d/50-opencode.conf
chmod 600 ~/.config/environment.d/50-opencode.confNever commit the key anywhere.
Install LiteLLM. FastAPI must be pinned to 0.140.6 or older because newer
FastAPI removed get_flat_dependant, which the proxy still imports
(see BerriAI/litellm#35763):
uv tool install "litellm[proxy]" --with "fastapi==0.140.6"Generate ~/.config/litellm/opencode.yaml with one entry per model. The
openai/chat_completions/<model> prefix forces LiteLLM's Responses to Chat
Completions bridge instead of passing /responses through to the provider:
model_list:
- model_name: kimi-k3
litellm_params:
model: openai/chat_completions/kimi-k3
api_base: https://opencode.ai/zen/go/v1
api_key: os.environ/OPENCODE_API_KEY
# repeat for every model id returned by /models
litellm_settings:
drop_params: trueRun it as a systemd user service, ~/.config/systemd/user/litellm-opencode.service:
[Unit]
Description=LiteLLM bridge for OpenCode Go (Responses to Chat Completions)
After=network-online.target
[Service]
EnvironmentFile=%h/.config/environment.d/50-opencode.conf
ExecStart=%h/.local/bin/litellm --config %h/.config/litellm/opencode.yaml --host 127.0.0.1 --port 4000
Restart=on-failure
RestartSec=5
[Install]
WantedBy=default.targetsystemctl --user enable --now litellm-opencode.serviceCodex only trusts a provider model catalog served in its own ModelsResponse
schema ({"models": [ModelInfo, ...]}, snake_case, see
codex-rs/protocol/src/openai_models.rs); a plain OpenAI /v1/models list
fails to deserialize and Codex silently falls back to its built-in catalog.
Run a small shim in front of the bridge that serves /v1/models in Codex
format (templating each entry from a known-good ~/.codex/models_cache.json
entry) and proxies everything else to LiteLLM. This repo's reference shim
lives at port 4001 with LiteLLM on 4000; run it as a second systemd user
service. Point base_url at the shim, not at LiteLLM directly.
Also enable the remote models feature in ~/.codex/config.toml:
[features]
remote_models = trueWith ChatGPT-account auth active, Codex replaces the picker catalog with the
provider list, so the picker shows exactly the OpenCode Go models while the
provider is active; remove the top-level model_provider line to go back to
the stock picker.
Add to ~/.codex/config.toml:
model_provider = "opencode_go"
model = "kimi-k3"
[model_providers.opencode_go]
name = "OpenCode Go"
base_url = "http://127.0.0.1:4001/v1"
env_key = "OPENCODE_API_KEY"
wire_api = "responses"The top-level model_provider/model keys make OpenCode Go the active host;
the visibility feature only applies to API-key authenticated hosts.
Create linux-features/features.json (gitignored, local-only):
{
"enabled": [
"api-key-model-visibility"
]
}make bootstrap-nativeTo install this variant side by side with a stock codex-desktop or the
official app, repackage dist/deb-root under a different package name and
/opt path, drop the updater files, and rename the desktop entry, launcher,
icon, and AppArmor profile accordingly. Note that all variants share the same
upstream Codex user profile: chat history carries over, but only one can
run at a time because of the upstream single-instance lock.
OPENCODE_API_KEY=<key> /opt/codex-desktop/resources/codex exec \
--skip-git-repo-check -c model_provider=opencode_go -c model=kimi-k3 \
'Reply with exactly: OPENCODE BRIDGE OK'Then launch the app and pick any OpenCode Go model (for example minimax-m3,
kimi-k3, glm-5.2) from the model picker.
- The picker can list models your plan cannot use; the provider rejects those at request time.
- ChatGPT-account hosts keep the upstream filtering rules; only API-key hosts are affected.
- A handful of OpenCode Go models (the DeepSeek v4 family, gpt-5.6-luna,
grok-4.5 at the time of writing) also accept
/responsesnatively, but the bridge covers the whole catalog uniformly.
- LiteLLM 1.97.0's Responses streaming transformer crashes with
IndexError: list index out of rangeon provider chunks with emptychoices(for example MiniMax usage-only chunks), which surfaces in the app as "stream disconnected before completion". Until the fix lands upstream, guard the threechunk.choices[0]accesses inlitellm/responses/litellm_completion_transformation/streaming_iterator.py(_ensure_output_item_for_chunk,_is_reasoning_end,_get_delta_string_from_streaming_choices) to return early whenchoicesis empty. - The shim can serve BOTH catalogs at once: it merges the ChatGPT
subscription catalog (fetched from the ChatGPT backend with the tokens the
app maintains in
~/.codex/auth.json) with the OpenCode Go list, and routes each/responsescall by model slug: ChatGPT-catalog models go tochatgpt.com/backend-api/codexwith the subscription auth, everything else goes to the LiteLLM bridge. Picking a GPT model or an OpenCode model in the same picker both just work.contrib/opencode/codex-hostremains as a fallback to flip the whole host if ever needed.
With a ChatGPT account signed in, Codex pins a thread to the ChatGPT backend
once a subscription model has been used in it, bypassing the configured
provider. Switching that thread to an OpenCode model then fails with "model
is not supported when using Codex with a ChatGPT account". Switch models
freely within one family inside a chat, but start a new chat when moving
between a subscription model and an OpenCode model. Do not set
forced_login_method = "api" to work around this: it logs the app out and
deletes ~/.codex/auth.json.