Appearance
Providers
This guide explains how to configure provider-backed model access for package examples, FastAPI apps, and source demos. It does not compare model quality or recommend one provider over another.
This guide is for developers who want to run real hosted model calls from SwarmForge. It assumes that you know which provider you want to use and can set environment variables or pass configuration in code.
After reading this guide, you should be able to:
- set the required environment variables for your integration path
- configure
ModelConfig(...) - understand which variables are used by package examples, FastAPI apps, and the demo UI
The provider layer exposes one OpenAI-compatible client wrapper and one configuration object. Every runnable runtime example in these docs uses this layer, so you should explicitly choose both the provider and the model before you run anything.
Environment Variables By Service
Use the lists below instead of relying on .env.example being open locally.
Package runtime examples
Applies to Getting Started, Create Your First Agent, Create Your First Multi-Agent Swarm, and Orchestration.
Always set:
MODEL_PROVIDERLLM_MODEL
Then set the provider-specific auth variable for the provider you chose:
- OpenRouter:
OPENROUTER_API_KEY - Gemini:
GEMINI_API_KEYorGOOGLE_API_KEY - OpenAI-compatible
openai:OPENAI_API_KEY - Any other provider name such as
anthropic-proxy:${PROVIDER_NAME_UPPER}_API_KEY
Optional OpenRouter attribution headers:
OPENROUTER_SITE_URLOPENROUTER_APP_NAMEOPENROUTER_FALLBACK_MODELScomma-separated fallback model slugs
Optional OpenRouter latency/cost tuning:
OPENROUTER_SERVICE_TIER"priority"or"flex"(OpenAI models only)
FastAPI server defaults
Applies to API, examples/fastapi_swarm.py, and examples/fastapi_server.py.
FastAPI reads provider configuration from the server environment or from default_model_config in your app wiring. Set the same provider variables as the package runtime examples:
MODEL_PROVIDERLLM_MODEL- matching API key variable for that provider
Optional server bind variables:
SWARMFORGE_HOSTdefault:127.0.0.1SWARMFORGE_PORTdefault:8000
Demo UI
Applies to the local demo-ui/ app when it is connected to examples/fastapi_swarm.py.
The demo UI reads only these variables from the root .env:
MODEL_PROVIDERoptional, defaults the provider selectorLLM_MODELoptional, defaults the model fieldSWARMFORGE_HOSToptional, defaults API host to127.0.0.1SWARMFORGE_PORToptional, defaults API port to8000
The demo UI does not read provider API keys directly. Those stay on the server side.
OpenRouter conversation script
Applies to examples/openrouter_conversation.py.
Required:
MODEL_PROVIDER=openrouterLLM_MODELOPENROUTER_API_KEYOPENROUTER_SITE_URLOPENROUTER_APP_NAME
Supported Modes
- OpenRouter through
https://openrouter.ai/api/v1 - Gemini OpenAI-compatible mode through
https://generativelanguage.googleapis.com/v1beta/openai/ - other OpenAI-compatible endpoints by overriding
base_url,api_key, andmodel
OpenRouter
Copy the repository example config first:
bash
cp .env.example .envThen set the OpenRouter values in .env:
dotenv
MODEL_PROVIDER=openrouter
LLM_MODEL=openrouter/auto
OPENROUTER_API_KEY=sk-or-...
OPENROUTER_SITE_URL=https://your-app.example
OPENROUTER_APP_NAME="Your App Name"
OPENROUTER_FALLBACK_MODELS=openai/gpt-4o-mini,anthropic/claude-3.5-sonnetShell exports still work, but .env is the default path used by the examples and tests. Replace LLM_MODEL with the exact OpenRouter model slug you want when you need stable behavior instead of router-selected defaults.
Default OpenRouter settings:
- provider:
openrouter - model:
openrouter/auto - base URL:
https://openrouter.ai/api/v1
Gemini OpenAI-compat
Set Gemini values in .env:
dotenv
MODEL_PROVIDER=gemini
LLM_MODEL=gemini-3-flash-preview
GEMINI_API_KEY=...GOOGLE_API_KEY is also supported if you prefer that variable name.
Gemini uses:
- provider:
gemini - base URL:
https://generativelanguage.googleapis.com/v1beta/openai/
OpenAI-compatible Custom Provider
If you are not using OpenRouter or Gemini, set:
dotenv
MODEL_PROVIDER=openai
LLM_MODEL=gpt-4.1-mini
OPENAI_API_KEY=...If you construct ModelConfig(...) directly in code, you can also override base_url there for another OpenAI-compatible endpoint.
Configuration Surface
ModelConfig supports:
providerbase_urlapi_keymodeltemperaturemax_tokenssite_urlapp_namedefault_headersdefault_chat_paramsfallback_modelscache_enabled(OpenRouter only)cache_ttl(OpenRouter only)prompt_caching(OpenRouter only)prompt_cache_ttl(OpenRouter only)service_tier(OpenRouter/OpenAI only)
Minimal client setup
python
from swarmforge.env import require_env_vars
from swarmforge.evaluation.provider import ModelConfig, OpenAIClientWrapper
env = require_env_vars("MODEL_PROVIDER", "LLM_MODEL")
client = OpenAIClientWrapper(ModelConfig())
response = client.chat_completion(
messages=[
{"role": "system", "content": "You are a concise assistant."},
{
"role": "user",
"content": (
"Reply with one sentence that names the active provider and model: "
f"{env['MODEL_PROVIDER']} / {env['LLM_MODEL']}"
),
},
]
)
print(response.choices[0].message.content)Switch providers by changing .env values rather than editing the code sample.
For tool and handoff examples, choose a model that supports tool or function calling.
Response Caching (OpenRouter)
OpenRouter offers a response caching feature that caches identical API request responses server-side. Cache hits return immediately with zero billing and lower latency — the request never reaches the provider.
How it works
Two requests are considered identical when they share the same API key, model, endpoint type, streaming mode, and request body (including all parameters). OpenRouter generates a cache key from these inputs. Cache is scoped to your API key — different keys don't share cache.
Default TTL is 300 seconds (5 minutes), configurable from 1 to 86400 seconds (24 hours).
Enabling via environment
dotenv
OPENROUTER_CACHE_ENABLED=true
OPENROUTER_CACHE_TTL=600Enabling in code
python
from swarmforge.evaluation.provider import ModelConfig
config = ModelConfig(
provider="openrouter",
model="openrouter/auto",
cache_enabled=True,
cache_ttl=600, # optional, defaults to 300
)Passing cache_enabled=False disables caching even if the env var is set. Omitting it (or passing None) falls back to the env var.
The X-OpenRouter-Cache and X-OpenRouter-Cache-TTL headers are injected automatically into every request made by the OpenAIClientWrapper when caching is enabled.
Cache clearing
To force a fresh response for a specific request, pass X-OpenRouter-Cache-Clear: true via extra_headers or default_headers:
python
config = ModelConfig(
cache_enabled=True,
default_headers={"X-OpenRouter-Cache-Clear": "true"},
)Limitations
- Cache is scoped to your API key — rotating the key empties the cache
- Concurrent identical requests arriving before the first response is cached both result in a miss
- Available only for OpenRouter provider
- Not available with account-level Zero Data Retention (ZDR)
Prompt Caching (OpenRouter)
Prompt caching is a provider-level feature that reduces cost when repeated prompt prefixes (e.g. system instructions, long context) are shared across requests. Unlike response caching, this is not free — cached tokens are billed at a discount — but OpenRouter uses provider sticky routing to maximize hit rates.
Provider behavior
| Provider | Requires config | Notes |
|---|---|---|
| OpenAI | No | Automatic, minimum 1024 tokens |
| DeepSeek | No | Automatic |
| Gemini 2.5 | Yes (prompt_caching) | Needs cache_control breakpoints in messages |
| Anthropic Claude | Yes (prompt_caching) | Needs cache_control at request level or per-block |
| Grok | No | Automatic |
For Anthropic models, enabling prompt_caching injects a top-level cache_control into the request body, which tells OpenRouter to apply automatic caching — the system advances cache breakpoints forward as conversation history grows. This is ideal for multi-turn conversations.
For OpenAI and DeepSeek, prompt caching is automatic regardless of this flag (when prompts are long enough).
Enabling via environment
dotenv
OPENROUTER_PROMPT_CACHING=true
OPENROUTER_PROMPT_CACHE_TTL=1h # optional: "1h" for 1-hour TTL (default 5min)Enabling in code
python
from swarmforge.evaluation.provider import ModelConfig
config = ModelConfig(
provider="openrouter",
prompt_caching=True,
prompt_cache_ttl="1h", # optional, default is 5-minute TTL
)The cache_control field is merged into extra_body of every request made by the OpenAIClientWrapper when prompt caching is enabled.
Explicit cache breakpoints
For fine-grained control (e.g. caching only specific content blocks), pass cache_control directly in message content via default_chat_params. This is useful when you want to cache a large system prompt but keep user messages dynamic:
python
config = ModelConfig(
prompt_caching=False, # disable automatic injection
default_chat_params={
"extra_body": {
"cache_control": {"type": "ephemeral"}
}
},
)For Gemini explicit breakpoints, insert cache_control inside message content blocks — though this requires constructing the messages array manually rather than relying on automatic injection.
Notes
- Prompt caching and response caching can be used together; they operate at different layers
- Provider sticky routing ensures subsequent requests hit the same provider endpoint for warm caches
- Cache hit metrics appear in
usage.prompt_tokens_details.cached_tokensin API responses
Latency Optimization
Time-to-first-token (TTFT) depends on provider routing, model selection, request size, and client configuration. These are the highest-ROI levers available in the SDK.
Pin your model
openrouter/auto evaluates available providers on every request to pick the cheapest or fastest route. This adds 100–300ms of routing overhead. Pin to a specific model slug to skip evaluation:
dotenv
# Instead of this (adds routing latency):
LLM_MODEL=openrouter/auto
# Use this (direct provider+model):
LLM_MODEL=openai/gpt-4o
LLM_MODEL=anthropic/claude-sonnet-4Service tier (OpenAI models only)
OpenRouter supports a service_tier parameter that controls queue priority. "priority" reduces provider-side queue wait at higher cost; "flex" reduces cost at potentially higher latency.
dotenv
OPENROUTER_SERVICE_TIER=priorityOr in code:
python
config = ModelConfig(
provider="openrouter",
service_tier="priority",
)Accepted values: auto, default, flex, priority. Only applies to OpenAI provider models.
Enable response caching
Cache identical requests server-side. Hits return with zero billing and no provider round-trip:
dotenv
OPENROUTER_CACHE_ENABLED=true
OPENROUTER_CACHE_TTL=600Enable prompt caching
Cache repeated prompt prefixes (system instructions, long context) at the provider level. OpenAI and DeepSeek do this automatically; Anthropic and Gemini require explicit opt-in:
dotenv
OPENROUTER_PROMPT_CACHING=trueClient timeout
The SDK now sets a default 30-second timeout on all HTTP calls (configurable at construction). Requests that hang will fail fast instead of blocking for the SDK default of 10 minutes:
python
from swarmforge.evaluation.provider import ModelConfig, OpenAIClientWrapper
client = OpenAIClientWrapper(
ModelConfig(),
timeout=15.0, # seconds
max_retries=1,
)Connection reuse
The OpenAIClientWrapper configures HTTP connection pooling with keep-alive so subsequent requests reuse the same TCP connection instead of opening a new one for every call.
Wrapper Behavior
OpenAIClientWrapper.chat_completion(...) passes through:
- chat messages
- optional tool definitions
- optional timeout override
- optional structured
response_format - optional provider-specific
extra_params
Notes
.envvalues are loaded automatically from the current working directory upward- provider-specific headers are applied for OpenRouter attribution
- OpenRouter can retry with fallback models from
fallback_modelsorOPENROUTER_FALLBACK_MODELS - extra OpenAI-compatible request fields can be passed through
default_chat_params - explicit
api_keyvalues support${ENV_VAR}substitution