langchain.js
    Preparing search index...

    Function 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);
      • 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

      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

      Parameters

      • options: any

        Configuration options for the middleware

      Returns AgentMiddleware<
          undefined,
          ZodObject<
              {
                  descriptionPrefix: ZodDefault<ZodString>;
                  interruptOn: ZodOptional<
                      ZodRecord<
                          ZodString,
                          ZodUnion<
                              [
                                  ZodBoolean,
                                  ZodObject<
                                      {
                                          allowedDecisions: ZodArray<ZodEnum<(...)>, "many">;
                                          argsSchema: ZodOptional<ZodRecord<(...), (...)>>;
                                          description: ZodOptional<ZodUnion<(...)>>;
                                      },
                                      "strip",
                                      ZodTypeAny,
                                      {
                                          allowedDecisions: ((...) | (...) | (...))[];
                                          argsSchema?: Record<(...), (...)>;
                                          description?: string | ((...args: ...) => ...);
                                      },
                                      {
                                          allowedDecisions: ((...) | (...) | (...))[];
                                          argsSchema?: Record<(...), (...)>;
                                          description?: string | ((...args: ...) => ...);
                                      },
                                  >,
                              ],
                          >,
                      >,
                  >;
              },
              "strip",
              ZodTypeAny,
              {
                  descriptionPrefix: string;
                  interruptOn?: Record<
                      string,
                      | boolean
                      | {
                          allowedDecisions: ("approve" | "edit" | "reject")[];
                          argsSchema?: Record<string, any>;
                          description?:
                              | string
                              | (
                                  (
                                      ...args: [
                                          ToolCall,
                                          AgentBuiltInState,
                                          Runtime<(...)>,
                                          ...unknown[],
                                      ],
                                  ) => string | Promise<(...)>
                              );
                      },
                  >;
              },
              {
                  descriptionPrefix?: string;
                  interruptOn?: Record<
                      string,
                      | boolean
                      | {
                          allowedDecisions: ("approve" | "edit" | "reject")[];
                          argsSchema?: Record<string, any>;
                          description?:
                              | string
                              | (
                                  (
                                      ...args: [
                                          ToolCall,
                                          AgentBuiltInState,
                                          Runtime<(...)>,
                                          ...unknown[],
                                      ],
                                  ) => string | Promise<(...)>
                              );
                      },
                  >;
              },
          >,
          any,
      >

      A middleware instance that can be passed to createAgent

      Basic usage with selective tool approval

      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]
      });

      Handling approval requests

      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
      );
      }

      Different decision types

      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"
      }]
      };

      Production use case with database operations

      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"
      });

      Using dynamic callable descriptions

      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`;
      }
      }
      }
      });
      • 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
      • createAgent for agent creation
      • Command for resuming interrupted execution