feat(langchain): add per-action human-in-the-loop middleware#10176
feat(langchain): add per-action human-in-the-loop middleware#10176pawel-twardziak wants to merge 7 commits intolangchain-ai:mainfrom
Conversation
🦋 Changeset detectedLatest commit: e1af613 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
After testing it, I can confirm this solves the problem I was facing. Thanks pawel-twardziak! |
|
and Christian Bromann (@christian-bromann) and Hunter Lovell (@hntrl) here is the python PR for this feature langchain-ai/langchain#32996 |
| const toolMessage = new ToolMessage({ | ||
| content, | ||
| name: toolCall.name, | ||
| tool_call_id: toolCall.id!, | ||
| status: "error", | ||
| }); |
There was a problem hiding this comment.
This tool message creation is ignoring if the tool message should also have some artifacts.
There was a problem hiding this comment.
hmmm can tool message have artifacts?
I'll look at that. Thanks Eric Lozano (@elozano98) for spotting on this matter.
There was a problem hiding this comment.
I'm not sure this should be implemented here. Here's why:
- This ToolMessage is a synthetic rejection message - it's created when a human rejects a tool call. The tool was never actually executed, so there are no artifacts to include. The message content is just a rejection notice (e.g., "User rejected the tool call for toolName").
- The same pattern exists in the original HITL middleware - identical construction without artifacts. So this isn't something the PR introduced differently.
- Artifacts on ToolMessage are typically used to attach structured data from actual tool execution results (e.g., binary data, files). For a rejection that never ran the tool, there's nothing meaningful to attach.
Summary
Motivation
The existing humanInTheLoopMiddleware treats a batch of tool calls as all-or-nothing: if any single action is rejected, the entire batch is discarded and control returns to the model. This is correct for strict oversight scenarios, but overly restrictive when an agent proposes multiple independent actions (e.g., a safe calculation alongside a risky file write). Users need a way to selectively approve/reject individual actions within the same interrupt without losing the approved work.
Key behavioral differences from humanInTheLoopMiddleware
Test plan
Diagrams
HITL:
https://link.excalidraw.com/readonly/hradCmix5ALfqpy9QlI8?darkMode=true
PA-HITL:
https://link.excalidraw.com/readonly/bNKuDF8Ry1z11AX21ouA?darkMode=true
Alternative solution
Add a no-batch-reject feature to the existing HITL middleware -> #10186