Metadata-Version: 2.4
Name: onstoa
Version: 0.2.3
Summary: Usage-based billing SDK for AI applications
Project-URL: Homepage, https://onstoa.com
Project-URL: Repository, https://github.com/stoa-org/stoa
Project-URL: Documentation, https://docs.onstoa.com
Author-email: Stoa <hello@stoa.dev>
License-Expression: MIT
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT 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
Requires-Dist: httpx>=0.25.0
Requires-Dist: orjson>=3.9.0
Requires-Dist: pydantic>=2.0.0
Requires-Dist: pyjwt[crypto]>=2.0.0
Requires-Dist: tenacity>=8.0.0
Requires-Dist: uuid-utils>=0.6.0
Provides-Extra: all
Requires-Dist: anthropic>=0.18.0; extra == 'all'
Requires-Dist: elevenlabs>=1.0.0; extra == 'all'
Requires-Dist: openai>=1.0.0; extra == 'all'
Provides-Extra: anthropic
Requires-Dist: anthropic>=0.18.0; extra == 'anthropic'
Provides-Extra: dev
Requires-Dist: anthropic>=0.18.0; extra == 'dev'
Requires-Dist: elevenlabs>=1.0.0; extra == 'dev'
Requires-Dist: openai>=1.0.0; extra == 'dev'
Requires-Dist: pre-commit>=3.0.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
Requires-Dist: pytest-httpx>=0.30.0; extra == 'dev'
Requires-Dist: pytest-xdist>=3.5.0; extra == 'dev'
Requires-Dist: pytest>=7.0.0; extra == 'dev'
Requires-Dist: ruff>=0.1.0; extra == 'dev'
Requires-Dist: ty>=0.0.7; extra == 'dev'
Provides-Extra: elevenlabs
Requires-Dist: elevenlabs>=1.0.0; extra == 'elevenlabs'
Provides-Extra: openai
Requires-Dist: openai>=1.0.0; extra == 'openai'
Description-Content-Type: text/markdown

# Stoa SDK

Usage-based billing SDK for AI applications. Wrap your AI provider calls to automatically meter usage and bill your users.

## Installation

```bash
pip install onstoa
```

## Quick Start

```python
from fastapi.responses import RedirectResponse

from stoa import Stoa, StoaChargeRequiredError

# Reads STOA_API_KEY from environment
stoa = Stoa()

# Use your app's user ID for billing calls
client = stoa.openai(user_id="user_123")

try:
    response = client.chat.completions.create(
        model="gpt-4",
        messages=[{"role": "user", "content": "Hello!"}]
    )
except StoaChargeRequiredError:
    payment = stoa.create_payment(
        user_id="user_123",
        email="ada@example.com",
        return_url="https://app.example.com/billing/stoa-return",
    )
    raise RedirectResponse(payment.hosted_url, status_code=303)
```

Typical flow:
1. call `stoa.create_payment(...)` when you want to start a payment flow
2. keep using your app `user_id` in SDK billing calls
3. if a metered call raises `StoaChargeRequiredError`, call `stoa.create_payment(...)`

If you already have users who predate your Stoa integration, you can also add a
one-time lazy fallback before their first billable AI request. The operation is
idempotent.

## Onboard Users

Stoa can stay invisible to the customer while still owning the canonical user
and membership records.

### Explicitly bootstrap a member

`register_member(...)` is still available if you want to bootstrap a user eagerly
during signup.

```python
from stoa import Stoa

stoa = Stoa(api_key=STOA_API_KEY, base_url=STOA_BASE_URL)

result = stoa.register_member(
    registration_secret=STOA_REGISTRATION_SECRET,
    user_id=user.id,
    email=user.email,
    name=user.name,
    avatar_url=user.avatar_url,
)

# Optional: store the membership ID for debugging or richer account UX.
persist_membership_binding(
    user_id=user.id,
    membership_id=result.membership_id,
)
```

Stoa registers the user for billing in your app.
Your app can continue billing that user with their `user_id`.

#### Start a payment flow

If you want an explicit pay or add-funds action in your product:

```python
payment = stoa.create_payment(
    user_id=user.id,
    email=user.email,
    return_url="https://app.example.com/billing/stoa-return",
)
```

Send the user to `payment.hosted_url`. The response also carries the payment ID.

#### Recover from `StoaChargeRequiredError`

If a metered call fails because the user is not yet chargeable in Stoa, start a
hosted payment flow explicitly with `create_payment(...)`:

```python
from fastapi.responses import RedirectResponse

from stoa import StoaChargeRequiredError

client = stoa.openai(user_id=user.id)

try:
    response = client.chat.completions.create(
        model="gpt-4",
        messages=[{"role": "user", "content": "Hello!"}]
    )
except StoaChargeRequiredError:
    payment = stoa.create_payment(
        user_id=user.id,
        email=user.email,
        return_url="https://app.example.com/billing/stoa-return",
    )
    raise RedirectResponse(payment.hosted_url, status_code=303)
```

## Supported Providers

### OpenAI

```python
client = stoa.openai(user_id="user_123")

# Chat completions
response = client.chat.completions.create(
    model="gpt-4",
    messages=[{"role": "user", "content": "Explain quantum computing"}]
)

# Embeddings
embeddings = client.embeddings.create(
    model="text-embedding-3-small",
    input="Hello world"
)
```

### Anthropic

```python
client = stoa.anthropic(user_id="user_123")

response = client.messages.create(
    model="claude-3-5-sonnet-20241022",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Write a haiku about Python"}]
)
```

### OpenRouter

Access 100+ models through a single API:

```python
client = stoa.openrouter(user_id="user_123")

# Use any model available on OpenRouter
response = client.chat.completions.create(
    model="meta-llama/llama-3.1-70b-instruct",
    messages=[{"role": "user", "content": "Hello!"}]
)
```

### ElevenLabs

```python
client = stoa.elevenlabs(user_id="user_123")

audio = client.text_to_speech.convert(
    voice_id="JBFqnCBsd6RMkjVDRZzb",
    text="Hello, welcome to our application!"
)
```

## Environment Variables

| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| `STOA_API_KEY` | Yes | - | Your Stoa application API key |
| `STOA_REGISTRATION_SECRET` | No | - | Registration signing secret for onboarding users from your app |

### Example .env file

```bash
# Required
STOA_API_KEY=stoa_app_xxx
STOA_REGISTRATION_SECRET=stoa_reg_xxx

# Optional - override API endpoint
STOA_BASE_URL=https://api.onstoa.com
```

## Documentation

See [docs.onstoa.com](https://docs.onstoa.com) for full documentation.
