Skip to content

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

kaid config provider

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:

kaid config model

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)!
  1. Select a built-in provider preset, or a custom provider you defined.
  2. Pin the model slug. Built-in lists are suggestions, not restrictions.
  3. 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 --timeout and kaid scan --timeout set the concurrent-task budget in minutes (default 15; 0 disables 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 --timeout does 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 legacy llm section, in seconds.
  • KAID_LLM_TIMEOUT environment 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.

kaid config set providers.anthropic.api_key_cmd "op read op://dev/anthropic/api-key"

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:

kaid config set max_tokens 400000

The setting applies to both kaid review and kaid scan. Use --max-tokens for a one-off override without changing the saved configuration:

kaid review --max-tokens 400000
kaid scan --max-tokens 400000

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.

kaid config set effort high
kaid config unset effort      # back to the default medium

--effort low|medium|high overrides the saved value for a single run.

Verify connectivity

kaid llm test

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:

kaid config set providers.anthropic.extra_body '{"thinking":{"type":"disabled"}}'

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:

kaid config set language Chinese
kaid config set language English

See Also