Metadata-Version: 2.4
Name: starseer
Version: 0.4.7
Summary: Route LLM calls through Starport gateway
Project-URL: Repository, https://github.com/starseer-ai/starseer-python-sdk
Author-email: Starseer <support@starseer.ai>
License: Apache-2.0
License-File: LICENSE
Keywords: ai,anthropic,gemini,google,llm,openai,proxy,tracing
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.10
Provides-Extra: dev
Requires-Dist: anthropic>=0.18.0; extra == 'dev'
Requires-Dist: google-genai>=1.0.0; extra == 'dev'
Requires-Dist: openai>=1.0.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
Requires-Dist: pytest>=7.0.0; extra == 'dev'
Requires-Dist: respx>=0.20.0; extra == 'dev'
Description-Content-Type: text/markdown

# Starseer Python SDK

Route LLM API calls through Starport gateway.

## Install

```bash
pip install starseer
```

## Updating

Check your installed version:

```bash
starseer --version
```

When you run `starseer install` or `uninstall`, the CLI does a lightweight,
cached (24h) check against PyPI and prints a one-line hint if a newer version is
available. The check is skipped automatically in non-interactive/CI contexts, and
you can disable it entirely by setting `STARSEER_NO_UPDATE_CHECK=1`.

To upgrade, use the built-in command — it detects how starseer was installed
(pip, pipx, `uv tool`, or an editable checkout) and runs the right upgrade, or
prints the exact command to run when it can't do so safely:

```bash
# Report installed vs. latest version without changing anything
starseer upgrade --check

# Upgrade in place (prompts for confirmation when interactive)
starseer upgrade

# Upgrade without the confirmation prompt
starseer upgrade --yes
```

## Quick Setup (CLI) — Agent Frameworks

The SDK ships a `starseer install` CLI that configures AI agent frameworks
(currently **Claude Code**, **Claude Desktop**, **OpenCode**, **Codex**, and
**Gemini CLI**) to route their traffic through Starport. If you want to run that
tool directly from the repo rather than installing via pip:

```bash
git clone https://github.com/Starseer-ai/starseer-python-sdk
cd starseer-python-sdk
uv sync
uv run starseer install
#uv run python -m starseer install #(on Windows)
```

Configure AI agent frameworks (Claude Code, OpenCode, Codex, etc.) to use Starport with a single command:

```bash
# Interactive — walks you through setup (prompts for OAuth or API key)
starseer install

# Silent — Claude Code via OAuth (sign in with your claude.ai account through Starport)
starseer install --auth oauth --frameworks claude-code --scope global

# Silent — Claude Code via Starseer virtual key
starseer install --auth api-key --api-key ssk_... --frameworks claude-code --scope global

# Silent — OpenCode (Anthropic + OpenAI providers, both routed through Starport)
starseer install --api-key ssk_... --frameworks opencode --providers anthropic,openai --scope global

# Silent — Codex (API-key auth; export STARSEER_API_KEY before running `codex`)
starseer install --api-key ssk_... --frameworks codex --scope global

# Silent — Claude Desktop (writes the gateway redirect + installs the token helper)
starseer install --frameworks claude-desktop --scope global
```

**Authentication methods for Claude Code:**

- **OAuth** (default when `--api-key` is not provided): You sign into Claude Code with your
  claude.ai account as usual. Starport proxies your traffic. Only `ANTHROPIC_BASE_URL`
  is written to `settings.json` — starseer does not write or update an API key in OAuth
  mode. Note: if you previously ran an API-key install, the existing `ANTHROPIC_API_KEY`
  is left in place; run `starseer uninstall` first if you want to clear it.
- **API key**: Claude Code authenticates using a Starseer virtual key (`ssk_...`).
  Both `ANTHROPIC_BASE_URL` and `ANTHROPIC_API_KEY` are written. Requires that you are
  logged out of claude.ai (the installer will prompt, or pass `--logout` to do it automatically).

**OpenCode:**

