models.toml
models.toml declares providers and the models they serve, and the order of the models decides which one handles each task. A provider’s api_key lives in secrets.toml under the same [providers.<name>] table. This file only holds base_url and the models.
[providers.ollama]
base_url = "http://localhost:11434/v1"
[models.qwen3-5-4b]
model = "qwen3.5:4b"
provider = "ollama"
tasks = ["chat", "summarize"]
[models.llama3-2-1b]
model = "llama3.2:1b"
provider = "ollama"
tasks = ["chat"]
Each task uses the first model in the file that lists it. Above, chat and summarize both use qwen3-5-4b, and llama3-2-1b waits behind it. To switch, comment the first one out (the next one listing the task takes over) or move another above it. A task no model lists is off, along with the features that need it. chat is required in local mode, and without it the CLI exits with an error.
The tasks:
chatembeddingimage-generationttsandsttreranksummarize: writes the summary a long conversation is compacted into, condenses oversized tool results and runs thesummarizetool. The chat model does it when there’s none.
Each [models.<id>] entry has:
model: the provider’s own model name, sent in API requests;provider: a key from[providers.*];tasks: what it can be used for. One model can serve several, like chat and summarize above;context_window: optional, how many tokens the model takes in.
The id is yours to choose. The wizard uses the last part of the model name, cleaned up (qwen3.5:4b becomes qwen3-5-4b, accounts/fireworks/models/glm-5p3-flash becomes glm-5p3-flash), and adds -<provider> when two providers serve the same name. A purely numeric id is refused, since it would lose its place in the order. The models further down stay in the file, for a persona to pin by id, or for kaja doctor to fall back to when the one in use stops answering. Falling back comments out the broken model, so the next one takes over.
The header shows how full the chat model’s context is (12,345 / 32,768 tokens (38%)). Without context_window, Kaja asks the server once per run. llama.cpp and Ollama report the size they actually run with, and some hosted providers list it with their models. If nothing answers, it assumes 32,768. kaja doctor shows the number each chat model got and where it came from. Set it by hand when the guess is wrong, for example when you start Ollama with a bigger num_ctx:
[models.qwen3-5-4b]
model = "qwen3.5:4b"
provider = "ollama"
tasks = ["chat"]
context_window = 65536
Ollama ignores the key but needs some value, so set one in secrets.toml:
[providers.ollama]
api_key = "ollama"
Examples
Example files live in docs/config. They’re generated from catalog.toml, the same provider catalog the setup wizard writes models.toml from. Each example shows one model per task, and the wizard also keeps the models it didn’t pick, for pins and switching.
| File | Providers | Tasks |
|---|---|---|
models.ollama.toml | Ollama | chat (qwen3.5:4b), embedding. Fully local |
models.default.toml | Fireworks, xAI, Speaches | chat, summarize, embedding, rerank, image-generation, tts, stt. What kaja config fetch --offline writes |
models.llama.toml | llama.cpp | chat against a local llama-server |
models.xai.toml | xAI | chat (grok-4.3), image-generation. One hosted key |
models.openrouter.toml | OpenRouter | chat (stealth/space-bunny-alpha). One hosted key |
Next: