Overview

The Vercel AI SDK enables streaming AI responses in Next.js applications. HumanLayer adds human oversight to your AI features.

Installation

npm install humanlayer ai

Basic Example

Here’s a simple example showing how to use HumanLayer with the Vercel AI SDK:

import { tool, generateText } from "ai";
import { createOpenAI } from "@ai-sdk/openai";
import { humanlayer } from "humanlayer-vercel-ai-sdk";
import { z } from "zod";

const hl = humanlayer({
  verbose: true,
});

// Simple math operations
const add = tool({
  parameters: z.object({
    a: z.number(),
    b: z.number(),
  }),
  execute: async (args) => {
    return args.a + args.b;
  },
});

// Multiply requires approval
const multiplyTool = tool({
  parameters: z.object({
    a: z.number(),
    b: z.number(),
  }),
  execute: async (args) => {
    return args.a * args.b;
  },
});

// Wrap multiply with approval requirement
const multiply = hl.requireApproval({ multiplyTool });

// Human consultation tool
const contactHuman = hl.humanAsTool();

const openai = createOpenAI({
  compatibility: "strict",
});

// Generate text with tool access
const { text, steps } = await generateText({
  model: openai("gpt-4"),
  tools: {
    add,
    multiply,
    contactHuman,
  },
  maxSteps: 5,
  prompt: "multiply 2 and 5, then add 32 to the result",
});

Complete Next.js Example

API Route (app/api/chat/route.ts)

import { humanlayer } from "humanlayer";
import { StreamingTextResponse, LangChainStream } from "ai";
import { ChatOpenAI } from "langchain/chat_models/openai";
import { AIMessage, HumanMessage, SystemMessage } from "langchain/schema";

// Initialize with contact channel
const hl = humanlayer({
  runId: "support-chat",
  contactChannel: {
    slack: {
      channelOrUserId: "C123456",
      contextAboutChannelOrUser: "the support team",
    },
  },
});

// Functions requiring approval
const updateSubscription = hl.requireApproval(
  async (userId: string, plan: string) => {
    // Subscription logic here
    return `Updated ${userId} to ${plan}`;
  },
);

const issueCredit = hl.requireApproval(
  async (userId: string, amount: number) => {
    // Credit logic here
    return `Issued $${amount} credit to ${userId}`;
  },
);

// Support team consultation
const askSupport = hl.humanAsTool({
  responseOptions: [
    {
      name: "approve",
      title: "Approve Request",
      description: "Grant the customer's request",
    },
    {
      name: "deny",
      title: "Deny Request",
      description: "Deny with explanation",
    },
    {
      name: "escalate",
      title: "Escalate",
      description: "Escalate to management",
    },
  ],
});

export async function POST(req: Request) {
  const { messages, userId } = await req.json();
  const { stream, handlers } = LangChainStream();

  const llm = new ChatOpenAI({
    streaming: true,
    callbacks: [handlers],
  });

  llm.call(
    [
      new SystemMessage("You are a helpful customer support assistant."),
      ...messages.map((m: any) =>
        m.role === "user"
          ? new HumanMessage(m.content)
          : new AIMessage(m.content),
      ),
    ],
    {
      tools: [
        {
          name: "updateSubscription",
          description: "Update a user's subscription plan",
          parameters: {
            type: "object",
            properties: {
              userId: { type: "string" },
              plan: { type: "string" },
            },
            required: ["userId", "plan"],
          },
        },
        {
          name: "issueCredit",
          description: "Issue account credit to user",
          parameters: {
            type: "object",
            properties: {
              userId: { type: "string" },
              amount: { type: "number" },
            },
            required: ["userId", "amount"],
          },
        },
        {
          name: "askSupport",
          description: "Ask support team for help",
          parameters: {
            type: "object",
            properties: {
              message: { type: "string" },
            },
            required: ["message"],
          },
        },
      ],
    },
  );

  return new StreamingTextResponse(stream);
}

Client Component (app/page.tsx)

"use client";

import { useChat } from "ai/react";

export default function Chat() {
  const { messages, input, handleInputChange, handleSubmit } = useChat();

  return (
    <div className="p-4 max-w-lg mx-auto">
      <div className="space-y-4">
        {messages.map(m => (
          <div key={m.id} className={`p-4 rounded-lg ${
            m.role === "user" ? "bg-blue-100" : "bg-gray-100"
          }`}>
            <p className="font-semibold">{m.role}</p>
            <p>{m.content}</p>
          </div>
        ))}
      </div>

      <form onSubmit={handleSubmit} className="mt-4">
        <input
          value={input}
          onChange={handleInputChange}
          placeholder="How can I help?"
          className="w-full p-2 border rounded"
        />
        <button
          type="submit"
          className="mt-2 px-4 py-2 bg-blue-500 text-white rounded"
        >
          Send
        </button>
      </form>
    </div>
  );
}

Key Features

  1. Streaming Responses

    • Real-time AI output
    • Responsive UI
    • Error handling
  2. Human Oversight

    • Approval workflows
    • Support team consultation
    • Structured responses
  3. Type Safety

    • Full TypeScript support
    • Validated parameters
    • Error boundaries

Configuration

# .env.local
OPENAI_API_KEY=your-openai-key
HUMANLAYER_API_KEY=your-humanlayer-key

Next Steps