Skip to main content

aer1-langgraph

AER-1 verifiable workflow receipts for LangGraph multi-agent swarms.

Attach one callback handler to your graph and every run emits a hash-chained, offline-verifiable verifiable workflow receipt (AER-1, Section 8): which agent did what, in what order, with per-step hashes and one Merkle root over the whole swarm. Parallel Send branches are recorded with their true branch structure, not flattened. No network calls, no behavior changes, the handler only observes.

Install

pip install aer1-langgraph

Use it (three lines)

from aer1_langgraph import AER1LangGraphHandler

handler = AER1LangGraphHandler(goal="Triage the support queue")  # line 1
result = app.invoke({"tickets": [...]}, config={"callbacks": [handler]})  # line 2
receipt = handler.finalize(final_answer=result)                   # line 3
assert handler.verify(receipt) == []                              # VALID
handler.save("receipt.json", workflow=receipt)

That is the whole integration. The receipt is a plain JSON object you can store, ship to an auditor, or render in a UI.

Swarm receipts

Each node execution becomes one receipt step carrying the node name as agent identity (tool and agent on every step). A three-agent swarm with two parallel workers produces steps like:

seq=1 tool=router      agent=router
seq=2 tool=worker      agent=worker   branch=worker:9f3a...
seq=3 tool=worker      agent=worker   branch=worker:41bc...
seq=4 tool=aggregator  agent=aggregator

Steps carry run_id, parent_run_id, and the LangGraph checkpoint namespace, so the true execution graph (who ran in parallel under whom) is reconstructible from the receipt. handler.swarm_summary() returns the agent roster, step counts, and branch groups in one call.

Tool calls and model calls inside a node are folded into that node's step: the step's receipt_hash is SHA-256 over the canonical JSON of the node name, inputs, outputs, tool calls (name, arguments, output), model-call summaries, branch metadata, and any error. The hash commits to what the agent actually did; the receipt stays compact.

What the receipt contains

Workflow level (AER-1 Section 8, Table 2):

  • type, version, workflow_id, receipt_id, session_id
  • goal, status
  • steps: one record per node execution, seq 1..n in completion order
  • merkle_root: Section 8.1 root over the ordered step receipt ids
  • output_hash: SHA-256 of the final graph output
  • verify_url: where the verification procedure is documented

Step level (AER-1 Section 8, Table 3, plus swarm fields):

  • seq, receipt_id, tool (= node name), receipt_hash
  • started_at, ended_at, status
  • agent, run_id, parent_run_id, checkpoint_ns
  • tool_calls, llm_calls (folded records)

Verification

handler.verify(receipt) runs the full offline check and returns a list of failure reasons, empty when valid:

  • all Table 2 / Table 3 members present and well-formed
  • seq values exactly 1..n in order, no gaps
  • no two steps share a receipt_id (MM-1)
  • merkle_root matches the recomputed Section 8.1 root
  • strict RFC 3339 timestamps, lowercase UUIDs, 64-char hex digests

Tamper with any field and verification fails. Try it:

receipt["steps"][0]["tool"] = ""
assert handler.verify(receipt) != []  # fails, as it should

Notes

  • One handler observes one run at a time. Call handler.reset() or construct a fresh handler before the next run. If a new root graph run starts on a handler that already finished one, it auto-resets so two runs never mix into one receipt.
  • The __start__ / __end__ pseudo-nodes are machinery, not agent work, and are not recorded as steps. Conditional-edge router functions are user code and are recorded.
  • Steps still in flight when finalize() runs (interrupted runs) are flushed as status: "incomplete" rather than dropped.
  • session_id defaults to a fresh UUID per handler; pass your own to correlate receipts across runs.
  • verify_url defaults to the AER-1 specification page; point it at your own verifier in production.

Spec

AER-1: Agent Execution Receipts, draft-zambo-aer1, https://datatracker.ietf.org/doc/draft-zambo-aer1/

License

Apache-2.0

Metadata

Release files for aer1-langgraph 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for aer1-langgraph 0.1.0
File Size Uploaded
aer1_langgraph-0.1.0.tar.gz 14.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for aer1-langgraph 0.1.0
File Interpreter ABI Platform
aer1_langgraph-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 24.4 kB

Release files / aer1_langgraph-0.1.0.tar.gz

Download URL aer1_langgraph-0.1.0.tar.gz
Size 14.2 kB
Tags Source
SHA-256 checksum
How to use checksums
7a86925e0738ebd7f25c8e747e7c0724f62f9fc4a6130959ca60f5ededee42af
BLAKE2b-256 checksum
How to use checksums
d408fb1ed97a3b96ac09664e94899b6d4539c573316ac3561c002d0d0ce00a1b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via rambo-pypi-grab/0.1.0

Release files / aer1_langgraph-0.1.0-py3-none-any.whl

Download URL aer1_langgraph-0.1.0-py3-none-any.whl
Size 10.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
44e54afa3c764f02e1ab0ab6b411b0cd29fde649094d4f4957bab1140489b7b0
BLAKE2b-256 checksum
How to use checksums
90edad4734b5cc390e619248d382b144af9fee4a0804e7ed6e124469fb97a313
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via rambo-pypi-grab/0.1.0

Release history Release notifications | RSS feed

0.1.2

2 release files

0.1.1

2 release files

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page