OpenCode supports many LLM providers; pass `--providers` to choose which ones to route
through Starport (default: `anthropic`). Each selected provider gets a
`provider.<name>.options.{baseURL, apiKey}` block in `opencode.json` pointing at the
gateway path for that provider, including the upstream API's version prefix where
known: `<gateway>/anthropic/v1`, `<gateway>/openai/v1`, `<gateway>/google/v1beta`.
Other provider names pass through unchanged (e.g. `<gateway>/xai`) — OpenCode's
AI-SDK-based clients append only the endpoint name (`/messages`, `/chat/completions`),
so the version prefix has to live in baseURL. The same Starseer virtual key (`ssk_...`)
authenticates all providers; OpenCode is **API-key only** — OAuth mode is not supported.
Project scope writes `./opencode.json` at your repo root; add it to `.gitignore` if you
don't want to commit it.

**Codex:**

Codex's installer writes a sentinel-fenced `[model_providers.starseer]` block into
`~/.codex/config.toml` (or `./.codex/config.toml` for project scope) plus a top-level
`model_provider = "starseer"` line. Codex resolves API keys via env-var indirection,
so **before running `codex` you must export your Starseer key**:

```bash
export STARSEER_API_KEY=ssk_...
codex
```

Codex is currently **API-key only** in this installer; the "Sign in with ChatGPT"
OAuth/Device-Code flow is planned but requires gateway-side work that hasn't shipped
yet. The installer leaves all user-authored TOML outside the sentinel-fenced block
untouched, and `starseer uninstall --frameworks codex` only removes the fenced range
(if a top-level `model_provider` line exists outside our block, the installer warns
about it — Codex would reject the file with two assignments).

**Claude Desktop:**

Claude Desktop routes inference through Starport using a managed provider config
plus a small **token helper** that supplies a fresh OAuth access token on each
refresh. `starseer install --frameworks claude-desktop` writes that provider
config (macOS and Windows; Linux is left to manual setup) and installs the helper
into `~/.starseer/bin`. Requires a Claude subscription (Teams, **not** Enterprise).

The helper reads credentials that **Starseer mints itself** via `starseer login`
(see below) — it does **not** depend on Claude Code being installed or logged in.
On an interactive install the configurator runs the sign-in automatically when
you're not already signed in; a non-interactive install instead prints a note to
run `starseer login`. A global `starseer uninstall` removes the helper and the
stored credentials; a project-scoped uninstall leaves the (global) credentials
in place.

## Signing in to Claude (`starseer login`)

`starseer login` performs an OAuth 2.0 authorization-code + PKCE sign-in against
Anthropic's endpoints and stores the resulting access/refresh tokens in a
Starseer-owned file at `~/.starseer/.credentials.json` (mode `0600`). The Claude
Desktop token helper then refreshes those tokens as needed. This is the auth step
for the Claude Desktop redirect; you normally run it once.

```bash
# Open a browser, sign in, and store credentials (uses a loopback callback)
starseer login

# Sign in again even if valid credentials already exist
starseer login --force

# Headless / no usable browser: print the URL, paste the code back
starseer login --no-browser

# Non-interactive (CI/scripts): fail instead of prompting
starseer login --no-input
```

Override the credential location with `STARSEER_CREDS_FILE` if you don't want the
default `~/.starseer/.credentials.json`.

> **Note:** the OAuth endpoints, client id, and scopes are observed from the
> first-party Claude clients, not an officially documented public API. This makes
> setup self-contained (no Claude Code dependency) but is **not** an officially
> sanctioned integration — it still uses Anthropic's first-party client id, so
> the usual subscription terms apply.

### Checking the OAuth endpoints (`--probe`)

Because those endpoints are undocumented, they can change. `starseer login --probe`
checks that they're still reachable and honoring the grants we rely on — **without
signing in or sending any secrets** (it uses deliberately invalid inputs):

```bash
starseer login --probe
```

Example output:

```text
✓ authorize endpoint: HTTP 200 (reachable)
✓ token endpoint (authorization_code): HTTP 400 {invalid_request_error}
✓ token endpoint (refresh_token): HTTP 400 invalid_grant
✧ OAuth endpoints look healthy
```

It exits non-zero if any probe fails, so you can use it as a health check in a
script. You can point the whole flow (login and probe) at alternate endpoints
with the `CLAUDE_OAUTH_AUTHORIZE_URL`, `CLAUDE_OAUTH_TOKEN_URL`, and
`CLAUDE_OAUTH_CLIENT_ID` environment variables.

Run `starseer install --help` (or `starseer login --help`) for all options.

