Skip to content

Client Configurations

Pre-built configurations for popular coding CLIs. Every config here assumes:

  • Endpoint: https://ellm.nrp-nautilus.io/v1, Anthropic-compatible at /anthropic
  • Token: the value from the LLM token page, passed in Authorization: Bearer … or whatever the client’s API-key field expects

API Access covers getting a token, and cache_salt for tenants whose prompts should not be cached across users.

VS Code

https://code.visualstudio.com

VS Code reaches NRP models through Chat: Manage Language Models, which adds them to the Copilot Chat model picker.

  1. Command Palette (Ctrl+Shift+P / Cmd+Shift+P) → Chat: Manage Language Models
  2. Add ModelsCustom Endpoint
  3. Endpoint URL: https://ellm.nrp-nautilus.io/v1/chat/completions

The apiKey below uses VS Code’s ${input:...} substitution, so VS Code asks for your token the first time you use a model and stores it securely from then on.

Paste into the Add Models prompt 3 models
{
"name": "NRP",
"vendor": "customendpoint",
"apiKey": "${input:chat.lm.secret.2630e22e}",
"apiType": "chat-completions",
"models": [
{
"id": "glm-5",
"name": "glm-5",
"url": "https://ellm.nrp-nautilus.io/v1/chat/completions",
"toolCalling": true,
"vision": false,
"maxInputTokens": 1048576,
"maxOutputTokens": 100000
},
{
"id": "qwen3",
"name": "qwen3",
"url": "https://ellm.nrp-nautilus.io/v1/chat/completions",
"toolCalling": true,
"vision": true,
"maxInputTokens": 1000000,
"maxOutputTokens": 100000
},
{
"id": "kimi",
"name": "kimi",
"url": "https://ellm.nrp-nautilus.io/v1/chat/completions",
"toolCalling": true,
"vision": true,
"maxInputTokens": 131072,
"maxOutputTokens": 100000
}
]
}

omp

https://omp.sh (oh-my-pi), whose custom-model reference covers the fields below.

~/.omp/agent/models.yml 2 models
providers:
nrp:
baseUrl: https://ellm.nrp-nautilus.io/v1
api: openai-completions
apiKey: <YOUR_API_KEY>
models:
- id: kimi
name: kimi
reasoning: true
supportsTools: true
input: [text, image]
contextWindow: 131072
omitMaxOutputTokens: true
- id: qwen3-small
name: qwen3-small
reasoning: true
thinking:
mode: effort
efforts: [low, medium, xhigh]
defaultLevel: xhigh
supportsTools: true
input: [text, image]
contextWindow: 1000000
omitMaxOutputTokens: true

OpenCode

https://github.com/anomalyco/opencode

Ctrl+PSwitch models finds NRP; Switch model variant picks a thinking level. Either set OPENAI_API_KEY or replace {env:OPENAI_API_KEY} with the key itself.

~/.config/opencode/opencode.jsonc qwen3 as the default model

"model" takes a provider/model id, so the default is NRP/qwen3. "small_model" is what OpenCode uses for side tasks such as session titles — qwen3-small keeps those off the largest GPUs. An editor validating against opencode.ai/config.json underlines both values, because the schema autocompletes from the models.dev catalog and cannot know about a custom provider; OpenCode resolves them from the provider block.

{
"$schema": "https://opencode.ai/config.json",
"model": "NRP/qwen3",
"small_model": "NRP/qwen3-small",
"provider": {
"NRP": {
"npm": "@ai-sdk/openai-compatible",
"name": "NRP",
"options": {
"baseURL": "https://ellm.nrp-nautilus.io/v1",
"apiKey": "{env:OPENAI_API_KEY}"
},
"models": {
"qwen3": {
"name": "qwen3",
"limit": {
"context": 1000000,
"output": 65536
},
"modalities": {
"input": ["text", "image", "video"],
"output": ["text"]
},
"tool_call": true,
"reasoning": true,
"attachment": true
},
"qwen3-small": {
"name": "qwen3-small",
"limit": {
"context": 1000000,
"output": 65536
},
"tool_call": true,
"reasoning": true
}
}
}
}
}

"limit.output" caps a single response, not the context window. Never set it close to or above "context".

~/.config/opencode/opencode.jsonc every model in the catalog

Nine models, each with its context window, modalities, and thinking controls. qwen3-embedding is an embedding model rather than a chat model, so it is not listed.

Thinking is selected per model with Ctrl+PSwitch model variant, and the key differs by model: qwen3 and qwen3-small take a top-level reasoning_effort (low, medium, or xhigh, the default) and turn thinking off through chat_template_kwargs.enable_thinking; glm-5 and deepseek-v4-flash take reasoning_effort inside chat_template_kwargs (low, high, or max, the default), and only deepseek-v4-flash can turn thinking off, with thinking: false. Staying on a model’s default variant leaves the server-side default in place. gemma-small is served without a reasoning parser, so its thinking arrives inline in the answer rather than as a separate reasoning block.

