Providers
A provider is a source of inference. You declare which ones you have; a cognet declares the roles it needs; the runtime matches them at boot.
That is the whole configuration step. You never write down which model runs which part of an agent — that is decided from what you actually have, every time it starts.
// ~/.axon/profiles/<you>/profile.config.ts
export default defineProfile({
providers: [Anthropic(), Ollama(), Axon()],
})
Declare once, on your profile, and every agent you run inherits it. If you are signed in
and have declared nothing at all, Axon() is already there — the rest of this tab is for
when you want your own keys, your own hardware, or a specific vendor.
A provider is a catalogue, not a model
This is the one idea everything else follows from.
A provider answers "what can I supply" — a list of models with their context windows and modalities. It does not answer "which model should this agent use", because that question cannot be settled when you write the config: it depends on which cognet is running and what roles it declares.
So a cognet asks for main: { type: "generate", in: "text", out: "text", context: 100_000 }
and the runtime finds something you have that fits. A cognet asking for a vision model on
a machine with none says so at axon prepare, before anything runs — not at the first
call that needs it.
One consequence worth internalising: an agent someone else published runs on your providers, not theirs. They never named a vendor, so there is nothing in their agent to be wrong about yours.
The three postures
Choosing a provider is really choosing about cost, keys, and where your tokens go.
Managed — Axon(). Inference through Axon Cloud, billed to
your account. No keys, works the moment you are signed in, and its catalogue covers
everything reachable through OpenRouter. The default, and what a fresh axon init ships
with.
Bring your own key. Your credential, your vendor relationship, their rates — no
markup, and Axon is not in the request path. Either an aggregator that fronts many
vendors (OpenRouter(),
Codex()) or a vendor directly:
Anthropic() ·
OpenAI() ·
Google() ·
XAI() ·
DeepSeek() ·
Mistral() ·
Moonshot() ·
ZAI() ·
Groq() ·
Cerebras() ·
Perplexity()
Local — Ollama(). Inference on your own hardware. No API
costs, and nothing leaves your machine — the strongest privacy posture available. The
terminal manages the server and model downloads for you.
And one for the test suite: Mock() replaces inference with
a deterministic local responder. The full loop still runs — tool calls execute, context
accumulates — which is what makes agent tests fast, free, and
repeatable.
Declaring more than one
Most setups have several. A local model for the cheap roles, a frontier route for the hard ones, managed inference to cover whatever is left:
export default defineProfile({
providers: [Ollama(), Anthropic(), Axon()],
})
Order is preference. When one model is reachable through several of your providers —
Claude Sonnet is available on Anthropic(), OpenRouter() and Axon() — the one you
listed first wins. That is what makes [Anthropic(), Axon()] mean "my key first, managed
as the fallback".
Order only ever breaks a tie between candidates that all satisfy the role. It can never make an unusable model usable, and it never overrides a local model, which always ranks first. Behaviour is the full account.
Credentials
Every factory takes the same options: key for your own credential, url for a
self-hosted or regional endpoint, slots to cap concurrent calls.
type ProviderOptions = {
/** Your own credential, instead of the environment or your account vault. */
key?: string
/** Endpoint override — self-hosted daemons, regional endpoints, proxies. */
url?: string
/** Ceiling on concurrent calls a fanned-out role may spend here. */
slots?: number
}
Where a key comes from when you do not pass one depends on the provider:
| Credential | |
|---|---|
Axon() | Your signed-in session. Nothing to set |
Codex(), OpenRouter() | Your account vault — connect once, works on every machine you sign in from |
| Direct vendors | An environment variable, named on each provider's page |
Ollama(), Mock() | None |
A provider you declared but never configured is not an error. Declaring one is a statement about what you have, not a promise that it is set up — the failure comes when something tries to use it, naming the variable you need to set.
Picking a model
You do not have to. If you want to, model: on the agent is a preference applied to
the cognet's primary role:
export default defineAgent({ model: "anthropic:claude-sonnet-4-6" })
The provider:model form pins the route as well as the model; a bare claude-sonnet-4-6
names the model and lets your provider order choose how to reach it. Either way it is a
preference: a pin naming something you cannot supply falls back to ranking and tells you
it did, rather than refusing to boot.
Press * in the terminal to override the model for the current session. See
Models.
engine: is removed. It named one model for a whole agent, which could not survive a
cognet declaring several roles. Sources move to your profile's providers:; a model
preference becomes model:. See defineAgent.