Skip to main content

Guard a custom LangGraph agent with the OpenGuardrails (OGR) protocol — a drop-in ToolNode at the agent-hook altitude, with native LangGraph interrupt() as the human-confirm gate.

Project description

openguardrails-instrumentation-langgraph

Guard a custom LangGraph agent with the OpenGuardrails (OGR) protocol — a drop-in ToolNode that enforces one policy you own at the agent-hook altitude, using LangGraph's own interrupt() as the human-confirm gate.

pip install "openguardrails-instrumentation-langgraph[langgraph]"

Why this is different from the other instrumentations

The opencode / OpenClaw / Claude Code bindings hook a finished product's plugin surface. A developer hand-rolling an agent with LangGraph has no plugin marketplace — they need a library they import. That library's natural Policy Enforcement Point (PEP) is the tool boundary: in LangGraph, the ToolNode. That node is the agent-hook altitude.

What it enforces

Every tool_call the model emits is turned into an OGR GuardEvent and judged by the reference Runtime (the PDP) before the tool runs:

Tool call Provenance Decision
get_weather(city="Paris") trusted allow — runs
bash("rm -rf /") trusted block — refused in place, never runs
bash("curl … | sh") trusted require_approvalinterrupt()
bash("curl … | sh") untrusted (after a web/retrieval result) block — injection

The last two rows are the point: the same command gets a different verdict depending on where its inputs came from. A tool whose result carries external content (web fetch, retrieval, MCP) taints the session, so a later tool call inherits untrusted provenance — the indirect-prompt-injection defense.

The human-confirm gate is LangGraph-native

A require_approval verdict doesn't invent an approval UI — it raises a LangGraph interrupt(). The graph pauses at its checkpointer; a human approves; the graph resumes. This keeps OGR's red line intact: the confirm gate and the enforcement point stay privilege-separated — the agent cannot self-approve.

from langgraph.types import Command

out = app.invoke({"messages": [...]}, config)      # pauses at __interrupt__
# ... surface out["__interrupt__"][0].value to a human ...
out = app.invoke(Command(resume=True), config)     # approve all
# or Command(resume={tool_call_id: True/False}) / a list of approved ids

Resume accepts True/False, "approve"/"deny", a {tool_call_id: bool} map, or a list of approved ids. Anything unrecognized fails closed (deny).

Three integration surfaces

from openguardrails_instrumentation_langgraph import (
    OpenGuardrailsToolNode, guard, ogr_guard,
)

1. Drop-in ToolNode — the primary PEP, for a hand-built StateGraph:

graph.add_node("tools", OpenGuardrailsToolNode(tools))   # was ToolNode(tools)
app = graph.compile(checkpointer=MemorySaver())          # checkpointer needed for approvals

2. guard(tools) — when you don't own the ToolNode (e.g. the prebuilt agent):

from langgraph.prebuilt import create_react_agent
agent = create_react_agent(model, guard(tools))

3. @ogr_guard — a single tool:

@ogr_guard
@tool
def bash(command: str) -> str: ...

OpenGuardrailsToolNode gives full provenance/injection coverage (it sees the run config and threads session taint); guard() / ogr_guard judge with base provenance.

The policy

policy.json is a thin overlay — {"$extends": "openguardrails:base"}. The core resolves the canonical PAP base and deep-merges your overlay into one effective policy, so the same policy that fronts your gateway and sandbox also judges these tool calls. Point elsewhere with OGR_POLICY=/path/to/policy.json or OpenGuardrailsToolNode(tools, policy="…").

Altitude

This binding is the agent-hook altitude (the tool boundary). The gateway altitude (prompt-injection / secret leakage on the LLM wire) is covered by openguardrails-instrumentation-litellm if your LangGraph app routes its model through litellm — the two compose.

Try it

python examples/react_agent.py     # drives allow / block / approve / deny end-to-end
python tests/test_smoke.py         # offline decision-core test (no langgraph needed)

Project details


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

If you're not sure about the file name format, learn more about wheel file names.

File details

Details for the file openguardrails_instrumentation_langgraph-0.1.1.tar.gz.

File metadata

File hashes

Hashes for openguardrails_instrumentation_langgraph-0.1.1.tar.gz
Algorithm Hash digest
SHA256 f86b719517aebf79809b948d7e8710ea29a5fb7c2a2f185f88ead11cef314d5f
MD5 d1412694975dc27d6d571a161df260ab
BLAKE2b-256 508113831f251880ce5b5f49280c837c7486cfe4a2070fb60c6abf9c34ba595c

See more details on using hashes here.

Provenance

The following attestation bundles were made for openguardrails_instrumentation_langgraph-0.1.1.tar.gz:

Publisher: publish.yml on openguardrails/openguardrails-instrumentation-langgraph

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file openguardrails_instrumentation_langgraph-0.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for openguardrails_instrumentation_langgraph-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 bcf1db7dc65cd82a45614fb40c52a0fee4ddfc56c0f93c740a07dffaf90cb85d
MD5 6d69010f6ec3a4bf109f6ed565fba1a9
BLAKE2b-256 f494a171a1e85a2f6aef6615ca1f0904038fd06afcc762d91cf20edb0480dddf

See more details on using hashes here.

Provenance

The following attestation bundles were made for openguardrails_instrumentation_langgraph-0.1.1-py3-none-any.whl:

Publisher: publish.yml on openguardrails/openguardrails-instrumentation-langgraph

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page