Stop juggling 5 different SDKs. Start building with one.
π Documentation β’ π Quick Start β’ π‘ Examples β’ π€ Contributing β’ β Support
- Why Orion SDK?
- Features
- Quick Start
- Supported Providers
- API Reference
- Examples
- Installation
- Advanced Usage
- Error Handling
- Troubleshooting
- FAQ
- Contributing
- License
Every major AI platform ships its own client library. OpenAI has openai, Anthropic has anthropic, Google has google-generativeai. Orion gives you the same thing β but unified.
Instead of juggling 5 different SDKs with 5 different APIs, different error formats, and different tool-calling conventions, you get one package, one interface, one complete() call.
from orion_sdk import OrionClient
client = OrionClient()
client.add_provider("anthropic", api_key="sk-ant-...")
client.add_provider("openai", api_key="sk-...")
response = client.complete("Hello, world!")
print(response.content)That's it. No provider lock-in, no API format memorization, no 300-page docs.
Tip
Use provider aliases to save keystrokes β "claude" instead of "anthropic", "gpt" instead of "openai", "local" instead of "ollama". See the full alias list below.
- π 5 providers, one API β OpenAI, Anthropic, Google, OpenRouter, Ollama
- βοΈ Automatic fallback chains β provider A fails? Try B, then C, automatically
- π Streaming support β async iterator of chunks for real-time output
- π οΈ Tool/function calling β unified format, auto-converts between provider schemas
- β‘ Token counting β tiktoken when available, smart estimation otherwise
- π Context window validation β catches overflow before it hits the API
- π¦ Rate limiting β per-provider token bucket with configurable RPM
- π Zero lock-in β swap providers by changing one string, not your codebase
- π§© Custom providers β subclass
Providerand register it - πΎ Message history β Built-in conversation context management
Note
You only need pip install orion_sdk for the core. Provider packages (openai, anthropic, google-generativeai) are optional β install only the ones you actually use.
git clone https://github.com/windyworldair/Orion.git
cd Orion
pip install -e .Important
PyPI coming in 2026! Once released, you'll use pip install orion-sdk. For now, clone from GitHub or wait for the official release.
from orion_sdk import create_client
client = create_client("anthropic", api_key="sk-ant-...")
response = client.complete("What is 2 + 2?")
print(response.content) # "4"Important
Never hardcode API keys in your code. Use environment variables β Orion SDK automatically picks up OPENAI_API_KEY, ANTHROPIC_API_KEY, GEMINI_API_KEY, and OPENROUTER_API_KEY from your environment.
from orion_sdk import OrionClient
client = OrionClient()
client.add_provider("anthropic", api_key="sk-ant-...")
client.add_provider("openai", api_key="sk-...")
client.add_provider("google", api_key="...")
# Try Anthropic β OpenAI β Google automatically
client.set_fallback_chain("anthropic", "openai", "google")
response = client.complete("Write a Python HTTP server")
print(response.content)
print(response.model) # "claude-sonnet-4-20250514"
print(response.provider) # "anthropic"
print(response.usage) # {"prompt_tokens": 42, "completion_tokens": 187}from orion_sdk import OrionClient
client = OrionClient()
client.add_provider("openai", api_key="sk-...", set_default=True)
for chunk in client.stream("Tell me a story"):
print(chunk.content, end="", flush=True)from orion_sdk import OrionClient, ToolDefinition
client = OrionClient()
client.add_provider("anthropic", api_key="sk-ant-...")
tools = [
ToolDefinition(
name="get_weather",
description="Get the current weather for a city",
parameters={
"type": "object",
"properties": {
"city": {"type": "string", "description": "City name"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
},
"required": ["city"]
}
)
]
response = client.complete("What's the weather in Tokyo?", tools=tools)
if response.has_tool_calls:
for call in response.tool_calls:
print(f"Call: {call.name}({call.arguments})")
# {"city": "Tokyo", "unit": "celsius"}| Provider | Package | Models | API Key |
|---|---|---|---|
| OpenAI | openai |
GPT-4o, o1, o3, GPT-4o-mini, GPT-4-turbo | OPENAI_API_KEY |
| Anthropic | anthropic |
Claude Opus 4, Claude Sonnet 4, Claude 3.5, Claude 3 | ANTHROPIC_API_KEY |
google-generativeai |
Gemini 2.5 Pro, Gemini 2.5 Flash, Gemini 2.0, Gemini 1.5 | GEMINI_API_KEY |
|
| OpenRouter | openai |
21+ models (Claude, GPT, Gemini, Grok, Llama, DeepSeek, Qwen...) | OPENROUTER_API_KEY |
| Ollama | requests |
Llama 3.3, Mistral, Qwen 2.5, CodeLlama, Phi3, Gemma2... | None β fully local |
# These all work
client.add_provider("claude", ...) # β anthropic
client.add_provider("gpt", ...) # β openai
client.add_provider("gemini", ...) # β google
client.add_provider("local", ...) # β ollamaTip
Aliases also work in complete() and stream() β just pass model="claude-opus-4" and Orion resolves the provider automatically.
client = OrionClient(config={
"default_provider": "anthropic",
"default_model": "claude-sonnet-4-20250514",
"timeout": 120.0,
"max_retries": 3,
"rate_limits": {"anthropic": 60, "openai": 120}
})| Method | Description |
|---|---|
add_provider(name, api_key, ...) |
Register a provider |
remove_provider(name) |
Remove a registered provider |
set_fallback_chain(*providers) |
Set failover priority |
complete(prompt, ...) |
Synchronous completion |
stream(prompt, ...) |
Streaming completion (async iterator) |
count_tokens(text, model) |
Count tokens |
count_messages_tokens(messages, model) |
Count tokens for a message list |
get_context_limit(model) |
Get model's context window size |
list_providers() |
List registered providers |
list_models(provider) |
List available models |
register_custom_provider(name, cls) |
Register a custom provider class |
# Message
msg = Message.user("Hello!")
msg = Message.system("You are helpful.")
msg.to_dict() # {"role": "user", "content": "Hello!"}
# Response
response.content # str
response.tool_calls # list[ToolCall]
response.model # str
response.provider # str
response.usage # {"prompt_tokens": ..., "completion_tokens": ...}
response.has_tool_calls # bool
# StreamChunk
chunk.content # str
chunk.finish_reason # str | None
chunk.model # str
# ToolDefinition β auto-converts to provider formats
tool.to_openai() # OpenAI schema
tool.to_anthropic() # Anthropic schema
tool.to_gemini_tools() # Gemini schemafrom orion_sdk import (
OrionError, # Base exception
ProviderError, # Error from a specific provider
ProviderNotFoundError, # Provider not registered
AuthenticationError, # Invalid API key
RateLimitError, # Rate limit hit
ContextOverflowError, # Input exceeds context window
AllProvidersFailedError, # All fallback providers failed
TimeoutError, # Request timed out
InvalidConfigError, # Bad configuration
)
try:
response = client.complete(prompt)
except ContextOverflowError as e:
print(f"Too many tokens: {e.tokens} > {e.limit}")
except AllProvidersFailedError as e:
print(f"All failed:\n{e.errors}")from orion_sdk import OrionClient, RateLimiter
client = OrionClient()
client.add_provider("anthropic", api_key="sk-ant-...")
client.add_provider("openai", api_key="sk-...")
# Built-in rate limiter (default 60 req/min per provider)
# Or configure at init:
client = OrionClient(config={
"rate_limits": {"anthropic": 50, "openai": 100}
})from orion_sdk import OrionClient, AllProvidersFailedError
client = OrionClient()
client.add_provider("anthropic", api_key="sk-ant-...")
client.add_provider("openai", api_key="sk-...")
client.set_fallback_chain("anthropic", "openai")
try:
response = client.complete("Explain quantum computing in 100 words")
print(f"β
Response from {response.provider}:")
print(f" {response.content}")
except AllProvidersFailedError as e:
print(f"β All providers failed: {e}")from orion_sdk import OrionClient
client = OrionClient()
client.add_provider("anthropic", api_key="sk-ant-...")
model = "claude-opus-4-20250514"
tokens = client.count_tokens("Hello world", model=model)
limit = client.get_context_limit(model)
print(f"Tokens: {tokens}, Limit: {limit}")
if tokens > limit:
print("β Input too long!")
else:
response = client.complete("Hello world", model=model)
print(f"Cost estimate: ${response.cost_estimate:.4f}")from orion_sdk import OrionClient, ToolDefinition
client = OrionClient()
client.add_provider("anthropic", api_key="sk-ant-...")
tools = [
ToolDefinition(
name="weather",
description="Get weather",
parameters={
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"]
}
)
]
response = client.complete("What's the weather in Tokyo?", tools=tools)
for call in response.tool_calls:
print(f"Tool: {call.name}, Args: {call.arguments}")from orion_sdk import OrionClient
client = OrionClient()
client.add_provider("anthropic", api_key="sk-ant-...")
client.add_provider("openai", api_key="sk-...")
for provider in ["anthropic", "openai"]:
response = client.complete("2+2=?", model=provider)
print(f"{provider}: {response.content}")from orion_sdk import OrionClient
client = OrionClient()
client.add_provider("openai", api_key="sk-...", set_default=True)
for chunk in client.stream("Write a haiku"):
print(chunk.content, end="", flush=True)from orion_sdk import OrionClient, ContextOverflowError
client = OrionClient()
client.add_provider("anthropic", api_key="sk-ant-...")
try:
huge_text = "x" * 1000000
response = client.complete(huge_text)
except ContextOverflowError as e:
print(f"β {e.tokens} tokens > {e.limit} limit")
print("π‘ Solution: Split input or use larger model")from orion_sdk import OrionClient, Provider, ProviderConfig, Response
from orion_sdk.providers.base import StreamChunk
class MockProvider(Provider):
NAME = "mock"
MODELS = {"mock": {"context": 4096, "output": 1024}}
def complete(self, messages, model="", **kwargs):
return Response(
content="Mock response",
model=model,
provider=self.NAME,
usage={"prompt_tokens": 10, "completion_tokens": 5}
)
def stream(self, messages, model="", **kwargs):
yield StreamChunk(content="Mock", model=model, finish_reason="stop")
def list_models(self):
return [{"id": m} for m in self.MODELS.keys()]
client = OrionClient()
client.register_custom_provider("mock", MockProvider)
client.add_provider("mock", api_key="test")
response = client.complete("Test")from orion_sdk import OrionClient, Message
client = OrionClient()
client.add_provider("anthropic", api_key="sk-ant-...")
messages = [
Message.system("You are a Python expert"),
Message.user("How do I reverse a list?"),
]
response = client.complete(messages=messages)
messages.append(Message.assistant(response.content))
messages.append(Message.user("What about strings?"))
response = client.complete(messages=messages)
print(response.content)from orion_sdk import OrionClient, RateLimitError
import time
client = OrionClient(config={"rate_limits": {"openai": 3}})
client.add_provider("openai", api_key="sk-...")
for i in range(5):
try:
print(f"Request {i+1}...")
response = client.complete("Hello")
print("β
Success")
except RateLimitError as e:
print(f"β³ Rate limited, retry in {e.retry_after}s")
time.sleep(e.retry_after)export ANTHROPIC_API_KEY="sk-ant-..."
export OPENAI_API_KEY="sk-..."
export GEMINI_API_KEY="..."from orion_sdk import OrionClient
# No API keys needed β reads from environment!
client = OrionClient()
client.add_provider("anthropic")
client.add_provider("openai")
client.add_provider("google")
response = client.complete("Hello!")git clone https://github.com/windyworldair/Orion.git
cd Orion
pip install -e .Note
You only need the core package. Provider dependencies are optional and installed on-demand.
# Just Anthropic
pip install -e ".[anthropic]"
# Anthropic + OpenAI
pip install -e ".[anthropic,openai]"
# All providers
pip install -e ".[all]"
# Development
pip install -e ".[dev]"from orion_sdk import OrionClient, __version__
print(f"Orion SDK {__version__} installed!")
client = OrionClient()
print(f"Ready to use!")Caution
Installing from source requires git and pip. Make sure you have both installed before proceeding.
from orion_sdk import OrionClient
client = OrionClient(config={
"rate_limits": {
"anthropic": 30, # 30 requests/min
"openai": 60, # 60 requests/min
"google": 20, # 20 requests/min
}
})
# Requests are automatically queued if rate limit hit
response = client.complete("Important request")Tip
Adjust rate limits based on your API plan. Check provider dashboards for your current limits.
from orion_sdk import OrionClient, ContextOverflowError
client = OrionClient()
client.add_provider("anthropic", api_key="sk-ant-...")
model = "claude-opus-4-20250514"
limit = client.get_context_limit(model) # 200,000
# Prevents wasting tokens on failed requests
huge_text = "x" * 500000
try:
response = client.complete(huge_text)
except ContextOverflowError as e:
print(f"β {e.tokens} > {e.limit}")
print("π‘ Solution: Use streaming, split input, or summarize")Warning
Context limits are checked before API calls. Exceeding them raises an error immediately β no tokens wasted.
from orion_sdk import OrionClient
client = OrionClient(config={
"enable_cache": True,
"cache_ttl": 3600 # 1 hour
})
# First call β counts tokens, caches result
tokens1 = client.count_tokens("Hello world", model="claude-opus-4")
# Second call β instant (from cache)
tokens2 = client.count_tokens("Hello world", model="claude-opus-4")Note
Caching improves performance for repeated token counts. Cache expires after TTL (time-to-live).
from orion_sdk import OrionClient
client = OrionClient()
# OpenAI settings
client.add_provider("openai", api_key="sk-...", organization="my-org")
# Anthropic settings
client.add_provider("anthropic", api_key="sk-ant-...", max_retries=5)
# Google settings
client.add_provider("google", api_key="...", timeout=30)
# Ollama settings
client.add_provider("ollama", base_url="http://localhost:11434")Important
Each provider can have different configuration. Set them appropriately for your use case.
from orion_sdk import (
OrionError,
ContextOverflowError,
RateLimitError,
AllProvidersFailedError,
AuthenticationError
)
try:
response = client.complete("Hello")
except AuthenticationError as e:
print(f"β Auth failed: {e}")
print("π‘ Check your API key and environment")
except ContextOverflowError as e:
print(f"β Input too long: {e.tokens} > {e.limit}")
except RateLimitError as e:
print(f"β³ Rate limited, retry after {e.retry_after}s")
except AllProvidersFailedError as e:
print(f"β All providers failed:")
for provider, error in e.errors.items():
print(f" {provider}: {error}")
except OrionError as e:
print(f"β General error: {e}")Caution
Always catch AllProvidersFailedError when using fallback chains β it means all options have failed.
from orion_sdk import OrionClient, AllProvidersFailedError
client = OrionClient()
client.add_provider("anthropic", api_key="sk-ant-...")
client.add_provider("openai", api_key="sk-...")
client.set_fallback_chain("anthropic", "openai")
try:
response = client.complete("Important request")
except AllProvidersFailedError as e:
print("β οΈ All providers down, using fallback")
response = get_cached_response() # Your fallback logicfrom orion_sdk import ProviderNotFoundError
try:
response = client.complete("Hello")
except ProviderNotFoundError as e:
print(f"Error: {e}")
print(f"Available: {client.list_providers()}")Solution: Use add_provider() before making requests
client.add_provider("anthropic", api_key="sk-ant-...")Solution:
- Check your API key is correct
- Use environment variables instead:
export ANTHROPIC_API_KEY="sk-ant-..."
Important
Never hardcode API keys. Use environment variables or .env files (ignored by git).
Solution: Increase rate limit or use fallback chains
client = OrionClient(config={
"rate_limits": {"anthropic": 120} # Higher limit
})Solution: Use larger model, split input, or summarize first
# Use model with bigger context
response = client.complete(text, model="claude-opus-4") # 200K tokensSolution: Increase timeout or use streaming
client = OrionClient(config={"timeout": 300}) # 5 minutesNote
Timeouts are often temporary. Retry with exponential backoff.
Q: When will this be on PyPI?
A: PyPI release is coming in 2026! For now, install from GitHub:
git clone https://github.com/windyworldair/Orion.git
cd Orion
pip install -e .Q: Is this production-ready?
A: We're in Beta. The core is solid, but we're actively improving. We recommend:
- Setting up error handling
- Using fallback chains
- Monitoring token usage
- Testing with your use case
Q: What if all fallback providers fail?
A: Orion raises AllProvidersFailedError with details on each failure:
from orion_sdk import AllProvidersFailedError
try:
response = client.complete("Hello")
except AllProvidersFailedError as e:
for provider, error in e.errors.items():
print(f"{provider}: {error}")Q: Can I use Orion offline?
A: Yes! Use Ollama for completely local inference:
client.add_provider("ollama", base_url="http://localhost:11434")
response = client.complete("Hello") # No internet neededQ: Does Orion cache responses?
A: Token counts are cached automatically. Response caching coming in v1.1.
Q: What about async/await support?
A: Coming in v1.1! Streaming provides similar benefits for long requests.
Q: How do I report bugs?
A: Open an issue on GitHub with:
- What you were doing
- What happened
- Error message
- Python version and OS
- Minimal code to reproduce
[!NOTE] Include as much detail as possible for faster resolution.
Q: Can I contribute?
A: Absolutely! See Contributing section.
orion_sdk/
βββ __init__.py # Public API
βββ client.py # OrionClient main class
βββ exceptions.py # 9 exception types
βββ ratelimit.py # Rate limiter
βββ tokens.py # Token counting
βββ providers/
βββ __init__.py # Registry
βββ base.py # Abstract base (β
Fixed typo: anthropic.py)
βββ openai.py # OpenAI
βββ anthropic.py # Anthropic Claude
βββ google.py # Google Gemini
βββ openrouter.py # OpenRouter
βββ ollama.py # Ollama local
Input Prompt
β
Validate Config
β
Count Tokens
β
Check Context Limit β ContextOverflowError if exceeded
β
Apply Rate Limiter
β
Send to Primary Provider
β
Success? β Return Response
β
No β Try Fallback Provider
β
Success? β Return Response
β
No β Raise AllProvidersFailedError
pip install -e ".[dev]"
pytestNote
Tests require mock providers to avoid actual API calls. Contribute tests for new features!
We love contributions! Here's how:
# Clone
git clone https://github.com/windyworldair/Orion.git
cd Orion
# Create branch
git checkout -b feature/amazing-thing
# Install dev tools
pip install -e ".[dev]"
# Make changes and test
pytest
# Commit
git commit -m "Add amazing feature"
# Push
git push origin feature/amazing-thing- β Tests β Unit tests for providers
- β Documentation β Examples, tutorials
- β Bug fixes β Help squash bugs
- β Features β Implement ideas
- β Performance β Optimize code
Important
Follow PEP 8, add type hints, and write tests for new features.
MIT License β Use freely for any purpose
See LICENSE for full text.
Questions? Issues? Ideas?
- π Bug Reports: GitHub Issues
- β Show Support: Star the repo!
Tip
For fastest response, include detailed error messages and minimal reproduction code.
If you find Orion SDK useful:
- β Star this repo β Help others discover us
- π’ Share with others β Tell your network
- π¬ Give feedback β Tell us what works
- π Report bugs β Help us improve
- π€ Contribute β Send pull requests