Metadata-Version: 2.4
Name: hashline
Version: 0.1.0
Summary: Python SDK for the Hashline agent audit ledger.
Project-URL: Homepage, https://hashline.dev
Project-URL: Documentation, https://hashline.dev
Project-URL: Repository, https://github.com/hashline-dev/sdk-python
Project-URL: Issues, https://github.com/hashline-dev/sdk-python/issues
Project-URL: Changelog, https://github.com/hashline-dev/sdk-python/releases
Author-email: Nikola Medjugorac <nikola@hashline.dev>
License: Apache-2.0
License-File: LICENSE
Keywords: agents,ai,audit,hashline,logging,observability
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
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.10
Requires-Dist: httpx>=0.27
Description-Content-Type: text/markdown

# hashline

Python SDK for the [Hashline](https://hashline.dev) agent audit ledger.

Hashline is a hosted, append-only, tamper-evident audit log for AI agent workloads.
This SDK lets you record every prompt, completion, tool call, and approval in a
verifiable chain — with one function call per event.

## Install

```bash
pip install hashline
```

Requires Python 3.10+ and no dependencies beyond `httpx`.

## Quick start

```python
from hashline import Client

client = Client(api_key="al_live_...")

ack = client.event(
    run_id="run_01HXYZ",
    type="tool_call",
    actor={"type": "agent", "id": "my-agent"},
    payload={"name": "web_search", "arguments": {"q": "hashline"}},
)
print(ack["hash"])  # sha256:...
```

## Async usage

```python
from hashline import AsyncClient

client = AsyncClient(api_key="al_live_...")

ack = await client.event(
    run_id="run_01HXYZ",
    type="tool_call",
    actor={"type": "agent", "id": "my-agent"},
    payload={"name": "web_search", "arguments": {"q": "hashline"}},
)
```

Both `Client` and `AsyncClient` expose the same methods:

| Method | Description |
|---|---|
| `event(*, run_id, type, actor, payload, ...)` | Append a single event |
| `batch(run_id, events)` | Append up to 500 events atomically |
| `get_run(run_id)` | Fetch a run by ID |
| `list_runs(*, agent_id, status, limit)` | List runs |
| `get_events(run_id, *, after_seq, limit)` | Fetch events for a run |
| `verify(run_id)` | Server-side hash-chain verification |
| `export(run_id, *, format)` | Request a JSONL export |
| `get_export(export_id)` | Poll export status |

## Behaviour

**Auto-retry.** The SDK retries on `408`, `429`, and `5xx` responses and on
transport errors (DNS, TCP, TLS, timeout). Uses exponential backoff with full
jitter and respects `Retry-After` headers. Configurable via `max_retries`
(default 3) and `timeout` (default 30 s per attempt).

**No-op mode.** Pass `enabled=False` (or set `HASHLINE_DISABLED=1`) and every
method returns `None` without making any network call. Useful in tests and local
dev — wire Hashline in unconditionally and disable it via env var.

```python
client = Client(api_key="al_live_...", enabled=False)
result = client.event(...)  # None, no network call
```

**Validation.** Bad inputs raise `ValidationError` before any I/O. Catches
empty `run_id`, unknown `type` values, missing `actor` fields, and oversized
batches.

**Typed errors.**

```python
from hashline.errors import HashlineError, ValidationError, APIError, NetworkError

try:
    client.event(...)
except ValidationError as e:
    # bad input — fix the callsite
    ...
except APIError as e:
    print(e.status, e.request_id, e.problem)
except NetworkError as e:
    # transport failure after all retries
    ...
```

**Env vars.**

| Variable | Effect |
|---|---|
| `HASHLINE_API_KEY` | Default API key |
| `HASHLINE_BASE_URL` | Override base URL (e.g. local dev server) |
| `HASHLINE_DISABLED` | Set to `1`, `true`, or `yes` to enable no-op mode |

## Security notes

- **Never use the SDK in browser code.** API keys must stay server-side.
- **The SDK does not scrub PII.** Filter sensitive data before passing it to
  `payload`.
- Rotate API keys via the Hashline dashboard if compromised.

## Examples

| File | What it shows |
|---|---|
| [`examples/basic.py`](examples/basic.py) | End-to-end: emit events, verify chain, export |
| [`examples/anthropic_tools.py`](examples/anthropic_tools.py) | Anthropic SDK tool-use loop |
| [`examples/openai_tools.py`](examples/openai_tools.py) | OpenAI SDK tool-use loop |

Run any example:

```bash
HASHLINE_API_KEY=al_live_... python examples/basic.py
```

## Development

```bash
# Install dev dependencies
pip install -e ".[dev]"
# or
pip install httpx pytest pytest-asyncio respx mypy ruff build

# Type check
mypy src/hashline

# Lint
ruff check .

# Format
ruff format .

# Tests
pytest -v

# Build
python -m build
```

## License

Apache-2.0 — see [LICENSE](LICENSE).
