# agentrouter-sdk (Python)

Python SDK for the **Tetrate Agent Router (TARS)** control
plane. A thin, idiomatic wrapper over the buf-generated
`protobuf-python` messages under `agentrouter_sdk._gen`. The SDK speaks
**Connect-JSON** over HTTP via `httpx` -- the same wire protocol the
Go and TS SDKs use.

Package: `agentrouter-sdk`.

## Install (from a downloaded tarball)

Download the Python SDK sdist from the AgentRouter **SDKs page**
(`/docs/sdks` in the dashboard) -- the download is session-gated, and the
Downloads table lists the version and SHA-256 for each artifact. We do not
publish to PyPI: consumption is a local sdist install -- nothing is fetched
from the public index.

Install the downloaded sdist (replace `<version>` with the version shown on
the SDKs page):

```
pip install ./agentrouter-python-<version>.tar.gz
```

Optionally verify its SHA-256 against the digest shown on that page:

```
shasum -a 256 agentrouter-python-<version>.tar.gz
```

A local sdist install is the only customer-facing install mechanism: the
SDK is not published to PyPI. See the SDKs page (`/docs/sdks`) for
downloads and `/docs/api-reference/install` for the full install guide.

## Usage

```python
import os
from agentrouter_sdk import Client

with Client(base_url=os.environ["AGENTROUTER_BASE_URL"]) as router:
    providers = router.catalog.list_providers().providers
    for p in providers:
        print(p.name)
```

Auth:

```python
Client(api_key="tat_live_...")        # sets Authorization + X-Api-Key
Client(session_token="session.jwt")   # sets Authorization: Bearer ...
Client.from_standard_env()            # reads AGENTROUTER_API_KEY
```

Local dev:

```python
from agentrouter_sdk import Client, Endpoints

with Client(endpoints=Endpoints(
    identity="http://localhost:9091",
    catalog="http://localhost:9100",
    inference="http://localhost:9097",
)) as router:
    me = router.me.get()
    reply = router.inference.prompt("acme", "platform", "Hello!", model="openai/gpt-4o-mini")
```

## Surface

Per-service accessors on `Client`:

- `client.me` -- `login`, `logout`, `get`, `issue_key`, `list_keys`, `revoke_key`
- `client.identity` -- `resolve_api_key`
- `client.customers` / `client.tenancy` -- `create`, `get`, `list`, `update`, `archive`
- `client.projects` -- `create`, `get`, `list`, `update`, `archive`,
                      `add_member`, `list_members`, `update_member`, `remove_member`
- `client.catalog` -- `list_providers`, `get_provider`, `list_models`, `get_model`,
                      `upsert_provider`, `upsert_model`, `set_provider_credential`,
                      `delete_provider`, `delete_model`, `assign_model_to_project`,
                      `list_project_models`, `unassign_model_from_project`
- `client.aikeys` -- `register`, `get`, `list`, `revoke`, `fetch`
- `client.inference` -- `prompt(customer_id, project_id, prompt, *, model, key_id, idempotency_key)`
- `client.clients` -- `create`, `list`, `get`, `update_title`, `add_key`,
                      `rotate_key`, `disable_key`, `enable_key`, `destroy_key`,
                      `emergency_revoke`
- `client.telemetry` -- `create`, `list`, `get`, `update`, `delete`
- `client.mcp.profiles` -- `create`, `get`, `list`, `update`, `delete`
- `client.mcp.catalog` -- `upsert`, `get`, `list`, `set_enabled`, `delete`

## Conventions

- **Context-managed** -- `with Client(...) as router:` closes the
  underlying `httpx.Client` deterministically.
- **Typed errors** -- failures raise `AgentrouterError` with `.code` (the
  canonical lowercase gRPC status name, e.g. `"unauthenticated"`) and
  `.reason` (the machine-readable RFC 7807 reason from the
  `google.rpc.ErrorInfo` detail the platform attaches -- e.g.
  `"MODEL_NOT_FOUND"`, `"CROSS_TENANT_DENIED"`; `""` when absent):

  ```python
  try:
      router.inference.prompt(model="nope")
  except AgentrouterError as e:
      if e.reason == "MODEL_NOT_FOUND":
          ...  # handle a bad model selector
  ```
- **Auth** -- `api_key` sets both `Authorization` and `X-Api-Key`;
  `session_token` sets `Authorization: Bearer ...`. Mutually exclusive.
- **Retry** -- transient codes (`unavailable`, `deadline_exceeded`,
  `aborted`) retried with exponential backoff; override via `retry=`.

## Tests

```
pytest tests/
```

## Quickstart

The sdist bundles a runnable quickstart at `examples/quickstart`. After
`pip install ./agentrouter-python-<version>.tar.gz` it unpacks next to the package;
run it directly:

```
python examples/quickstart/quickstart.py
```

It reads `AGENTROUTER_API_KEY` (+ optional `AGENTROUTER_BASE_URL`) and prints a
single prompt's completion.

## Codegen

```
make sdk.generate  # from the repo root
```

regenerates `sdks/python/src/agentrouter_sdk/_gen/` from the latest `api/tars/*`
protos. Run twice to confirm idempotency.
