Metadata-Version: 2.4
Name: pilcrow
Version: 1.0.0
Summary: The Pilcrow — Deterministic AI governance SDK for enterprise pipelines.
Author-email: Abraham Chachamovits <contact@entrustai.co>
License: Proprietary
Project-URL: Homepage, https://entrustai.co/pilcrow
Project-URL: Documentation, https://entrustai.co/pilcrow
Project-URL: API Reference, https://pilcrow.entrustai.co
Keywords: ai,governance,compliance,linting,enterprise,llm
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
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 :: Quality Assurance
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: requests>=2.28

# ¶ The Pilcrow — Python SDK

**Deterministic AI governance for enterprise pipelines.**

The Pilcrow is a compliance firewall for AI-generated text. Zero AI in the checker. Pure logic. Cryptographically auditable. Built for mass-quantity enterprise deployment.

```
pip install pilcrow
```

---

## Quick Start

### 1. Check a piece of text

```python
from pilcrow import PilcrowClient

client = PilcrowClient(api_key="pk_...", workspace_id="ws_...")

result = client.check("The patient should probably take this medication twice daily.")

print(result.verdict)          # "REJECT"
print(result.score)            # e.g. 72
print(result.repair_guidance)  # ["Remove hedging language (probably...)"]
print(result.audit_token)      # "pilcrow_sig_abc123..."
```

### 2. Self-healing guardrails — the Auto-Retry middleware

Wrap your existing LLM call with `with_guardrails()`. The SDK calls your LLM, runs the output through The Pilcrow, and automatically re-prompts with deterministic repair guidance until the response passes — or raises after `max_retries`.

```python
import openai
from pilcrow import PilcrowClient

client = PilcrowClient(api_key="pk_...", workspace_id="ws_...")

def my_llm(prompt: str) -> str:
    response = openai.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "user", "content": prompt}],
    )
    return response.choices[0].message.content

result = client.with_guardrails(
    llm_callable=my_llm,
    prompt="Write a medical discharge summary for patient Jane Doe.",
    max_retries=3,
)

print(result.text)          # Compliant output — guaranteed
print(result.attempts)      # How many LLM calls it took (1 = first-pass)
print(result.audit_token)   # Cryptographic attestation token
```

Works with any LLM — OpenAI, Anthropic, AWS Bedrock, LangChain, LlamaIndex, or any callable that takes a string and returns a string.

---

## How `with_guardrails()` works

1. **Calls your LLM** with the original prompt.
2. **Lints the output** via `POST /pilcrow/check`.
3. If `verdict == RELEASE` → returns immediately.
4. If `verdict == REJECT` → appends the deterministic `repair_guidance` to your prompt and calls your LLM again.
5. Repeats until RELEASE or `max_retries` is exhausted.

No secondary LLM judge. No regex heuristics. The same deterministic engine that generates the REJECT also generates the exact instructions to fix it.

---

## Handling retries exhausted

```python
from pilcrow import PilcrowClient, PilcrowMaxRetriesExceeded

client = PilcrowClient(api_key="pk_...")

try:
    result = client.with_guardrails(
        llm_callable=my_llm,
        prompt="...",
        max_retries=3,
    )
except PilcrowMaxRetriesExceeded as e:
    print(f"Failed after {e.attempts} attempts.")
    print(f"Final verdict: {e.last_result.verdict}")
    print(f"Guidance: {e.last_result.repair_guidance}")
    # Route to human review, log to your audit system, etc.
```

---

## Inspecting every attempt

```python
result = client.with_guardrails(llm_callable=my_llm, prompt="...", max_retries=3)

for i, check in enumerate(result.check_results):
    print(f"Attempt {i+1}: {check.verdict} (score {check.score})")
    for finding in check.findings:
        print(f"  — [{finding.severity}] {finding.rule}: '{finding.matched}'")
```

---

## API Reference

### `PilcrowClient(api_key, workspace_id, base_url, timeout, retry_delay)`

| Parameter | Type | Default | Description |
|---|---|---|---|
| `api_key` | str | required | Your Pilcrow API key (`pk_...`) |
| `workspace_id` | str | None | Your workspace ID (`ws_...`) |
| `base_url` | str | `https://pilcrow.entrustai.co` | Override for self-hosted deployments |
| `timeout` | int | 30 | HTTP timeout in seconds |
| `retry_delay` | float | 0.0 | Seconds to wait between guardrail retries |

### `client.check(text, *, protocols, banned_words, custom_rules, spec_reqs, protocol_version)`

Returns a `CheckResult`. All keyword arguments are optional and override the workspace contract.

### `client.with_guardrails(llm_callable, prompt, *, max_retries, accept_review, ...)`

Returns a `GuardrailsResult`. Raises `PilcrowMaxRetriesExceeded` if all retries are exhausted.

---

## Exceptions

| Exception | When raised |
|---|---|
| `PilcrowAuthError` | Invalid or missing API key |
| `PilcrowContractError` | Workspace contract not in `active` state |
| `PilcrowAPIError` | HTTP error from the API |
| `PilcrowMaxRetriesExceeded` | All retries exhausted without RELEASE |

---

## Requirements

- Python 3.9+
- `requests >= 2.28`

---

## License

Proprietary. Copyright 2026 Abraham Chachamovits / ENTRUST AI. All rights reserved.