## Usage — Python SDK

```python
import starseer
import openai

starseer.protect(openai)

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

Async clients are also supported:

```python
client = openai.AsyncOpenAI()
response = await client.chat.completions.create(...)
```

To restore original SDK behavior:

```python
starseer.unprotect(openai)
```

## Tracing

Group multiple LLM calls under a single trace for correlated observability:

```python
with starseer.trace() as t:
    response1 = openai_client.chat.completions.create(...)
    response2 = anthropic_client.messages.create(...)
    print(f"Trace ID: {t.trace_id}")
```

All calls within the context share the same W3C trace ID.

## Configuration

Set your virtual key via environment variable:

```bash
export STARSEER_API_KEY=ssk_...
```

Or configure in code:

```python
starseer.configure(
    api_key="ssk_...",
    gateway_url="https://gateway.starseer.ai",  # optional
    openai_route="/openai/v1",              # optional, customize routing
    anthropic_route="/anthropic",           # optional
    auto_trace=True,                        # optional, auto-generate trace headers
)
```

## Environment Variables

| Variable | Required | Default |
|----------|----------|---------|
| `STARSEER_API_KEY` | Yes | - |
| `STARSEER_GATEWAY_URL` | No | `https://gateway.starseer.ai` |
| `STARSEER_NO_UPDATE_CHECK` | No | - (set to any value to disable the CLI's PyPI update check) |
| `STARSEER_CREDS_FILE` | No | `~/.starseer/.credentials.json` (where `starseer login` stores Claude tokens) |
| `CLAUDE_OAUTH_AUTHORIZE_URL` | No | `https://claude.ai/oauth/authorize` (override the `starseer login` authorize endpoint) |
| `CLAUDE_OAUTH_TOKEN_URL` | No | `https://platform.claude.com/v1/oauth/token` (override the token/refresh endpoint) |
| `CLAUDE_OAUTH_CLIENT_ID` | No | first-party Claude client id (override the OAuth client id) |
| `CLAUDE_OAUTH_SCOPE` | No | `org:create_api_key user:profile user:inference` (override the requested scopes; must match the client id) |

## vLLM Routing

Route OpenAI SDK calls to a vLLM backend through the gateway:

```python
starseer.configure(
    gateway_url="http://localhost:8080",
    api_key="ssk_...",
    openai_route="/vllm/v1"
)

starseer.protect(openai)

client = openai.OpenAI()
response = client.chat.completions.create(
    model="meta-llama/Llama-3-8b",
    messages=[{"role": "user", "content": "Hello!"}]
)
```

vLLM provides an OpenAI-compatible API, you can use the standard OpenAI SDK with the `openai_route` pointed to your vLLM endpoint.

## Cross-Provider Routing (Starport)

Use the OpenAI SDK to call Anthropic or other providers through Starport's OpenAI-compatible translation layer:

```python
import starseer
import openai

starseer.configure(
    gateway_url="https://your-starport-proxy.com",
    openai_route="/anthropic/openai/v1"
)

starseer.protect(openai)

client = openai.OpenAI()
response = client.chat.completions.create(
    model="claude-sonnet-4-20250514",
    messages=[{"role": "user", "content": "Hello!"}]
)
```

This allows you to use OpenAI SDK patterns while routing requests to Anthropic models on the backend.

## Supported SDKs

- `openai` (sync and async)
- `anthropic` (sync and async)
- `google-genai` (also detects the deprecated `google-generativeai`)

## Development

```bash
# Install with dev dependencies
uv sync --dev

# Run tests
uv run pytest

# Lint and format
uv run ruff check src/
uv run ruff format src/
```

### Live OAuth endpoint tests

Most tests mock the network. The OAuth contract against Anthropic's real
endpoints is covered by opt-in live tests that only run when you ask for them:

```bash
# Verify the authorize page + both grants against the real endpoints
STARSEER_LIVE_OAUTH=1 uv run pytest tests/integration -v

# Also run the human-in-the-loop full browser sign-in (requires you to sign in)
STARSEER_LIVE_OAUTH=1 STARSEER_LIVE_OAUTH_INTERACTIVE=1 uv run pytest tests/integration -v
```

The non-interactive checks are equivalent to `starseer login --probe` and are a
quick way to catch drift in the undocumented OAuth contract.
