Metadata-Version: 2.4
Name: keeps-ai
Version: 0.1.0
Summary: Fail-open server SDK for Keeps media-native product analytics
Author: Keeps
License-Expression: MIT
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Typing :: Typed
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: httpx<1,>=0.24

# keeps-ai

The server-only Python SDK for Keeps media-native product analytics. Capture is
fail-open: validation, buffering, and delivery failures never change application
behavior.

```bash
pip install keeps-ai
```

```python
import os
from keeps import Keeps

keeps = Keeps(api_key=os.environ["KEEPS_API_KEY"])
keeps.identify(user_id, {"plan": "pro"})
generation = keeps.generation(
    model="fal-ai/flux-2-pro",
    provider="fal",
    input={"prompt": prompt, "seed": seed, "steps": 28},
    user_id=user_id,
    session_id=session_id,
    source_assets=[source_row_id] if source_row_id else [],
    properties={"feature": "product-ad", "surface": "canvas"},
)
result = generation.complete(
    requested_assets=1,
    assets=[
        {
            "external_id": output_row_id,
            "media_type": "image",
            "url": url,
            "index": 0,
        }
    ],
    cost={"usd": 0.04},
)
keeps.track(
    "downloaded",
    asset=result.asset_ids[0],
    user_id=user_id,
    session_id=session_id,
    properties={"format": "png"},
)
```

Use `generation.fail(reason="Safety policy", code="nsfw", blocked=True)` only
when the terminal state is useful product context. Generic provider failures may
be omitted.

`input` is exactly what went to the provider. `properties` are application-known
annotations; do not manually label asset content. Every asset needs one stable
customer `external_id`; URLs are optional fetch references and never identity.

User IDs are HMAC-pseudonymized with the workspace API key before leaving the
process. Still use a stable internal ID and never pass email, prompts with
secrets, direct PII, or media bytes. Use `with Keeps(...) as keeps`, call
`keeps.flush(timeout=2.0)`, or wrap a serverless handler with
`keeps.with_flush(handler)` at a bounded shutdown boundary.
