Skip to main content

langgraph-guardrail

PyPI version Python 3.10+ License: MIT

Policy enforcement and guardrails for LangGraph AI agents.

Define what your agent can and can't do in a simple YAML file. Every tool call gets checked before it executes. Dangerous actions get blocked. Suspicious actions get escalated for human approval. Every decision is recorded in a tamper-evident audit log.

Why

AI agents can now take real actions — send emails, query databases, call APIs, delete accounts. Without guardrails, a single misaligned decision can cost real money or leak real data. langgraph-guardrail gives you a single chokepoint where every action is checked against your rules, paused for human approval when needed, and cryptographically logged.

Install

pip install langgraph-guardrail

For LangGraph integration (uses interrupt() for human-in-the-loop):

pip install "langgraph-guardrail[langgraph]"

Requires Python 3.10+.

Quick Start

1. Create a policy file (policy.yaml):

version: "1"
tools:
  query_data:
    action: allow
  send_email:
    action: require_approval
    reason: "External emails need human review"
  delete_account:
    action: block
    reason: "Account deletion is forbidden for autonomous agents"

2. Load it and check tool calls:

from langgraph_guard import load_policy, guarded_tool_call, GovernanceBlockedError

policy = load_policy("policy.yaml")

try:
    result = guarded_tool_call(
        "delete_account",
        {"user_id": 123},
        policy,
        my_delete_function,
    )
except GovernanceBlockedError as e:
    print(f"Blocked: {e}")
    # -> Blocked: Tool 'delete_account' is blocked by policy: Account deletion...

LangGraph Integration

Use GuardNode inside any LangGraph graph for native human-in-the-loop:

from langgraph.checkpoint.memory import MemorySaver
from langgraph.graph import END, START, StateGraph
from langgraph.types import Command

from langgraph_guard import load_policy, prompt_cli
from langgraph_guard.integrations.langgraph import GuardNode

policy = load_policy("policy.yaml")
guard = GuardNode(policy)

def send_email_node(state):
    return {"result": guard.run("send_email", state["args"], _real_send_email)}

builder = StateGraph(State)
builder.add_node("send_email", send_email_node)
builder.add_edge(START, "send_email")
builder.add_edge("send_email", END)

graph = builder.compile(checkpointer=MemorySaver())
config = {"configurable": {"thread_id": "user-1"}}

# First pass — pauses at interrupt()
result = graph.invoke(initial_state, config=config)

# Human approves
payload = result["__interrupt__"][0].value
approved = prompt_cli(payload["tool_name"], payload["args"], payload.get("reason", ""))

# Resume the graph
final = graph.invoke(Command(resume=approved), config=config)

The graph pauses at the exact tool call, saves state to the checkpointer, and resumes seamlessly with the human's decision.

Audit Trail

Every decision is recorded in a hash-chained JSONL log. Tampering with any entry breaks the chain and is detected.

from langgraph_guard import AuditLog
from langgraph_guard.integrations.langgraph import GuardNode

audit = AuditLog("audit.jsonl")
guard = GuardNode(policy, audit=audit)

# After running your agent:
ok, failed_at = audit.verify()
assert ok, f"Tampering detected at sequence {failed_at}"

for entry in audit.tail(10):
    print(f"{entry.sequence} | {entry.tool_name} | {entry.decision}")

Each entry stores its own SHA-256 hash and the hash of the previous entry. If anyone edits, removes, or reorders entries, verify() reports the exact sequence number where the chain breaks.

Policy Schema

version: "1"

tools:
  <tool_name>:
    action: allow | block | require_approval
    reason: "optional explanation shown to humans and logs"
  • allow — the tool runs immediately
  • block — the tool never runs; the agent receives a BLOCKED message
  • require_approval — the graph pauses; a human decides, then the graph resumes

API Reference

load_policy(path: str) -> dict[str, ToolPolicy]

Load a YAML policy file. Raises PolicyError if malformed.

guarded_tool_call(tool_name, args, policy, tool_fn) -> Any

Execute a tool following policy. Raises:

  • GovernanceBlockedError if the policy says block
  • ApprovalRequiredError if the policy says require_approval
  • ToolNotFoundError if the tool isn't in the policy

GuardNode(policy, audit=None)

LangGraph integration. .run(tool_name, args, tool_fn) behaves like guarded_tool_call but uses interrupt() for approvals.

AuditLog(path)

Hash-chained audit log. Methods:

  • .append(tool_name, args, decision, reason="") — write an entry
  • .verify() -> (bool, Optional[int]) — check integrity
  • .tail(n=10) -> list[AuditEntry] — recent entries

prompt_cli(tool_name, args, reason="") -> bool

Interactive terminal approval prompt.

Demos

  • examples/demo_mock.py — deterministic, shows all three branches in 10 seconds
  • examples/demo_agent.py — real LLM (Ollama) decides which tool to call
  • examples/demo_interrupt.py — full interrupt/resume flow with audit logging

Development

git clone https://github.com/Yuvarajunakal/langgraph-guard.git
cd langgraph-guard
python -m venv venv
venv\Scripts\activate    # Windows
pip install -e ".[dev]"
pytest tests/ -v

Status

Alpha — the core API is stable, but expect additions. See CHANGELOG.md for what's shipped.

License

MIT — see LICENSE.

Metadata

Release files for langgraph-guardrail 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for langgraph-guardrail 0.1.1
File Size Uploaded
langgraph_guardrail-0.1.1.tar.gz 12.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for langgraph-guardrail 0.1.1
File Interpreter ABI Platform
langgraph_guardrail-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 23.7 kB

Release files / langgraph_guardrail-0.1.1.tar.gz

Download URL langgraph_guardrail-0.1.1.tar.gz
Size 12.9 kB
Tags Source
SHA-256 checksum
How to use checksums
e52c767f0d6e21ce3460de72fedbf4c08525f38b8a07bbb3c9985437f754eded
BLAKE2b-256 checksum
How to use checksums
34ab6551ac158840fb1f51b9446b8f3dd13db10c43ff9cebef084890ec600037
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.7

Release files / langgraph_guardrail-0.1.1-py3-none-any.whl

Download URL langgraph_guardrail-0.1.1-py3-none-any.whl
Size 10.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
cb5c734b7784c9dd82fda772dca6271f8d04c26013f9da5059c867bf0fa3243a
BLAKE2b-256 checksum
How to use checksums
2a8e5807978b4133f89de2bd0346b5424599235e90d0cdf2ce80a83ffd9602ea
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.7

Release history Release notifications | RSS feed

0.1.2

2 release files

This release

0.1.1 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page