{
"$schema": "https://opencode.ai/config.json",
"model": "NRP/qwen3",
"small_model": "NRP/qwen3-small",
"provider": {
"NRP": {
"npm": "@ai-sdk/openai-compatible",
"name": "NRP",
"options": {
"baseURL": "https://ellm.nrp-nautilus.io/v1",
"apiKey": "{env:OPENAI_API_KEY}"
},
"models": {
"qwen3": {
"name": "qwen3",
"limit": {
"context": 1000000,
"output": 65536
},
"modalities": {
"input": ["text", "image", "video"],
"output": ["text"]
},
"tool_call": true,
"reasoning": true,
"attachment": true,
"variants": {
"low": {
"reasoning_effort": "low"
},
"medium": {
"reasoning_effort": "medium"
},
"instruct": {
"chat_template_kwargs": {
"enable_thinking": false
}
}
}
},
"qwen3-small": {
"name": "qwen3-small",
"limit": {
"context": 1000000,
"output": 65536
},
"modalities": {
"input": ["text", "image", "video"],
"output": ["text"]
},
"tool_call": true,
"reasoning": true,
"attachment": true,
"variants": {
"low": {
"reasoning_effort": "low"
},
"medium": {
"reasoning_effort": "medium"
},
"instruct": {
"chat_template_kwargs": {
"enable_thinking": false
}
}
}
},
"glm-5": {
"name": "glm-5",
"limit": {
"context": 1048576,
"output": 65536
},
"modalities": {
"input": ["text"],
"output": ["text"]
},
"tool_call": true,
"reasoning": true,
"variants": {
"low": {
"chat_template_kwargs": {
"reasoning_effort": "low"
}
},
"high": {
"chat_template_kwargs": {
"reasoning_effort": "high"
}
}
}
},
"deepseek-v4-flash": {
"name": "deepseek-v4-flash",
"limit": {
"context": 1048576,
"output": 65536
},
"modalities": {
"input": ["text", "image"],
"output": ["text"]
},
"tool_call": true,
"reasoning": true,
"attachment": true,
"variants": {
"low": {
"chat_template_kwargs": {
"reasoning_effort": "low"
}
},
"high": {
"chat_template_kwargs": {
"reasoning_effort": "high"
}
},
"instruct": {
"chat_template_kwargs": {
"thinking": false
}
}
}
},
"kimi": {
"name": "kimi",
"limit": {
"context": 131072,
"output": 32768
},
"modalities": {
"input": ["text", "image", "video"],
"output": ["text"]
},
"tool_call": true,
"reasoning": true,
"attachment": true
},
"minimax-m2": {
"name": "minimax-m2",
"limit": {
"context": 204800,
"output": 32768
},
"modalities": {
"input": ["text"],
"output": ["text"]
},
"tool_call": true,
"reasoning": true
},
"gpt-oss": {
"name": "gpt-oss",
"limit": {
"context": 131072,
"output": 32768
},
"modalities": {
"input": ["text"],
"output": ["text"]
},
"tool_call": true,
"reasoning": true
},
"gemma": {
"name": "gemma",
"limit": {
"context": 262144,
"output": 32768
},
"modalities": {
"input": ["text", "image", "video"],
"output": ["text"]
},
"tool_call": true,
"reasoning": true,
"attachment": true,
"variants": {
"instruct": {
"chat_template_kwargs": {
"enable_thinking": false
}
}
}
},
"gemma-small": {
"name": "gemma-small",
"limit": {
"context": 262144,
"output": 32768
},
"modalities": {
"input": ["text", "image", "video"],
"output": ["text"]
},
"tool_call": true,
"reasoning": true,
"attachment": true,
"variants": {
"instruct": {
"chat_template_kwargs": {
"enable_thinking": false
}
}
}
}
}
}
}
}

To pin options that should apply on every request to a model — cache_salt, preserve_thinking, or a fixed effort — add an "options" block alongside "name". Anything in "options" is sent with each request; anything in "variants" only while that variant is selected:

"deepseek-v4-flash": {
"name": "deepseek-v4-flash",
"options": {
"chat_template_kwargs": {
"thinking": true,
"preserve_thinking": true
}
},
// ...
},

Crush

https://github.com/charmbracelet/crush

Search for NRP in the model list once the config is in place. See also Andrea Zonca’s writeup, Configure NRP LLM with OpenCode and Crush.

~/.config/crush/crush.json 1 model — repeat the block per model

Adjust "default_max_tokens" as needed; never push it close to or above "context_window". Use "enable_thinking" or "thinking" as the model requires.

