Metadata-Version: 2.4
Name: haruto
Version: 0.1.0
Summary: Official Python SDK for the Haruto grounded PDF parsing API.
Author: Harsh Sharma
License: MIT
Project-URL: Homepage, https://github.com/HarshSharma0801
Project-URL: Documentation, https://github.com/HarshSharma0801
Keywords: pdf,parsing,document,table-extraction,grounded,haruto
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: httpx>=0.24

# haruto

Official Python SDK for the [Haruto](https://github.com/HarshSharma0801) grounded PDF parsing API — self-hosted document intelligence for clinical protocols, claims, faxes, and hundred-thousand-page archives.

Every parsed element comes back **grounded**: page number + bounding box on each heading, paragraph, table cell, and figure, so downstream systems verify values against the source instead of trusting a transcription.

- **One dependency** (`httpx`) — Python 3.9+
- **Network-resilient by default** — exponential-backoff retries on connection errors and 5xx; uploads retry only when the server never responded (never a double-submit)
- **Built for huge documents** — async jobs, page-range results, webhooks

```bash
pip install haruto
```

## Quickstart

```python
from haruto import Haruto

hr = Haruto("https://your-haruto-deploy.example.com", "wk_...")

job = hr.parse("protocol.pdf", mode="accurate")   # upload, returns immediately
hr.wait_for(job["job_id"])                        # poll with backoff
doc = hr.result(job["job_id"])                    # grounded JSON

for el in doc["elements"]:
    el["type"]   # "heading" | "paragraph" | "table" | "figure" | ...
    el["page"]   # 1-based page number
    el["bbox"]   # {x0, y0, x1, y1} in PDF points, top-left origin
    el["text"]   # reading-order text
    el["table"]  # cell grid with row/col spans, when type == "table"
```

Small documents can skip the poll entirely:

```python
done = hr.parse("letter.pdf", wait=True)   # blocks until parsed
```

The client is a context manager — `with Haruto(url, key) as hr:` closes the connection pool for you.

## Parse options

```python
hr.parse(
    "protocol.pdf",
    mode="accurate",        # "accurate" (layout + table models, default) | "fast" (text-layer only, ~100x faster)
    ocr="auto",             # "auto" (OCR only pages that need it, default) | "off" | "force"
    webhook_url="https://your.app/hooks/haruto",   # POSTed on completion
    wait=False,             # True = block until done (small docs)
)
```

## Results

```python
doc = hr.result(job_id)                        # full grounded JSON
md  = hr.result(job_id, format="markdown")     # tables reconstructed
txt = hr.result(job_id, format="text")         # reading-order text

# large documents: fetch a page range instead of the whole result
part = hr.result(job_id, pages="1200-1225")
```

Results are retained for a configurable window after completion (24 h by default on a standard deployment), then deleted — parsed content is a delivery, not an archive. A purged result raises `HarutoError` with status `410`.

## Jobs

```python
hr.job(job_id)        # status, page counts, progress
hr.jobs(limit=25)     # list your jobs
hr.cancel(job_id)     # stop a running job
hr.requeue(job_id)    # resume an interrupted job (crash recovery)
```

`wait_for(job_id, poll_seconds=5.0)` polls until the job reaches a terminal state and raises `HarutoError` if the job failed.

## Webhooks

Completion webhooks are signed. Verify with a constant-time compare and a five-minute replay window:

```python
from haruto import verify_webhook

ok = verify_webhook(
    webhook_secret,
    raw_body,                                   # exact bytes received
    request.headers["X-Haruto-Timestamp"],
    request.headers["X-Haruto-Signature"],
)
```

## Errors

Every non-2xx response raises `HarutoError` with `.status` and the server's message:

| Status | Meaning |
|---|---|
| `401` | bad or missing API key |
| `402` | page quota exhausted |
| `409` | result requested before the job succeeded |
| `410` | result deleted by the retention window — re-submit |
| `413` | upload exceeds the deployment's size limit |
| `429` | rate limited |
| `0`   | network unreachable after all retries |

## Self-hosting

Haruto runs entirely on your infrastructure — no LLMs, no external calls; documents never leave your network. Accuracy is measured, never estimated: the engine ships with a reproducible benchmark harness covering 17 parsers (including Reducto, Pulse, and LlamaParse) on exact machine-generated ground truth.

MIT © Harsh Sharma
