LLMProxy has one provider-execution boundary with several transport adapters around it, including local Unix-socket calls through SafeRPC. Authentication, quota checks, policy, routing, accounting, and tracing do not belong in HTTP-only code.
Request path
LLMProxy.chat/2 ReqLLM provider HTTP routes SafeRPC
│ │ │ │
└─────────────────┴────────┬───────┴─────────────┘
▼
LLMProxy.Provider
│
actor and API-key lookup
│
quota and public-model access
│
before-request guardrails
│
per-key concurrent admission
│
cache key and catalog plan
│
timeout / retry / fallback / circuit
│
response and stream normalization
│
guardrails / cache / usage / tracing
│
caller-specific responseCore request contracts use ReqLLM.Context, ReqLLM.Message, and LLMProxy.Protocol.Request. OpenAI and Anthropic wire maps stay at protocol and transport boundaries.
Module map
LLMProxy
├── Provider in-process execution boundary and ReqLLM provider
│ ├── Credential redacted request-scoped provider credential
│ └── TokenCodec at-rest credential codec and key migration tools
├── HTTP
│ ├── Router standalone Plug router
│ ├── RouteSpec route table shared with Phoenix
│ └── Routes protocol adapters
├── Phoenix.Router Phoenix forwarding macros
├── Providers
│ ├── Registry provider/model lookup and catalog resolution
│ ├── Execution attempts, fallback, timeout, and retry handling
│ ├── CircuitBreaker deployment health state
│ └── Routing deployment ordering strategies
├── Protocol OpenAI/Anthropic request normalization
├── Catalog public model aliases and deployment data
├── TokenPool credential selection and persisted cooldown state
├── Cache adapter, deterministic key, and policy
├── GuardrailPipeline request, response, and stream policy hooks
├── ConcurrencyLimiter monitored per-key request and stream leases
├── Accounting usage, spend, traces, and message recording
├── Telemetry telemetry events and OpenTelemetry spans
├── Stream normalized events, SSE writing, and heartbeats
├── Storage public facade and adapter boundary
├── Admin optional Incant resources and dashboard
└── RPC SafeRPC server for admin and operationsTransport adapters
Local Elixir
LLMProxy.chat/2 normalizes a prompt or ReqLLM context, then calls LLMProxy.Provider. It returns %LLMProxy.Response{} with the normalized ReqLLM response, provider identity, usage, trace ID, and cache metadata.
ReqLLM
LLMProxy.Provider registers as ReqLLM provider :llm_proxy. The adapter builds an internal request and halts the Req pipeline with the response produced by the same provider boundary.
A ReqLLM call can execute locally or pass its request through SafeRPC when a client/socket option is provided.
HTTP
LLMProxy.HTTP.RouteSpec is shared by the standalone Plug router and Phoenix macros. Route modules authenticate, parse wire requests, call the provider boundary, and render protocol-shaped responses or SSE events.
HTTP modules should not implement provider selection, accounting, or fallback.
SafeRPC
SafeRPC exposes typed operation tuples over a local Unix socket. Ordinary chat calls target LLMProxy; remote admin operations target LLMProxy.Admin; drain operations target LLMProxy.Ops.
The socket transports portable request and response data. Repos, schemas, callbacks, policies, and side effects remain in the LLMProxy service VM.
Catalog and providers
The catalog separates public model identity from deployment identity:
%LLMProxy.Catalog.Model{
name: "fast",
deployments: [
%LLMProxy.Catalog.Deployment{
provider_name: "openai-primary",
provider: LLMProxy.Providers.ReqLLM,
upstream_model: "gpt-4.1-mini",
token_pool: "openai-production",
timeout_ms: 30_000
}
]
}Named providers with an adapter execute through LLMProxy.Providers.ReqLLM. Built-in modules remain for native or specialized integrations.
Deployment attempts carry provider identity, upstream model, token pool, timeout, order, weight, and circuit-breaker settings. This keeps credential selection and health state attached to the route that needs them.
Storage
LLMProxy.Storage delegates to config :llm_proxy, storage: .... The default LLMProxy.Storage.Ecto implementation uses LLMProxy.Storage.Repo, which delegates to the configured Ecto repo.
LLMProxy.Storage
│
▼
configured storage adapter
│
▼
LLMProxy.Storage.Ecto
│
▼
LLMProxy.Storage.Repo
│
▼
host repo / bundled SQLite / bundled QuackDBLibrary hosts usually provide their existing repo. The standalone production release uses the bundled QuackDB repo and supervises a managed local QuackDB process.
Provider-token schemas contain stored values only. A configured token codec
encodes writes before Ecto receives them. The token pool selects a stored row and
decodes it into a redacted LLMProxy.Provider.Credential only at the provider
boundary. Admin lists and schema inspection do not receive plaintext values.
Database-specific queries and migrations branch on the configured repo adapter. Provider execution depends on the storage facade, not a concrete database.
Resource cleanup test budget
The loopback resource stress test repeats normal, complete streaming, and client-canceled streaming requests in one BEAM. Its listener uses two acceptors and sixteen maximum connections. Its client pool uses one connection. The test requires an inherited BEAM file-descriptor limit of 128 and reports the measured limit when it is lower. After warm-up, the allowed growth is two BEAM ports and eight processes. All provider work, drain leases, optional concurrency leases, and the test listener must return to zero or stop before the test completes.
Optional integrations
Optional modules use compile-time availability checks:
- Incant admin modules load only when Incant is installed.
- SQLite repo modules load only when
ecto_sqlite3is installed. - Phoenix router integration requires Phoenix in the host.
- Igniter installation support requires Igniter.
The gateway core remains usable without those packages.
Ownership rules
- Provider execution and fallback belong under
LLMProxy.ProviderandLLMProxy.Providers. - HTTP modules parse and render only.
- Phoenix embedding helpers belong under
LLMProxy.Phoenix. - Deployment selection belongs under
LLMProxy.Providers.Routing, not Phoenix routing. - Endpoint/model differences belong in provider data unless they require a new protocol.
- Storage access goes through
LLMProxy.StorageorLLMProxy.Storage.Repofacades. LLMProxy.Adminis the only service-owned admin surface; do not add a second public admin API.- SafeRPC transports operations but does not move executable service internals between VMs.