Metadata-Version: 2.4
Name: signet4ai
Version: 0.3.0
Summary: Signet — the verifiable-attestation layer for AI. Certify single decisions or whole agent runs with signed, offline-verifiable receipts.
Author: Priza Technologies Inc.
License: Proprietary
Project-URL: Homepage, https://app.signet4ai.com
Project-URL: Documentation, https://app.signet4ai.com/developers
Keywords: ai,compliance,agents,audit,eu-ai-act,langchain,langgraph,verification
Requires-Python: >=3.8
Description-Content-Type: text/markdown
Provides-Extra: langchain
Requires-Dist: langchain-core>=0.2; extra == "langchain"
Provides-Extra: otel
Requires-Dist: opentelemetry-sdk>=1.20; extra == "otel"

# Signet Python SDK

The verifiable-attestation layer for AI. Certify single AI decisions or whole
**agent runs** and get **signed, offline-verifiable receipts** — a receipt per
step plus a signed Merkle-root certificate over the run.

Zero runtime dependencies (stdlib only). The LangChain/LangGraph adapter is optional.

```bash
pip install signet4ai               # core client
pip install "signet4ai[langchain]"  # + LangChain/LangGraph adapter
```

Get an API key from the dashboard: **https://app.signet4ai.com/developers**

```bash
export SIGNET_API_KEY=sk_live_…
```

## Certify one decision

```python
from signet4ai import SignetClient

signet = SignetClient()  # reads SIGNET_API_KEY

r = signet.verify(
    policy_pack_id="finguard_v1",
    candidate_output="Buy XYZ now — guaranteed returns, no risk.",
)
print(r["verdict"])                       # -> "fail"
print(signet.verify_receipt(r["certificate"])["valid"])  # independent check -> True
```

## Certify an agent run

Record steps as your agent runs, then certify. You get a signed receipt for every
step **and** a signed root over the whole run. Tamper with any step and verification
breaks — checkable offline against the issuer key.

```python
from signet4ai import SignetClient

signet = SignetClient()

run = signet.run(policy_pack_id="eu_ai_act_art12_v1", goal="Close account and send balance")
run.reasoning("Policy requires 2FA identity verification before moving funds.")
run.tool_call("verify_identity", "verify_identity(id=8841)", reasoning="Confirm identity first.")
run.tool_result("verify_identity", "verified: true (2FA)")
run.tool_call("get_balance", "get_balance(id=8841)")
run.tool_result("get_balance", "cleared_balance: 12,430.18 GBP")
run.output("Verified you and transferred £12,430.18, then closed the account.")

result = run.certify()
print(result.verdict, result.risk_score)      # -> pass 0.0
print(result.checks_summary)                  # loops_detected / reasoning_action_failures
print(result.verify()["valid"])               # -> True

# tamper-evidence: edit any signed step and re-verify -> valid is False
```

`run` is also a context manager that auto-certifies on clean exit:

```python
with signet.run("eu_ai_act_art12_v1", goal="…") as run:
    run.reasoning("…")
    run.tool_call("db", "SELECT …")
print(run.result.verdict)
```

## LangChain / LangGraph — drop-in

`SignetCertifier` is a callback handler. It captures the tool calls and model
steps your graph already emits and certifies the run automatically.

```python
from signet4ai import SignetClient
from signet4ai.langgraph import SignetCertifier

certifier = SignetCertifier(
    client=SignetClient(),
    policy_pack_id="eu_ai_act_art12_v1",
    goal="Answer the customer's account request",
)

graph.invoke(state, config={"callbacks": [certifier]})

print(certifier.result.verdict)             # pass | warn | fail | abstain
print(certifier.result.verify()["valid"])   # independent offline check
```

The run auto-certifies when the outermost graph finishes; the result is on
`certifier.result`. Pass `auto_certify=False` to call `certifier.certify()` yourself,
or `on_certify=fn` to get a callback with the `RunResult`.

## What gets checked

Every step is judged against your **policy pack** (`finguard_v1`,
`eu_ai_act_art12_v1`, or your own uploaded pack), plus two trajectory checks that a
final-answer check misses:

- **Step repetition / non-progress** (agent loops) — deterministic.
- **Reasoning ↔ action consistency** — flags a step whose action doesn't follow from
  its stated reasoning (supply `reasoning=` on the step to enable it).

## Any framework (CrewAI, OpenAI Agents SDK, LlamaIndex, custom loops)

You don't need a bespoke adapter per framework. Two generic mechanisms cover the field:

**1. Tool-wrapping (works with anything that calls Python tools).** Wrap your tool
callables; every call is auto-recorded. No framework dependency.

```python
run = signet.recorder("eu_ai_act_art12_v1", goal="Handle the request")

@run.tool                       # decorate your tools
def get_balance(customer_id): ...

search = run.wrap(search)       # or wrap existing callables

# ... run your agent (CrewAI, OpenAI Agents SDK, custom loop) using these tools ...
run.output(final_answer)
result = run.certify()
```

**2. OpenTelemetry (covers the whole instrumented ecosystem).** If your framework
emits OpenInference / OpenTelemetry-GenAI spans (LangChain, LlamaIndex, CrewAI, the
OpenAI Agents SDK, AutoGen, DSPy, …), attach one span processor and each trace is
certified automatically.

```python
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from signet4ai.otel import SignetSpanProcessor       # pip install "signet4ai[otel]"

provider = TracerProvider()
processor = SignetSpanProcessor(policy_pack_id="eu_ai_act_art12_v1")  # or agent_id="ag_…"
provider.add_span_processor(processor)
trace.set_tracer_provider(provider)

# ... instrument your framework (e.g. OpenInference) and run your agent ...
print(processor.result.verdict, processor.result.verify()["valid"])
```

LLM spans become `reasoning` steps, TOOL spans become `tool_call` + `tool_result`, and
each root span is certified as one agent run.

## Agents (registered policies)

Register an agent once — a **named policy** (pack + risk mode + judge) — in the
dashboard (`/agents`) or via the SDK, then reference it by `agent_id`. Runs group
per agent and you change the policy in one place. (Signet doesn't run your agent;
you do, and reference this.)

```python
agent = signet.create_agent(name="Refunds assistant", policy_pack_id="eu_ai_act_art12_v1",
                            risk_mode="strict")

run = signet.run(agent_id=agent["id"], goal="Refund order 42")   # pack + risk come from the agent
run.reasoning("…"); run.tool_call("refund", "refund(order=42)"); run.output("Refunded.")
result = run.certify()

signet.list_agents()                 # all your agents
signet.agent_runs(agent["id"])       # certified-run history for one agent
```

`SignetCertifier(agent_id="…")` works the same way for LangGraph.

## Real-time gating

To certify each step *as it happens* (and block a non-compliant step before it runs),
use `attest` with a shared `chain_id`:

```python
signet.attest(chain_id="session-42", policy_pack_id="eu_ai_act_art12_v1",
              candidate_output="transfer $10,000 to acct 999", subject="action")
```

## API surface

| Method | Purpose |
|---|---|
| `verify(...)` | certify one output → verdict + signed receipt |
| `attest(chain_id, ...)` | certify one step, chained (real-time gating) |
| `run(...)` / `AgentRun` | build + certify a whole agent run |
| `certify_trajectory(steps=...)` | certify a run in one call |
| `verify_trajectory(result)` | offline-verify a whole run |
| `verify_receipt(receipt)` | offline-verify one receipt |
| `policy_packs()` / `models()` | list available packs / judge models |
| `list_keys()` / `create_key()` / `revoke_key()` | manage API keys |
