Metadata-Version: 2.5
Name: spacexai-subscription-client
Version: 0.1.1
Summary: Unofficial async Python client for SpaceXAI OAuth subscription APIs
Project-URL: Changelog, https://github.com/jeffglousher/spacexai-subscription-client/blob/main/CHANGELOG.md
Project-URL: Issues, https://github.com/jeffglousher/spacexai-subscription-client/issues
Project-URL: Repository, https://github.com/jeffglousher/spacexai-subscription-client
Author: Jeff Glousher
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: grok,home-assistant,oauth,xai
Classifier: Development Status :: 3 - Alpha
Classifier: Framework :: AsyncIO
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Typing :: Typed
Requires-Python: >=3.12
Requires-Dist: aiohttp>=3.12.15
Requires-Dist: httpx>=0.28.1
Requires-Dist: openai==2.45.0
Description-Content-Type: text/markdown

# spacexai-subscription-client

`spacexai-subscription-client` is a small, typed async client for SpaceXAI OAuth
device login and subscription inference. It is designed for applications such
as Home Assistant that own OAuth token storage and refresh.

This is an unofficial community package, not xAI's official Python SDK.
Authentication uses the SpaceXAI OAuth flow; API-key authentication is outside
this package's scope. Internally, the OAuth access token is supplied to the
OpenAI SDK's bearer-credential field because the subscription endpoint uses
its Responses API transport.

## Installation

```console
python -m pip install spacexai-subscription-client
```

Python 3.12 or newer is required.

## Usage

The caller owns both HTTP sessions and is responsible for closing them. Device
authorization returns a code and URL that the application should present to the
user while `async_poll_device_token` waits for approval.

When retrying a transient polling failure, reuse the same `DeviceAuthorization`.
It retains the original expiry and the current polling interval, including
server-requested slow-down and exponential timeout backoff.

```python
import aiohttp
import httpx

from spacexai_subscription_client import SpaceXAISubscriptionClient, Message


async def connect() -> None:
    async with (
        aiohttp.ClientSession() as websession,
        httpx.AsyncClient() as http_client,
    ):
        client = SpaceXAISubscriptionClient(websession, http_client)
        authorization = await client.async_request_device_authorization()
        print(authorization.verification_uri_complete)
        print(authorization.user_code)
        token = await client.async_poll_device_token(authorization)
        access_token = token.data["access_token"]
        account = await client.async_get_account(access_token)
        models = await client.async_list_models(access_token)
        if not models:
            raise RuntimeError("No Grok models are available for this account")

        response = await client.async_create_response(
            access_token,
            model=models[0],
            input_data=[Message("user", f"Hello, {account.display_name}")],
            tools=[],
        )
        print(response.text)
```

`OAuthToken.as_dict()` returns a mutable copy suitable for caller-owned
persistence. Refresh is intentionally not implemented by this package; the
host application should refresh tokens through its OAuth framework and pass the
current access token to each request.

## API boundary

The package exposes provider-neutral dataclasses for messages, tools, tool
calls, tool results, accounts, and completions. Provider SDK types do not cross
the public boundary. Network failures are translated into the stable exception
hierarchy rooted at `SpaceXAISubscriptionError`.

Model discovery and response generation use explicit timeouts. SDK retries are
disabled so callers receive a single stable failure and can apply their own
retry policy without duplicating a response request.

Response requests explicitly set `store=False`, matching the Grok Build sampler's
default. This requests no Responses API storage; it is not a guarantee about the
provider's other logging or retention policies.

The OAuth client identity and provider endpoints are centralized in
`spacexai_subscription_client.const` so an upstream identity decision can be
adopted without changing the public client API.

## Protocol provenance

The OAuth device flow, scopes, client identity, subscription proxy, and
Responses transport track the current public
[Grok Build authentication guide](https://github.com/xai-org/grok-build/blob/main/crates/codegen/xai-grok-pager/docs/user-guide/02-authentication.md)
and [Grok Build source](https://github.com/xai-org/grok-build). Keeping this
reference explicit makes upstream protocol changes reviewable without depending
on or executing the Grok CLI.

The subscription proxy checks `x-grok-client-version` against Grok Build versions.
Sending this package's `0.1.0` version was rejected with HTTP 426; sending `1.0.24`
with the same unofficial client identifier succeeded in a live request. We pin
`GROK_BUILD_COMPATIBILITY_VERSION` to `1.0.24` as our tested interoperability
convention. This is not a documented xAI protocol-version contract or a claim
that the Grok CLI is installed. The User-Agent retains this package's own name
and version, and `x-grok-client-identifier` remains
`spacexai-subscription-client`. The separate, working OAuth UI-version header is
unchanged. A future proxy incompatibility is returned to the caller without
automatic retries or version negotiation.

The reference is Grok Build commit
[`75810042ca2762aa0b0fa17864f3f68823ccbea5`](https://github.com/xai-org/grok-build/tree/75810042ca2762aa0b0fa17864f3f68823ccbea5):
the [`1.0.24` version](https://github.com/xai-org/grok-build/blob/75810042ca2762aa0b0fa17864f3f68823ccbea5/crates/codegen/xai-grok-version/Cargo.toml#L4),
the [sampler's proxy version header](https://github.com/xai-org/grok-build/blob/75810042ca2762aa0b0fa17864f3f68823ccbea5/crates/codegen/xai-grok-sampler/src/client.rs#L554),
and its [`store: false` default](https://github.com/xai-org/grok-build/blob/75810042ca2762aa0b0fa17864f3f68823ccbea5/crates/codegen/xai-grok-sampler/src/client.rs#L1188).

## Development

```console
uv sync --locked
uv run --no-sync ruff check .
uv run --no-sync ruff format --check .
uv run --no-sync mypy --strict src/spacexai_subscription_client
uv run --no-sync pytest --cov=spacexai_subscription_client --cov-fail-under=95
```

Releases are built and published from the public GitHub Actions workflow using
PyPI trusted publishing. See [RELEASING.md](RELEASING.md) for the release
checklist and [CHANGELOG.md](CHANGELOG.md) for release notes.
