Metadata-Version: 2.4
Name: lenz-io
Version: 1.0.0rc7
Summary: Official Python SDK for the Lenz Claim Verification API for AI Product Teams
Project-URL: Homepage, https://lenz.io
Project-URL: Documentation, https://lenz.io/api/v1/docs/
Project-URL: Repository, https://github.com/lenzhq/lenz-io-python
Project-URL: Issues, https://github.com/lenzhq/lenz-io-python/issues
Project-URL: Changelog, https://github.com/lenzhq/lenz-io-python/blob/main/CHANGELOG.md
Author-email: Lenz <support@lenz.io>
License-Expression: MIT
License-File: LICENSE
Keywords: ai,api,fact-check,hallucination,lenz,llm,sdk,verification
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT 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: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.9
Requires-Dist: httpx>=0.27
Requires-Dist: pydantic>=2.0
Provides-Extra: dev
Requires-Dist: mypy>=1.10; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: responses>=0.25; extra == 'dev'
Requires-Dist: respx>=0.21; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Description-Content-Type: text/markdown

# lenz-io

Official Python SDK for the [Lenz Claim Verification API for AI Product Teams](https://lenz.io/developers).

**Two API primitives for AI product teams.** `extract` pulls verifiable
claims out of any text — free, 1000 calls/key/day. `verify` checks one
with a 7-model panel and citations, ~90s. Use them together or alone.

Built for teams whose AI output is async or document-shaped: legal-memo
generators, deep-research products, due-diligence platforms, vertical
agents producing structured deliverables. Not chat AI, not voice AI,
not real-time copilots — pipeline runs are the wrong shape for those.

```bash
pip install lenz-io
```

## Quickstart — the canonical integration

```python
from lenz_io import Lenz

client = Lenz(api_key="lenz_...")

# 1. Pull factual claims out of your model output (free, instant)
claims = client.extract(text=llm_output).claims

# 2. Verify the ones that matter (~90s each, 7-model panel)
for claim in claims:
    v = client.verify_and_wait(claim=claim).verdict
    print(v.label, v.score, v.confidence)
```

## How verification works

Frame → Collect Evidence → Debate (2 models, 2 rounds) → Adjudicate
(3 models: sources, logic, context) → Conclude. ~90 seconds wall-clock
per claim.

## Magical-moment demo

```python
from lenz_io import Lenz

client = Lenz(api_key="lenz_...")

v = client.verify_and_wait(claim="Sharks don't get cancer")
print(v.verdict.label, v.verdict.score)
# false 2.0

for source in v.sources[:3]:
    print(" -", source.title, source.url)
```

The demo claim is pre-cached so this returns in ~1.5s. Your own claims
hit the full pipeline (~60-90s) — use webhooks for production async flows.

> **Get your webhook secret here →** [lenz.io/api-integration](https://lenz.io/api-integration)

## What you get

- **`client.verify_and_wait(...)`** — submit + poll until the pipeline lands. Returns a typed `Verification`.
- **`client.verify(...)`** — async submit; returns a `task_id`. Use webhooks for the callback.
- **`client.extract(text=...)`** — pull verifiable claims out of any text (free, capped at 1000/key/day).
- **`client.verify_batch(claims=[...])`** — fan-out for multi-claim LLM outputs.
- **`client.verifications.{list,get,delete,set_visibility}(...)`** — manage past verifications.
- **`client.followup.{history,send,reset}(verification_id)`** — Q&A on a verification.
- **`client.library.{list,get}(...)`** — browse the public catalog (no API key needed).
- **`client.usage()`** — credits and rate-limit remaining.

### Webhooks

```python
from lenz_io import LenzWebhooks, VerificationCompleted, VerificationNeedsInput

webhooks = LenzWebhooks(secret="whsec_...")

# In your web handler:
event = webhooks.parse(raw_body=request.body, headers=request.headers)
if isinstance(event, VerificationCompleted):
    vid, result = event.verification_id, event.result
    ...
elif isinstance(event, VerificationNeedsInput):
    tid, ni = event.task_id, event.needs_input
    ...
```

If you're on Python 3.10+ a `match` statement reads even cleaner — events are
plain dataclasses, so structural pattern matching works.

Signature verification is HMAC-SHA256 over the raw body; the SDK does it for
you and rejects tampered or replayed payloads.

See [`examples/core/fastapi_webhook.py`](examples/core/fastapi_webhook.py)
for a runnable FastAPI receiver, and [`examples/core/verify_llm_output.py`](examples/core/verify_llm_output.py)
for the headline extract-and-verify integration pattern.

## Errors

Every error subclass is typed and carries a `request_id` you can quote on
support tickets:

```python
from lenz_io import LenzAuthError, LenzRateLimitError, LenzValidationError

try:
    client.verify_and_wait(claim="...")
except LenzAuthError as exc:
    print(exc)
    # Unauthorized
    #   Cause:  Invalid api key
    #   Fix:    Generate a new key at https://lenz.io/api-integration.
    #   Docs:   https://lenz.io/docs/auth
    #   Request ID: req_abc123
except LenzRateLimitError as exc:
    time.sleep(exc.retry_after)
except LenzValidationError as exc:
    for field_err in exc.errors:
        print(field_err["loc"], field_err["msg"])
```

## Resuming a verification

If a `verify_and_wait` call exceeds its `timeout` (default 120s) or your
process dies mid-poll, the pipeline keeps running. The exception carries the
`task_id`:

```python
from lenz_io import LenzTimeoutError

try:
    client.verify_and_wait(claim="...", timeout=30)
except LenzTimeoutError as exc:
    print("resume later via:", exc.task_id)

# Later (different process / restart):
status = client.get_status("tsk_abc123")
if status.status == "completed":
    print(status.result.verdict.label)
```

## Idempotency

`verify_and_wait` sends an auto-generated `Idempotency-Key` on every call by
default, so a network drop after submit doesn't spawn a duplicate verification
or charge a second credit. Override with `idempotency_key="..."` to pin a
specific key, or `idempotency=False` to opt out.

## Configuration

```python
Lenz(
    api_key="lenz_...",                  # or set LENZ_API_KEY env var
    base_url="https://lenz.io/api/v1",   # override for staging / local
    timeout=30.0,
    max_retries=3,
)
```

Environment variables:

- `LENZ_API_KEY` — read if `api_key=` is not passed
- `LENZ_BASE_URL` — read if `base_url=` is not passed

## Compatibility

- Python 3.9, 3.10, 3.11, 3.12
- Works in CI/CD (no interactive prompts, no global state)
- Mockable for tests: every HTTP call goes through `httpx`; use `respx` or
  inject your own `httpx.Client` via `Lenz(..., http_client=...)`

## Bug reports + feature requests

[github.com/lenzhq/lenz-io-python/issues](https://github.com/lenzhq/lenz-io-python/issues)

For commercial use, volume pricing, or onboarding support,
[get in touch](https://lenz.io/contact).

## License

MIT. See [LICENSE](LICENSE).