{
"$schema": "https://charm.land/crush.json",
"options": {
"disable_metrics": true,
"disable_provider_auto_update": false,
"debug": false,
"debug_lsp": false,
"attribution": {
"trailer_style": "none",
"generated_with": false
}
},
"mcp": {},
"providers": {
"nrp": {
"name": "NRP",
"type": "openai-compat",
"base_url": "https://ellm.nrp-nautilus.io/v1",
"api_key": "<LLM_API_KEY>",
"models": [
{
"id": "kimi",
"name": "kimi",
"context_window": 131072,
"default_max_tokens": 32768,
"modalities": {
"input": ["text", "image", "video"],
"output": ["text"]
}
}
]
}
}
}

For "chat_template_kwargs" or other "extra_body" parameters, add this within each model, at the same level as "models":

"extra_body": {
"chat_template_kwargs": {
"thinking": true,
"preserve_thinking": true
}
},

pi

https://pi.dev

Ctrl-L switches models — search for NRP, or type /model modelname.

~/.pi/agent/models.json 2 models

apiKey accepts the literal key, an environment variable name (OPENAI_API_KEY), or a shell command (!decrypt_key NRP).

{
"providers": {
"nrp": {
"baseUrl": "https://ellm.nrp-nautilus.io/v1",
"api": "openai-completions",
"apiKey": "<YOUR_API_KEY>",
"models": [
{
"id": "kimi",
"name": "kimi",
"input": ["text", "image"],
"contextWindow": 131072,
"reasoning": true
},
{
"id": "qwen3-small",
"name": "qwen3-small",
"input": ["text", "image"],
"contextWindow": 1000000,
"reasoning": true,
"compat": {
"thinkingFormat": "qwen-chat-template",
"supportsDeveloperRole": false
}
}
]
}
}
}

Kimi CLI

https://github.com/MoonshotAI/kimi-cli

~/.kimi/config.toml 1 model
default_model = "kimi"
default_thinking = true
default_yolo = false
default_plan_mode = false
show_thinking_stream = true
merge_all_available_skills = true
telemetry = false
[providers.nrp]
type = "openai_legacy"
base_url = "https://ellm.nrp-nautilus.io/v1"
api_key = "<YOUR_API_KEY>"
[models.kimi]
provider = "nrp"
model = "kimi"
max_context_size = 131072
capabilities = ["thinking", "image_in", "video_in"]
[background]
max_running_tasks = 4
keep_alive_on_exit = false
agent_task_timeout_s = 3600

Claude Code

https://github.com/anthropics/claude-code

Claude Code talks to NRP through the Anthropic-compatible endpoint at /anthropic.

~/.claude/settings.json any model, by name
{
"env": {
"ANTHROPIC_BASE_URL": "https://ellm.nrp-nautilus.io/anthropic",
"ANTHROPIC_AUTH_TOKEN": "<llm-token>",
"ANTHROPIC_MODEL": "<name-of-nrp-model>",
"ANTHROPIC_DEFAULT_OPUS_MODEL": "<name-of-nrp-model>",
"ANTHROPIC_DEFAULT_SONNET_MODEL": "<name-of-nrp-model>",
"ANTHROPIC_DEFAULT_HAIKU_MODEL": "<name-of-nrp-model>",
"ANTHROPIC_DEFAULT_FABLE_MODEL": "<name-of-nrp-model>",
"CLAUDE_CODE_SUBAGENT_MODEL": "<name-of-nrp-model>",
"ENABLE_TOOL_SEARCH": "false",
"CLAUDE_CODE_AUTO_COMPACT_WINDOW": "<context-size-of-model>",
"CLAUDE_CODE_EFFORT_LEVEL": "max",
"CLAUDE_STREAM_IDLE_TIMEOUT_MS": "3000000",
"CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1",
"CLAUDE_CODE_DISABLE_FEEDBACK_SURVEY": "1",
"CLAUDE_CODE_ENABLE_TELEMETRY": "0",
"DISABLE_TELEMETRY": "1",
"API_TIMEOUT_MS": "3000000",
"CLAUDE_CODE_MAX_RETRIES": "10"
}
}

Copilot CLI

https://github.com/github/copilot-cli

Environment variables any model, by name

Set COPILOT_PROVIDER_MAX_PROMPT_TOKENS to the model’s context length and COPILOT_PROVIDER_MAX_OUTPUT_TOKENS to a smaller value — roughly 1/16 to 1/4 of the context, depending on the task.

Terminal window
COPILOT_PROVIDER_BASE_URL="https://ellm.nrp-nautilus.io/v1"
COPILOT_PROVIDER_KEY="<llm-token>"
COPILOT_MODEL="<name-of-nrp-model>"
COPILOT_PROVIDER_MAX_PROMPT_TOKENS="<context-length>"
COPILOT_PROVIDER_MAX_OUTPUT_TOKENS="<max-output>"
U.S. National Science Foundation

This work was supported in part by National Science Foundation (NSF) awards CNS-1730158, ACI-1540112, ACI-1541349, OAC-1826967, OAC-2112167, CNS-2100237, CNS-2120019.