LangChain / LangGraph
Full working examples:
- examples/demo-agent — LangGraph with manual guard
- examples/langgraph-demo-agent — outreach agent with approval flow
Auto-patch (simplest)
python
import gavrun
gavrun.configure(
api_key="gavrun_live_...",
agent_manager_url="https://api.gavrun.ai",
auto_patch=True, # default — patches BaseTool.invoke and BaseChatModel.invoke
)
# Every @tool call is now governed automatically
from langchain_core.tools import tool
@tool
def delete_order(order_id: str) -> str:
"""Delete an order from the database."""
return db.delete(order_id)Manual guard
For tools that need approval polling, guard manually to control execution flow:
python
from langchain_core.tools import tool
from gavrun.client import GavrunDecisionDenied
@tool
def refund_order(order_id: str) -> str:
"""Refund an order."""
client = gavrun.get_client()
result = client.guard(
action="refund_order",
execute=lambda: None, # deferred
tools=["refund_order"],
prompt=f"Refund order {order_id}",
)
if result.denied:
raise GavrunDecisionDenied("Refund denied by governance policy.")
if result.requires_human_review:
approval_id = result.decision.get("approval_id")
while True:
status = client.get_approval_status(approval_id=approval_id)
if status["status"] == "approved":
return process_refund(order_id)
elif status["status"] == "denied":
raise GavrunDecisionDenied("Refund denied by reviewer.")
time.sleep(3)
return process_refund(order_id)LangGraph StateGraph
python
from langgraph.graph import StateGraph
from langgraph.prebuilt import ToolNode
graph = StateGraph(State)
graph.add_node("agent", agent_node)
graph.add_node("tools", ToolNode(tools, handle_tool_errors=True))
# ToolNode catches GavrunDecisionDenied and passes the message to the LLMUse handle_tool_errors=True so denied tool calls become tool-error messages the LLM can read rather than crashing the graph.