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.
- aer1_langgraph-0.1.0/PKG-INFO +133 -0
- aer1_langgraph-0.1.0/README.md +117 -0
- aer1_langgraph-0.1.0/aer1_langgraph/__init__.py +21 -0
- aer1_langgraph-0.1.0/aer1_langgraph/handler.py +605 -0
- aer1_langgraph-0.1.0/aer1_langgraph.egg-info/PKG-INFO +133 -0
- aer1_langgraph-0.1.0/aer1_langgraph.egg-info/SOURCES.txt +10 -0
- aer1_langgraph-0.1.0/aer1_langgraph.egg-info/dependency_links.txt +1 -0
- aer1_langgraph-0.1.0/aer1_langgraph.egg-info/requires.txt +2 -0
- aer1_langgraph-0.1.0/aer1_langgraph.egg-info/top_level.txt +1 -0
- aer1_langgraph-0.1.0/pyproject.toml +26 -0
- aer1_langgraph-0.1.0/setup.cfg +4 -0
- aer1_langgraph-0.1.0/tests/test_handler.py +329 -0
|
@@ -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"
|