agentafk
Configuration

Model Slots

Bind capability tiers (local / small / medium / large) to specific models and providers, including per-slot API keys and endpoints for mixing Anthropic with local shims or hosted OpenAI.

Model slots let you assign specific models to four fixed capability tierslocal, small, medium, and large (cheapest → most capable). They're fixed labels; bind any model to each one and skills and subagents pick the right tier automatically. The local tier is unset by default — configure it to point at a local OpenAI-compatible shim (Ollama, LM Studio, MLX, vLLM).

This means you can mix providers per tier: Anthropic on one tier, a local OpenAI-compatible shim on another, and hosted OpenAI on a third — all in the same process.

Default bindings

An unconfigured install uses these defaults:

TierDefault model IDIdentity alias (fixed)
local(unset — configure via AFK_MODEL_LOCAL)
smallclaude-haiku-4-5-20251001haiku
mediumclaude-sonnet-5sonnet, sonnet_1m
largeclaude-opus-5opus, opus_1m

Note: These default model IDs reflect the latest available models as of this writing. As new versions ship, these defaults are updated in the codebase (src/agent/session/model-slots.ts). You can override any slot via environment variables (AFK_MODEL_SMALL, AFK_MODEL_MEDIUM, AFK_MODEL_LARGE) or afk.config.json — see Configuring slots for details.

If you never touch slots, behavior is identical to before this feature existed.

Configuring slots in afk.config.json

Each slot accepts a bare model ID string, or an object with optional per-slot provider credentials. The full object shape (ModelSlotConfigEntry, source: src/cli/config/types.ts:246) is:

FieldTypeDescription
idstringConcrete model ID this tier resolves to (e.g. claude-sonnet-5, gpt-4o-mini). Required when using the object form.
namestringOptional user-defined alias. When set, you can select this tier by name (case-insensitive) anywhere a model is named — AFK_MODEL, --model, /model, or the agent/skill tools' model parameter. Custom names take priority over neutral tier names (small/medium/large) and identity aliases.
provider"anthropic" | "openai" | "chatgpt-oauth"Explicit provider override. Inferred from the model ID when omitted. Use when a bare shim ID (no gpt-/org/model signal) must route to a specific provider. "chatgpt-oauth" (or the "chatgpt" shorthand) forces the ChatGPT-subscription OAuth credential (~/.codex/auth.json) for this tier only — see Per-slot ChatGPT-subscription OAuth.
baseUrlstringPer-slot endpoint. For Anthropic-routed tiers, the Messages API base; for OpenAI-routed tiers, the Chat Completions base. Wins over global AFK_OPENAI_BASE_URL / AFK_LOCAL_BASE_URL for this tier only.
apiKeystringPer-slot API key. Wins over global credentials for this tier only.
{
  "models": {
    // Bare ID — provider inferred, global credentials used.
    "small": "claude-haiku-4-5-20251001",

    // Object — local OpenAI-compatible shim with its own endpoint.
    "medium": {
      "id": "mlx-community/Qwen3-32B-4bit",
      "name": "local",
      "provider": "openai",
      "baseUrl": "http://localhost:8080/v1",
      "apiKey": "local"
    },

    // Object — hosted OpenAI with its own key.
    "large": { "id": "gpt-4.1", "provider": "openai", "apiKey": "sk-…" }
  }
}

provider is anthropic, openai, or chatgpt-oauth (inferred from the ID when omitted; chatgpt-oauth must be set explicitly — see Models & Providers). For an Anthropic-routed tier, baseUrl is the Messages API base; for an OpenAI-routed tier it is the Chat Completions base. A per-slot value wins over the corresponding global (AFK_OPENAI_BASE_URL, ANTHROPIC_API_KEY, etc.) for that tier only.

The full field reference for afk.config.json is generated under Reference.

Configuring slots via environment variables

Per-tier env vars override the afk.config.json models block. A file-provided name or provider is preserved when env overrides only the ID or credentials.

VariableBinds
AFK_MODEL_SMALLsmall tier model ID
AFK_MODEL_MEDIUMmedium tier model ID
AFK_MODEL_LARGElarge tier model ID
AFK_MODEL_SMALL_BASE_URLsmall tier endpoint base URL
AFK_MODEL_MEDIUM_BASE_URLmedium tier endpoint base URL
AFK_MODEL_LARGE_BASE_URLlarge tier endpoint base URL
AFK_MODEL_SMALL_API_KEYsmall tier API key
AFK_MODEL_MEDIUM_API_KEYmedium tier API key
AFK_MODEL_LARGE_API_KEYlarge tier API key

Example — run a local shim on the small tier from the command line:

AFK_MODEL_SMALL='mlx-community/Qwen3-32B-4bit' \
AFK_MODEL_SMALL_BASE_URL='http://localhost:8080/v1' \
AFK_MODEL_SMALL_API_KEY='local' \
afk i

Note: provider is config-only. When a bare ID bound via env needs an explicit provider override, set provider in the afk.config.json slot object; env-bound IDs fall back to ID-inference.

Selecting a tier

Anywhere a model is named — AFK_MODEL, --model, the REPL's /model command, and the agent/compose/skill tools' model parameter — you can pass:

  • A tier name: local, small, medium, large
  • A custom name you set in the binding's name field (case-insensitive)
  • An identity alias: haiku, sonnet/sonnet_1m, opus/opus_1m, fable
  • The auto sentinel (passthrough — picks the session model)
  • A raw concrete model ID

Resolution order (first match wins):

  1. Custom name (user-assigned name on a binding) → tier
  2. Neutral name (local | small | medium | large) → tier
  3. Identity alias (haiku/sonnet/opus/fable/*_1m) → its own fixed model ID — never a tier
  4. Raw model ID or auto sentinel (passthrough)

Identity aliases are decoupled from capability tiers. sonnet always names Claude Sonnet, opus always Claude Opus, and so on, regardless of how you've rebound the small/medium/large tiers. Rebinding medium to a non-Claude model changes the medium tier — and the session default, since AFK_MODEL defaults to the medium tier, not the sonnet identity — but it does not change what sonnet means. Only the neutral tier names and your custom names follow tier bindings; identity aliases never do.

How routing works

Assign a non-Anthropic model to a tier (e.g. small → gpt-4o-mini) and that tier routes to the OpenAI-compatible provider automatically.

Each tier's credentials are applied automatically — an Anthropic key can't leak into an OpenAI-routed tier.

What you can mix in the same process

  • All bound tiers on Anthropic — small/medium/large by default; local is unset until you configure it (unchanged default behavior).
  • Each tier on a different provider, endpoint, and key — e.g. Anthropic on large, a local MLX shim on small, hosted OpenAI on medium.
  • A ChatGPT-subscription model (provider: "chatgpt-oauth"), a separately-keyed OpenAI model, and an Anthropic model, all coexisting in the same session — see Per-slot ChatGPT-subscription OAuth.
  • Per-tier credentials via afk.config.json or AFK_MODEL_* env vars.

On this page