import { Inferable, Interrupt, approvalRequest } from "inferable";

const inferable = new Inferable({
  apiSecret: process.env.INFERABLE_API_SECRET,
});

const deleteUserWorkflow = inferable.workflows.create({
  name: "deleteUser",
  inputSchema: z.object({
    executionId: z.string(),
    userId: z.string(),
  }),
});

deleteUserWorkflow.version(1).define(async (ctx, input) => {
  if (!ctx.approved) {
    return Interrupt.approval();
  }

  //...
});

Overview

Human-in-the-loop (HITL) allow you to require human approval before certain actions are executed. This is useful for:

  • Reviewing and approving high-risk operations
  • Validating model outputs before taking action

Inferable provides a simple API for creating approval requests and approving them. It accomplishes this by returning a special result type as a result from either a Tool or Workflow.

It is important to note that the Interrupt.approval() is applicable in both Tools and Workflows. It’s usage is the same within each.

Implementation

The Interrupt.approval() function is currently not available in the .Net SDK. Please see .Net Issue.

To implement HITL in Inferable, use the Interrupt.approval() helper when you need approval within your functions:

Approval Request result

By returning the Interrupt.approval() result, you are in fact returning a special result type that the execution engine can handle. In this case, the parent Run or Workflow will be paused and the approval request will be created.

Approval Flow

When Interrupt.approval() is returned from a Tool or Workflow:

  1. The Workflow or Run is paused.

  2. The approval request is created.

  3. An authorized user must approve or reject the request using the API or the App UI.

  4. If the approval is successful,

    4.1 The workflow will resume execution, and will call the handler again with the same input.

    4.2 At this point, since the context.approved is true, the handler will continue without entering the return Interrupt.approval() block.

  5. If the approval is rejected

    5.1 The tool / workflow will be rejected. If this is a tool, the agent will be notified that the tool call was rejected.

Pausing a Workflow

When a Workflow is paused, Inferable’s durable execution engine will save the state and resume when the approval is resolved.

Since humans can take a long time to approve, the run or workflow can be paused for an indefinite amount of time. There’s no theoretical upper limit on how long a run can be paused for.

Notifications

By default, approvals can be provided within the Workflow or Run’s UI. If enabled, the Slack integration can also be used to notify users / channels of approval requests.

Modeling the Approval Block

It’s important to reiterate that the function may be called multiple times with the same input, if the request is approved.

Anything that sits before the if (context.approved) { ... } block will be executed at least twice.

Example:

async (input, context) => {
  console.log(`Request to delete user ${input.userId}`);

  if (!context.approved) {
    return Interrupt.approval();
  }

  // unless context.approved is true, the function can not continue to this point

  console.log(`Deleting user ${input.userId}`);

  return db.customers.delete({
    userId: input.userId,
  });
},

// This will result in:
// Request to delete user 123
// Request to delete user 123
// Deleting user 123