Metadata-Version: 2.4
Name: sdkrouter
Version: 0.1.1
Summary: Unified SDK for AI services with OpenAI compatibility
Project-URL: Homepage, https://github.com/markolofsen/sdkrouter
Project-URL: Documentation, https://sdkrouter.com
Project-URL: Repository, https://github.com/markolofsen/sdkrouter
Author-email: markolofsen <dev@markolofsen.com>
License-Expression: MIT
Keywords: ai,api,cdn,llm,ocr,openai,sdk,vision
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Requires-Dist: beautifulsoup4>=4.12.0
Requires-Dist: httpx<1.0.0,>=0.25.0
Requires-Dist: lxml>=5.0.0
Requires-Dist: markdownify>=0.14.0
Requires-Dist: openai<3.0.0,>=1.0.0
Requires-Dist: pydantic-settings>=2.0.0
Requires-Dist: pydantic<3.0.0,>=2.0.0
Requires-Dist: rich>=14.0.0
Requires-Dist: tenacity>=9.0.0
Requires-Dist: tiktoken>=0.5.0
Requires-Dist: toon-python>=0.1.2
Provides-Extra: dev
Requires-Dist: build>=1.0.0; extra == 'dev'
Requires-Dist: mypy>=1.13.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.24.0; extra == 'dev'
Requires-Dist: pytest-cov>=6.0.0; extra == 'dev'
Requires-Dist: pytest>=8.0.0; extra == 'dev'
Requires-Dist: questionary>=2.0.0; extra == 'dev'
Requires-Dist: ruff>=0.8.0; extra == 'dev'
Requires-Dist: toml>=0.10.0; extra == 'dev'
Requires-Dist: twine>=5.0.0; extra == 'dev'
Description-Content-Type: text/markdown

# SDKRouter

Unified Python SDK for AI services with OpenAI compatibility. Access 300+ LLM models through a single interface, plus vision analysis, CDN, URL shortening, and HTML cleaning tools.

## Installation

```bash
pip install sdkrouter
```

## Quick Start

```python
from sdkrouter import SDKRouter

client = SDKRouter(api_key="your-api-key")

# OpenAI-compatible chat completions
response = client.chat.completions.create(
    model="openai/gpt-4o",
    messages=[{"role": "user", "content": "Hello!"}]
)
print(response.choices[0].message.content)
```

## Features

### Chat Completions (OpenAI-Compatible)

```python
# Non-streaming
response = client.chat.completions.create(
    model="anthropic/claude-3.5-sonnet",
    messages=[{"role": "user", "content": "Explain quantum computing"}],
    max_tokens=500,
)

# Streaming
for chunk in client.chat.completions.create(
    model="openai/gpt-4o",
    messages=[{"role": "user", "content": "Count to 5"}],
    stream=True,
):
    print(chunk.choices[0].delta.content or "", end="")
```

### Structured Output (Pydantic)

Get type-safe responses with automatic JSON schema generation:

```python
from pydantic import BaseModel, Field
from sdkrouter import SDKRouter

class Step(BaseModel):
    explanation: str = Field(description="Explanation of the step")
    result: str = Field(description="Result of this step")

class MathSolution(BaseModel):
    steps: list[Step] = Field(description="Solution steps")
    final_answer: float = Field(description="The final answer")

client = SDKRouter()
result = client.parse(
    model="openai/gpt-4o",
    messages=[
        {"role": "system", "content": "You are a math tutor. Show your work."},
        {"role": "user", "content": "Solve: 3x + 7 = 22"},
    ],
    response_format=MathSolution,
)

solution = result.choices[0].message.parsed
for i, step in enumerate(solution.steps, 1):
    print(f"{i}. {step.explanation} → {step.result}")
print(f"Answer: x = {solution.final_answer}")
```

### Vision Analysis

```python
result = client.vision.analyze(
    image_url="https://example.com/image.jpg",
    prompt="Describe this image",
)
print(result.description)
print(f"Cost: ${result.cost_usd:.6f}")
```

#### Quality Tiers

| Tier | Model | Use Case |
|------|-------|----------|
| `fast` | gpt-4o-mini | Quick analysis, lower cost |
| `balanced` | gpt-4o | Default, good quality/cost ratio |
| `best` | claude-3.5-sonnet | Highest accuracy |

```python
result = client.vision.analyze(
    image_url="https://example.com/image.jpg",
    model_quality="best",  # fast | balanced | best
)
```

### OCR (Text Extraction)

```python
result = client.vision.ocr(
    image_url="https://example.com/document.jpg",
    language_hint="en",  # optional
)
print(result.text)
```

#### OCR Modes

| Mode | Speed | Accuracy | Use Case |
|------|-------|----------|----------|
| `tiny` | Fastest | Basic | Simple text, receipts |
| `small` | Fast | Good | Standard documents |
| `base` | Medium | High | Default, balanced |
| `maximum` | Slow | Best | Complex layouts, handwriting |

```python
result = client.vision.ocr(
    image_url="https://example.com/document.jpg",
    mode="maximum",  # tiny | small | base | maximum
)
```

### CDN File Storage

```python
# Upload bytes
file = client.cdn.upload(
    b"file content",
    filename="document.txt",
    is_public=True,
)
print(file.url)
print(file.short_url)

# Upload from URL
file = client.cdn.upload(
    url="https://example.com/image.png",
    filename="image.png",
)

# List files
files = client.cdn.list(page=1, page_size=20)
for f in files.results:
    print(f"{f.filename}: {f.size_bytes} bytes")

# Get file details
file = client.cdn.get("file-uuid")

# Delete file
client.cdn.delete("file-uuid")

# Statistics
stats = client.cdn.stats()
print(f"Total files: {stats.total_files}")
print(f"Total size: {stats.total_size_bytes} bytes")
```

