Skip to content

Latest commit

Β 

History

20 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

🌟 Orion SDK

Unified AI Provider Interface β€” One API to Rule Them All

Python Status License Version Providers Models Streaming

GitHub Issues License

Stop juggling 5 different SDKs. Start building with one.

πŸ“š Documentation β€’ πŸš€ Quick Start β€’ πŸ’‘ Examples β€’ 🀝 Contributing β€’ ⭐ Support


πŸ“‘ Table of Contents


🎯 Why Orion SDK?

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.


✨ Features

  • πŸ”€ 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 Provider and register it
  • πŸ’Ύ Message history β€” Built-in conversation context management

πŸš€ Quick Start

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.

Install from Source (PyPI coming soon in 2026)

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.

One-liner

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.

Multi-provider with fallback

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}

Streaming

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)

Tool calling

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"}

πŸ“Š Supported Providers

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 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

Provider Aliases

# These all work
client.add_provider("claude",   ...)  # β†’ anthropic
client.add_provider("gpt",     ...)  # β†’ openai
client.add_provider("gemini",  ...)  # β†’ google
client.add_provider("local",   ...)  # β†’ ollama

Tip

Aliases also work in complete() and stream() β€” just pass model="claude-opus-4" and Orion resolves the provider automatically.


πŸ“š API Reference

OrionClient

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

Data Types

# 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 schema

Exceptions

from 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}")

Rate Limiting

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}
})

πŸ’‘ Examples

Example 1: Hello World

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}")

Example 2: Token Counting & Cost

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}")

Example 3: Tool Calling

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}")

Example 4: Multi-Provider Comparison

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}")

Example 5: Streaming Response

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)

Example 6: Handling Context Overflow

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")

Example 7: Custom Provider

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")

Example 8: Multi-Turn Conversation

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)

Example 9: Rate Limit Handling

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)

Example 10: Environment Variable Config

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!")

πŸ“¦ Installation

From Source (Recommended for now)

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.

With Specific Providers

# Just Anthropic
pip install -e ".[anthropic]"

# Anthropic + OpenAI
pip install -e ".[anthropic,openai]"

# All providers
pip install -e ".[all]"

# Development
pip install -e ".[dev]"

Verify Installation

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.


🎯 Advanced Usage

Rate Limiting Deep Dive

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.

Context Window Validation

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.

Token Caching

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).

Provider-Specific Configuration

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.


🚨 Error Handling

Common Errors

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.

Graceful Degradation

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 logic

πŸ”§ Troubleshooting

Issue: "Provider not found"

from 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-...")

Issue: "Invalid API key"

Solution:

  1. Check your API key is correct
  2. Use environment variables instead:
    export ANTHROPIC_API_KEY="sk-ant-..."

Important

Never hardcode API keys. Use environment variables or .env files (ignored by git).

Issue: "Rate limit exceeded"

Solution: Increase rate limit or use fallback chains

client = OrionClient(config={
    "rate_limits": {"anthropic": 120}  # Higher limit
})

Issue: "Context overflow"

Solution: Use larger model, split input, or summarize first

# Use model with bigger context
response = client.complete(text, model="claude-opus-4")  # 200K tokens

Issue: "Timeout"

Solution: Increase timeout or use streaming

client = OrionClient(config={"timeout": 300})  # 5 minutes

Note

Timeouts are often temporary. Retry with exponential backoff.


❓ FAQ

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 needed
Q: 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.


πŸ—οΈ Architecture

File Structure

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

Request Flow

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

πŸ§ͺ Testing

Run Tests

pip install -e ".[dev]"
pytest

Note

Tests require mock providers to avoid actual API calls. Contribute tests for new features!


🀝 Contributing

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

Areas We Need Help

  • βœ… 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.


πŸ“„ License

MIT License β€” Use freely for any purpose

See LICENSE for full text.


πŸ™ Support

Questions? Issues? Ideas?

  • πŸ› Bug Reports: GitHub Issues
  • ⭐ Show Support: Star the repo!

Tip

For fastest response, include detailed error messages and minimal reproduction code.


⭐ Show Your Support

If you find Orion SDK useful:

  1. ⭐ Star this repo β€” Help others discover us
  2. πŸ“’ Share with others β€” Tell your network
  3. πŸ’¬ Give feedback β€” Tell us what works
  4. πŸ› Report bugs β€” Help us improve
  5. 🀝 Contribute β€” Send pull requests

Built with ❀️ by Windyworld

GitHub β€’ Issues β€’

Β© 2026 Windyworld. MIT License.

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages