Skip to main content
When an agent uses several tools, you may want to check usage before calling the model and record each step’s response. Laravel AI SDK middleware does not wrap the whole agent just once. It runs on every step of the generation. Based on the official Laravel 13 documentation and the v1.3.0 implementation of laravel/ai, this page goes a step beyond the AI SDK basics and explains execution order and stop conditions.

The unit that middleware runs on

When an agent receives a model response, runs tools, and calls the model again, the next model call is a separate step. Right before each step, the middleware returned from HasMiddleware::middleware() runs in the order it was declared. The PendingStep passed to the middleware’s handle(PendingStep $step, Closure $next) contains steps, an array of completed steps, usage, their cumulative usage, and number, the current zero-based step number. On the first step, usage does not yet include the usage of the current model call. The then() attached to the result of $next($step) runs after the model responds and before any tool calls run. It is not a hook that is called only after tools run. Also, usage at this point is the total of previously completed steps, and it does not predict the token count of the in-progress request.

Registering per-step processing

This example logs only the step number and the number of input tokens used so far. Avoid unconditionally saving prompt text or model output to logs.
Implement HasMiddleware on the agent and return instances from middleware().
Put Stop first in the middleware() list. When the limit is exceeded and the next step does not start, the subsequent LogAgentSteps does not run for that step. If you reverse the order, only the “starting” log may be recorded, without the “model responded” log. Return the result of $next($step) from handle(). The then() callback does not need to return the response.

Stop prevents the next model call

Stop::when(...) ends processing before the step when the condition is true, and Stop::unless(...) does so when the condition is false. The example above skips the next call once the cumulative input tokens of completed steps reach 50,000 or more. It is not a hard limit that forcibly stops a single model call before it crosses the threshold.
Stop does not abort an in-progress request to the provider. If you need a strict guarantee on usage limits, do not rely only on next-step checks. Also consider limits on the model or provider side.
Middleware that wants to stop on a custom condition can also return Laravel\Ai\Gateway\StepResult::stop(). From handle(), return the result of $next($step), a StepResponse, or StepResult::stop(). Returning an unrelated type throws an exception.

Verifying execution order

To avoid confusing model responses with tool execution, check at least the following cases.
  • For a single-step generation, the starting log and the model response log each appear once.
  • For additional steps following tools, the number increases from 0, and the usage of later steps includes the usage of earlier steps.
  • When the stop condition is met from the start, no model call and no LogAgentSteps logs occur.
  • When the condition is reached partway through, the earlier steps remain and the model call for the next step does not occur.
Separate the normal path from the stopping path and check how many times each step runs. If then() needs side effects, design them on the assumption that it is not called on paths that fail or stop before a model response is received.

Primary sources

Last modified on October 21, 2026