> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://humanloop.com/docs/v5/integrations/vercel-ai-sdk/agent-nodejs-observability/llms.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://humanloop.com/_mcp/server. > Add logging to your Vercel AI SDK application with Humanloop. Add Humanloop observability to a chat agent by calling the tool built with Vercel AI SDK. It builds on the AI SDK's [Node.js example](https://sdk.vercel.ai/docs/getting-started/nodejs). > **Tip** > > Looking for Next.js? See the [guide here](/docs/v5/integrations/vercel-ai-sdk/agent-nextjs-observability). ## Prerequisites #### Account setup Create a Humanloop Account 1. [Create an account](https://app.humanloop.com/signup) or [log in](https://app.humanloop.com/login) to Humanloop 2. Get a Humanloop API key from [Organization Settings](https://app.humanloop.com/account/api-keys). Add an OpenAI API Key If you're the first person in your organization, you'll need to add an API key to a model provider. 1. Go to OpenAI and [grab an API key](https://platform.openai.com/api-keys). 2. In Humanloop [Organization Settings](https://app.humanloop.com/account/api-keys) set up OpenAI as a model provider. > **Info** > > Using the Prompt Editor will use your OpenAI credits in the same way that the > OpenAI playground does. Keep your API keys for Humanloop and the model > providers private. #### Create a new project Start by creating a new directory for your project and initializing it: **`npm`** ```bash title="npm" mkdir my-ai-agent cd my-ai-agent npm init -y ``` **`pnpm`** ```bash title="pnpm" mkdir my-ai-agent cd my-ai-agent pnpm init ``` **`yarn`** ```bash title="yarn" mkdir my-ai-agent cd my-ai-agent yarn init -y ``` #### Install dependencies To follow this guide, you'll also need Node.js 18+ installed on your machine. Install `humanloop`, `ai`, and `@ai-sdk/openai`, the AI SDK's OpenAI provider, along with other necessary dependencies. **`npm`** ```bash title="npm" npm install humanloop ai @ai-sdk/openai zod dotenv npm install -D @types/node tsx typescript ``` **`pnpm`** ```bash title="pnpm" pnpm add humanloop ai @ai-sdk/openai zod dotenv pnpm add -D @types/node tsx typescript ``` **`yarn`** ```bash title="yarn" yarn add humanloop ai @ai-sdk/openai zod dotenv yarn add -D @types/node tsx typescript ``` #### Configure API keys Add a `.env` file to your project with your Humanloop and OpenAI API keys. ```bash touch .env ``` #### .env ``` HUMANLOOP_API_KEY= OPENAI_API_KEY= ``` ## Full code If you'd like to immediately try out the full example, you can copy and paste the code below and run the file. #### Setup **`npm`** ```bash title="npm" wordWrap npm install humanloop ai @ai-sdk/openai zod dotenv npm install -D @types/node tsx typescript npm install dotenv @opentelemetry/sdk-node @opentelemetry/auto-instrumentations-node ``` **`pnpm`** ```bash title="pnpm" wordWrap pnpm add humanloop ai @ai-sdk/openai zod dotenv pnpm add -D @types/node tsx typescript pnpm add dotenv @opentelemetry/sdk-node @opentelemetry/auto-instrumentations-node ``` **`yarn`** ```bash title="yarn" wordWrap yarn add humanloop ai @ai-sdk/openai zod dotenv yarn add -D @types/node tsx typescript yarn add dotenv @opentelemetry/sdk-node @opentelemetry/auto-instrumentations-node ``` **`.env`** ```plaintext highlight={4-6} title=".env" wordWrap HUMANLOOP_API_KEY= OPENAI_API_KEY= OTEL_EXPORTER_OTLP_ENDPOINT=https://api.humanloop.com/v5/import/otel OTEL_EXPORTER_OTLP_PROTOCOL=http/json OTEL_EXPORTER_OTLP_HEADERS="X-API-KEY=" # Humanloop API key ``` #### Code **`agent.ts`** ```typescript title="agent.ts" maxLines=20 import { openai } from "@ai-sdk/openai"; import { CoreMessage, streamText, tool } from "ai"; import { z } from "zod"; import * as readline from "node:readline/promises"; import { NodeSDK } from "@opentelemetry/sdk-node"; import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node"; import dotenv from "dotenv"; dotenv.config(); const sdk = new NodeSDK({ instrumentations: [getNodeAutoInstrumentations()], }); sdk.start(); async function exit() { console.log("Assistant: Shutting down..."); await sdk.shutdown(); process.exit(0); } const terminal = readline.createInterface({ input: process.stdin, output: process.stdout, }); const messages: CoreMessage[] = [ { role: "system", content: "You are a helpful assistant. If the user asks you to exit, you should exit the program.", }, ]; async function main() { while (true) { const userInput = await terminal.question("You: "); if (userInput === "exit") { break; } messages.push({ role: "user", content: userInput }); const result = streamText({ model: openai("gpt-4o"), messages, maxSteps: 5, experimental_telemetry: { isEnabled: true, metadata: { "humanloop.directoryPath": "Vercel AI/Weather", }, }, tools: { weather: tool({ description: "Get the weather in a location (in Celsius)", parameters: z.object({ location: z .string() .describe("The location to get the weather for"), }), execute: async ({ location }) => ({ location, temperature: Math.round((Math.random() * 30 + 5) * 10) / 10, // Random temp between 5°C and 35°C }), }), }, }); let fullResponse = ""; process.stdout.write("\nAssistant: "); for await (const delta of result.textStream) { fullResponse += delta; process.stdout.write(delta); } process.stdout.write("\n\n"); messages.push({ role: "assistant", content: fullResponse }); } await exit(); } main().catch(console.error); ``` **`npm`** ```bash title="npm" npx tsx agent.ts ``` **`pnpm`** ```bash title="pnpm" pnpm tsx agent.ts ``` **`yarn`** ```bash title="yarn" yarn tsx agent.ts ``` ## Create the agent We start with a simple chat agent capable of function calling. **`agent.ts`** ```typescript title="agent.ts" maxLines=50 import { openai } from "@ai-sdk/openai"; import { CoreMessage, streamText, tool } from "ai"; import { z } from "zod"; import * as readline from "node:readline/promises"; async function exit() { console.log("Assistant: Shutting down..."); process.exit(0); } const terminal = readline.createInterface({ input: process.stdin, output: process.stdout, }); const messages: CoreMessage[] = [ { role: "system", content: "You are a helpful assistant. If the user asks you to exit, you should exit the program.", }, ]; async function main() { while (true) { const userInput = await terminal.question("You: "); if (userInput === "exit") { break; } messages.push({ role: "user", content: userInput }); const result = streamText({ model: openai("gpt-4o"), messages, maxSteps: 5, tools: { weather: tool({ description: "Get the weather in a location (in Celsius)", parameters: z.object({ location: z .string() .describe("The location to get the weather for"), }), execute: async ({ location }) => ({ location, temperature: Math.round((Math.random() * 30 + 5) * 10) / 10, // Random temp between 5°C and 35°C }), }), }, }); let fullResponse = ""; process.stdout.write("\nAssistant: "); for await (const delta of result.textStream) { fullResponse += delta; process.stdout.write(delta); } process.stdout.write("\n\n"); messages.push({ role: "assistant", content: fullResponse }); } await exit(); } main().catch(console.error); ``` This agent can provide weather updates for a user-provided location. ```plaintext $ npx tsx agent.ts You: What's the weather like in London? Assistant: The current temperature in London is 20°C. You: exit Assistant: Shutting down... ``` ## Log to Humanloop The agent works and is capable of function calling. However, we rely on inputs and outputs to reason about the behavior. Humanloop logging allows you to observe the steps taken by the agent, which we will demonstrate below. We'll use Vercel AI SDK's built-in OpenTelemetry tracing to log to Humanloop. ### Set up OpenTelemetry Install dependencies. **`npm`** ```bash title="npm" npm install dotenv @opentelemetry/sdk-node @opentelemetry/auto-instrumentations-node ``` **`pnpm`** ```bash title="pnpm" pnpm add dotenv @opentelemetry/sdk-node @opentelemetry/auto-instrumentations-node ``` **`yarn`** ```bash title="yarn" yarn add dotenv @opentelemetry/sdk-node @opentelemetry/auto-instrumentations-node ``` Configure the [OpenTelemetry exporter](https://opentelemetry.io/docs/specs/otel/protocol/exporter/) to forward logs to Humanloop. **`.env`** ```plaintext highlight={4-6} title=".env" wordWrap HUMANLOOP_API_KEY= OPENAI_API_KEY= OTEL_EXPORTER_OTLP_ENDPOINT=https://api.humanloop.com/v5/import/otel OTEL_EXPORTER_OTLP_PROTOCOL=http/json OTEL_EXPORTER_OTLP_HEADERS="X-API-KEY=" # Humanloop API key ``` ### Trace AI SDK calls Vercel AI SDK will now forward OpenTelemetry logs to Humanloop. The telemetry metadata associates Logs with your Files on Humanloop. The `humanloop.directoryPath` specifies the path to a Directory where your Files and Logs will be located. **`agent.ts`** ```typescript title="agent.ts" maxLines=50 highlight={6-16,20,51-57} import { openai } from "@ai-sdk/openai"; import { CoreMessage, streamText, tool } from "ai"; import { z } from "zod"; import * as readline from "node:readline/promises"; import { NodeSDK } from "@opentelemetry/sdk-node"; import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node"; import dotenv from "dotenv"; dotenv.config(); const sdk = new NodeSDK({ instrumentations: [getNodeAutoInstrumentations()], }); sdk.start(); async function exit() { console.log("Assistant: Shutting down..."); await sdk.shutdown(); process.exit(0); } const terminal = readline.createInterface({ input: process.stdin, output: process.stdout, }); const messages: CoreMessage[] = [ { role: "system", content: "You are a helpful assistant. If the user asks you to exit, you should exit the program.", }, ]; async function main() { while (true) { const userInput = await terminal.question("You: "); if (userInput === "exit") { break; } messages.push({ role: "user", content: userInput }); const result = streamText({ model: openai("gpt-4o"), messages, maxSteps: 5, experimental_telemetry: { isEnabled: true, metadata: { "humanloop.directoryPath": "Vercel AI/Weather", }, }, tools: { weather: tool({ description: "Get the weather in a location (in Celsius)", parameters: z.object({ location: z .string() .describe("The location to get the weather for"), }), execute: async ({ location }) => ({ location, temperature: Math.round((Math.random() * 30 + 5) * 10) / 10, // Random temp between 5°C and 35°C }), }), }, }); let fullResponse = ""; process.stdout.write("\nAssistant: "); for await (const delta of result.textStream) { fullResponse += delta; process.stdout.write(delta); } process.stdout.write("\n\n"); messages.push({ role: "assistant", content: fullResponse }); } await exit(); } main().catch(console.error); ``` ### Run the agent **`npm`** ```bash title="npm" npx tsx agent.ts ``` **`pnpm`** ```bash title="pnpm" pnpm tsx agent.ts ``` **`yarn`** ```bash title="yarn" yarn tsx agent.ts ``` Have a conversation with the agent, and try asking about the weather in a city (in Celsius or Fahrenheit). When you're done, type `exit` to close the program. ### Explore logs on Humanloop Now you can explore your logs on the [Humanloop platform](https://app.humanloop.com), and see the steps taken by the agent during your conversation. You can see below the full trace of prompts and tool calls that were made. ![](/docs/_fern-img/1e39e4b3bf5f1d76b5470e07508d90c4eb567b18495660d33c7695f045838403.webp) ## Debugging If you run into any issues, add OpenTelemetry debug logging to ensure your Exporter is working correctly. **`npm`** ```bash title="npm" npm install @opentelemetry/api ``` **`pnpm`** ```bash title="pnpm" pnpm add @opentelemetry/api ``` **`yarn`** ```bash title="yarn" yarn add @opentelemetry/api ``` **`agent.ts`** ```typescript title="agent.ts" import { diag, DiagConsoleLogger, DiagLogLevel } from "@opentelemetry/api"; diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.DEBUG); ``` ## Next steps Logging is the first step to observing your AI product. Read these guides to learn more about evals on Humanloop: * Add [monitoring Evaluators](/docs/v5/guides/observability/monitoring) to evaluate Logs as they're made against a File. * See evals in action in our [tutorial on evaluating an agent](/docs/v5/tutorials/agent-evaluation). > Add Humanloop observability to your Vercel AI SDK project.