LLM authentication, provider routing, and tier management
Overview
The models subsystem decides which model runs a task, how it
authenticates, and what it costs. It is the cost-optimization core:
every task is classified into a tier (CHEAP, CAPABLE, PREMIUM),
each tier maps to a concrete model in a registry, and an auth strategy
chooses between your Claude subscription and the Anthropic API per call.
It owns three concerns: the registry (models, tiers, pricing), the
router (task → tier → model selection, including adaptive routing
from telemetry), and auth/provider configuration (subscription vs
API, persisted to ~/.attune/).
It is not responsible for executing workflows, tracking cross-session telemetry beyond routing stats, or managing memory — those live in their own subsystems. You touch the models layer when you need to know why a task picked a given model, change provider/auth, or read pricing for a cost estimate.
Two CLI namespaces front this subsystem: attune auth (authentication
strategy) and attune provider (provider selection). The Python API
under attune.models is the programmatic equivalent.
Concepts
Three ideas compose the whole subsystem:
- Tiers —
ModelTieris a three-value enum (CHEAP,CAPABLE,PREMIUM). Every task type maps to exactly one tier viaTASK_TIER_MAP; unknown tasks default toCAPABLE. - Registry —
MODEL_REGISTRYis a nesteddict[str, dict[str, ModelInfo]]keyed by provider then tier. EachModelInfocarries the modelid, per-million input/output cost, and capability flags.get_model(provider, tier)resolves one. - Auth strategy —
AuthStrategydecides, per module, whether to run on your subscription or the API.AuthModeisSUBSCRIPTION,API, orAUTO(size-based). The strategy is persisted at~/.attune/auth_strategy.json(AUTH_STRATEGY_FILE).
The three tiers
| Tier | Enum value | Use for |
|---|---|---|
CHEAP |
"cheap" |
summarize, classify, triage, lint, format, simple Q&A |
CAPABLE |
"capable" |
generate code, fix bugs, review security, write tests, refactor |
PREMIUM |
"premium" |
coordinate, synthesize, architectural decisions, final review |
get_tier_for_task(task_type) returns the ModelTier for a task
string or TaskType; get_tasks_for_tier(tier) lists the task strings
in a tier.
Core data structures
| Type | What it represents |
|---|---|
ModelInfo |
One model: id, provider, tier, input_cost_per_million, output_cost_per_million, max_tokens, supports_vision, supports_tools. Convenience properties cost_per_1k_input / cost_per_1k_output / model_id / name are read-only. |
AuthStrategy |
Auth configuration: subscription_tier, default_mode, the small/medium LOC thresholds, and setup_completed. Note setup_completed is a plain field, not a method. |
LLMResponse |
A completed call: content, model_id, provider, tier, tokens_input, tokens_output, cost_estimate, latency_ms. Aliased properties cost, total_tokens, success, input_tokens, output_tokens are read-only. |
ProviderConfig |
Provider selection: mode (ProviderMode.SINGLE), primary_provider, available_providers, cost_optimization. |
How auth resolves a mode
AuthStrategy.get_recommended_mode(module_lines) resolves by
subscription tier first, not by size. If default_mode is not
AUTO, it returns that. In AUTO, the PRO and API_ONLY tiers
always return AuthMode.API (pay-per-token is more economical there)
and module size is never consulted. Only MAX / ENTERPRISE tiers use
the size thresholds: modules under small_module_threshold (500) — and
medium modules under medium_module_threshold (2000) when
prefer_subscription is set — favor the subscription; larger ones
favor the API for its 1M context window. The zero-config default tier
is PRO, so out of the box AUTO returns API regardless of size.
estimate_cost(module_lines, mode) returns the projected cost for a
given mode so you can compare before committing.