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.

1
A URL means a custom endpoint

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")
2
ollama/ means local Ollama

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")
3
Everything else goes to OpenRouter

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
Resolution at a glance
Model stringProviderBase URLAuth
https://host/v1/model-idCustom OpenAI-compatiblehttps://host/v1resolved key
ollama/NAMELocal Ollamahttp://localhost:11434/v1none
vendor/modelOpenRouterhttps://openrouter.ai/api/v1resolved key
bare-nameOpenRouter (vendor inferred)https://openrouter.ai/api/v1resolved 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 withInferred prefixExampleResolves to
claudeanthropicclaude-sonnet-4anthropic/claude-sonnet-4
gpt, o1, o3, o4openaigpt-4oopenai/gpt-4o
geminigooglegemini-2.0-flashgoogle/gemini-2.0-flash
llamameta-llamallama-3.3-70bmeta-llama/llama-3.3-70b
deepseekdeepseekdeepseek-v3deepseek/deepseek-v3
mistralmistralaimistral-largemistralai/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.

Tip

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.

1
Explicit api_key= argument

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-...")
2
Environment variables, then the config file

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-..."
3
No key

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.

~/.agentroute/config
# OpenRouter catch-all key
api_key=sk-or-...
 
# Optional per-provider overrides
openai_api_key=sk-...
openai_base_url=https://api.openai.com/v1

The 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.

Caution

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/.

local.py
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.

custom.py
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)
Note

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"), optional content, and tool-call fields (tool_calls, tool_call_id, name).
  • LLMRequest — what gets sent: the model id, a list[Message], optional tools schemas, temperature, max_tokens, and an extra dict[str, Any] for provider-specific options.
  • LLMResponse — what comes back: content, any tool_calls, a usage dict, and the resolved model.

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.

Next steps