LangSmith를 사용하여 Vercel AI SDK의 실행을 추적할 수 있습니다. 이 가이드에서는 예제를 통해 설명합니다.

Installation

이 wrapper는 AI SDK v5와 langsmith>=0.3.63이 필요합니다. 이전 버전의 AI SDK 또는 langsmith를 사용하는 경우, 이 페이지의 OpenTelemetry (OTEL) 기반 접근 방식을 참조하세요.
Vercel AI SDK를 설치합니다. 이 가이드는 아래 코드 스니펫에서 Vercel의 OpenAI integration을 사용하지만, 다른 옵션도 사용할 수 있습니다.
npm install ai @ai-sdk/openai zod

Environment configuration

export LANGSMITH_TRACING=true
export LANGSMITH_API_KEY=<your-api-key>

# The examples use OpenAI, but you can use any LLM provider of choice
export OPENAI_API_KEY=<your-openai-api-key>

# For LangSmith API keys linked to multiple workspaces, set the LANGSMITH_WORKSPACE_ID environment variable to specify which workspace to use.
export LANGSMITH_WORKSPACE_ID=<your-workspace-id>

Basic setup

AI SDK method를 import하고 wrap한 다음, 평소처럼 사용하세요:
import { openai } from "@ai-sdk/openai";
import * as ai from "ai";

import { wrapAISDK } from "langsmith/experimental/vercel";

const { generateText, streamText, generateObject, streamObject } =
  wrapAISDK(ai);

await generateText({
  model: openai("gpt-5-nano"),
  prompt: "Write a vegetarian lasagna recipe for 4 people.",
});
LangSmith 대시보드에서 이와 같은 trace를 볼 수 있습니다. tool call이 있는 실행도 추적할 수 있습니다:
import * as ai from "ai";
import { tool, stepCountIs } from "ai";
import { openai } from "@ai-sdk/openai";
import { z } from "zod";

import { wrapAISDK } from "langsmith/experimental/vercel";

const { generateText, streamText, generateObject, streamObject } =
  wrapAISDK(ai);

await generateText({
  model: openai("gpt-5-nano"),
  messages: [
    {
      role: "user",
      content: "What are my orders and where are they? My user ID is 123",
    },
  ],
  tools: {
    listOrders: tool({
      description: "list all orders",
      inputSchema: z.object({ userId: z.string() }),
      execute: async ({ userId }) =>
        `User ${userId} has the following orders: 1`,
    }),
    viewTrackingInformation: tool({
      description: "view tracking information for a specific order",
      inputSchema: z.object({ orderId: z.string() }),
      execute: async ({ orderId }) =>
        `Here is the tracking information for ${orderId}`,
    }),
  },
  stopWhen: stepCountIs(5),
});
결과는 이와 같은 trace가 됩니다. 다른 AI SDK method도 평소처럼 정확히 동일하게 사용할 수 있습니다.

traceable과 함께 사용하기

AI SDK call 주위나 AI SDK tool call 내부에 traceable call을 wrap할 수 있습니다. 이는 LangSmith에서 실행을 그룹화하려는 경우 유용합니다:
import * as ai from "ai";
import { tool, stepCountIs } from "ai";
import { openai } from "@ai-sdk/openai";
import { z } from "zod";

import { traceable } from "langsmith/traceable";
import { wrapAISDK } from "langsmith/experimental/vercel";

const { generateText, streamText, generateObject, streamObject } =
  wrapAISDK(ai);

const wrapper = traceable(async (input: string) => {
  const { text } = await generateText({
    model: openai("gpt-5-nano"),
    messages: [
      {
        role: "user",
        content: input,
      },
    ],
    tools: {
      listOrders: tool({
        description: "list all orders",
        inputSchema: z.object({ userId: z.string() }),
        execute: async ({ userId }) =>
          `User ${userId} has the following orders: 1`,
      }),
      viewTrackingInformation: tool({
        description: "view tracking information for a specific order",
        inputSchema: z.object({ orderId: z.string() }),
        execute: async ({ orderId }) =>
          `Here is the tracking information for ${orderId}`,
      }),
    },
    stopWhen: stepCountIs(5),
  });
  return text;
}, {
  name: "wrapper",
});

