Architecture
Understand how go-llm-router turns a provider name into a unified Agent and normalizes provider responses.
System overview
graph TB
App[Caller Application]
Server[cmd/test]
Router[core/router]
Contract[core Agent / StreamAgent]
Policy[Shared Policy Reasoning / Mode]
Providers[Provider Adapters]
APIs[Provider APIs]
OAuth[core/oauth]
Keychain[System Keychain]
App --> Router
App --> Contract
Server --> Router
Router --> Contract
Contract --> Policy
Contract --> Providers
Providers --> APIs
OAuth --> Keychain
OAuth --> Providers
Layers
| Layer | Packages | Responsibility |
|---|---|---|
| Caller | Application code, cmd/test |
Build messages and tools; choose model, reasoning level, execution mode, and synchronous or streaming consumption |
| Router | core/router |
Parse a provider name and construct the matching core.Agent |
| Shared core | core |
Define transport-neutral types, reasoning and fast-tier policy, and HTTP client defaults |
| Provider adapter | core/<provider> |
Authenticate, convert payloads, select endpoints, and normalize responses |
| OAuth | core/oauth/* |
Login, token storage, expiry checks, and refresh |
Router
router.New takes the text before @ as the provider prefix, drops an optional [tag], looks the prefix up in the factory table, and constructs the Agent. A missing key returns an error. The bracket tag affects naming only, never selection.
Shared policy
core standardizes call parameters while deliberately staying out of the wire payload. ClampReasoning caps Reasoning to each model's range; SupportFast matches Mode against the provider/model whitelist, and only a hit lets the adapter add the native field (Claude's speed, the service_tier used by the OpenAI and Grok families). The served tier is always read back from the response, never assumed from the request.
Provider adapters
Each adapter owns system-prompt merging, message and tool conversion, authentication, endpoint selection, and upstream decoding, exposing only core.Output or core.StreamEvent. OpenAI and Copilot switch between the Chat Completions and Responses APIs per model; Codex and Grok OAuth consume SSE internally and return a completed result; Claude and Copilot additionally expose SendStream.
OAuth lifecycle
OAuth packages persist tokens as JSON in the operating-system keychain. Codex and Grok apply a 60-second safety buffer to expiry checks, then exchange a refresh grant and write the replacement back; Copilot uses the GitHub device flow and refreshes through a session token.
Request flow
- The caller builds
router.Configwith aNamesuch asopenai@gpt-5.4. router.Newselects a factory and constructs an Agent.- The caller invokes
SendorSendStreamwith messages, optional tools, aReasoninglevel, and aMode. - The adapter clamps the reasoning level, checks fast-tier eligibility, shapes the provider payload, and sends the request.
- The response is converted to
core.Output(or a sequence ofStreamEventvalues) and returned with the HTTP status code and error. - Usage is unified into
Input,Output,CacheCreate, andCacheRead; the served tier lands inOutput.ServiceTier.
Further reading
The module-level expansion — per-module diagrams, sequence diagrams, and the OAuth state machine — lives in the repository's doc/architecture.md.
Related pages
- Core Concepts — shared contracts and normalization rules.
- API Reference — exported types and functions.
- Configuration — credentials and routing names.