aer1-langgraph 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,133 @@
1
+ Metadata-Version: 2.4
2
+ Name: aer1-langgraph
3
+ Version: 0.1.0
4
+ Summary: AER-1 verifiable workflow receipts for LangGraph multi-agent swarms
5
+ License: Apache-2.0
6
+ Project-URL: Homepage, https://datatracker.ietf.org/doc/draft-zambo-aer1/
7
+ Keywords: aer-1,langgraph,ai-agents,verifiable-receipts,audit,tracing
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
12
+ Requires-Python: >=3.10
13
+ Description-Content-Type: text/markdown
14
+ Requires-Dist: langgraph>=0.6
15
+ Requires-Dist: langchain-core>=0.3
16
+
17
+ # aer1-langgraph
18
+
19
+ AER-1 verifiable workflow receipts for [LangGraph](https://github.com/langchain-ai/langgraph)
20
+ multi-agent swarms.
21
+
22
+ Attach one callback handler to your graph and every run emits a hash-chained,
23
+ offline-verifiable **verifiable workflow receipt** (AER-1, Section 8):
24
+ which agent did what, in what order, with per-step hashes and one Merkle root
25
+ over the whole swarm. Parallel `Send` branches are recorded with their true
26
+ branch structure, not flattened. No network calls, no behavior changes, the
27
+ handler only observes.
28
+
29
+ ## Install
30
+
31
+ ```bash
32
+ pip install aer1-langgraph
33
+ ```
34
+
35
+ ## Use it (three lines)
36
+
37
+ ```python
38
+ from aer1_langgraph import AER1LangGraphHandler
39
+
40
+ handler = AER1LangGraphHandler(goal="Triage the support queue") # line 1
41
+ result = app.invoke({"tickets": [...]}, config={"callbacks": [handler]}) # line 2
42
+ receipt = handler.finalize(final_answer=result) # line 3
43
+ assert handler.verify(receipt) == [] # VALID
44
+ handler.save("receipt.json", workflow=receipt)
45
+ ```
46
+
47
+ That is the whole integration. The receipt is a plain JSON object you can
48
+ store, ship to an auditor, or render in a UI.
49
+
50
+ ## Swarm receipts
51
+
52
+ Each node execution becomes one receipt step carrying the node name as
53
+ agent identity (`tool` and `agent` on every step). A three-agent swarm
54
+ with two parallel workers produces steps like:
55
+
56
+ ```
57
+ seq=1 tool=router agent=router
58
+ seq=2 tool=worker agent=worker branch=worker:9f3a...
59
+ seq=3 tool=worker agent=worker branch=worker:41bc...
60
+ seq=4 tool=aggregator agent=aggregator
61
+ ```
62
+
63
+ Steps carry `run_id`, `parent_run_id`, and the LangGraph checkpoint
64
+ namespace, so the true execution graph (who ran in parallel under whom)
65
+ is reconstructible from the receipt. `handler.swarm_summary()` returns
66
+ the agent roster, step counts, and branch groups in one call.
67
+
68
+ Tool calls and model calls inside a node are folded into that node's
69
+ step: the step's `receipt_hash` is SHA-256 over the canonical JSON of
70
+ the node name, inputs, outputs, tool calls (name, arguments, output),
71
+ model-call summaries, branch metadata, and any error. The hash commits
72
+ to what the agent actually did; the receipt stays compact.
73
+
74
+ ## What the receipt contains
75
+
76
+ Workflow level (AER-1 Section 8, Table 2):
77
+
78
+ - `type`, `version`, `workflow_id`, `receipt_id`, `session_id`
79
+ - `goal`, `status`
80
+ - `steps`: one record per node execution, seq 1..n in completion order
81
+ - `merkle_root`: Section 8.1 root over the ordered step receipt ids
82
+ - `output_hash`: SHA-256 of the final graph output
83
+ - `verify_url`: where the verification procedure is documented
84
+
85
+ Step level (AER-1 Section 8, Table 3, plus swarm fields):
86
+
87
+ - `seq`, `receipt_id`, `tool` (= node name), `receipt_hash`
88
+ - `started_at`, `ended_at`, `status`
89
+ - `agent`, `run_id`, `parent_run_id`, `checkpoint_ns`
90
+ - `tool_calls`, `llm_calls` (folded records)
91
+
92
+ ## Verification
93
+
94
+ `handler.verify(receipt)` runs the full offline check and returns a
95
+ list of failure reasons, empty when valid:
96
+
97
+ - all Table 2 / Table 3 members present and well-formed
98
+ - `seq` values exactly 1..n in order, no gaps
99
+ - no two steps share a `receipt_id` (MM-1)
100
+ - `merkle_root` matches the recomputed Section 8.1 root
101
+ - strict RFC 3339 timestamps, lowercase UUIDs, 64-char hex digests
102
+
103
+ Tamper with any field and verification fails. Try it:
104
+
105
+ ```python
106
+ receipt["steps"][0]["tool"] = ""
107
+ assert handler.verify(receipt) != [] # fails, as it should
108
+ ```
109
+
110
+ ## Notes
111
+
112
+ - One handler observes one run at a time. Call `handler.reset()` or
113
+ construct a fresh handler before the next run. If a new root graph
114
+ run starts on a handler that already finished one, it auto-resets so
115
+ two runs never mix into one receipt.
116
+ - The `__start__` / `__end__` pseudo-nodes are machinery, not agent
117
+ work, and are not recorded as steps. Conditional-edge router
118
+ functions are user code and are recorded.
119
+ - Steps still in flight when `finalize()` runs (interrupted runs) are
120
+ flushed as `status: "incomplete"` rather than dropped.
121
+ - `session_id` defaults to a fresh UUID per handler; pass your own to
122
+ correlate receipts across runs.
123
+ - `verify_url` defaults to the AER-1 specification page; point it at
124
+ your own verifier in production.
125
+
126
+ ## Spec
127
+
128
+ AER-1: Agent Execution Receipts, `draft-zambo-aer1`,
129
+ https://datatracker.ietf.org/doc/draft-zambo-aer1/
130
+
131
+ ## License
132
+
133
+ Apache-2.0
@@ -0,0 +1,117 @@
1
+ # aer1-langgraph
2
+
3
+ AER-1 verifiable workflow receipts for [LangGraph](https://github.com/langchain-ai/langgraph)
4
+ multi-agent swarms.
5
+
6
+ Attach one callback handler to your graph and every run emits a hash-chained,
7
+ offline-verifiable **verifiable workflow receipt** (AER-1, Section 8):
8
+ which agent did what, in what order, with per-step hashes and one Merkle root
9
+ over the whole swarm. Parallel `Send` branches are recorded with their true
10
+ branch structure, not flattened. No network calls, no behavior changes, the
11
+ handler only observes.
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ pip install aer1-langgraph
17
+ ```
18
+
19
+ ## Use it (three lines)
20
+
21
+ ```python
22
+ from aer1_langgraph import AER1LangGraphHandler
23
+
24
+ handler = AER1LangGraphHandler(goal="Triage the support queue") # line 1
25
+ result = app.invoke({"tickets": [...]}, config={"callbacks": [handler]}) # line 2
26
+ receipt = handler.finalize(final_answer=result) # line 3
27
+ assert handler.verify(receipt) == [] # VALID
28
+ handler.save("receipt.json", workflow=receipt)
29
+ ```
30
+
31
+ That is the whole integration. The receipt is a plain JSON object you can
32
+ store, ship to an auditor, or render in a UI.
33
+
34
+ ## Swarm receipts
35
+
36
+ Each node execution becomes one receipt step carrying the node name as
37
+ agent identity (`tool` and `agent` on every step). A three-agent swarm
38
+ with two parallel workers produces steps like:
39
+
40
+ ```
41
+ seq=1 tool=router agent=router
42
+ seq=2 tool=worker agent=worker branch=worker:9f3a...
43
+ seq=3 tool=worker agent=worker branch=worker:41bc...
44
+ seq=4 tool=aggregator agent=aggregator
45
+ ```
46
+
47
+ Steps carry `run_id`, `parent_run_id`, and the LangGraph checkpoint
48
+ namespace, so the true execution graph (who ran in parallel under whom)
49
+ is reconstructible from the receipt. `handler.swarm_summary()` returns
50
+ the agent roster, step counts, and branch groups in one call.
51
+
52
+ Tool calls and model calls inside a node are folded into that node's
53
+ step: the step's `receipt_hash` is SHA-256 over the canonical JSON of
54
+ the node name, inputs, outputs, tool calls (name, arguments, output),
55
+ model-call summaries, branch metadata, and any error. The hash commits
56
+ to what the agent actually did; the receipt stays compact.
57
+
58
+ ## What the receipt contains
59
+
60
+ Workflow level (AER-1 Section 8, Table 2):
61
+
62
+ - `type`, `version`, `workflow_id`, `receipt_id`, `session_id`
63
+ - `goal`, `status`
64
+ - `steps`: one record per node execution, seq 1..n in completion order
65
+ - `merkle_root`: Section 8.1 root over the ordered step receipt ids
66
+ - `output_hash`: SHA-256 of the final graph output
67
+ - `verify_url`: where the verification procedure is documented
68
+
69
+ Step level (AER-1 Section 8, Table 3, plus swarm fields):
70
+
71
+ - `seq`, `receipt_id`, `tool` (= node name), `receipt_hash`
72
+ - `started_at`, `ended_at`, `status`
73
+ - `agent`, `run_id`, `parent_run_id`, `checkpoint_ns`
74
+ - `tool_calls`, `llm_calls` (folded records)
75
+
76
+ ## Verification
77
+
78
+ `handler.verify(receipt)` runs the full offline check and returns a
79
+ list of failure reasons, empty when valid:
80
+
81
+ - all Table 2 / Table 3 members present and well-formed
82
+ - `seq` values exactly 1..n in order, no gaps
83
+ - no two steps share a `receipt_id` (MM-1)
84
+ - `merkle_root` matches the recomputed Section 8.1 root
85
+ - strict RFC 3339 timestamps, lowercase UUIDs, 64-char hex digests
86
+
87
+ Tamper with any field and verification fails. Try it:
88
+
89
+ ```python
90
+ receipt["steps"][0]["tool"] = ""
91
+ assert handler.verify(receipt) != [] # fails, as it should
92
+ ```
93
+
94
+ ## Notes
95
+
96
+ - One handler observes one run at a time. Call `handler.reset()` or
97
+ construct a fresh handler before the next run. If a new root graph
98
+ run starts on a handler that already finished one, it auto-resets so
99
+ two runs never mix into one receipt.
100
+ - The `__start__` / `__end__` pseudo-nodes are machinery, not agent
101
+ work, and are not recorded as steps. Conditional-edge router
102
+ functions are user code and are recorded.
103
+ - Steps still in flight when `finalize()` runs (interrupted runs) are
104
+ flushed as `status: "incomplete"` rather than dropped.
105
+ - `session_id` defaults to a fresh UUID per handler; pass your own to
106
+ correlate receipts across runs.
107
+ - `verify_url` defaults to the AER-1 specification page; point it at
108
+ your own verifier in production.
109
+
110
+ ## Spec
111
+
112
+ AER-1: Agent Execution Receipts, `draft-zambo-aer1`,
113
+ https://datatracker.ietf.org/doc/draft-zambo-aer1/
114
+
115
+ ## License
116
+
117
+ Apache-2.0
@@ -0,0 +1,21 @@
1
+ """aer1-langgraph: AER-1 verifiable workflow receipts for LangGraph."""
2
+
3
+ from .handler import (
4
+ AER1LangGraphHandler,
5
+ DEFAULT_VERIFY_URL,
6
+ WORKFLOW_TYPE,
7
+ WORKFLOW_VERSION,
8
+ merkle_root,
9
+ verify_workflow_receipt,
10
+ )
11
+
12
+ __all__ = [
13
+ "AER1LangGraphHandler",
14
+ "DEFAULT_VERIFY_URL",
15
+ "WORKFLOW_TYPE",
16
+ "WORKFLOW_VERSION",
17
+ "merkle_root",
18
+ "verify_workflow_receipt",
19
+ ]
20
+
21
+ __version__ = "0.1.0"