Configuration
The config file lives at ~/.kaid/config.json. You have three ways
to edit it:
- Interactive TUI -
kaid config provider/kaid config model, with guided menus. - Command line -
kaid config set <key> <value>, ideal for scripts and CI. - Manual edit (not recommended) - the JSON file directly.
Avoid editing the JSON by hand
A manual edit is reformatted on the next kaid config set write. Use the
CLI so the file stays valid and you get validation errors while the value
is still in front of you.
Configuring a model¶
Recommended: interactive setup¶
It lets you pick a built-in or custom provider, enter an API key, choose a model, saves everything to the config file, and then runs kaid llm test once to verify the endpoint. To switch models later:
Non-interactive setup (CI / no-TUI environments)¶
Write to the same config with kaid config set:
kaid config set provider anthropic # (1)!
kaid config set model claude-opus-4-6 # (2)!
kaid config set providers.anthropic.api_key sk-ant-xxxxxxxxxx # (3)!
- Select a built-in provider preset, or a custom provider you defined.
- Pin the model slug. Built-in lists are suggestions, not restrictions.
- Store the credential. If omitted, kaid falls back to the provider's environment variable.
Built-in providers¶
The following providers ship with kaid, with the Base URL and protocol
preset. Most require an API key. If providers.<name>.api_key is unset,
kaid falls back to the corresponding environment variable. The chatgpt
provider instead uses the ChatGPT credentials created by kaid auth login.
| Name | Protocol | Base URL | API key env var |
|---|---|---|---|
anthropic |
anthropic | https://api.anthropic.com |
ANTHROPIC_API_KEY |
bedrock |
anthropic-bedrock | derived from aws_region |
— (AWS credential chain) |
chatgpt |
openai-responses | https://api.openai.com/v1 |
not applicable (Sign in with ChatGPT) |
openai |
openai | https://api.openai.com/v1 |
OPENAI_API_KEY |
openai-responses |
openai-responses | https://api.openai.com/v1 |
OPENAI_RESPONSES_API_KEY |
openrouter |
openai | https://openrouter.ai/api/v1 |
OPENROUTER_API_KEY |
gemini |
openai | https://generativelanguage.googleapis.com/v1beta/openai |
GEMINI_API_KEY |
dashscope |
openai | https://dashscope.aliyuncs.com/compatible-mode/v1 |
DASHSCOPE_API_KEY |
dashscope-tokenplan |
openai | https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1 |
DASHSCOPE_TOKENPLAN_KEY |
volcengine |
openai | https://ark.cn-beijing.volces.com/api/v3 |
ARK_API_KEY |
deepseek |
openai | https://api.deepseek.com |
DEEPSEEK_API_KEY |
tencent-tokenhub |
openai | https://tokenhub.tencentmaas.com/v1 |
TENCENT_TOKENHUB_API_KEY |
hy-tokenplan |
openai | https://api.lkeap.cloud.tencent.com/plan/v3 |
TENCENT_HUNYUAN_TOKENPLAN_KEY |
iflytek |
openai | https://spark-api-open.xf-yun.com/v1 |
SPARK_API_KEY |
kimi |
openai | https://api.moonshot.cn/v1 |
MOONSHOT_API_KEY |
kimi-global |
openai | https://api.moonshot.ai/v1 |
MOONSHOT_GLOBAL_API_KEY |
z-ai |
openai | https://open.bigmodel.cn/api/paas/v4 |
Z_AI_API_KEY |
mimo |
openai | https://api.xiaomimimo.com/v1 |
MIMO_API_KEY |
minimax |
openai | https://api.minimax.io/v1 |
MINIMAX_GLOBAL_API_KEY |
minimax-cn |
openai | https://api.minimaxi.com/v1 |
MINIMAX_API_KEY |
baidu-qianfan |
openai | https://qianfan.baidubce.com/v2 |
QIANFAN_API_KEY |
siliconflow |
openai | https://api.siliconflow.com/v1 |
SILICONFLOW_GLOBAL_API_KEY |
siliconflow-cn |
openai | https://api.siliconflow.cn/v1 |
SILICONFLOW_API_KEY |
novita |
openai | https://api.novita.ai/openai |
NOVITA_API_KEY |
xai |
openai | https://api.x.ai/v1 |
XAI_API_KEY |
Built-in providers' model lists are suggestions for kaid config model, not
restrictions on --model. An override absent from both the built-in list and
providers.<name>.models produces a warning on stderr; the provider validates
the model when the request is sent. Custom providers retain their existing
--model validation rules.
Sign in with ChatGPT preview¶
This preview adds the chatgpt built-in provider for the public Responses API. It has no API-key environment variable. See the ChatGPT preview guide for sign-in, consent and usage restrictions.
Configure the provider and optional supported request fields with kaid config set. Choose a model from kaid llm models or set an explicit model slug:
kaid auth login --provider chatgpt
kaid config set provider chatgpt
kaid config set providers.chatgpt.model <model-slug>
kaid config set providers.chatgpt.extra_body '{"reasoning":{"effort":"low"}}'
kaid llm test
The provider accepts explicit model values even if the account-specific catalog omits them. It accepts extra_body fields reasoning, text and prompt_cache_key; it rejects endpoint, protocol and credential overrides.
Overriding a built-in provider's Base URL¶
Every built-in provider has a preset Base URL (shown in the table above).
To point a built-in provider at a different endpoint — for example a
self-hosted LiteLLM gateway that is rarely at the preset default
http://localhost:4000/v1 — set providers.<name>.url:
The chatgpt preview is an exception: its public endpoint is fixed and URL overrides are rejected.
kaid config set provider litellm
kaid config set model openai/gpt-5.4
kaid config set providers.litellm.api_key "$LITELLM_API_KEY"
kaid config set providers.litellm.url https://gateway.internal:8000/v1
The configured url takes precedence over the preset Base URL. When
providers.<name>.url is unset (or cleared), kaid falls back to the
preset default — so you only need to set it when your endpoint differs.
AWS Bedrock¶
With its default anthropic-bedrock protocol, bedrock uses the same Messages API as anthropic, but requests are
SigV4-signed from the standard AWS credential chain instead of carrying an API
key, and the region decides the host. There is no api_key to set, and none is
accepted as a substitute for a signature:
kaid config set provider bedrock
kaid config set model us.anthropic.claude-sonnet-4-6
kaid config set providers.bedrock.aws_region us-west-2
kaid config set providers.bedrock.aws_profile example-profile
If you set providers.bedrock.protocol to openai for an OpenAI-compatible gateway, configure the gateway URL in providers.bedrock.url and supply its API key through providers.bedrock.api_key or providers.bedrock.api_key_cmd. Selecting another model in kaid config provider preserves the saved API key.
| Field | Meaning |
|---|---|
providers.bedrock.aws_region |
Region whose bedrock-runtime host serves the request. Falls back to AWS_REGION or the active profile. |
providers.bedrock.aws_profile |
Named profile to resolve credentials from. Falls back to AWS_PROFILE or the ambient chain. |
Both fields are optional: left unset, the standard chain decides, as with any
other AWS tool. Pinning them makes a run reproducible without exporting
AWS_PROFILE first, which matters most on CI runners carrying a different
default.
Model identifiers are scoped to an account and a region, so the list kaid
ships is a starting point rather than a closed set — an inference profile ID or
an application inference profile ARN valid for your account is accepted even
when absent from it. Run aws bedrock list-inference-profiles --region <region>
to see what an account offers; a version suffix such as -v1:0 is invalid for
the newer families.
With the default anthropic-bedrock protocol, kaid llm test reports the region
and profile in place of a URL. The region determines the host:
Source: provider:bedrock
Region: us-east-1
Profile: example-profile
Model: claude-sonnet-5
✓ Connection test successful
Bedrock is not available through llm.protocol or KAID_LLM_PROTOCOL. That
block describes one URL and one token, has nowhere to put a region or a profile,
and bedrock uses neither value it does carry, so the combination is rejected
rather than accepted and ignored.
Custom providers¶
Names such as mimo-v2.6-flash and foo.api_key are supported. Before
setting fields on a new name containing dots, create and select it with
kaid config set provider <name>; this changes the active provider. Existing
dotted entries can be edited without selecting them, through either
providers.<name>.<field> or custom_providers.<name>.<field>. Names must
not start or end with a dot, contain consecutive dots, or have empty or
whitespace-only segments. Dot-free names still support implicit creation.
Any provider name not in the table above is treated as custom and must
supply at least url and protocol (protocol is anthropic,
openai, openai-responses, or anthropic-bedrock):
kaid config set provider my-gateway
kaid config set custom_providers.my-gateway.url https://gateway.internal.com/v1
kaid config set custom_providers.my-gateway.protocol openai
kaid config set custom_providers.my-gateway.model llama-3-70b
kaid config set custom_providers.my-gateway.api_key "$MY_API_KEY"
Use openai-responses when a provider or model requires the OpenAI
Responses API (/v1/responses):
kaid config set provider openai-responses-gateway
kaid config set custom_providers.openai-responses-gateway.url https://api.openai.com/v1
kaid config set custom_providers.openai-responses-gateway.protocol openai-responses
kaid config set custom_providers.openai-responses-gateway.model gpt-5
kaid config set custom_providers.openai-responses-gateway.api_key "$OPENAI_API_KEY"
A custom provider on the anthropic-bedrock protocol needs no url — the
region decides the host — and takes the same AWS fields as the built-in. This is
how a second region or profile gets its own entry:
kaid config set provider bedrock-eu
kaid config set custom_providers.bedrock-eu.protocol anthropic-bedrock
kaid config set custom_providers.bedrock-eu.aws_region eu-west-1
kaid config set custom_providers.bedrock-eu.aws_profile eu-profile
kaid config set custom_providers.bedrock-eu.model eu.anthropic.claude-sonnet-4-6
The url can be either the API base URL or the full /responses endpoint — kaid normalizes it either way.
A local model served by Ollama is just a custom provider pointing at the local OpenAI-compatible endpoint:
kaid config set provider ollama
kaid config set custom_providers.ollama.url http://127.0.0.1:11434/v1
kaid config set custom_providers.ollama.protocol openai
kaid config set custom_providers.ollama.model qwen3:32b
kaid config set custom_providers.ollama.api_key ollama
Ollama ignores the API key, but custom providers require a non-empty
api_key (there is no environment-variable fallback for them), so set
any placeholder value. The model itself must support native tool
calling — see
"No tool calls parsed" (local models / Ollama)
in the FAQ before picking one.
Timeouts¶
Task and request timeouts are independent:
kaid review --timeoutandkaid scan --timeoutset the concurrent-task budget in minutes (default 15;0disables the task deadline). This budget includes all LLM calls, tool work, and retry waits for the task. Review scales it by the number of effort rounds.- The per-request HTTP timeout is in seconds (default 300).
Raising
--timeoutdoes not change it; disabling the task deadline does not disable request timeouts. A shorter remaining task budget always wins.
Each LLM request has an HTTP timeout, defaulting to 300 seconds. Slow local models (or large files) can need more. Three knobs, in increasing scope:
providers.<name>.timeout_sec/custom_providers.<name>.timeout_sec— per-provider, in seconds.llm.timeout_sec— for the legacyllmsection, in seconds.KAID_LLM_TIMEOUTenvironment variable — integer seconds; overrides the config-file value for every resolution path.
Both timeout_sec keys can be set with kaid config set:
{
"custom_providers": {
"ollama": { "url": "http://127.0.0.1:11434/v1", "protocol": "openai", "timeout_sec": 900 }
}
}
For example, KAID_LLM_TIMEOUT=900 kaid review --timeout 30 allows an
individual request up to 15 minutes within the task budget. Without the
environment variable or a configured timeout_sec, requests still time
out after 5 minutes even with --timeout 30.
An expired per-request deadline ends the call without automatically replaying
it, including when reading a response body or stream. For consistently slow
requests, increase the request timeout rather than retrying with the same
limit. The existing SDK retry policy is unchanged: retryable connection and
HTTP failures use up to 5 retries with backoff and provider Retry-After
hints. The SDK's per-attempt deadline also bounds its retry wait. Cancellation
or an exhausted task deadline stops both requests and retry waits.
Diagnostics distinguish LLM request timeout (check KAID_LLM_TIMEOUT or
provider timeout_sec) from caller deadline exceeded, which means the
calling operation's context has expired. For a review/scan task deadline,
check --timeout; other operations, such as background memory compression
and kaid llm test, have their own deadlines.
API key from a command¶
Instead of storing a key in the config file, api_key_cmd fetches it at
runtime from a secret manager (1Password, pass, gopass, …). Its trimmed,
single-line stdout becomes the key. The same option is available for the
legacy llm block as auth_token_cmd.
Your OS keyring works the same way, through the tool it already ships with, so
the key lives in the Keychain or Secret Service rather than in config.json:
# macOS Keychain
kaid config set providers.anthropic.api_key_cmd \
"security find-generic-password -s kaid-anthropic -w"
# Linux (Secret Service: GNOME Keyring, KWallet, …)
kaid config set providers.anthropic.api_key_cmd \
"secret-tool lookup service kaid-anthropic"
Precedence: a static api_key always wins (if both are set, the command is
ignored and a warning is printed); otherwise api_key_cmd runs; only if
neither is set does kaid fall back to the provider's environment variable.
The command runs once per kaid invocation and must succeed: a non-zero exit,
empty output, multi-line output, or more than 64KiB of output is a hard error
(kaid never silently falls back). It must complete within 60 seconds, which
includes any time you spend answering a prompt. The command inherits your
terminal's stdin and stderr, so interactive prompts (pinentry, Touch ID) both
appear and can be answered. If the command leaves a background daemon holding
its stdout pipe (gpg-agent, a first-use op daemon), the credential still
arrives but every kaid run pauses an extra 5 seconds waiting for that pipe to
close — redirect the daemon's output (>/dev/null 2>&1) to get rid of the wait.
On Windows the command runs through cmd.exe, not sh, so a command written
for one is generally not portable to the other: %VAR% and ^ are cmd.exe
metacharacters, while $VAR expansion and \ escaping do not apply there.
Quoted arguments are passed through verbatim, so
op read "op://Private/My Vault/api-key" works as written.
Since the value is executed as a shell command, config.json is trusted
input — keep it owned by you and not writable by anyone else (kaid writes it
with 0600 permissions).
Additional retry status codes¶
Some LLM providers use non-standard 4xx status codes for transient errors, such
as returning 403 or 400 for rate limiting. Use retry_codes to make kaid
retry these requests using the existing SDK retry mechanism.
retry_codes is an array of integers. It can be set as llm.retry_codes or
custom_providers.<name>.retry_codes. When using kaid config set, pass the codes as
a comma-separated list:
kaid config set llm.retry_codes 403,400
kaid config set custom_providers.my-gateway.retry_codes 403,400
Only 4xx HTTP status codes are accepted. 408, 409, and 429 are already
retried by the SDK. When read from the config file, these redundant codes are
ignored. When supplied through kaid config set, kaid also prints a warning and
omits them from the saved value. All 5xx responses are already retried by the
SDK and cannot be added to retry_codes.
Prompt limit¶
max_tokens is the prompt (input) ceiling for a single subtask
(one file or a bundle of related files). The embedded
templates default to 200,000 tokens for kaid review and 58,888 for
kaid scan. Change it for a model with a different context window by
saving max_tokens:
The setting applies to both kaid review and kaid scan. Use --max-tokens
for a one-off override without changing the saved configuration:
The per-run flag takes precedence over max_tokens; when neither is set, kaid
uses the embedded task-template default. The limit is independent of the
model's output cap (MAX_COMPLETION_TOKENS, 16384 in both templates)
and of --max-tokens-budget, which caps total token use for a whole run.
Restore the embedded default with kaid config unset max_tokens.
Review effort¶
effort sets how many review rounds each subtask gets: low = 1,
medium (the default) = 2, high = 3. More rounds find more issues at
proportionally higher cost.
--effort low|medium|high overrides the saved value for a single run.
Verify connectivity¶
Reuse existing environment variables¶
If you already have Anthropic-compatible ANTHROPIC_* or kaid's own KAID_LLM_*
environment variables configured, kaid picks them up automatically — no
config file needed.
Using CC-Switch¶
If you use CC-Switch with its
routing service
enabled, point the provider url at the local proxy — no other setup is
required:
# Claude (Anthropic-compatible)
kaid config set providers.anthropic.url http://127.0.0.1:15721
# Codex / OpenAI-compatible — set that provider's url key instead
kaid config set providers.<name>.url http://127.0.0.1:15721/v1
api_key can be any value. extra_body (and other per-provider fields)
still apply as usual.
Send vendor-specific fields¶
Some providers require non-standard request fields (such as Bedrock-style
thinking). Use extra_body (merged into every request) to send them
without patching the source:
Session affinity for prompt caching¶
kaid derives a prompt-cache affinity key for every LLM conversation, scoped
to the review session and the task within it
(<session-id>-<task-type>-<scope-hash>). Prompt caches match on prefixes,
so per-conversation keys keep each growing conversation (such as a file's
review tool-loop) on a consistent cache node instead of pinning the whole
run to one hot key; the session-ID prefix lets provider-side cache logs be
correlated with kaid session records.
To opt in, embed the {kaid_session_key} template variable in
extra_headers or extra_body values wherever your provider expects the
key — kaid substitutes the conversation's key per request and sends nothing
otherwise:
# By OpenAI-style request body field (e.g. prompt_cache_key)
kaid config set providers.openai.extra_body '{"prompt_cache_key": "{kaid_session_key}"}'
# By HTTP header (e.g. x-session-affinity)
kaid config set custom_providers.my-gateway.extra_headers "x-session-affinity={kaid_session_key}"
Configuring the review language¶
language determines which language review comments are written in;
it defaults to English when unset:
See Also¶
- Quickstart - minimal setup and first review.
- CLI Reference - every flag the review command accepts.