is it possible to directly use the langchain builtin middleware like ModelCallLimitMiddleware in my custom stateGraph.
Much appreciate if you help me
is it possible to directly use the langchain builtin middleware like ModelCallLimitMiddleware in my custom stateGraph.
Much appreciate if you help me
hi @Dev
what do you mean by my custom stateGraph?
Afaik LangChain’s built‑in agent middleware (e.g., ModelCallLimitMiddleware) is wired into the agent graph that create_agent builds. In a hand-built StateGraph, there is no generic graph.add_middleware(...) and the built-ins assume the standard agent loop nodes (model, tools, end) and jump semantics.
I reckon you can instantiate ModelCallLimitMiddleware and call its hooks around your model node:
before_model(state, runtime) (or abefore_model) before the model; if it returns {"jump_to": "end", "messages": [...]}, route to your END node; if exit_behavior="error", handle ModelCallLimitExceededErrorafter_model(state, runtime) (or aafter_model) after the model to increment countersMaybe sth like this:
from langchain.agents.middleware.model_call_limit import ModelCallLimitMiddleware, ModelCallLimitExceededError
limit = ModelCallLimitMiddleware(thread_limit=10, run_limit=5, exit_behavior="end")
def before_model_node(state, runtime):
updates = limit.before_model(state, runtime) or {}
# If updates includes {"jump_to": "end"}, add your conditional edge to END.
return updates
def model_node(state):
# ... call your model ...
return {"messages": [...]} # your AIMessage(s)
def after_model_node(state, runtime):
return limit.after_model(state, runtime) or {}
Key caveats:
The middleware’s hook_config(can_jump_to=["end"]) metadata isn’t wired automatically in a plain StateGraph; you must add conditional edges based on the returned jump_to
The built-in uses UntrackedValue for the run counter; replicate that by not checkpointing run_model_call_count (or resetting it per run)
Hello Pawel
I succeed to implement the langchain builtin middleware in the stateGraph
Thank
@Dev Do you mind giving an example of how you are making this work?
limit = ToolCallLimitMiddleware(
thread_limit=None,
run_limit=DEFAULT_TOOL_LIMIT,
exit_behavior=DEFAULT_EXIT_BEHAVIOR,
)
def limit_check_node(state: GraphState, runtime):
"""
MUST be placed after tool execution.
This is the only correct place where tool call count is updated.
"""
updates = limit.after_model(state, runtime) or {}
if updates.get("jump_to") == "end":
return {"__end__": True}
return updates
Hello @pawel-twardziak
I have another significant challenge to implement the ToolCallLimitMiddleware
I set the tool call limit to 5. When I ask the LLM to create 6 images, the assistant returns:
tool_calls = [call1, call2, call3, call4, call5, call6]
What I want is to execute only the first 5 tool calls, ignore the 6th, and then return control to the LLM.
However, with my current implementation using ToolCallLimitMiddleware, all 6 tool calls are executed sequentially, and the middleware only triggers after the 6th call, which defeats the purpose of the limit.
How can I modify the implementation so that only the first 5 tool calls are executed and the 6th is skipped?
Hi @Dev
Hmm that is an interesting issue ![]()
Could you create another post since this one is related to original issue and already resolved?
Just to keep things clear and well structured here on the forum. Appreciate ![]()
You can tag me there and I will answer today later on.
Thank you, I just did it.
I’m attaching the link here as the follow up