from langchain.agents.middleware import (
before_model, after_model, wrap_tool_call,
AgentState
)
from langchain.tools.tool_node import ToolCallRequest
from langchain.messages import AIMessage, ToolMessage
from langgraph.runtime import Runtime
from typing import Any, Callable
import time
# Stop runaway conversations
@before_model(can_jump_to=["end"])
def message_limit(state: AgentState, runtime: Runtime) -> dict[str, Any] | None:
if len(state["messages"]) >= 50:
return {
"messages": [AIMessage("Conversation limit reached.")],
"jump_to": "end",
}
return None
# Log every model response
@after_model
def log_response(state: AgentState, runtime: Runtime) -> dict[str, Any] | None:
msg = state["messages"][-1]
print(f"[{time.strftime('%H:%M:%S')}] {str(msg.content)[:80]}")
return None
# Catch tool errors gracefully
@wrap_tool_call
def safe_tools(
request: ToolCallRequest,
handler: Callable[[ToolCallRequest], ToolMessage],
) -> ToolMessage:
try:
return handler(request)
except Exception as e:
return ToolMessage(
content=f"Tool error: {e}",
tool_call_id=request.tool_call["id"],
)LangGraph Middleware for Agents
Python
LLM
LangGraph
Using middleware hooks to intercept and control agent behavior in LangGraph
LangGraph 1.0 introduced a middleware system for agents built with create_agent. Middleware lets you hook into the agent’s model-call / tool-call loop without building a full custom graph.
pip install langchain>=1.0.0 langgraph>=1.0.0 langchain-openaiHow It Works
Middleware wraps the agent loop at five hook points:
| Hook | When | Use Case |
|---|---|---|
before_model |
Before each LLM call | Guard rails, state checks |
modify_model_request |
Right before invocation | Swap models, filter tools |
after_model |
After LLM responds | Validate, redact, log |
wrap_model_call |
Wraps entire model call | Retries, timing |
wrap_tool_call |
Wraps each tool execution | Error handling, monitoring |
Decorator-Based Middleware
Class-Based Middleware
Bundle multiple hooks with shared state into a single class.
from langchain.agents.middleware import AgentMiddleware
class TimingMiddleware(AgentMiddleware):
"""Track how long each model call takes."""
def before_model(self, state: AgentState, runtime: Runtime) -> dict[str, Any] | None:
self._start = time.time()
return None
def after_model(self, state: AgentState, runtime: Runtime) -> dict[str, Any] | None:
elapsed = time.time() - self._start
print(f"Model call took {elapsed:.2f}s")
return NonePutting It Together
from langchain.agents import create_agent
from langchain.tools import tool
@tool
def search(query: str) -> str:
"""Search the web."""
return f"Results for: {query}"
agent = create_agent(
model="gpt-4.1",
tools=[search],
system_prompt="You are a helpful assistant.",
middleware=[
message_limit,
log_response,
safe_tools,
TimingMiddleware(),
],
)
result = agent.invoke(
{"messages": [{"role": "user", "content": "Search for LangGraph middleware docs"}]}
)
print(result["messages"][-1].content)Built-In Middleware
LangChain ships several ready-to-use middleware:
| Middleware | What It Does |
|---|---|
SummarizationMiddleware |
Auto-summarizes old messages when token count exceeds a threshold |
HumanInTheLoopMiddleware |
Requires human approval before executing specified tools |
ModelFallbackMiddleware |
Falls back to alternate models on failure |
ToolRetryMiddleware |
Retries failed tool calls with exponential backoff |
PIIMiddleware |
Detects and redacts/blocks PII in messages |
from langchain.agents.middleware import SummarizationMiddleware
summarizer = SummarizationMiddleware(
model="openai:gpt-4o-mini",
trigger=("tokens", 4000),
keep=("messages", 20),
)Key Things to Know
- Execution order:
before_modelruns in list order;after_modelruns in reverse order (like web middleware stacks) jump_toletsbefore_modelshort-circuit the loop – must declare targets withcan_jump_to- Scope: Middleware hooks into the agent loop (model + tool calls), not arbitrary LangGraph nodes. For custom
StateGraphnodes, wrap the node functions directly - Requires LangChain/LangGraph 1.0+ (released Oct 2025). Replaces the older
create_react_agentfromlanggraph.prebuilt