Local servers run a model on your own machine: no API key, no per-token cost, and they keep
working when a cloud provider is down. CAELUM supports four of them by name. They all
expose an OpenAI-compatible API, so they only need the openai SDK.
| Provider name | Server | Default address |
|---|---|---|
ollama |
Ollama | http://localhost:11434/v1 |
llamacpp |
llama.cpp llama-server |
http://localhost:8080/v1 |
vllm |
vLLM | http://localhost:8000/v1 |
lmstudio |
LM Studio | http://localhost:1234/v1 |
Cloud first, local model as the last resort
from caelum import OpenAI
client = OpenAI(
retry=True,
fallback=["anthropic:claude-haiku-5-5", "ollama:qwen3:1.7b"],
)
If OpenAI fails with a transient error (rate limit, 5xx, timeout, network), CAELUM tries
Claude, then the local model. Your code still receives a normal ChatCompletion.
caelum.routed_to(response) tells you who answered. Local answers count as $0 in
client.metrics.
The Anthropic client can fall back to a local model too: the request is translated to the
OpenAI format and the answer translated back into a Message.
A local model is usually smaller and slower than a cloud model. Use it as a last resort that keeps your product answering, not as an equal replacement.
Local model first, cloud as the backup
Point the client at the local server with base_url:
from caelum import OpenAI
client = OpenAI(
base_url="http://localhost:11434/v1",
api_key="ollama", # ignored by the server, required by the SDK
fallback=["openai:gpt-4o-mini"],
provider_keys={"openai": "sk-..."}, # or OPENAI_API_KEY in the environment
)
response = client.chat.completions.create(
model="qwen3:1.7b", messages=[{"role": "user", "content": "Hello"}]
)
CAELUM recognizes a localhost address (the port names the server: 11434 = Ollama,
8080 = llama.cpp, 8000 = vLLM, 1234 = LM Studio). Two consequences:
- requests are counted as free, whatever the model is called. If your
localhostaddress is a proxy to a paid API (e.g. a LiteLLM proxy forwarding togpt-4o), the cost shown byclient.metricswill be $0: use the provider's real URL to track costs; - the
openai:fallback tier reaches the real OpenAI API, not your local server.
Routing simple requests to a local model
routes:
- name: short-questions-stay-local
match:
messages_max_tokens: 200
target: qwen3:1.7b
provider: ollama
See Routing for the full rule format.
Streaming
An OpenAI-format client can fall back to a local model while streaming, because both speak the same format. Streaming fallback between OpenAI and Anthropic formats is not supported yet.
Another address or machine
| Server | Environment variable |
|---|---|
| Ollama | CAELUM_OLLAMA_BASE_URL, or Ollama's own OLLAMA_HOST |
| llama.cpp | CAELUM_LLAMACPP_BASE_URL |
| vLLM | CAELUM_VLLM_BASE_URL |
| LM Studio | CAELUM_LMSTUDIO_BASE_URL |
export CAELUM_OLLAMA_BASE_URL=http://gpu-box.local:11434/v1
For full control, pass a ready-made client:
provider_clients={"ollama": openai.OpenAI(base_url=..., api_key="x")}.
Getting started with Ollama
# install from https://ollama.com, then:
ollama pull qwen3:1.7b
ollama run qwen3:1.7b "Say hi" # check that it works
Ollama starts in the background and listens on port 11434. On a laptop without a dedicated graphics card, models of 1 to 2 billion parameters are a practical size.