# SpendLil Python SDK

Drop-in wrapper for OpenAI, Anthropic, Google, and Mistral that routes all AI calls through your SpendLil proxy — giving you cost tracking, compliance controls, PII detection, rate limiting, and audit logs with minimal code changes.

## Install

```bash
pip install spendlil
# or with optional provider packages:
pip install "spendlil[openai]"
pip install "spendlil[anthropic]"
pip install "spendlil[all]"
```

## Quick start

```python
import os
from spendlil import SpendLil, SpendLilError

sl = SpendLil(
    agent_id="your-agent-id",           # from SpendLil dashboard
    base_url="https://spendlil.yourco.com/api",
)

# ── OpenAI (drop-in replacement) ─────────────────────────
openai = sl.openai(api_key=os.environ["OPENAI_API_KEY"])

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

print(response["choices"][0]["message"]["content"])
print(response["_spendlil"].tier)        # 'frontier'
print(response["_spendlil"].model)       # 'gpt-4o'

# ── Anthropic (drop-in replacement) ──────────────────────
anthropic = sl.anthropic(api_key=os.environ["ANTHROPIC_API_KEY"])

msg = anthropic.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Summarise this..."}],
)

print(msg["content"][0]["text"])

# ── Auto-routing (SpendLil picks the best model) ─────────
auto = openai.chat.completions.auto_create(
    messages=[{"role": "user", "content": "Write a haiku about Python"}],
)
print(auto["_spendlil"].model)   # e.g. 'gpt-4o-mini' — SpendLil chose

# ── Async ─────────────────────────────────────────────────
import asyncio

async def main():
    response = await openai.chat.completions.async_create(
        model="gpt-4o",
        messages=[{"role": "user", "content": "Hello async!"}],
    )
    print(response["choices"][0]["message"]["content"])

asyncio.run(main())

# ── Async streaming ───────────────────────────────────────
async def stream():
    async for chunk in openai.chat.completions.async_stream(
        model="gpt-4o",
        messages=[{"role": "user", "content": "Count to 5"}],
    ):
        delta = chunk.get("choices", [{}])[0].get("delta", {}).get("content", "")
        print(delta, end="", flush=True)

asyncio.run(stream())

# ── Session / context management ─────────────────────────
session_sl = sl.with_session("user-session-abc123")
session_openai = session_sl.openai(api_key=os.environ["OPENAI_API_KEY"])
# All requests with this client share context in SpendLil (STICKY/POOL mode)
```

## Error handling

```python
from spendlil import SpendLilError

try:
    response = openai.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "user", "content": "Hello"}],
    )
except SpendLilError as e:
    print(f"HTTP {e.status_code}: {e.message}")
    if e.meta and e.meta.quota_exceeded:
        print("Agent quota exceeded — try again later")
```

## SpendLil metadata

Every response includes a `_spendlil` key with a `SpendLilMeta` dataclass:

| Attribute | Type | Description |
|-----------|------|-------------|
| `tracked` | `bool` | Request was logged by SpendLil |
| `model` | `str` | Model actually used |
| `provider` | `str` | Provider used |
| `tier` | `str` | economy / standard / frontier |
| `fallback` | `bool` | Fallback model was used |
| `escalated` | `bool` | Request was auto-escalated |
| `escalated_from` | `str` | Original model before escalation |
| `translated` | `bool` | Cross-provider format translation applied |
| `session_key` | `str` | Active session key |
| `context_mode` | `str` | none / sticky / pool |
| `quota_exceeded` | `bool` | Agent quota was exceeded |

## Batch / deferred processing

```python
response = openai.chat.completions.create(
    model="gpt-4o",
    messages=[...],
    priority="batch",   # deferred to SpendLil batch queue
)
# Returns 202 with batch job ID
```
