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.0.tar.gz.

File metadata

File hashes

Hashes for openguardrails_instrumentation_langgraph-0.1.0.tar.gz
Algorithm Hash digest
SHA256 bdb6ec92c3ee67a74ac64cd97f1ddfc75d8d04244a6be2cd35f41fc094aa58ad
MD5 0ebb54428c176e3a57f6df8db84b5675
BLAKE2b-256 d68cf5f1093054cc9631a1c5d12884162973af6d392fa93220db4f5da91d1ecb

See more details on using hashes here.

Provenance

The following attestation bundles were made for openguardrails_instrumentation_langgraph-0.1.0.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.0-py3-none-any.whl.

File metadata

File hashes

Hashes for openguardrails_instrumentation_langgraph-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a18b44fe4f275dd477d706d92788eeac03a117fb1a3cd3440ba6b0c3b48b4444
MD5 b4e68d1b1b275ad035d82a3ec9a51b85
BLAKE2b-256 d90c2ff2c2b8a9f2ed234cb11b33be5229b81c08951cf84a747298230cf91087

See more details on using hashes here.

Provenance

The following attestation bundles were made for openguardrails_instrumentation_langgraph-0.1.0-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