await wrapper("What are my orders and where are they? My user ID is 123.");
결과 trace는 이와 같이 보입니다.

Tracing in serverless environments

serverless 환경에서 추적할 때는 환경이 종료되기 전에 모든 실행이 flush되기를 기다려야 합니다. 이를 위해 AI SDK method를 wrap할 때 LangSmith Client instance를 전달한 다음, await client.awaitPendingTraceBatches()를 호출할 수 있습니다. 생성하는 모든 traceable wrapper에도 이를 전달해야 합니다:
import * as ai from "ai";
import { tool, stepCountIs } from "ai";
import { openai } from "@ai-sdk/openai";
import { z } from "zod";

import { Client } from "langsmith";
import { traceable } from "langsmith/traceable";
import { wrapAISDK } from "langsmith/experimental/vercel";

const client = new Client();

const { generateText, streamText, generateObject, streamObject } =
  wrapAISDK(ai, { client });

const wrapper = traceable(async (input: string) => {
  const { text } = await generateText({
    model: openai("gpt-5-nano"),
    messages: [
      {
        role: "user",
        content: input,
      },
    ],
    tools: {
      listOrders: tool({
        description: "list all orders",
        inputSchema: z.object({ userId: z.string() }),
        execute: async ({ userId }) =>
          `User ${userId} has the following orders: 1`,
      }),
      viewTrackingInformation: tool({
        description: "view tracking information for a specific order",
        inputSchema: z.object({ orderId: z.string() }),
        execute: async ({ orderId }) =>
          `Here is the tracking information for ${orderId}`,
      }),
    },
    stopWhen: stepCountIs(5),
  });
  return text;
}, {
  name: "wrapper",
  client,
});

try {
  await wrapper("What are my orders and where are they? My user ID is 123.");
} finally {
  await client.awaitPendingTraceBatches();
}
Next.js를 사용하는 경우, 이 로직을 넣을 수 있는 편리한 after hook이 있습니다:
import { after } from "next/server"
import { Client } from "langsmith";


export async function POST(request: Request) {
  const client = new Client();

  ...

  after(async () => {
    await client.awaitPendingTraceBatches();
  });

  return new Response(JSON.stringify({ ... }), {
    status: 200,
    headers: { "Content-Type": "application/json" },
  });
};
serverless 환경에서 rate limit 관리에 대한 정보를 포함한 자세한 내용은 이 페이지를 참조하세요.

Passing LangSmith config

AI SDK method를 처음 wrap할 때와 providerOptions.langsmith를 통해 실행하는 동안 모두 LangSmith 전용 config를 wrapper에 전달할 수 있습니다. 여기에는 metadata(나중에 LangSmith에서 실행을 필터링하는 데 사용할 수 있음), 최상위 실행 이름, tag, 사용자 정의 client instance 등이 포함됩니다. wrap할 때 전달된 config는 wrapped method로 수행하는 모든 향후 호출에 적용됩니다:
import { openai } from "@ai-sdk/openai";
import * as ai from "ai";

import { wrapAISDK } from "langsmith/experimental/vercel";

const { generateText, streamText, generateObject, streamObject } =
  wrapAISDK(ai, {
    metadata: {
      key_for_all_runs: "value",
    },
    tags: ["myrun"],
  });

await generateText({
  model: openai("gpt-5-nano"),
  prompt: "Write a vegetarian lasagna recipe for 4 people.",
});
반면 providerOptions.langsmith를 통해 runtime에 config를 전달하면 해당 실행에만 적용됩니다. 적절한 typing을 보장하기 위해 createLangSmithProviderOptions에서 config를 import하고 wrap하는 것을 권장합니다:
import { openai } from "@ai-sdk/openai";
import * as ai from "ai";

import {
  wrapAISDK,
  createLangSmithProviderOptions,
} from "langsmith/experimental/vercel";

const { generateText, streamText, generateObject, streamObject } =
  wrapAISDK(ai);

const lsConfig = createLangSmithProviderOptions({
  metadata: {
    individual_key: "value",
  },
  name: "my_individual_run",
});

