Instructions for coding agents (and people) working in this repository. CLAUDE.md imports this file.
theagentrouter (imported as theagentrouter) is an open-source Python library. Agent frameworks use it to call AI models. It keeps LiteLLM's way of calling models (completion, provider/model names, response fields, error names) in a small install whose only dependency is httpx.
It never calls a provider itself: every call goes to an Agent Router gateway, the local one or one set with AGENTROUTER_BASE_URL, and both get the same requests. Claude goes in Anthropic Messages, everything else in OpenAI Chat Completions, with the model name as the caller wrote it.
| Path | What it holds |
|---|---|
src/theagentrouter/__init__.py |
Public API: completion, acompletion, model lookups |
src/theagentrouter/routing.py |
Where a call goes: api_base, a gateway you set, or the local gateway. Never a provider directly; settings from the environment and AGENTROUTER_HOME/config.json |
src/theagentrouter/routes.py |
Routes: names of your own with fallbacks and retries (route, Retry), stored in AGENTROUTER_HOME/routes.json and served by the local gateway |
src/theagentrouter/local_gateway.py |
The local gateway: which providers the environment has, the aigw config, starting, finding and stopping aigw |
local-gateway/ |
The theagentrouter-local-gateway package: the aigw program, one wheel per platform |
packages/theagentrouter/ |
The theagentrouter package: no code; installs theagentrouter-core (the root package) and the local gateway |
src/theagentrouter/openai_format.py |
Build OpenAI requests; read responses and SSE streams |
src/theagentrouter/anthropic_format.py |
Convert OpenAI-style messages and tools to Anthropic Messages; read responses and streams back |
src/theagentrouter/transport.py |
HTTP with httpx, sync and async, streaming, error mapping |
src/theagentrouter/exceptions.py |
LiteLLM's error names; from_http maps HTTP errors to them |
src/theagentrouter/types.py |
LiteLLM-shaped response types; readable by attribute and as dicts |
src/theagentrouter/models.py, model_catalog.json |
Model prices and limits from theagentrouter/agent-router-models (bundled; never fetched at run time) |
src/theagentrouter/compat/ |
The LiteLLM-compatible module and install_as_litellm() |
surface.txt |
The recorded public surface |
tests/ |
Tests against a local fake provider; test_adk.py and test_openai_agents.py run real Google ADK and OpenAI Agents SDK agents |
scripts/ |
surface.py, sync_models.py, smoke_real.py, smoke_adk_real.py, smoke_local_gateway.py, smoke_routes.py |
- Keep LiteLLM's API. Same function names, arguments, response fields and error names. A framework that works with LiteLLM must work after
install_as_litellm(). - No provider code beyond the two formats. The SDK never calls a provider; a new provider is one row in
local_gateway.PROVIDERS(itsaigwschema, address and key variables), which the local gateway's config is written from. Anything that needs translation belongs in the gateway, not here. - One dependency. Do not add runtime dependencies without a strong reason;
httpxis the only one. - The public surface is a contract.
tests/test_surface.pyfails when the surface changes. After a deliberate change, runpython scripts/surface.py --writeand say why in the change description. - Settings come from the environment, then
AGENTROUTER_HOME/config.json. Do not add a second place to read them. - No secrets in tests or examples. Tests use the fake provider in
tests/fake_provider.py. Real-provider checks live inscripts/smoke_real.pyand read keys from the environment.
- The local gateway (
src/theagentrouter/local_gateway.py, package inlocal-gateway/) has end-to-end tests that are skipped unless the package is installed. Before finishing a change to routing or the gateway, runmake local-gateway AIGW=/path/to/aigw, thenpytest: a skipped run proves nothing about the gateway. With keys in.env, also runscripts/smoke_local_gateway.py. - Never commit the
aigwprogram (local-gateway/src/theagentrouter_local_gateway/bin/, git-ignored), and never write a key value into the gateway's config: the local config names private key files (keys/<VARIABLE>, mode 0600) and an exported config holds${VAR}references, both filled in byaigw. - The local gateway and a gateway set with
AGENTROUTER_BASE_URLget the same requests: the model name as written, Claude in Messages under/anthropic/v1, the rest in OpenAI format under/v1. Never add an SDK-only header, and never send a gateway key asAuthorizationon a Messages call (Agent Router passes it on to Anthropic). The gateway's config has one rule per model that passes the provider its own name, spread over routes of at most 15 rules (Gateway API's limit is 16 andaigwadds one). Anthropic goes in its own Messages format to/anthropic/v1/messages, becauseaigwdoes not translate OpenAI to Anthropic. Details indocs/local-gateway.md.
python3 -m venv .venv && .venv/bin/pip install -e ".[dev]"
.venv/bin/pytest # all tests, no keys needed
.venv/bin/ruff check . && .venv/bin/ruff format --check .
.venv/bin/python scripts/surface.py --write # after a deliberate API change
.venv/bin/python scripts/sync_models.py # refresh the model tableDEVELOPMENT.md has the details, including the framework tests and the release steps. User docs are in docs/: keep install.md, usage.md, coming-from-litellm.md and frameworks.md in step with the code, especially the LiteLLM lists when compat/litellm.py or transport.IGNORED changes.
pytest,ruff checkandruff format --checkpass.- New behavior has a test against the fake provider.
CHANGELOG.mdhas a line under "Unreleased" for anything a user would notice.