LangChain 1.4.6 adds an opt-in `per_call` mode to `HumanInTheLoopMiddleware`. Instead of one review object whose decisions are matched positionally to every gated call in a model turn, each tool call can now raise its own interrupt, carry its own response schema, and be resumed by interrupt ID.
## What changed
In the default `batched` mode, a reviewer receives one list of actions and returns a list of decisions in the same order. The new `interrupt_mode="per_call"` path moves review into the tool-call middleware chain. Each gated call produces a `tool_approval` request containing the tool-call ID, name, arguments, description, and a schema for the decisions that particular call accepts.
The schema is more than UI metadata. LangGraph validates an answer before saving it. An edit cannot switch to another tool, and Pydantic-backed tool arguments are type-checked. Invalid answers remain answerable instead of being committed as checkpoint state. Tools defined only by raw JSON Schema are a weaker case: their edit schema is displayed but not enforced by this path.
## Direct verification
I checked out the `langchain==1.4.6` tag target (`84ba23c414eca6bf6f6ebd1394a3006378113540`) and ran the complete human-in-the-loop middleware unit-test module on Windows with Python 3.13.14:
```text
79 passed in 5.12s
```
The passing cases include per-call approve, edit, reject and respond decisions; routing two calls to answers by interrupt ID; rejecting an invalid answer without saving it; asynchronous invocation; and composing the gate with retry and tool-error middleware.
I did not run a live model, a remote checkpointer, or a production approval client.
## The boundary is clearer, not atomic
Per-call interrupts remove positional coupling and give approval clients a machine-readable contract for each decision. That is a meaningful improvement for agents that issue several tools in parallel: a UI can render one review card per call and validate the reply before resume.
But the release documentation records an important failure mode. If several interrupt answers are resumed together and one is invalid, other tools may already have executed while still appearing pending. Retrying all answers can therefore execute those calls twice. The safe operational choices are to validate every answer against its advertised schema before sending a batch, or resume calls one at a time.
Two more constraints matter for dependable deployments:
- A conditional `when` predicate runs again on resume and must return the same result. If it flips to false, a rejection can be skipped and the tool can run.
- Middleware placed before the per-call gate runs again up to the pause on resume, so it must not perform side effects before delegating. The HITL middleware should wrap retry or tool-error middleware.
The interpretation: LangChain has made the approval protocol easier to address, render, and validate, but exactly-once execution still belongs to the surrounding orchestration design. Approval is a checkpointed interaction boundary—not a transaction.