await generateText({
  model: openai("gpt-5-nano"),
  prompt: "Write a vegetarian lasagna recipe for 4 people.",
  providerOptions: {
    langsmith: lsConfig,
  },
});

Redacting data

사용자 정의 input/output 처리 함수를 지정하여 AI SDK가 LangSmith로 전송하는 input과 output을 사용자 정의할 수 있습니다. 이는 LangSmith로 전송하지 않으려는 민감한 데이터를 처리하는 경우 유용합니다. output 형식은 사용하는 AI SDK method에 따라 다르므로, wrapped method에 개별적으로 config를 정의하고 전달하는 것을 권장합니다. 최상위 수준에서 generateText를 호출하면 내부적으로 LLM을 호출하고 여러 번 호출할 수 있으므로, AI SDK call 내의 하위 LLM 실행에 대해서도 별도의 함수를 제공해야 합니다. 또한 input과 output에 대한 적절한 type을 얻기 위해 createLangSmithProviderOptions에 generic parameter를 전달하는 것을 권장합니다. 다음은 generateText에 대한 예제입니다:
import {
  wrapAISDK,
  createLangSmithProviderOptions,
} from "langsmith/experimental/vercel";
import * as ai from "ai";
import { openai } from "@ai-sdk/openai";

const { generateText } = wrapAISDK(ai);

const lsConfig = createLangSmithProviderOptions<typeof generateText>({
  processInputs: (inputs) => {
    const { messages } = inputs;
    return {
      messages: messages?.map((message) => ({
        providerMetadata: message.providerOptions,
        role: "assistant",
        content: "REDACTED",
      })),
      prompt: "REDACTED",
    };
  },
  processOutputs: (outputs) => {
    return {
      providerMetadata: outputs.providerMetadata,
      role: "assistant",
      content: "REDACTED",
    };
  },
  processChildLLMRunInputs: (inputs) => {
    const { prompt } = inputs;
    return {
      messages: prompt.map((message) => ({
        ...message,
        content: "REDACTED CHILD INPUTS",
      })),
    };
  },
  processChildLLMRunOutputs: (outputs) => {
    return {
      providerMetadata: outputs.providerMetadata,
      content: "REDACTED CHILD OUTPUTS",
      role: "assistant",
    };
  },
});

const { text } = await generateText({
  model: openai("gpt-5-nano"),
  prompt: "What is the capital of France?",
  providerOptions: {
    langsmith: lsConfig,
  },
});

// Paris.
console.log(text);
실제 반환 값에는 원본의 redact되지 않은 결과가 포함되지만 LangSmith의 trace는 redact됩니다. 여기 예제가 있습니다. tool input/output을 redact하려면 다음과 같이 execute method를 traceable로 wrap하세요:
import * as ai from "ai";
import { tool, stepCountIs } from "ai";
import { openai } from "@ai-sdk/openai";
import { z } from "zod";

import { Client } from "langsmith";
import { traceable } from "langsmith/traceable";
import { wrapAISDK } from "langsmith/experimental/vercel";

const client = new Client();

const { generateText, streamText, generateObject, streamObject } =
  wrapAISDK(ai, { client });

const { text } = await generateText({
  model: openai("gpt-5-nano"),
  messages: [
    {
      role: "user",
      content: "What are my orders? My user ID is 123.",
    },
  ],
  tools: {
    listOrders: tool({
      description: "list all orders",
      inputSchema: z.object({ userId: z.string() }),
      execute: traceable(
        async ({ userId }) => {
          return `User ${userId} has the following orders: 1`;
        },
        {
          processInputs: (input) => ({ text: "REDACTED" }),
          processOutputs: (outputs) => ({ text: "REDACTED" }),
          run_type: "tool",
          name: "listOrders",
        }
      ) as (input: { userId: string }) => Promise<string>,
    }),
  },
  stopWhen: stepCountIs(5),
});
traceable 반환 type이 복잡하므로 cast가 필요합니다. cast를 피하려면 AI SDK tool wrapper 함수를 생략할 수도 있습니다.
Connect these docs programmatically to Claude, VSCode, and more via MCP for real-time answers.
I