
# OpenAI SDK (Python)

The OpenAI SDK is the canonical client. It works with LowRouter
unchanged once you set `base_url` and `api_key`.

## Install

```bash
pip install openai
```

## A non-streaming completion

```python
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://lowrouter.ai/api/v1",
    api_key=os.environ["LOWROUTER_API_KEY"],
)

response = client.chat.completions.create(
    model="lowrouter/auto",
    messages=[
        {"role": "user", "content": "In one sentence, what is a vector database?"}
    ],
)

print(response.choices[0].message.content)
```

## A streaming completion

```python
stream = client.chat.completions.create(
    model="lowrouter/auto",
    messages=[{"role": "user", "content": "Count to 5 slowly"}],
    stream=True,
)
for chunk in stream:
    delta = chunk.choices[0].delta.content
    if delta:
        print(delta, end="", flush=True)
```

## Reading the eco metadata

LowRouter's per-request metadata lives outside the OpenAI schema, so
the typed SDK fields don't surface it. Read it from the raw response:

```python
response = client.chat.completions.create(
    model="lowrouter/auto",
    messages=[{"role": "user", "content": "hi"}],
)
extra = response.model_extra or {}
eco = extra.get("lowrouter", {}).get("eco")
if eco:
    print(f"{eco['carbon_per_1k_tokens_g']:.3f} gCO2e/1k tokens "
          f"({eco['accuracy']})")
```

`response.model_extra` is the canonical Pydantic-v2 escape hatch for
non-schema fields. On older SDK versions the attribute is
`response.__pydantic_extra__`.

## Pinning a region with extra_body

The OpenAI SDK passes unknown kwargs through `extra_body`:

```python
response = client.chat.completions.create(
    model="openai/gpt-4o-mini",
    messages=[{"role": "user", "content": "hi"}],
    extra_body={"route": {"region": "eu-west"}},
)
```

If you need this on every request, make it a default by wrapping the
client:

```python
def make_client(region="eu-west"):
    base = OpenAI(
        base_url="https://lowrouter.ai/api/v1",
        api_key=os.environ["LOWROUTER_API_KEY"],
        default_headers={"X-LowRouter-Region": region},
    )
    return base
```

`X-LowRouter-Region` is honoured the same as `route.region` in the
body.

## Async

The async client follows the same pattern:

```python
import asyncio
from openai import AsyncOpenAI

client = AsyncOpenAI(
    base_url="https://lowrouter.ai/api/v1",
    api_key=os.environ["LOWROUTER_API_KEY"],
)

async def main():
    r = await client.chat.completions.create(
        model="lowrouter/auto",
        messages=[{"role": "user", "content": "hi"}],
    )
    print(r.choices[0].message.content)

asyncio.run(main())
```
