Models & providers
How AgentRoute resolves a model string to a provider, infers vendors, and finds your API key — plus Ollama, custom endpoints, and the low-level message types.
A model is just a string. You hand Agent something like "claude-sonnet-4" and AgentRoute figures out which provider to talk to, which base URL to hit, and which API key to use. One OpenRouter key works for every hosted model, so most of the time there is nothing else to configure.
from agentroute import Agent
agent = Agent(name="assistant", model="claude-sonnet-4")
result = agent.run("Summarize the plot of Hamlet in one sentence.")
print(result)This page explains how that string is resolved, how vendor prefixes are inferred, where your API key comes from, and how to point at a local Ollama server or any OpenAI-compatible endpoint. The exact signatures live in the models reference.
How a model string is resolved
Agent passes your model to resolve_model, which turns it into a concrete provider connection. Resolution follows three rules, checked in order.
If the string starts with http:// or https://, the last path segment is the model id and everything before it is the base URL. This targets any OpenAI-compatible endpoint directly.
# base_url -> https://my-gateway.example.com/v1, model_id -> my-model
agent = Agent(name="a", model="https://my-gateway.example.com/v1/my-model")If the string starts with ollama/, AgentRoute targets a local Ollama server at http://localhost:11434/v1 with no real auth (a placeholder key is sent because the OpenAI client requires one).
agent = Agent(name="a", model="ollama/llama3.2")Any other string is routed through OpenRouter at https://openrouter.ai/api/v1. A string that already contains a / (for example anthropic/claude-sonnet-4) is used as-is; a bare name (for example claude-sonnet-4) gets a vendor prefix inferred for it.
agent = Agent(name="a", model="claude-sonnet-4") # -> anthropic/claude-sonnet-4
agent = Agent(name="b", model="anthropic/claude-sonnet-4") # used verbatim| Model string | Provider | Base URL | Auth |
|---|---|---|---|
https://host/v1/model-id | Custom OpenAI-compatible | https://host/v1 | resolved key |
ollama/NAME | Local Ollama | http://localhost:11434/v1 | none |
vendor/model | OpenRouter | https://openrouter.ai/api/v1 | resolved key |
bare-name | OpenRouter (vendor inferred) | https://openrouter.ai/api/v1 | resolved key |
Vendor inference for bare names
When you pass a bare name with no /, AgentRoute infers the OpenRouter vendor prefix from how the name starts. This lets you write "gpt-4o" instead of "openai/gpt-4o".
| Name starts with | Inferred prefix | Example | Resolves to |
|---|---|---|---|
claude | anthropic | claude-sonnet-4 | anthropic/claude-sonnet-4 |
gpt, o1, o3, o4 | openai | gpt-4o | openai/gpt-4o |
gemini | google | gemini-2.0-flash | google/gemini-2.0-flash |
llama | meta-llama | llama-3.3-70b | meta-llama/llama-3.3-70b |
deepseek | deepseek | deepseek-v3 | deepseek/deepseek-v3 |
mistral | mistralai | mistral-large | mistralai/mistral-large |
If a bare name does not match any of these prefixes, it is sent to OpenRouter unchanged. When in doubt, write the full vendor/model slug and skip inference entirely.
Matching is case-insensitive and prefix-based. Claude-Sonnet-4 and claude-sonnet-4 infer the same vendor.
API-key resolution
For any provider that needs auth, AgentRoute looks for a key in a fixed order and uses the first one it finds.
A key passed to Agent(api_key=...) (or directly to resolve_model(api_key=...)) wins over everything else.
agent = Agent(name="a", model="claude-sonnet-4", api_key="sk-or-...")If no key was passed, AgentRoute calls Config().api_key(kind=...), which checks environment variables first and then ~/.agentroute/config. For OpenRouter — the catch-all for every hosted model — the variables checked are AGENTROUTE_API_KEY then OPENROUTER_API_KEY.
export AGENTROUTE_API_KEY="sk-or-..."If nothing is found and the endpoint needs no auth (such as Ollama or a local URL gateway), the request proceeds without a key.
The config file
Config reads ~/.agentroute/config, a simple key=value file. Blank lines and lines starting with # are ignored. The OpenRouter key uses the bare api_key name; other provider kinds use a <kind>_api_key name, and each kind may also set a <kind>_base_url override.
# OpenRouter catch-all key
api_key=sk-or-...
# Optional per-provider overrides
openai_api_key=sk-...
openai_base_url=https://api.openai.com/v1The provider kind is derived from the model string during resolution — hosted models all resolve to the openrouter kind, so for the common case you only ever set AGENTROUTE_API_KEY or the api_key= line above. URL and ollama/ strings skip the Config lookup entirely.
The environment variable always beats the config file for the same provider. If a stale AGENTROUTE_API_KEY is exported in your shell, it overrides the key in ~/.agentroute/config.
Using Ollama
Run a model locally with no key. Make sure Ollama is running and the model is pulled (ollama pull llama3.2), then prefix the name with ollama/.
from agentroute import Agent
agent = Agent(name="local", model="ollama/llama3.2")
result = agent.run("Give me three names for a Rust web framework.")
print(result)To point at a remote Ollama host or a non-default port, pass base_url explicitly — it overrides the http://localhost:11434/v1 default.
agent = Agent(
name="local",
model="ollama/llama3.2",
base_url="http://gpu-box.lan:11434/v1",
)Using a custom endpoint
Any OpenAI-compatible server works. You can encode the URL in the model string, or keep the string clean and pass base_url (and api_key, if the endpoint needs one) as separate arguments. The explicit base_url argument always wins over a URL embedded in the model string.
from agentroute import Agent
# Endpoint and key passed as arguments
agent = Agent(
name="gateway",
model="my-model",
base_url="https://my-gateway.example.com/v1",
api_key="sk-...",
)
result = agent.run("Ping?")
print(result)When you supply base_url, the model string is treated as a plain model id — no vendor inference and no OpenRouter routing. This is the escape hatch for self-hosted gateways, vLLM, LM Studio, or any provider that speaks the OpenAI chat-completions API.
The message and request types
You rarely touch these directly — the agentic loop builds them for you — but they define the contract between AgentRoute and a provider. A custom Model implementation receives an LLMRequest and returns an LLMResponse.
- Message — one OpenAI-compatible turn: a
role("system","user","assistant", or"tool"), optionalcontent, and tool-call fields (tool_calls,tool_call_id,name). - LLMRequest — what gets sent: the
modelid, alist[Message], optionaltoolsschemas,temperature,max_tokens, and anextradict[str, Any]for provider-specific options. - LLMResponse — what comes back:
content, anytool_calls, ausagedict, and the resolvedmodel.
The Model protocol is just three async methods — complete, complete_stream, and close — so anything that satisfies them can be passed wherever a model is expected, including straight into Agent(model=...).
from agentroute import Agent, resolve_model
# Resolve once, reuse across agents
model = resolve_model("claude-sonnet-4")
agent = Agent(name="assistant", model=model)See the models reference for full signatures, defaults, and the streaming LLMChunk type.