Metadata-Version: 2.4
Name: calmlens
Version: 0.18.1
Summary: Python SDK for CalmLens moderation, storage, and CDN delivery
Author: CalmLens
License-Expression: MIT
Project-URL: Homepage, https://calmlens.com
Project-URL: Documentation, https://calmlens.com/docs/sdk/python
Project-URL: Repository, https://github.com/CalmLens/sdks/tree/main/python
Project-URL: Changelog, https://calmlens.com/docs/changelog
Keywords: calmlens,moderation,content-moderation,cdn,sdk
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
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: Programming Language :: Python :: 3.14
Classifier: Operating System :: OS Independent
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Typing :: Typed
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# CalmLens Python SDK

Server-side Python SDK for [CalmLens](https://calmlens.com) content moderation, storage and CDN delivery.
It requires Python 3.10 or later, has no runtime dependencies and is fully typed (`py.typed`, checked with `mypy --strict`).

## Install

```sh
python -m pip install calmlens
```

## Quick start

Create an API key and copy your project ID as described in
[Authentication](https://calmlens.com/docs/guides/authentication). Keep both on your server.

```python
from calmlens import CalmLensServerClient

# Reads CALMLENS_API_KEY and CALMLENS_PROJECT_ID from the environment when not passed.
client = CalmLensServerClient()

asset = client.text.classify(text="Great photo!", external_id="comment-42")
if asset["classification"]["moderation"]["status"] == "allowed":
    print("Allowed: safe to show")

with open("photo.jpg", "rb") as file:  # streamed, not read into memory
    queued = client.assets.create(name="photo.jpg", content={"data": file, "type": "image/jpeg"})

decided = client.assets.wait_for_decision(asset_id=queued["id"], timeout=30)
```

Methods take snake_case keyword arguments, or one options dict with the API's camelCase keys
(`client.assets.retrieve({"assetId": "..."})`). Responses are plain dicts with the API's camelCase
field names, typed as `TypedDict`s in `calmlens.types`.

`assets.create` queues processing; `assets.classify` and `text.classify` wait for the decision.
Act on `classification.moderation.status`:

| Status           | What to do                                                                    |
| ---------------- | ----------------------------------------------------------------------------- |
| `pending`        | Moderation hasn't decided yet: wait, poll or handle a webhook.                |
| `allowed`        | Show the content.                                                             |
| `limited`        | Show it with reduced reach: keep it out of recommendations and notifications. |
| `age-restricted` | Show it only to viewers your app has age-checked.                             |
| `blocked`        | Don't show it. The content is kept for review.                                |
| `purged`         | Don't show it. The stored content has been deleted.                           |

Treat a status you don't recognise, and a failed request, as not allowed.

## What you can do

- [Moderate text, images, audio and video](https://calmlens.com/docs/sdk/python#moderate-content) and
  [read the classification report](https://calmlens.com/docs/guides/classification-results).
- [Stream uploads](https://calmlens.com/docs/sdk/python/reference#uploads), including
  [multipart uploads](https://calmlens.com/docs/sdk/python/reference#multipart-uploads) and uploads from a URL.
- [Moderate replies with their conversation](https://calmlens.com/docs/sdk/python/reference#replies-in-a-conversation).
- [Sign upload tokens](https://calmlens.com/docs/sdk/python/reference#upload-tokens) for
  [upload presets](https://calmlens.com/docs/guides/upload-presets) used from browsers.
- [Manage assets](https://calmlens.com/docs/sdk/python/reference#assets): list, search, update, move, retry,
  record reviewer decisions, explain decisions and delete content.
- [Organise assets in folders](https://calmlens.com/docs/sdk/python/reference#folders) and
  [compare uploads with hash lists](https://calmlens.com/docs/sdk/python/reference#hash-lists).
- [Deliver content through the CDN](https://calmlens.com/docs/sdk/python/reference#cdn) with
  [lenses and transforms](https://calmlens.com/docs/sdk/python/reference#lenses), signed URLs and
  [viewer claims](https://calmlens.com/docs/sdk/python/reference#viewer-claims).
- [Verify and parse webhooks](https://calmlens.com/docs/sdk/python/reference#webhooks) from Flask, Django or
  FastAPI, or [poll the events feed](https://calmlens.com/docs/sdk/python/reference#events-feed).
- [Handle errors](https://calmlens.com/docs/sdk/python/reference#errors) with stable error codes, and rely on
  [safe retries and idempotency keys](https://calmlens.com/docs/sdk/python/reference#retries-and-idempotency).
- [Configure the client](https://calmlens.com/docs/sdk/python/reference#configuration): timeouts, proxies,
  connection reuse across threads and forked workers, and use from `asyncio`.

## Testing

`MockCalmLensServerClient` from `calmlens.testing` is the real client with an in-memory API behind it,
for offline tests. See [Python testing](https://calmlens.com/docs/sdk/python/testing).

## Command-line tool

Use the standalone [CalmLens CLI](https://calmlens.com/docs/guides/local-development) to forward signed
webhook events to your machine and trigger test uploads.

## Links

- [Documentation](https://calmlens.com/docs/sdk/python)
- [SDK reference](https://calmlens.com/docs/sdk/python/reference)
- [API reference](https://calmlens.com/docs/api)
- [Changelog](https://github.com/CalmLens/sdks/blob/main/python/CHANGELOG.md)
- [License (MIT)](https://github.com/CalmLens/sdks/blob/main/python/LICENSE)
