Library mode embeds LLMProxy in an existing Elixir application. The host application owns storage and supervision; LLMProxy supplies provider execution, routing, authentication, accounting, and optional HTTP routes.
Use this mode when calls originate inside one application or when an existing Phoenix endpoint should expose the gateway.
Host application configuration
Point LLMProxy at the host repo. The repo must already be supervised by the host application.
# config/runtime.exs
config :llm_proxy,
repo: MyApp.Repo,
http_enabled: false,
master_key: System.fetch_env!("LLM_PROXY_MASTER_KEY"),
providers: %{
"openai" => %{api_keys: System.fetch_env!("OPENAI_API_KEYS")},
"anthropic" => %{api_keys: System.fetch_env!("ANTHROPIC_API_KEYS")}
},
models: [
fast: [
routing: :ordered,
routes: [
[to: :openai, model: "gpt-4.1-mini"],
[to: :anthropic, model: "claude-3-5-haiku-20241022", order: 2]
]
]
]Set http_enabled: false unless you intentionally want LLMProxy to start its own Cowboy listener. Phoenix-mounted routes do not require that listener.
LLMProxy detects the configured repo adapter through repo.__adapter__/0. The storage implementation supports SQLite, PostgreSQL, MySQL-compatible adapters, and QuackDB/DuckDB. Database adapter dependencies belong to the host application.
Migrations
With Igniter installed, add LLMProxy's migration path to the host aliases:
mix igniter.install llm_proxy
The installer adds mix llm_proxy.migrate and updates conventional ecto.setup and test aliases to include both migration directories:
priv/repo/migrations
deps/llm_proxy/priv/repo/migrationsRun migrations:
mix llm_proxy.migrate
Without Igniter, pass both paths explicitly:
mix ecto.migrate \
--migrations-path priv/repo/migrations \
--migrations-path deps/llm_proxy/priv/repo/migrations
Release migration code should pass both directories to Ecto.Migrator.run/4. Do not copy LLMProxy migrations into the host directory; keeping the dependency path avoids duplicate versions and makes upgrades explicit.
In-process calls
LLMProxy.chat/2 accepts a prompt, a message list, or a ReqLLM.Context:
{:ok, response} =
LLMProxy.chat("Summarize the last deployment",
model: "fast",
api_key: llm_proxy_key,
temperature: 0.2,
max_tokens: 400,
metadata: %{"deployment_id" => "dep_123"},
tags: ["operations"]
)
text = ReqLLM.Response.text(response.message)
trace_id = response.trace_id
usage = response.usagePass either:
:api_keywith a raw LLMProxy key or an existing API-key schema/map; or:actorwith%LLMProxy.Actor{}.
The key determines model access, quotas, budget limits, attribution, and stored usage. The master key is accepted for bootstrap and operator calls but should not be distributed to ordinary clients.
ReqLLM provider
LLMProxy registers itself as the ReqLLM provider :llm_proxy when the application starts:
model = %{
provider: :llm_proxy,
id: "fast",
model: "fast"
}
{:ok, response} =
ReqLLM.Generation.generate_text(model, "Hello",
api_key: llm_proxy_key
)
ReqLLM.Response.text(response)This still executes in-process. It is useful when application code already speaks ReqLLM and should gain LLMProxy routing and accounting without an HTTP round trip.
Phoenix routes
Add Phoenix as a dependency in the host, then import LLMProxy's router macros:
defmodule MyAppWeb.Router do
use MyAppWeb, :router
use LLMProxy.Router
scope "/" do
pipe_through :api
llm_proxy "/llm", setup: false
end
endThe mount exposes core routes below /llm, including:
/llm/v1/models
/llm/v1/chat/completions
/llm/v1/messages
/llm/v1/responses
/llm/v1/moderations
/llm/v1/feedbackGeneration, moderation, and feedback routes authenticate with Authorization: Bearer <key> or x-api-key. Model listing is public. Setup helper routes remain disabled unless setup: true is passed.
SafeRPC calls
A remote BEAM consumer can call the same execution path over a Unix socket with SafeRPC:
{:ok, request} = LLMProxy.Provider.chat_request("Hello", model: "fast")
{:ok, response} =
SafeRPC.call(socket, {LLMProxy, :chat}, request,
meta: %{api_key: llm_proxy_key},
timeout: 30_000
)Configure :rpc_socket in the LLMProxy host to start its SafeRPC server. The socket is also the boundary used by the optional Incant admin host.
Custom storage
Advanced hosts can replace the Ecto-backed implementation:
config :llm_proxy,
storage: MyApp.LLMProxyStorageThe module implements LLMProxy.Storage.Adapter. Keep provider execution independent of the storage implementation; callers should use the public LLMProxy.Storage facade rather than concrete repo modules.