LangChain Reference home pageLangChain ReferenceLangChain Reference
  • GitHub
  • Main Docs
Deep Agents
LangChain
LangGraph
Integrations
LangSmith
LangChain
  • Universal
  • Hub
  • Node
  • Load
  • Serializable
  • Encoder Backed
  • File System
  • In Memory
LangChain Core
  • Agents
  • Caches
  • Base
  • Dispatch
  • Web
  • Manager
  • Promises
  • Chat History
  • Context
  • Base
  • Langsmith
  • Documents
  • Embeddings
  • Errors
  • Example Selectors
  • Indexing
  • Base
  • Chat Models
  • Llms
  • Profile
  • Load
  • Serializable
  • Memory
  • Messages
  • Tool
  • Output Parsers
  • Openai Functions
  • Openai Tools
  • Outputs
  • Prompt Values
  • Prompts
  • Retrievers
  • Document Compressors
  • Runnables
  • Graph
  • Singletons
  • Stores
  • Structured Query
  • Tools
  • Base
  • Console
  • Log Stream
  • Run Collector
  • Tracer Langchain
  • Stream
  • Async Caller
  • Chunk Array
  • Context
  • Env
  • Event Source Parse
  • Format
  • Function Calling
  • Hash
  • Json Patch
  • Json Schema
  • Math
  • Ssrf
  • Stream
  • Testing
  • Tiktoken
  • Types
  • Vectorstores
Text Splitters
MCP Adapters
⌘I

LangChain Assistant

Ask a question to get started

Enter to send•Shift+Enter new line

Menu

LangChain
UniversalHubNodeLoadSerializableEncoder BackedFile SystemIn Memory
LangChain Core
AgentsCachesBaseDispatchWebManagerPromisesChat HistoryContextBaseLangsmithDocumentsEmbeddingsErrorsExample SelectorsIndexingBaseChat ModelsLlmsProfileLoadSerializableMemoryMessagesToolOutput ParsersOpenai FunctionsOpenai ToolsOutputsPrompt ValuesPromptsRetrieversDocument CompressorsRunnablesGraphSingletonsStoresStructured QueryToolsBaseConsoleLog StreamRun CollectorTracer LangchainStreamAsync CallerChunk ArrayContextEnvEvent Source ParseFormatFunction CallingHashJson PatchJson SchemaMathSsrfStreamTestingTiktokenTypesVectorstores
Text Splitters
MCP Adapters
Language
Theme
JavaScriptlangchainindexhumanInTheLoopMiddleware
Function●Since v1.1

humanInTheLoopMiddleware

Creates a Human-in-the-Loop (HITL) middleware for tool approval and oversight.

This middleware intercepts tool calls made by an AI agent and provides human oversight capabilities before execution. It enables selective approval workflows where certain tools require human intervention while others can execute automatically.

A invocation result that has been interrupted by the middleware will have a __interrupt__ property that contains the interrupt request.

import { type HITLRequest, type HITLResponse } from "langchain";
import { type Interrupt } from "langchain";

const result = await agent.invoke(request);
const interruptRequest = result.__interrupt__?.[0] as Interrupt<HITLRequest>;

// Examine the action requests and review configs
const actionRequests = interruptRequest.value.actionRequests;
const reviewConfigs = interruptRequest.value.reviewConfigs;

// Create decisions for each action
const resume: HITLResponse = {
  decisions: actionRequests.map((action, i) => {
    if (action.name === "calculator") {
      return { type: "approve" };
    } else if (action.name === "write_file") {
      return {
        type: "edit",
        editedAction: { name: "write_file", args: { filename: "safe.txt", content: "Safe content" } }
      };
    }
    return { type: "reject", message: "Action not allowed" };
  })
};

// Resume with decisions
await agent.invoke(new Command({ resume }), config);

Features

  • Selective Tool Approval: Configure which tools require human approval
  • Multiple Decision Types: Approve, edit, or reject tool calls
  • Asynchronous Workflow: Uses LangGraph's interrupt mechanism for non-blocking approval
  • Custom Approval Messages: Provide context-specific descriptions for approval requests

Decision Types

When a tool requires approval, the human operator can respond with:

  • approve: Execute the tool with original arguments
  • edit: Modify the tool name and/or arguments before execution
  • reject: Provide a manual response instead of executing the tool
