OpenRouter-compatible LLM router with unified batch support. Route requests across OpenAI, Anthropic, and Google with a single API.
Project description
anymodel
OpenRouter-compatible LLM router with unified batch support for Python. Self-hosted, zero fees.
Route requests across OpenAI, Anthropic, and Google with a single API. Add any OpenAI-compatible provider. Run as an SDK or standalone HTTP server.
Install
pip install anymodel
Quick Start
Set your API keys as environment variables:
export OPENAI_API_KEY=sk-...
export ANTHROPIC_API_KEY=sk-ant-...
export GOOGLE_API_KEY=AIza...
SDK Usage
import asyncio
from anymodel import AnyModel
async def main():
client = AnyModel()
response = await client.chat.completions.create(
model="anthropic/claude-sonnet-4-6",
messages=[{"role": "user", "content": "Hello!"}],
)
print(response["choices"][0]["message"]["content"])
asyncio.run(main())
Streaming
stream = await client.chat.completions.create(
model="openai/gpt-4o",
messages=[{"role": "user", "content": "Write a haiku"}],
stream=True,
)
async for chunk in stream:
content = chunk["choices"][0].get("delta", {}).get("content", "")
print(content, end="", flush=True)
Supported Providers
Set the env var and go. Models are auto-discovered from each provider's API.
| Provider | Env Var | Example Model |
|---|---|---|
| OpenAI | OPENAI_API_KEY |
openai/gpt-4o |
| Anthropic | ANTHROPIC_API_KEY |
anthropic/claude-sonnet-4-6 |
GOOGLE_API_KEY |
google/gemini-2.5-pro |
|
| Mistral | MISTRAL_API_KEY |
mistral/mistral-large-latest |
| Groq | GROQ_API_KEY |
groq/llama-3.3-70b-versatile |
| DeepSeek | DEEPSEEK_API_KEY |
deepseek/deepseek-chat |
| xAI | XAI_API_KEY |
xai/grok-3 |
| Together | TOGETHER_API_KEY |
together/meta-llama/Llama-3.3-70B-Instruct-Turbo |
| Fireworks | FIREWORKS_API_KEY |
fireworks/accounts/fireworks/models/llama-v3p3-70b-instruct |
| Perplexity | PERPLEXITY_API_KEY |
perplexity/sonar-pro |
| Ollama | OLLAMA_BASE_URL |
ollama/llama3.3 |
Fallback Routing
Try multiple models in order. If one fails, the next is attempted:
response = await client.chat.completions.create(
model="",
models=[
"anthropic/claude-sonnet-4-6",
"openai/gpt-4o",
"google/gemini-2.5-pro",
],
route="fallback",
messages=[{"role": "user", "content": "Hello"}],
)
Tool Calling
response = await client.chat.completions.create(
model="anthropic/claude-sonnet-4-6",
messages=[{"role": "user", "content": "What's the weather in NYC?"}],
tools=[{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get current weather for a location",
"parameters": {
"type": "object",
"properties": {"location": {"type": "string"}},
"required": ["location"],
},
},
}],
tool_choice="auto",
)
for call in response["choices"][0]["message"].get("tool_calls", []):
print(call["function"]["name"], call["function"]["arguments"])
Batch Processing
Process many requests with native provider batch APIs or concurrent fallback.
Submit and wait
results = await client.batches.create_and_poll({
"model": "openai/gpt-4o-mini",
"requests": [
{"custom_id": "req-1", "messages": [{"role": "user", "content": "Summarize AI"}]},
{"custom_id": "req-2", "messages": [{"role": "user", "content": "Summarize ML"}]},
],
})
for result in results["results"]:
print(result["custom_id"], result["response"]["choices"][0]["message"]["content"])
Submit now, check later
# Submit and get the batch ID
batch = await client.batches.create({
"model": "anthropic/claude-haiku-4-5",
"requests": [
{"custom_id": "req-1", "messages": [{"role": "user", "content": "Summarize AI"}]},
],
})
print(batch["id"]) # "batch-abc123"
# Check status any time
status = await client.batches.get("batch-abc123")
print(status["status"]) # "pending", "processing", "completed"
# Wait for results when ready
results = await client.batches.poll("batch-abc123")
# List all batches
all_batches = await client.batches.list()
# Cancel a batch
await client.batches.cancel("batch-abc123")
Batch configuration
client = AnyModel({
"batch": {
"poll_interval": 10.0, # default poll interval in seconds
"concurrency_fallback": 10, # concurrent request limit for non-native providers
},
"io": {
"read_concurrency": 30, # concurrent file reads (default: 20)
"write_concurrency": 15, # concurrent file writes (default: 10)
},
})
Configuration
client = AnyModel({
"anthropic": {"api_key": "sk-ant-..."},
"openai": {"api_key": "sk-..."},
"aliases": {
"default": "anthropic/claude-sonnet-4-6",
"fast": "anthropic/claude-haiku-4-5",
"smart": "anthropic/claude-opus-4-6",
},
"defaults": {
"temperature": 0.7,
"max_tokens": 4096,
"retries": 2,
},
})
# Use aliases as model names
response = await client.chat.completions.create(
model="fast",
messages=[{"role": "user", "content": "Quick answer"}],
)
Config File
Create anymodel.config.json in your project root:
{
"anthropic": {
"api_key": "${ANTHROPIC_API_KEY}"
},
"aliases": {
"default": "anthropic/claude-sonnet-4-6"
},
"defaults": {
"temperature": 0.7,
"max_tokens": 4096
}
}
${ENV_VAR} references are interpolated from environment variables.
Custom Providers
Add any OpenAI-compatible endpoint:
client = AnyModel({
"custom": {
"ollama": {
"base_url": "http://localhost:11434/v1",
"models": ["llama3.3", "mistral"],
},
},
})
response = await client.chat.completions.create(
model="ollama/llama3.3",
messages=[{"role": "user", "content": "Hello from Ollama"}],
)
Server Mode
Run as a standalone HTTP server compatible with the OpenAI SDK:
pip install anymodel[server]
anymodel serve --port 4141
Then point any OpenAI-compatible client at it:
from openai import OpenAI
client = OpenAI(base_url="http://localhost:4141/api/v1", api_key="unused")
response = client.chat.completions.create(
model="anthropic/claude-sonnet-4-6",
messages=[{"role": "user", "content": "Hello via server"}],
)
Also Available
- Node.js:
@probeo/anymodelon npm
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file anymodel_py-0.1.0.tar.gz.
File metadata
- Download URL: anymodel_py-0.1.0.tar.gz
- Upload date:
- Size: 29.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ca71583fdc71e392148cf90695bc9e896a5d63ea5ca5cda0935154d8f35a14c9
|
|
| MD5 |
ed996595c47d0b63e4015ec2489fd3fb
|
|
| BLAKE2b-256 |
6fe03e60622d1cac047ed804f56eebcb47f24fd6fa8a0939ea9dcde282c3b3a6
|
File details
Details for the file anymodel_py-0.1.0-py3-none-any.whl.
File metadata
- Download URL: anymodel_py-0.1.0-py3-none-any.whl
- Upload date:
- Size: 38.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
780240496d628d06f50cb306c2354173dff07ea72a96128638b16320ff0b2775
|
|
| MD5 |
297af78841a4109ade9e029bfe4c4cc4
|
|
| BLAKE2b-256 |
de35974adf47ed2c11f8d6e9c5a33e079b7f61af0c1b77b690698e85cccc1641
|