### URL Shortener

```python
# Create short link
link = client.shortlinks.create(
    target_url="https://example.com/very-long-url-here",
    custom_slug="my-link",  # optional
    max_hits=1000,  # optional limit
)
print(link.short_url)
print(link.code)

# List links
links = client.shortlinks.list()
for link in links.results:
    print(f"{link.code}: {link.hit_count} hits")

# Statistics
stats = client.shortlinks.stats()
print(f"Total links: {stats.total_links}")
print(f"Total hits: {stats.total_hits}")
```

### HTML Cleaner

```python
result = client.cleaner.clean(
    html_content,
    output_format="markdown",  # html | markdown | text
    remove_scripts=True,
    remove_styles=True,
    max_tokens=4000,  # optional token limit
)
print(result.cleaned_html)
print(f"Original: {result.original_size} bytes")
print(f"Cleaned: {result.cleaned_size} bytes")
print(f"Compression: {result.compression_ratio:.1f}x")
```

### LLM Models API

```python
# List available models with pagination
models = client.models.list(page=1, page_size=50)
for m in models.results:
    print(f"{m.model_id}: context={m.context_length}")

# Get model details
model = client.models.get("openai/gpt-4o")
print(f"Context: {model.context_length} tokens")
print(f"Vision: {model.supports_vision}")
print(f"Price: ${model.pricing.prompt}/M input")

# List providers
providers = client.models.providers()
for p in providers.providers:
    print(f"{p.name}: {p.model_count} models")

# Calculate cost
cost = client.models.calculate_cost(
    "openai/gpt-4o",
    input_tokens=1000,
    output_tokens=500,
)
print(f"Input: ${cost.input_cost_usd:.6f}")
print(f"Output: ${cost.output_cost_usd:.6f}")
print(f"Total: ${cost.total_cost_usd:.6f}")

# Statistics
stats = client.models.stats()
print(f"Total models: {stats.total_models}")
print(f"Vision models: {stats.vision_models}")
```

### Token Utilities

```python
from sdkrouter.utils import count_tokens, count_messages_tokens

# Count tokens in text
tokens = count_tokens("Hello, world!")
print(f"Tokens: {tokens}")

# Count tokens in messages
messages = [
    {"role": "system", "content": "You are helpful."},
    {"role": "user", "content": "Hello!"},
]
tokens = count_messages_tokens(messages)
print(f"Message tokens: {tokens}")
```

## Async Support

All features support async operations:

```python
from sdkrouter import AsyncSDKRouter
import asyncio

async def main():
    client = AsyncSDKRouter(api_key="your-api-key")

    # Async chat
    response = await client.chat.completions.create(
        model="openai/gpt-4o",
        messages=[{"role": "user", "content": "Hello!"}]
    )

    # Async structured output
    result = await client.parse(
        model="openai/gpt-4o",
        messages=[...],
        response_format=MyModel,
    )

    # Parallel requests
    results = await asyncio.gather(
        client.vision.analyze(image_url="..."),
        client.cdn.list(),
        client.models.stats(),
    )

asyncio.run(main())
```

## Configuration

```python
from sdkrouter import SDKRouter

# Environment variables (auto-loaded)
# SDKROUTER_API_KEY - API key
# SDKROUTER_BASE_URL - Custom base URL

# Direct configuration
client = SDKRouter(
    api_key="your-key",
    base_url="https://your-server.com",
    timeout=60.0,
    max_retries=3,
)

# Use OpenRouter directly
client = SDKRouter(
    openrouter_api_key="your-openrouter-key",
    use_self_hosted=False,
)
```

## Type Safety

All responses are fully typed with Pydantic models:

```python
from sdkrouter.tools import (
    VisionAnalyzeResponse,
    OCRResponse,
    CDNFileDetail,
    ShortLinkDetail,
    CleanResponse,
    LLMModelDetail,
)

# IDE autocomplete works
result: VisionAnalyzeResponse = client.vision.analyze(...)
result.description  # str
result.cost_usd     # float
result.usage.total_tokens  # int
```

## Examples

See the [examples](./examples/) directory for complete working examples:

| Example | Description |
|---------|-------------|
| `01_quickstart.py` | Quick start with all features |
| `02_chat.py` | Chat completions, streaming, multi-turn |
| `03_structured_output.py` | Pydantic structured output |
| `04_vision.py` | Vision analysis with quality tiers |
| `05_cdn.py` | File upload, download, management |
| `06_shortlinks.py` | URL shortening |
| `07_models.py` | LLM models listing, cost calculation |
| `08_cleaner.py` | HTML cleaning and compression |
| `09_keys.py` | API key management |
| `10_tokens.py` | Token counting utilities |
| `11_async.py` | Advanced async patterns |

## Supported Models

Access 300+ models from providers:

- **OpenAI**: GPT-4o, GPT-4 Turbo, GPT-4o-mini, o1, o1-mini
- **Anthropic**: Claude 3.5 Sonnet, Claude 3 Opus, Claude 3 Haiku
- **Google**: Gemini 2.0 Flash, Gemini 1.5 Pro
- **Meta**: Llama 3.3, Llama 3.2, Llama 3.1
- **Mistral**: Mistral Large, Mixtral, Codestral
- **DeepSeek**: DeepSeek V3, DeepSeek R1
- And many more via OpenRouter

## License

MIT