Copy
humanInTheLoopMiddleware(
  options: __type
): AgentMiddleware<undefined, ZodObject<__type, "strip", ZodTypeAny, __type, __type>, __type, readonly ClientTool | ServerTool[]>
  • Tool calls are processed in the order they appear in the AI message
  • Auto-approved tools execute immediately without interruption
  • Multiple tools requiring approval are bundled into a single interrupt request
  • The middleware operates in the afterModel phase, intercepting before tool execution
  • Requires a checkpointer to maintain state across interruptions

Used in Docs

  • Build a personal assistant with subagents
  • Frontend
  • Guardrails
  • Human-in-the-loop
  • Overview

Parameters

NameTypeDescription
options*__type

Configuration options for the middleware

Example 1

Basic usage with selective tool approval

Copy
import { humanInTheLoopMiddleware } from "langchain";
import { createAgent } from "langchain";

const hitlMiddleware = humanInTheLoopMiddleware({
  interruptOn: {
    // Interrupt write_file tool and allow edits or approvals
    "write_file": {
      allowedDecisions: ["approve", "edit"],
      description: "⚠️ File write operation requires approval"
    },
    // Auto-approve read_file tool
    "read_file": false
  }
});

const agent = createAgent({
  model: "openai:gpt-4",
  tools: [writeFileTool, readFileTool],
  middleware: [hitlMiddleware]
});

Example 2

Handling approval requests

Copy
import { type HITLRequest, type HITLResponse, type Interrupt } from "langchain";
import { Command } from "@langchain/langgraph";

// Initial agent invocation
const result = await agent.invoke({
  messages: [new HumanMessage("Write 'Hello' to output.txt")]
}, config);

// Check if agent is paused for approval
if (result.__interrupt__) {
  const interruptRequest = result.__interrupt__?.[0] as Interrupt<HITLRequest>;

  // Show tool call details to user
  console.log("Actions:", interruptRequest.value.actionRequests);
  console.log("Review configs:", interruptRequest.value.reviewConfigs);

  // Resume with approval
  const resume: HITLResponse = {
    decisions: [{ type: "approve" }]
  };
  await agent.invoke(
    new Command({ resume }),
    config
  );
}

Example 3

Different decision types

Copy
import { type HITLResponse } from "langchain";

// Approve the tool call as-is
const resume: HITLResponse = {
  decisions: [{ type: "approve" }]
};

// Edit the tool arguments
const resume: HITLResponse = {
  decisions: [{
    type: "edit",
    editedAction: { name: "write_file", args: { filename: "safe.txt", content: "Modified" } }
  }]
};

// Reject with feedback
const resume: HITLResponse = {
  decisions: [{
    type: "reject",
    message: "File operation not allowed in demo mode"
  }]
};

Example 4

Production use case with database operations

Copy
const hitlMiddleware = humanInTheLoopMiddleware({
  interruptOn: {
    "execute_sql": {
      allowedDecisions: ["approve", "edit", "reject"],
      description: "🚨 SQL query requires DBA approval\nPlease review for safety and performance"
    },
    "read_schema": false,  // Reading metadata is safe
    "delete_records": {
      allowedDecisions: ["approve", "reject"],
      description: "⛔ DESTRUCTIVE OPERATION - Requires manager approval"
    }
  },
  descriptionPrefix: "Database operation pending approval"
});

Example 5

Using dynamic callable descriptions

Copy
import { type DescriptionFactory, type ToolCall } from "langchain";
import type { AgentBuiltInState, Runtime } from "langchain/agents";

// Define a dynamic description factory
const formatToolDescription: DescriptionFactory = (
  toolCall: ToolCall,
  state: AgentBuiltInState,
  runtime: Runtime<unknown>
) => {
  return `Tool: ${toolCall.name}\nArguments:\n${JSON.stringify(toolCall.args, null, 2)}`;
};

const hitlMiddleware = humanInTheLoopMiddleware({
  interruptOn: {
    "write_file": {
      allowedDecisions: ["approve", "edit"],
      // Use dynamic description that can access tool call, state, and runtime
      description: formatToolDescription
    },
    // Or use an inline function
    "send_email": {
      allowedDecisions: ["approve", "reject"],
      description: (toolCall, state, runtime) => {
        const { to, subject } = toolCall.args;
        return `Email to ${to}\nSubject: ${subject}\n\nRequires approval before sending`;
      }
    }
  }
});
View source on GitHub