Models and providers
Provider types and presets, subscription logins, switching models per session, and token usage and cost.
On this page
Providers#
A provider's type is the API it speaks: anthropic, openai (any OpenAI-compatible Chat Completions API), responses (the OpenAI Responses API), gemini (Google's generateContent API) or bedrock (AWS Bedrock Converse, signed with the standard AWS credential chain). For well-known services, preset: fills in the type, base URL and key variable, and a provider named after a preset uses it without saying so:
providers:
groq: {} # same as preset: groq
fast:
preset: cerebras
local:
preset: ollama # no key needed
gemini:
keys_env: [GEMINI_API_KEY, GEMINI_API_KEY_2]
aws:
type: bedrock
region: us-east-1 # AWS credentials from the usual chain; keys_env may name Bedrock API keys instead
| Preset | Base URL | Key variable |
|---|---|---|
anthropic |
Anthropic API | ANTHROPIC_API_KEY |
openai |
https://api.openai.com/v1 |
OPENAI_API_KEY |
gemini (google) |
Gemini API (type gemini) |
GEMINI_API_KEY |
bedrock |
AWS (type bedrock) |
AWS credential chain |
openrouter |
https://openrouter.ai/api/v1 |
OPENROUTER_API_KEY |
groq |
https://api.groq.com/openai/v1 |
GROQ_API_KEY |
together |
https://api.together.ai/v1 |
TOGETHER_API_KEY |
fireworks |
https://api.fireworks.ai/inference/v1 |
FIREWORKS_API_KEY |
deepseek |
https://api.deepseek.com |
DEEPSEEK_API_KEY |
xai (grok) |
https://api.x.ai/v1 |
XAI_API_KEY |
mistral |
https://api.mistral.ai/v1 |
MISTRAL_API_KEY |
cerebras |
https://api.cerebras.ai/v1 |
CEREBRAS_API_KEY |
deepinfra |
https://api.deepinfra.com/v1/openai |
DEEPINFRA_API_KEY |
nvidia (nim) |
https://integrate.api.nvidia.com/v1 |
NVIDIA_API_KEY |
huggingface (hf) |
https://router.huggingface.co/v1 |
HF_TOKEN |
perplexity |
https://api.perplexity.ai |
PERPLEXITY_API_KEY |
moonshot (kimi) |
https://api.moonshot.ai/v1 |
MOONSHOT_API_KEY |
zai (glm) |
https://api.z.ai/api/paas/v4 |
ZAI_API_KEY |
minimax |
https://api.minimax.io/v1 |
MINIMAX_API_KEY |
qwen (dashscope) |
https://dashscope-intl.aliyuncs.com/compatible-mode/v1 (keys are region-bound; set base_url for your region) |
DASHSCOPE_API_KEY |
ollama |
http://localhost:11434/v1 |
optional OLLAMA_API_KEY |
lmstudio |
http://localhost:1234/v1 |
optional LM_API_TOKEN |
vllm |
http://localhost:8000/v1 |
optional VLLM_API_KEY |
llamacpp |
http://localhost:9931/v1 (older llama-server builds use 8080) |
optional LLAMA_API_KEY |
litellm |
http://localhost:4000/v1 |
optional LITELLM_API_KEY |
vercel |
https://ai-gateway.vercel.sh/v1 |
AI_GATEWAY_API_KEY |
cloudflare |
set base_url: https://gateway.ai.cloudflare.com/v1/ACCOUNT_ID/GATEWAY_ID/compat |
CLOUDFLARE_AI_GATEWAY_KEY |
Any field you set wins over the preset. habakkuk models lists each provider with its credential status and, where the API can list them, its models.
Retries, key rotation and fallback#
Errors are classified (rate limit, overload, auth, billing, context overflow, bad request, network). Transient ones retry with jittered backoff; rate limits and auth failures rotate to the next key in keys_env, with a cooldown per key; and errors another model can fix fall back down the agent's fallbacks. See Architecture: models.
Subscription logins#
A provider can use an account sign-in instead of keys:
habakkuk auth login chatgpt # ChatGPT plan: device code, then the Responses API the plan uses
habakkuk auth login copilot # GitHub Copilot plan: GitHub device code, then Copilot's OpenAI-compatible API
habakkuk auth list
habakkuk auth logout chatgpt
providers:
chatgpt:
auth: chatgpt # type responses is implied
copilot:
auth: copilot # type openai is implied
Tokens are kept in $HABAKKUK_HOME/auth.json (owner-only, replaced atomically) and refreshed shortly before they expire, under a file lock so two Habakkuk processes never refresh at once. There is no Claude subscription login: Anthropic's terms do not allow third-party apps to use Claude plan credentials, so use an API key.
Switching models#
In a chat, /model shows the models the conversation uses and /model provider/model switches this conversation only (the agent's own models stay as fallbacks); /model default switches back. The choice is stored per session.
Usage and cost#
Every model call's tokens (input, output, cache read and write) are recorded with its provider, model, agent and session in usage.db. habakkuk usage [-days 30] [-agent A] [-session S] prints totals and cost by model and by agent; /usage in a chat shows the current conversation. Costs come from a built-in table of list prices (standard tier, US dollars per million tokens), with long-prompt tiers where providers have them; calls through a subscription login count as $0. Add or correct prices in config:
pricing:
llama-4-scout: {input: 0.11, output: 0.34}
openrouter/anthropic/claude-sonnet-5-5: {input: 2, output: 10, cache_read: 0.1, cache_write: 2.5}
Spending limits#
Hard caps on what model calls cost, so a schedule or a runaway tool loop cannot run up a bill overnight. Set them for the whole install and per agent, in US dollars (numbers or strings: 5, "5.00"):
timezone: America/Toronto # days start at 00:00 here, months on the 1st
budget: # the whole install
daily: 5
monthly: 100
per_turn: 1.50 # one turn: its tool loop, compaction, title and sub-agents
unknown_price: allow # allow | block (see below)
agents:
habakkuk:
budget: {daily: 2, per_turn: 0.50} # this agent's own calls
- Every model call is checked first: turns, sub-agents, compaction, automatic titles, the approval reviewer, memory review and meeting notes all go through the same router, which asks the limits before sending. Once a limit is reached the call is not sent. A call already under way is not cut off, so spend can pass a limit by what the calls running at that moment cost, and then stops.
- Spend is kept in memory, loaded from
usage.dbat start and updated as each call finishes, so the check costs nothing measurable. Prices are the oneshabakkuk usageuses. - A capped turn ends cleanly with a reply saying which limit is used up, when it resets and where to raise it, for example: Paused: the daily budget of $5.00 for agent habakkuk is used up; it resets at 00:00 America/Toronto. Raise it with agents.habakkuk.budget.daily in config.yaml. The job finishes; it is not retried. A per-turn limit stops only that turn; the next message starts a fresh allowance.
- Background work tells you once. A scheduled task whose agent is out of budget is skipped without starting, and its chat gets one notice per limit and period; later skips are only logged. A board card is blocked with the reason. Memory review is skipped and tried again later.
- Who pays. A call counts toward the agent that made it and toward the install. A sub-agent's calls count toward the sub-agent's daily and monthly limits, and toward the per-turn limit of the turn that delegated to it.
- Warning at 80%. When a daily or monthly limit passes 80%, the chat whose call crossed it gets one warning per period (the log, when there is no chat).
- Unknown prices. A model missing from the price table counts as $0, with one warning per model in the log; add it under
pricing:. Withunknown_price: block, calls to such models are refused while any limit applies (the next model in the chain is tried). - Changing limits in
config.yamlapplies within seconds, without a restart.
See spend against the limits with habakkuk usage (today and this month for the install and each agent), /budget in a chat (this conversation's agent and the install), or GET /api/budget (API).
Speech-to-text#
Voice notes from chats and habakkuk talk are transcribed through a provider of type openai with an /audio/transcriptions endpoint:
transcription:
provider: openai # a provider with type: openai
model: gpt-4o-mini-transcribe # required
prompt: "Names and words to spell right: Habakkuk, Toronto" # optional