Overview
The Vercel AI SDK enables streaming AI responses in Next.js applications. HumanLayer adds human oversight to your AI features.Installation
Copy
Ask AI
npm install @humanlayer/sdk ai
Basic Example
Here’s a simple example showing how to use HumanLayer with the Vercel AI SDK:Copy
Ask AI
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)
Copy
Ask AI
import { humanlayer } from "@humanlayer/sdk";
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)
Copy
Ask AI
"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
-
Streaming Responses
- Real-time AI output
- Responsive UI
- Error handling
-
Human Oversight
- Approval workflows
- Support team consultation
- Structured responses
-
Type Safety
- Full TypeScript support
- Validated parameters
- Error boundaries
Configuration
Copy
Ask AI
# .env.local
OPENAI_API_KEY=your-openai-key
HUMANLAYER_API_KEY=your-humanlayer-key