Metadata-Version: 2.4
Name: rippit-sdk
Version: 0.5.0
Summary: Rippit SDK for logging conversation data
Project-URL: Repository, https://github.com/adtribute/analytics
License-Expression: Apache-2.0
License-File: LICENSE
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Typing :: Typed
Requires-Python: >=3.9
Requires-Dist: httpx>=0.25.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == 'dev'
Description-Content-Type: text/markdown

# rippit-sdk

The official Python SDK for shipping conversation data to [Rippit](https://www.rippit.com) -- the platform for analyzing AI conversation quality, identifying trends, and improving your AI products.

Use this SDK to log every turn of a conversation (both user messages and assistant responses) so Rippit can provide analytics, quality scoring, and insights across your conversations.

## Quickstart

```bash
pip install rippit-sdk
export RIPPIT_API_TOKEN="rpt_pat_abc123.xyzSecretTokenValue"
```

```python
import os
import rippit.sdk
from rippit.sdk import store_conversation_moment

# Pass your token here, or set the RIPPIT_API_TOKEN env var instead (see Configuration).
rippit.sdk.configure(api_token=os.environ["RIPPIT_API_TOKEN"])

# Log the user's message
store_conversation_moment(
    message="What's the weather like today?",
    role="user",
    conversation_id="conv-123",
)

# Log the assistant's response
store_conversation_moment(
    message="The weather today is sunny with a high of 75F.",
    role="assistant",
    conversation_id="conv-123",
    attributes={"model": "gpt-4"},
)
```

That's it. Each call sends the data to Rippit in the background without blocking your application.

## Requirements

- Python 3.9+
- A Rippit API token (get one from the [Rippit app settings page](https://app.rippit.com/settings/otherSettings#apiKeys) or the [Rippit CLI](https://rippit.sh))

## Configuration

You must provide an API token through **one** of the two methods below. No other setup is needed -- just provide the token and start calling `store_conversation_moment`.

**Environment variable** (recommended for production/CI -- no setup code required):

```bash
export RIPPIT_API_TOKEN="rpt_pat_abc123.xyzSecretTokenValue"
```

When the env var is set, `store_conversation_moment` picks it up automatically. No call to `configure()` is needed.

**Explicit in code** (handy for quick testing):

```python
import rippit.sdk

rippit.sdk.configure(api_token="rpt_pat_abc123.xyzSecretTokenValue")
```

`configure(api_token=...)` requires the `api_token` keyword argument -- it cannot be called without one. It takes priority over the environment variable.

If neither method is used, `store_conversation_moment` prints a warning and returns a no-op `Future` -- your application keeps running, but no data is sent.

## API Reference

### `store_conversation_moment`

Log a single conversation turn.

```python
from rippit.sdk import store_conversation_moment

store_conversation_moment(
    message="Hello from support",
    role="user",
    conversation_id="conv-456",
    attributes={
        "user_id": "user-789",
        "message_id": "msg-001",
        "model": "gpt-4",
    },
)
```

**Parameters**

`store_conversation_moment(message=..., role=..., conversation_id=..., attributes=...)`

| Parameter          | Required | Description                                          |
| ------------------ | -------- | ---------------------------------------------------- |
| `message`          | Yes      | The message content                                  |
| `role`             | Yes      | One of `"user"`, `"assistant"`, `"system"`, `"tool"` |
| `conversation_id`  | Yes      | Identifier linking all messages in a conversation    |
| `attributes`       | No       | Optional dict with additional fields (see below)     |

**Returns** `Future[None]` -- fire-and-forget by default, or call `.result()` if you need confirmation.

**Attributes**

| Attribute        | Default         | Description                                       |
| ---------------- | --------------- | ------------------------------------------------- |
| `app_dataset_id` | `"app_dataset"` | Dataset to log to (see [Datasets](#datasets))     |
| `message_id`     | `""`            | Unique identifier for this message                |
| `user_id`        | `""`            | Identifier for the user                           |
| `model`          | `""`            | Model name (e.g. `"gpt-4"`)                       |
| `raw_message`    | `""`            | Full model response payload, if available          |

You can also pass any additional keys in the attributes dict. These are stored alongside your log data so you can use them to segment, filter, or analyze your conversations however you like.

### `store_conversation_moments`

Send many moments in a single call. The SDK automatically batches them into groups of 10,000 and sends each batch sequentially.

> **Note:** All moments in a single call must target the same dataset. Specify it once via the top-level `app_dataset_id`, or uniformly in each moment's `attributes` -- do not mix the two approaches.

```python
from rippit.sdk import store_conversation_moments

store_conversation_moments(
    moments=[
        {"message": "Hello!", "role": "user", "conversation_id": "conv-123"},
        {"message": "Hi! How can I help?", "role": "assistant", "conversation_id": "conv-123", "attributes": {"model": "gpt-4"}},
        # ... up to any number of moments
    ],
    app_dataset_id="support_logs",  # optional, defaults to "app_dataset"
)
```

**Parameters**

| Parameter        | Required | Description                                                                |
| ---------------- | -------- | -------------------------------------------------------------------------- |
| `moments`        | Yes      | List of moment dicts (same shape as `store_conversation_moment` arguments) |
| `app_dataset_id` | No       | Dataset for the entire batch (see resolution rules below)                  |

**Returns** `Future[None]` -- fire-and-forget by default, or call `.result()` if you need confirmation.

Each moment in the `moments` list is a dict with keys matching `store_conversation_moment`'s keyword arguments: `message`, `role`, `conversation_id`, and optionally `attributes`.

#### `app_dataset_id` resolution

The optional top-level `app_dataset_id` parameter controls which dataset all moments are sent to. Per-moment `app_dataset_id` in `attributes` is also supported. Resolution follows these rules:

**When top-level `app_dataset_id` is provided:**

- Moments without `app_dataset_id` in their attributes use the top-level value.
- Moments whose `app_dataset_id` matches the top-level pass through normally.
- Moments whose `app_dataset_id` *disagrees* with the top-level are dropped with a warning. The remaining moments are still sent.

**When no top-level `app_dataset_id` is provided:**

- If no moments specify `app_dataset_id`, the default `"app_dataset"` is used.
- If all moments specify the same `app_dataset_id`, that value is used.
- Otherwise (any disagreement, or a mix of specified and unspecified), the entire batch is rejected with a warning.

#### Validation

All moments are validated before any are sent. If any moment has an invalid `message`, `role`, `conversation_id`, or reserved field conflict, the entire batch is rejected with a warning citing the failing index.

## Concepts

### Datasets

The SDK automatically creates and manages datasets in Rippit. On the first call, the SDK calls `/sdk/init` to ensure the dataset exists, then caches the result for the lifetime of the process. No manual setup needed.

By default, all logs go to a dataset called `"app_dataset"`. If your application has distinct areas you want to analyze separately, pass a different `app_dataset_id`:

```python
store_conversation_moment(message="...", role="user", conversation_id="c-1", attributes={"app_dataset_id": "support_logs"})
store_conversation_moment(message="...", role="user", conversation_id="c-2", attributes={"app_dataset_id": "onboarding_logs"})
```

The following fields are auto-generated by the SDK and do not need to be provided:

| Field         | Description                              |
| ------------- | ---------------------------------------- |
| `created_at`  | UTC timestamp when the log was created   |
| `updated_at`  | UTC timestamp (same as `created_at`)     |

### Async behavior

Both `store_conversation_moment` and `store_conversation_moments` send data to Rippit in a background thread and return a `Future`. By default this is fire-and-forget -- your application is never blocked.

If you need to wait for the send to complete (e.g. in a script or test):

```python
future = store_conversation_moment(message="hi", role="user", conversation_id="c-1")
future.result()  # blocks until the POST finishes

future = store_conversation_moments(moments=[...])
future.result()
```

### Error handling

The SDK is designed to **never** crash your application. All errors -- network failures, timeouts, server errors, missing fields, invalid roles, and reserved field conflicts -- are caught, logged as warnings to `stderr`, and a no-op `Future` is returned. No exceptions are ever raised.

To see network-level debug information:

```python
import logging
logging.getLogger("rippit.sdk").setLevel(logging.DEBUG)
```

### Framework compatibility

One dependency (`httpx`) + background threads means this SDK works out of the box in:

- FastAPI / Starlette
- Flask / Quart
- Django / Django REST Framework
- Celery workers
- AWS Lambda / Google Cloud Functions
- CLI scripts and notebooks

No special framework adapters needed.

## Development Setup

```bash
cd sdks/python
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
```

### Running Tests

```bash
pytest
```

### Running the Smoke Test

```bash
export RIPPIT_API_TOKEN="rpt_pat_abc123.xyzSecretTokenValue"
python examples/smoke_test.py
```

### Publishing to PyPI

1. Update the version in `pyproject.toml` and `src/rippit/sdk/_version.py`.

2. Update `CHANGELOG.md` with the new version and changes.

3. Build the package:

```bash
pip install build
python -m build
```

4. Upload to PyPI:

```bash
pip install twine
twine upload dist/*
```

To publish to Test PyPI first:

```bash
twine upload --repository testpypi dist/*
pip install --index-url https://test.pypi.org/simple/ rippit-sdk
```
