LLM Providers¶
Configure which AI model powers Lumen.
First time setup?
See the Installation guide for step-by-step instructions on setting up API keys and environment variables for each provider.
Quick start¶
Set your API key and launch:
Lumen auto-detects the provider from environment variables.
Specify provider explicitly¶
To use a specific provider, pass the --provider flag:
lumen-ai serve penguins.csv --provider anthropic
lumen-ai serve penguins.csv --provider google --model 'gemini-3.8-flash'
lumen-ai serve penguins.csv --provider kilo --model 'openai/gpt-4o-mini'
lumen-ai serve penguins.csv --provider ollama --model 'qwen3.6:35b-a3b'
lumen-ai serve penguins.csv --provider mlx
For more CLI options, see the CLI guide.
Different models per agent¶
Use cheap models for simple tasks, powerful models for complex tasks:
import lumen.ai as lmai
model_config = {
"default": {"model": "gpt-6-luna"}, # Cheap for most agents
"sql": {"model": "gpt-4.1"}, # Powerful for SQL
"vega_lite": {"model": "gpt-4.1"}, # Powerful for charts
"deck_gl": {"model": "gpt-4.1"}, # Powerful for 3D maps
"analyst": {"model": "gpt-4.1"}, # Powerful for analysis
}
llm = lmai.llm.OpenAI(model_kwargs=model_config)
ui = lmai.ExplorerUI(data='penguins.csv', llm=llm)
ui.servable()
Agent names map to model types: SQLAgent → "sql", VegaLiteAgent → "vega_lite", etc.
Track token usage and cost¶
LLM instances collect provider-reported token usage in llm.usage. Configure usage_pricing with USD rates per million tokens to estimate cost for each model; prices are not bundled with Lumen and must be kept current for your provider and account.
import asyncio
from lumen.ai.llm import OpenAI
async def main():
llm = OpenAI(
model_kwargs={"default": {"model": "gpt-4o-mini"}},
usage_pricing={
"gpt-4o-mini": {"input": 0.15, "cached": 0.075, "output": 0.60},
},
)
with llm.capture_usage() as request_usage:
await llm.invoke([{"role": "user", "content": "Say hello."}])
print(request_usage.input_tokens, request_usage.output_tokens)
print(request_usage.cached_tokens, request_usage.cost_usd)
print(llm.usage.cost_usd) # Total across calls made with this LLM instance
asyncio.run(main())
Set OPENAI_API_KEY before running this example. The usage_pricing keys must match the model names returned by the provider, including any provider prefix. Each model needs input and output rates; cached defaults to the input rate when omitted. For Anthropic, cache_write can price cache creation separately and also defaults to the input rate. Cached reads and writes are included in input_tokens; cached_tokens counts reads only. Inspect request_usage.records or llm.usage.records for individual responses and their model, token counts, and cost.
llm.usage aggregates the lifetime of that LLM instance. llm.capture_usage() creates a scoped collector for calls within the context, including calls from child async tasks; use separate contexts to attribute concurrent requests. A scope includes all provider calls made within it, such as tool rounds and structured-output retries. Consume a streaming response fully to receive its final usage report. If a provider does not return usage, no record is added. If a model has no pricing entry, its tokens are recorded but cost_usd is None for that record and for any collector containing it.
Configure temperature¶
Lower temperature = more deterministic. Higher = more creative.
model_config = {
"sql": {
"model": "gpt-4.1",
"temperature": 0.1, # Deterministic SQL
},
"chat": {
"model": "gpt-5.4-mini",
"temperature": 0.4, # Natural conversation
},
}
Recommended ranges: 0.1 (SQL) to 0.4 (chat).
Disabling temperature¶
Some newer models (e.g. reasoning models) reject a temperature argument. Set temperature=None to omit it from requests entirely, falling back to the provider's own default:
This works on every provider. Local backends (MLX, Llama.cpp) fall back to their built-in sampler default when None.
Supported providers¶
For installation and API key setup instructions, see the Installation guide.
Cloud providers¶
| Provider | Default Model | Popular Models |
|---|---|---|
| OpenAI | gpt-6-luna |
gpt-5.6-luna, gpt-6-sol, gpt-5.4-nano |
| Anthropic | claude-haiku-4-5 |
claude-sonnet-4-6, claude-opus-4-6 |
gemini-3.8-flash |
gemini-3.7-flash, gemini-3.5-flash, gemini-3.1-flash-lite |
|
| Mistral | mistral-small-latest |
mistral-medium-latest, mistral-large-latest, ministral-14b-latest |
| Azure OpenAI | gpt-5.4-mini |
gpt-5.4, gpt-5.4-nano |
| Azure Mistral | azureai |
mistral-large, mistral-small |
Reasoning Models Not Suitable for Dialog
Reasoning models like gpt-5, o4-mini, and gemini-2.0-flash-thinking are significantly slower than standard models. They are designed for single, complex queries that require deep thinking, not interactive chat interfaces. For dialog-based applications like Lumen, use standard models for better user experience.
The OpenAI default, gpt-6-luna, is a reasoning model. Chat completions rejects function tools while reasoning is active, so Lumen disables reasoning for it and keeps it as fast as a standard model. To use reasoning with function tools, switch to the responses API: lmai.llm.OpenAI(api="responses"). Some models, such as gpt-6.1-sol, cannot disable reasoning and only work with the responses API.
Local providers¶
| Provider | Default Model | Description |
|---|---|---|
| Ollama | qwen3.6:35b-a3b |
External service; requires ollama pull to manage and run models. |
| Llama.cpp | unsloth/Qwen3.6-35B-A3B-GGUF |
Embedded runner; automatically downloads GGUF models from HuggingFace. |
| MLX | mlx-community/Qwen3.5-9B-MLX-4bit |
Apple Silicon native; runs models in-process via Metal GPU or connects to mlx_lm.server. |
| AI Navigator | server-model |
Desktop GUI; provides a local OpenAI-compatible API once the API server is started. |
Recommended local models:
- General purpose:
qwen3.6:35b-a3b,qwen3.8:27b,gemma4:26b,gpt-oss:20b - Smaller machines:
qwen3.5:9b - Coding:
qwen3-coder:30b - Reasoning:
nemotron-3.5-lightning:30b
Codex CLI and Claude Code¶
Use a locally authenticated Codex CLI or Claude Code CLI as Lumen's LLM provider. This lets an individual user run Lumen with an existing CLI subscription, without supplying an API key to Lumen.
Before starting Lumen, install the relevant CLI, make sure its executable is
on your PATH, and sign in to it once:
Start Lumen without loading any data:
The CLI's configured default model is used unless you select one explicitly.
For example, Claude Code provides a stable sonnet alias:
The same --model option accepts model identifiers available to the
authenticated Codex CLI account.
To start with a dataset, add its path before the provider arguments:
Local development only
The server process launches a command-line agent for every LLM request. Do
not expose this configuration as a public or multi-user Lumen deployment.
Codex runs with its read-only sandbox and Claude Code uses plan mode by
default. Claude Code can use up to three turns per Lumen request so it can
complete internal tool calls. The providers collect a completed response
rather than streaming tokens, and do not yet expose Lumen function tools to
the coding CLIs.
GitHub Copilot CLI¶
Use a locally authenticated GitHub Copilot CLI as Lumen's LLM provider. It works with any Copilot plan, including Copilot Free.
Install the CLI, make sure copilot is on your PATH, and sign in once:
Start Lumen with it, with or without a dataset:
Copilot picks the model by default. Lumen then keeps one Copilot process
running and sends every request to it, which avoids several seconds of CLI
startup per request. If your plan lets you choose a model, pass a name listed
by copilot help config with --model; Lumen then starts the CLI once per
request, which is slower. Plans limited to automatic model selection, such as
Copilot Free, only accept --model auto; any other name fails with
Model ... from --model flag is not available.
Local development only
Lumen runs each request in a fresh temporary directory and starts Copilot
with every tool disabled, so it can only answer in text: it cannot run shell
commands, read or write files, fetch URLs, or call MCP servers, and it skips
instruction files such as AGENTS.md. Each Lumen question makes several
Copilot requests (seven to ten for a query and a chart), and each one counts
against your plan's Copilot usage allowance, which is small on Copilot
Free. Copilot keeps a copy of every prompt under ~/.copilot/session-state,
and Lumen does not disable any hooks or plugins you have configured for
Copilot.
Antigravity CLI¶
Use a locally authenticated Google Antigravity CLI (agy) as Lumen's LLM
provider. Requests count against your Antigravity account's usage limits.
Install the CLI, then run agy once and sign in with your Google account:
Start Lumen with it, with or without a dataset:
Lumen uses gemini-3.8-flash-low by default, a low-cost Flash model and the
fastest one in testing: a penguins query and chart took about 40 seconds,
compared with about 90 seconds using Antigravity's own default. Run
agy models to list the other models you can pass with --model.
Local development only
Each request runs in a fresh temporary directory, and Lumen keeps one agy
process started ahead of time so the next request does not wait for the CLI
to start. In this mode Antigravity blocks shell commands, reading and
writing files, fetching URLs and the browser, but it can still run web
searches, so text from your prompt or data may be sent to Google Search. If
the model reaches for a blocked tool, the request fails with an error that
names the tool. Any permissions.allow rules in
~/.gemini/antigravity-cli/settings.json also apply to Lumen's requests.
Small models (<= 8B)
Models with 8B parameters or fewer likely need --code-execution prompt to successfully create reliable Vega-Lite specifications.
Security
Setting --code-execution uses Python's exec function to run LLM-generated code locally. This is insecure as the model could generate and execute malicious code. Only use this with trusted models and in secure environments.
Router / Gateway providers¶
| Provider | Default Model | Description |
|---|---|---|
| Kilo | kilo-auto/free |
OpenAI-compatible gateway providing access to models from OpenAI, Anthropic, Google, Meta, Mistral, and more through a single API key. |
| OpenRouter | openai/gpt-6-luna |
OpenAI-compatible gateway providing access to models from OpenAI, Anthropic, Google, Meta, Mistral, and more through a single API key. |
| AWS Bedrock | us.anthropic.claude-haiku-4-5-20251001-v1:0 |
Enterprise gateway providing access to models from Anthropic, Amazon, Qwen, DeepSeek, and more. |
| LiteLLM | gpt-5.4-mini |
Unified router to access 100+ models across all supported LLM providers. |
| AI Catalyst | ai_catalyst |
Enterprise model server; provides access to validated, governed open-source models. |
AWS Bedrock options:
- AnthropicBedrock - Optimized for Claude models using Anthropic's SDK
- Bedrock - Universal access to all Bedrock models (Claude, Llama, Mistral, Titan, etc.)
Kilo¶
Kilo provides an OpenAI-compatible API for routing requests to models from multiple providers.
import lumen.ai as lmai
llm = lmai.llm.Kilo(
model_kwargs={
"default": {"model": "kilo-auto/free"},
"sql": {"model": "anthropic/claude-sonnet-4.6"},
"vega_lite": {"model": "openai/gpt-5.4-mini"},
}
)
ui = lmai.ExplorerUI(data='penguins.csv', llm=llm)
ui.servable()
OpenRouter¶
OpenRouter provides an OpenAI-compatible API for routing requests to models from multiple providers.
import lumen.ai as lmai
llm = lmai.llm.OpenRouter(
model_kwargs={
"default": {"model": "openai/gpt-6-luna"},
"sql": {"model": "deepseek/deepseek-v4-pro-0813"},
"vega_lite": {"model": "qwen/qwen3.8-flash"},
}
)
ui = lmai.ExplorerUI(data='penguins.csv', llm=llm)
ui.servable()
Advanced configuration¶
API keys¶
Each provider has three ways to supply an API key, resolved in this order:
api_keyparam — pass it directly at instantiationapi_key_env_var— a class variable naming the environment variable to read from; defaults to the value fromPROVIDER_ENV_VARSfor that providerPROVIDER_ENV_VARS— the module-level dict that is the source of truth for env var names; changing it updates bothapi_key_env_vardefaults andget_available_llm()auto-detection
import os
os.environ["OPENAI_API_KEY"] = "sk-..."
llm = lmai.llm.OpenAI() # api_key is auto-populated from OPENAI_API_KEY
If your key is stored under a different environment variable name, there are three ways to override it:
lmai.llm.OpenAI.api_key_env_var = "MY_OPENAI_KEY"
llm = lmai.llm.OpenAI() # now reads from MY_OPENAI_KEY
class MyOpenAI(lmai.llm.OpenAI):
api_key_env_var = "MY_OPENAI_KEY"
llm = MyOpenAI(model_kwargs={"default": {"model": "gpt-5.4-mini"}})
import lumen.ai.llm as llm_module
llm_module.PROVIDER_ENV_VARS["openai"] = "MY_OPENAI_KEY"
Option 3 also affects get_available_llm(), which uses PROVIDER_ENV_VARS to
auto-detect which provider to use at startup.
Note
api_key_env_var is a plain class variable, not a param. It must be set at
the class level — changing it on an instance has no effect, because
models() is a classmethod and resolves it from the class.
The default values for each provider are:
| Provider | Default api_key_env_var |
|---|---|
OpenAI |
OPENAI_API_KEY |
OpenRouter |
OPENROUTER_API_KEY |
Anthropic |
ANTHROPIC_API_KEY |
Google |
GEMINI_API_KEY |
MistralAI |
MISTRAL_API_KEY |
AzureOpenAI / AzureMistralAI |
AZUREAI_ENDPOINT_KEY |
Bedrock / AnthropicBedrock |
AWS_ACCESS_KEY_ID |
AICatalyst |
AI_CATALYST_API_KEY |
Ollama, LlamaCpp, MLX, AINavigator |
(none required) |
Checking available models¶
models() is a classmethod you can call directly to inspect what models are
available from a provider without instantiating the LLM:
print(lmai.llm.OpenAI.models())
# {'gpt-5.4-mini', 'gpt-4.1', 'gpt-4.1-nano', ...}
print(lmai.llm.Anthropic.models())
# {'claude-haiku-4-5', 'claude-sonnet-4-6', 'claude-opus-4-6', ...}
print(lmai.llm.OpenRouter.models())
# {'openai/gpt-6-luna', 'qwen/qwen3.8-flash', 'z-ai/glm-5.3-flash', ...}
Providers that don't support listing models (e.g. AzureOpenAI, LiteLLM) return
an empty set.
If you implement a custom provider subclass, override models() to support it:
class MyProvider(lmai.llm.Llm):
api_key_env_var = "MY_PROVIDER_API_KEY"
@classmethod
def models(cls) -> set[str]:
import requests
api_key = os.environ.get(cls.api_key_env_var)
if not api_key:
return set()
resp = requests.get("https://my-provider.com/models",
headers={"Authorization": f"Bearer {api_key}"})
return {m["id"] for m in resp.json()["data"]}
Custom endpoints¶
Override default API endpoints:
llm = lmai.llm.OpenAI(
api_key='...',
endpoint='https://your-custom-endpoint.com/v1'
)
Managed Identity (Azure)¶
Use Azure Active Directory authentication:
from azure.identity import DefaultAzureCredential, get_bearer_token_provider
token_provider = get_bearer_token_provider(
DefaultAzureCredential(),
"https://cognitiveservices.azure.com/.default"
)
llm = lmai.llm.AzureOpenAI(
api_version="2024-02-15-preview",
endpoint="https://your-resource.openai.azure.com/",
model_kwargs={
"default": {
"model": "gpt4o-mini",
"azure_ad_token_provider": token_provider
}
}
)
Prompt caching (Anthropic)¶
Anthropic caches the static prefix of each request (tools → system → messages) so repeated context isn't reprocessed, lowering latency and input-token cost. It is controlled by the cache parameter:
"5m"(default) — 5-minute cache; refreshes for free on each hit."1h"— 1-hour cache; costs more on write, useful for slower-cadence sessions.None— disabled.
Note
Caching only applies above a model-specific minimum prefix length (4,096 tokens for the default claude-haiku-4-5); shorter prefixes are silently not cached. AnthropicBedrock ignores this setting, since AWS Bedrock does not support automatic caching.
Fallback models (LiteLLM)¶
Automatically retry with backup models if primary fails:
llm = lmai.llm.LiteLLM(
model_kwargs={
"default": {"model": "gpt-5.4-mini"}
},
fallback_models=[
"gpt-5.4-nano",
"anthropic/claude-haiku-4-5",
"gemini/gemini-3.8-flash"
]
)
Remote Ollama server¶
Connect to Ollama running on another machine:
llm = lmai.llm.Ollama(
endpoint='http://your-server:11434/v1',
model_kwargs={"default": {"model": "qwen3.6:35b-a3b"}}
)
AI Navigator (Local)¶
Connect to Anaconda AI Navigator running on your machine:
By default, it uses http://localhost:8080/v1.
MLX (Apple Silicon)¶
Run models natively on Apple Silicon Macs using the MLX framework. Requires pip install mlx-lm.
Two modes of operation:
Key parameters:
| Parameter | Default | Description |
|---|---|---|
endpoint |
http://localhost:8080/v1 |
Set to None for in-process mode |
enable_thinking |
False |
Enable model reasoning/thinking mode |
max_tokens |
8192 |
Max tokens per response (per-request, does not change server defaults) |
temperature |
0.4 |
Sampling temperature (per-request, does not change server defaults) |
chat_template_kwargs |
{} |
Extra args for apply_chat_template (in-process mode only) |
Recommended models for Apple Silicon:
| Memory | Model | Size |
|---|---|---|
| 16 GB | mlx-community/Qwen3.5-9B-MLX-4bit |
~5 GB |
| 32 GB | mlx-community/Qwen3.8-27B-4bit |
~16 GB |
| 64 GB+ | mlx-community/Qwen3.6-35B-A3B-4bit |
~20 GB |
Server mode vs in-process
Server mode keeps the model loaded between requests and supports multiple clients. In-process mode is simpler (no separate terminal) but loads the model into your Python process.
AI Catalyst (Enterprise)¶
Connect to Anaconda AI Catalyst running on your company's infrastructure:
# Requires AI_CATALYST_BASE_URL and AI_CATALYST_API_KEY env vars
llm = lmai.llm.AICatalyst()
Or configure explicitly:
llm = lmai.llm.AICatalyst(
endpoint='https://your-company.anacondaconnect.com/api/v1/model-servers/your-server/v1',
api_key='...'
)
Model types¶
Agent class names convert to model types automatically:
| Agent | Model type |
|---|---|
| SQLAgent | sql |
| VegaLiteAgent | vega_lite |
| DeckGLAgent | deck_gl |
| ChatAgent | chat |
| AnalysisAgent | analysis |
| (others) | default |
Conversion rule: remove "Agent" suffix, convert to snake_case.
Additional model types:
edit- Used when fixing errorsui- Used for UI initialization
Model string formats¶
Different providers use different model string formats:
- OpenAI:
"gpt-6-luna","gpt-5.6-luna","gpt-6-sol","gpt-5.4-nano" - Anthropic:
"claude-haiku-4-5","claude-sonnet-4-6","claude-opus-4-6" - OpenRouter:
"openai/gpt-6-luna","qwen/qwen3.8-flash","z-ai/glm-5.3-flash","deepseek/deepseek-v4.1-flash" - Google:
"gemini-3.8-flash","gemini-3.1-flash-lite" - Mistral:
"mistral-large-latest","mistral-small-latest" - Azure:
"your-deployment-name"(use your Azure deployment name) - Bedrock:
"us.anthropic.claude-haiku-4-5-20251001-v1:0","us.anthropic.claude-sonnet-4-6","us.amazon.nova-2-lite-v1:0" - LiteLLM:
"gpt-5.4-mini"(OpenAI),"anthropic/claude-sonnet-4-6"(Anthropic),"gemini/gemini-3.8-flash"(Google)
For OpenRouter and LiteLLM, use the provider/model format for routed models.
Troubleshooting¶
"API key not found" - Set environment variable or pass api_key= in Python.
Wrong model used - Model type names must be snake_case: "sql" not "SQLAgent".
High costs - Use gpt-5.4-mini or claude-haiku-4-5 for default, reserve gpt-5.4 or claude-sonnet-4-6 for critical tasks (sql, vega_lite, analyst).
Slow responses - Local models are slower than cloud APIs. Use cloud providers when speed matters.
AWS credentials not found - For Bedrock, ensure AWS credentials are configured. See Installation guide.
Best practices¶
Use efficient models elsewhere:
default- Simple tasks work well withgpt-5.4-miniorclaude-haiku-4-5chat- Conversation works with smaller models
Avoid reasoning models in dialog interfaces:
- Reasoning models (
o1,o1-mini,gemini-3.0-pro) are significantly slower - They're designed for single, complex queries, not interactive chat
- For Lumen's dialog interface, use standard models (
mistral-small-latest,gpt-5.4-mini,claude-sonnet-4-6) - Reserve reasoning models for batch processing or one-off complex analyses
Set temperature by task:
- 0.1 for SQL (deterministic)
- 0.3-0.4 for analysis and chat
- 0.5-0.7 for creative tasks
Test before deploying:
- Different models behave differently. Test with real queries.