The PRD's first acceptance gate now holds: grep -RinE '\bx\.ai\b|grok' crates/ --include='*.rs' → 0 matches (exempt: NOTICE and third-party license archives, README provenance, and the required 'Based on Grok Build Open Source' attribution, now sourced from version_attribution.txt). Wire-visible renames (both sides in this repo, changed in lockstep): - Auth method id 'grok.com' → 'kimi-code' (AuthMethodKind::KimiCode). - Every x.ai/* and _x.ai/* ACP ext method and meta key → kigi/* / _kigi/* (~200 names; grokShell → kigiShell). Session-file replay keeps a read-side alias for the legacy '_x.ai/session/update' method so existing updates.jsonl histories load; writes emit only the new name (both directions test-pinned). - Agent types grok-build* → kigi* with a documented legacy-prefix alias at resolution time so persisted sessions keep resolving. - ToolNamespace/BuiltinAgentName GrokBuild* → Kigi* (wire snake_case kigi/kigi_concise/kigi_hashline; schema regenerated); grok_build implementation dirs renamed to kigi*. - x-grok-* headers → x-kigi-*, __GROK_* sentinels → __KIGI_*, themes grokday/groknight → kigiday/kiginight (old persisted values fall back to the default theme), web_fetch allowlist xAI hosts → kimi.com + moonshot platforms, changelog CDN → this repo, grok-build changelog archives deleted. - BYOK default endpoint removed: [endpoints] api_base_url is now truly optional with NO default — consumers fail fast with the flag name when unset (no silent x.ai egress). Mock harnesses inject it explicitly. - System-prompt identity fixed: 'released by xAI' → 'an unofficial community CLI for Kimi' (template + regenerated encrypted form). Also repaired pre-existing grok-era test debt found by the sweep: the stale trace_classify default-model pin, the grok-pager UA label test, pty-harness stale-binary reuse and non-hermetic moonshot routing (a PTY test could previously reach the real api.moonshot.cn), and the outdated oauth fixture scope key. Gates: §9 grep 0; fmt clean; workspace check/clippy 0/0 (-D warnings); FULL cargo test --workspace: 234 suites, 21,961 passed, 0 failed; deny advisories ok.
11 KiB
Custom Models
Kigi connects to custom model endpoints for alternative providers, self-hosted models, and overriding built-in settings. This guide explains how to select models, configure endpoints, and integrate third-party providers.
Default Models
By default, Kigi uses models hosted by SpaceXAI, and new sessions start with kigi. Default models require no configuration. Authenticate with kigi login or an API key, then start a session.
List all available models:
kigi models
Selecting a Model
CLI Flag
kigi -p "Hello" -m kigi
Slash Command
In the TUI, switch models during a session:
/model kigi
Or use the alias:
/m kigi
Model Picker (Ctrl+M)
Press Ctrl+M from the scrollback pane to open the model picker. It lists all available models, both built-in and custom, and lets you switch with a single keystroke. With the prompt focused, Ctrl+M toggles multiline input instead -- use /model to switch without leaving the prompt.
Config Default
Set a persistent default in ~/.kigi/config.toml:
[models]
default = "kigi"
Supported API Backends
Kigi supports three API backends. Set api_backend in your [model.*] config to choose which protocol the model uses:
| Value | API | Default |
|---|---|---|
"chat_completions" |
OpenAI Chat Completions (/v1/chat/completions) |
Yes |
"responses" |
OpenAI Responses (/v1/responses) |
|
"messages" |
Anthropic Messages (/v1/messages) |
When you omit api_backend, Kigi uses chat_completions.
To send provider-specific authentication or version headers -- for example, Anthropic's x-api-key -- use the extra_headers field described below. Kigi sends those headers verbatim with every request to the endpoint.
Configuring Custom Models
Add custom model endpoints in ~/.kigi/config.toml under [model.<name>] sections:
[model.my-model]
model = "model-id" # Model identifier sent to the API
base_url = "https://api.example.com/v1" # OpenAI-compatible endpoint
name = "Display Name" # Shown in the model picker
description = "Model description" # Optional description
api_key = "sk-..." # API key for this provider (optional)
env_key = "XAI_API_KEY" # Env var holding the API key (optional; string or array)
api_backend = "chat_completions" # "chat_completions", "responses", or "messages"
temperature = 0.7 # Sampling temperature
top_p = 0.95 # Nucleus sampling parameter
max_completion_tokens = 8192 # Maximum tokens per response
context_window = 128000 # Total context window in tokens
extra_headers = { "x-api-key" = "sk-..." } # Extra request headers, sent verbatim (optional)
Credential Resolution
Kigi resolves the API key in this order:
- The
api_keyfield in the model config - The environment variable(s) named by
env_key— a single string or an array of names. The first set, non-empty value wins (for exampleenv_key = ["ANTHROPIC_AUTH_TOKEN", "LC_ANTHROPIC_AUTH_TOKEN"]for SSHLC_*forwarding) - Your signed-in session token (from
kigi login), for a model with noapi_key/env_keyof its own - The
XAI_API_KEYenvironment variable (global fallback; Kigi also acceptsKIGI_CODE_XAI_API_KEYfor backward compatibility)
Context Window
The context_window value tells Kigi when to trigger auto-compaction. When you override a known model, Kigi inherits that model's context window. When you define a new model and omit context_window, Kigi defaults to 200,000 tokens, so set it explicitly to match your provider.
Global Default Headers
To apply the same headers to every model in the catalog -- built-in, prefetched from /v1/models, or custom -- set them once under the global [models] section instead of repeating them per model:
[models]
extra_headers = { "X-Request-Tags" = "team=example,env=prod" }
These act as a base for each model's inference requests. A per-model [model.<id>].extra_headers entry overrides the global default per key (matched case-insensitively): a key set on the model wins, while any global-only keys are still inherited by that model. Like the per-model field, they ride on that model's inference calls -- not on separate services such as image generation or video generation -- which makes them handy for attribution tags (for example, cost tracking) without re-declaring them whenever a new model appears.
Global Default Values
A few common per-model settings can also be set once under [models] as a default for every model. A per-model [model.<id>] value always wins; the global only fills in where a model (or the server's model list) left the field unset:
[models]
temperature = 0.7
top_p = 0.95
max_completion_tokens = 8192
max_retries = 8
inference_idle_timeout_secs = 600
stream_tool_calls = true
This is a small, fixed set of environment-wide knobs. Settings that identify a specific model (model, base_url, api_key, context_window, ...) cannot be defaulted this way, and a few settings with their own dedicated configuration -- auto-compaction ([session]), the system-prompt label ([agent]), and reasoning effort ([models].default_reasoning_effort) -- keep their existing homes.
Note on
stream_tool_calls: this one affects request shape, not just sampling. A few endpoints (some BYOK providers) expect it left unset; if a globalstream_tool_calls = truecauses problems for such a model, opt that model out withstream_tool_calls = falsein its[model.<id>]block.
Overriding Built-in Models
You can override specific fields of built-in models without redefining everything. Only specify the fields you want to change:
# Override only the API key for a default model
[model.kigi-build]
api_key = "my-api-key"
# Override temperature and add a custom API key
[model.kigi-build]
temperature = 0.5
api_key = "sk-custom"
When you override a built-in model, Kigi starts with the default configuration (including the correct base_url), then applies only the fields you specify. Unspecified fields inherit from the default.
Priority Order
- Your config (
[model.*]) -- highest priority - Prefetched models from remote
/v1/models - Hardcoded defaults -- lowest priority
Provider Examples
Anthropic (Claude)
Use Claude models directly via the Anthropic Messages API:
[model.claude-opus]
model = "claude-opus-4-6"
base_url = "https://api.anthropic.com/v1"
name = "Claude Opus 4.6"
api_backend = "messages"
context_window = 200000
extra_headers = { "x-api-key" = "sk-ant-...", "anthropic-version" = "2023-06-01" }
The messages backend uses the Anthropic Messages protocol. Anthropic authenticates with an x-api-key header rather than Authorization: Bearer, so pass your key through extra_headers, which Kigi sends verbatim.
OpenAI (Chat Completions)
[model.gpt-4o]
model = "gpt-4o"
base_url = "https://api.openai.com/v1"
name = "GPT-4o"
env_key = "OPENAI_API_KEY"
api_backend defaults to "chat_completions", so you don't need to set it explicitly for OpenAI.
OpenAI (Responses API)
If your provider supports the newer Responses API:
[model.gpt-4o-responses]
model = "gpt-4o"
base_url = "https://api.openai.com/v1"
name = "GPT-4o (Responses)"
api_backend = "responses"
env_key = "OPENAI_API_KEY"
Ollama (Local Models)
Run models locally with Ollama:
[model.ollama-codellama]
model = "codellama"
base_url = "http://localhost:11434/v1"
name = "CodeLlama (Ollama)"
Make sure Ollama is running (ollama serve) and the model is pulled (ollama pull codellama).
Together AI
[model.together-mixtral]
model = "mistralai/Mixtral-8x7B-Instruct-v0.1"
base_url = "https://api.together.xyz/v1"
name = "Mixtral 8x7B"
env_key = "TOGETHER_API_KEY"
Local OpenAI-Compatible Server
Any server that implements the OpenAI Chat Completions or Responses API:
[model.local-llama]
model = "llama-3.1-70b"
base_url = "http://localhost:8080/v1"
name = "Local Llama"
temperature = 0.8
Custom Models Endpoint
Point Kigi at a custom OpenAI-compatible /v1/models endpoint instead of the default. Use this when your models sit behind a corporate gateway or a self-hosted inference service.
Environment Variables
| Variable | Required | Description |
|---|---|---|
KIGI_MODELS_BASE_URL |
Yes | Base URL for inference. Kigi fetches the model list from {base_url}/models. |
XAI_API_KEY |
Yes | API key sent as Authorization: Bearer. Kigi also accepts KIGI_CODE_XAI_API_KEY. |
KIGI_MODELS_LIST_URL |
No | Override the model-list URL when it differs from {base_url}/models. |
Setup
export KIGI_MODELS_BASE_URL="https://api.acme.com/v1"
export XAI_API_KEY="xai-..."
kigi
Config File Alternative
[endpoints]
models_base_url = "https://api.acme.com/v1"
# Override only the API key for a specific model
[model.kigi-build]
api_key = "my-api-key"
When you use [endpoints] with partial model overrides, Kigi inherits the base_url from the endpoints config, so you do not need to specify it in each [model.*] section.
Auth Behavior
When you set models_base_url, Kigi uses API key auth (Authorization: Bearer) instead of session auth. You do not need kigi login -- the API key is enough.
Using Custom Models
# List available models (including custom)
kigi models
# Use in the TUI via slash command
/model my-model
# Use in headless mode
kigi -p "Hello" -m my-model
# Set as default in config.toml:
[models]
default = "my-model"
Enterprise Deployment
A complete config for an enterprise deployment with custom models:
[cli]
auto_update = false
[auth]
auth_provider_command = "/usr/local/bin/my-company-auth-provider"
auth_provider_label = "Acme Corp"
auth_token_ttl = 3600
[models]
default = "company-kigi"
[model.company-kigi]
model = "kigi"
base_url = "https://kigi-proxy.acme.com/"
name = "Kigi Latest (Proxy)"
context_window = 128000
[features]
telemetry = false
Troubleshooting
Model Not Found
# List available models
kigi models
# Check config.toml for typos in [model.*] sections
Connection Errors
Verify the endpoint is reachable:
curl -s https://api.example.com/v1/models \
-H "Authorization: Bearer $XAI_API_KEY"
Debug Logging
RUST_LOG=debug KIGI_LOG_FILE=/tmp/kigi.log kigi
tail -f /tmp/kigi.log
Look for log entries containing model or sampling to trace model selection and API calls.