LangGraph adapter for SBO3L — drop a PolicyGuardNode between your plan and execute nodes to gate every action through SBO3L's policy boundary.
Project description
sbo3l-langgraph
LangGraph adapter for SBO3L. Drop a PolicyGuardNode between your plan and execute nodes to gate every payment-shaped action through SBO3L's policy boundary.
Install
pip install sbo3l-langgraph sbo3l-sdk langgraph
3-node graph
plan ──► policy_guard ──► execute
│
└─► END (when SBO3L denies)
from langgraph.graph import StateGraph, END
from sbo3l_sdk import SBO3LClientSync
from sbo3l_langgraph import PolicyGuardNode, route_after_guard, DENIED
class State(TypedDict, total=False):
proposed_action: dict # APRP body (written by `plan`)
policy_receipt: dict # set on allow (read by `execute`)
deny_reason: dict # set on deny (read by error handler)
result: str # downstream `execute` writes this
client = SBO3LClientSync("http://localhost:8730")
def plan(state):
return {"proposed_action": {
"agent_id": "research-agent-01",
"task_id": "demo-1",
"intent": "purchase_api_call",
# ...full APRP body...
}}
def execute(state):
receipt = state["policy_receipt"]
return {"result": f"executed; ref={receipt['receipt']['execution_ref']}"}
graph = StateGraph(State)
graph.add_node("plan", plan)
graph.add_node("policy_guard", PolicyGuardNode(client))
graph.add_node("execute", execute)
graph.set_entry_point("plan")
graph.add_edge("plan", "policy_guard")
graph.add_conditional_edges(
"policy_guard",
route_after_guard,
{"execute": "execute", DENIED: END},
)
graph.add_edge("execute", END)
app = graph.compile()
final = app.invoke({})
Behavior
Allow: writes state["policy_receipt"] with the full PaymentRequestResponse shape (decision, deny_code, matched_rule_id, request_hash, policy_hash, audit_event_id, receipt). The conditional edge routes to execute.
Deny / requires_human: writes state["deny_reason"] with code (deny_code or policy.deny/policy.requires_human fallback), decision, matched_rule_id, audit_event_id. Conditional edge routes to END (or wherever you wire DENIED).
Transport / auth failures: also writes state["deny_reason"] with code set to the RFC 7807 domain code (e.g. auth.required) or transport.failed for network errors. decision: "error".
Idempotency
PolicyGuardNode(
client,
idempotency_key=lambda action: f"{action['task_id']}-{action['nonce']}",
)
Sync vs async
PolicyGuardNode is sync — pair it with SBO3LClientSync (httpx.Client under the hood, safe inside any event loop).
If your graph is async (graph.astream()), use the async client (SBO3LClient) — but pass it via a sync wrapper or use the future AsyncPolicyGuardNode (TODO follow-up). For now passing async client to sync PolicyGuardNode writes a transport.async_client_in_sync_graph deny_reason — clear failure mode, no silent blocking.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sbo3l_langgraph-1.2.0.tar.gz.
File metadata
- Download URL: sbo3l_langgraph-1.2.0.tar.gz
- Upload date:
- Size: 7.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
db95b3e36f87008c89ddbb06c732e971542566b9bb64f60deb9103e8149a1aa2
|
|
| MD5 |
e893472af40d2347ecd6ebfbc5e4d16a
|
|
| BLAKE2b-256 |
870892bf7bc038fbceeb430160aaa5613f8ef09944b1643e8ed1b6b323a3b8bb
|
Provenance
The following attestation bundles were made for sbo3l_langgraph-1.2.0.tar.gz:
Publisher:
integrations-publish.yml on B2JK-Industry/SBO3L-ethglobal-openagents-2026
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sbo3l_langgraph-1.2.0.tar.gz -
Subject digest:
db95b3e36f87008c89ddbb06c732e971542566b9bb64f60deb9103e8149a1aa2 - Sigstore transparency entry: 1429390336
- Sigstore integration time:
-
Permalink:
B2JK-Industry/SBO3L-ethglobal-openagents-2026@cd0fcfb468f93816bee364f968f25114f7abe243 -
Branch / Tag:
refs/tags/langgraph-py-v1.2.0 - Owner: https://github.com/B2JK-Industry
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
integrations-publish.yml@cd0fcfb468f93816bee364f968f25114f7abe243 -
Trigger Event:
push
-
Statement type:
File details
Details for the file sbo3l_langgraph-1.2.0-py3-none-any.whl.
File metadata
- Download URL: sbo3l_langgraph-1.2.0-py3-none-any.whl
- Upload date:
- Size: 6.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dcddf3adc30ebebcfd336fb691c5c0db937cd398e48aaafd5f22ea41683868a1
|
|
| MD5 |
7fa003350987abc3cb8088a9e6a3246a
|
|
| BLAKE2b-256 |
9c8e14a5485e99bd072f0a7a3fd3e0fdff11f7246d7de2d925b221d108a0b7e8
|
Provenance
The following attestation bundles were made for sbo3l_langgraph-1.2.0-py3-none-any.whl:
Publisher:
integrations-publish.yml on B2JK-Industry/SBO3L-ethglobal-openagents-2026
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sbo3l_langgraph-1.2.0-py3-none-any.whl -
Subject digest:
dcddf3adc30ebebcfd336fb691c5c0db937cd398e48aaafd5f22ea41683868a1 - Sigstore transparency entry: 1429390338
- Sigstore integration time:
-
Permalink:
B2JK-Industry/SBO3L-ethglobal-openagents-2026@cd0fcfb468f93816bee364f968f25114f7abe243 -
Branch / Tag:
refs/tags/langgraph-py-v1.2.0 - Owner: https://github.com/B2JK-Industry
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
integrations-publish.yml@cd0fcfb468f93816bee364f968f25114f7abe243 -
Trigger Event:
push
-
Statement type: