LangGraph Middleware for Agents

Python
LLM
LangGraph
Using middleware hooks to intercept and control agent behavior in LangGraph
Author

Kedar Dabhadkar

Published

February 7, 2026

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-openai

How 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

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"],
        )

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 None

Putting 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_model runs in list order; after_model runs in reverse order (like web middleware stacks)
  • jump_to lets before_model short-circuit the loop – must declare targets with can_jump_to
  • Scope: Middleware hooks into the agent loop (model + tool calls), not arbitrary LangGraph nodes. For custom StateGraph nodes, wrap the node functions directly
  • Requires LangChain/LangGraph 1.0+ (released Oct 2025). Replaces the older create_react_agent from langgraph.prebuilt

References- LangChain Middleware Overview- Custom Middleware Documentation- LangChain Agent Middleware Blog Post- LangGraph GitHub Repository