observability

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

assistant-ui Observability

assistant-ui 可观测性

Always consult assistant-ui.com/llms.txt for the latest API.
Tracing and telemetry for an assistant-ui backend. Most of this is generic AI SDK telemetry; the assistant-ui specific part is the route handler and the
@assistant-ui/react-o11y
client primitives for rendering spans.
请始终参考 assistant-ui.com/llms.txt 获取最新API。
为assistant-ui后端提供追踪与遥测能力。大部分内容属于通用AI SDK遥测;assistant-ui专属部分为路由处理器,以及用于渲染链路的
@assistant-ui/react-o11y
客户端组件。

Contents

目录

References

参考资料

  • ./references/langfuse.md -- Langfuse tracing
  • ./references/langsmith.md -- LangSmith tracing
  • ./references/helicone.md -- Helicone proxy
  • ./references/react-o11y.md -- @assistant-ui/react-o11y client primitives
  • ./references/langfuse.md -- Langfuse 追踪配置
  • ./references/langsmith.md -- LangSmith 追踪配置
  • ./references/helicone.md -- Helicone 代理配置
  • ./references/react-o11y.md -- @assistant-ui/react-o11y 客户端组件说明

Where it plugs in

接入位置

Telemetry attaches to the server route that calls
streamText
/
generateText
, not to the React runtime. The frontend (
useChatRuntime
,
Thread
) is unchanged.
react-o11y
is a separate, optional client layer for drawing the trace waterfall in your own UI.
Thread (frontend) ──> /api/chat (streamText) ──> tracing backend
                                              └─> react-o11y (optional UI)
遥测功能需附加在调用
streamText
/
generateText
服务器路由上,而非React运行时。前端(
useChatRuntime
Thread
)无需修改。
react-o11y
是独立的可选客户端层,用于在自定义UI中绘制追踪瀑布流。
Thread (前端) ──> /api/chat (streamText) ──> 追踪后端
                                              └─> react-o11y (可选UI)

Provider routing

服务商路由

Langfuse   → OTel span processor + experimental_telemetry, propagateAttributes
LangSmith  → wrapAISDK(ai) wrapper, no OTel setup
Helicone   → proxy baseURL on the provider, no telemetry flag
react-o11y → client primitives to render spans you collected
Langfuse   → OTel 链路处理器 + experimental_telemetry,传播属性
LangSmith  → wrapAISDK(ai) 包装器,无需OTel配置
Helicone   → 在服务商中设置代理baseURL,无需遥测标识
react-o11y → 用于渲染收集到的链路的客户端组件

AI SDK telemetry (shared)

AI SDK遥测(通用)

Langfuse and any OTel backend reuse the AI SDK
experimental_telemetry
flag. Enable it per call:
ts
import { openai } from "@ai-sdk/openai";
import { streamText, convertToModelMessages } from "ai";
import type { UIMessage } from "ai";

export async function POST(req: Request) {
  const { messages }: { messages: UIMessage[] } = await req.json();
  const result = streamText({
    model: openai("gpt-5.4-nano"),
    messages: await convertToModelMessages(messages),
    experimental_telemetry: { isEnabled: true },
  });
  return result.toUIMessageStreamResponse();
}
For Langfuse, register an OTel span processor in
instrumentation.ts
and wrap the call so traces carry
userId
/
sessionId
:
ts
import { NodeSDK } from "@opentelemetry/sdk-node";
import { LangfuseSpanProcessor } from "@langfuse/otel";

export const langfuseSpanProcessor = new LangfuseSpanProcessor();

export async function register() {
  if (process.env.NEXT_RUNTIME !== "nodejs") return;
  const sdk = new NodeSDK({ spanProcessors: [langfuseSpanProcessor] });
  sdk.start();
}
ts
import { propagateAttributes } from "@langfuse/tracing";

const result = await propagateAttributes(
  { traceName: "chat-completion", userId, sessionId },
  async () =>
    streamText({
      model: openai("gpt-5.4-nano"),
      messages: await convertToModelMessages(messages),
      experimental_telemetry: { isEnabled: true },
    }),
);
LangSmith skips OTel entirely; wrap the
ai
module instead.
convertToModelMessages
is not wrapped, so import it from
ai
directly:
ts
import * as ai from "ai";
import { wrapAISDK } from "langsmith/experimental/vercel";
import { openai } from "@ai-sdk/openai";

const { streamText } = wrapAISDK(ai);

const result = streamText({
  model: openai("gpt-5.4-nano"),
  messages: await ai.convertToModelMessages(messages),
});
return result.toUIMessageStreamResponse();
See the per provider reference files for env vars, metadata tagging, and serverless flushing.
Langfuse及所有OTel后端复用AI SDK的
experimental_telemetry
标识,需在每次调用时启用:
ts
import { openai } from "@ai-sdk/openai";
import { streamText, convertToModelMessages } from "ai";
import type { UIMessage } from "ai";

export async function POST(req: Request) {
  const { messages }: { messages: UIMessage[] } = await req.json();
  const result = streamText({
    model: openai("gpt-5.4-nano"),
    messages: await convertToModelMessages(messages),
    experimental_telemetry: { isEnabled: true },
  });
  return result.toUIMessageStreamResponse();
}
对于Langfuse,需在
instrumentation.ts
中注册OTel链路处理器,并包装调用以携带
userId
/
sessionId
ts
import { NodeSDK } from "@opentelemetry/sdk-node";
import { LangfuseSpanProcessor } from "@langfuse/otel";

export const langfuseSpanProcessor = new LangfuseSpanProcessor();

export async function register() {
  if (process.env.NEXT_RUNTIME !== "nodejs") return;
  const sdk = new NodeSDK({ spanProcessors: [langfuseSpanProcessor] });
  sdk.start();
}
ts
import { propagateAttributes } from "@langfuse/tracing";

const result = await propagateAttributes(
  { traceName: "chat-completion", userId, sessionId },
  async () =>
    streamText({
      model: openai("gpt-5.4-nano"),
      messages: await convertToModelMessages(messages),
      experimental_telemetry: { isEnabled: true },
    }),
);
LangSmith完全跳过OTel,直接包装
ai
模块。
convertToModelMessages
无需包装,直接从
ai
导入:
ts
import * as ai from "ai";
import { wrapAISDK } from "langsmith/experimental/vercel";
import { openai } from "@ai-sdk/openai";

const { streamText } = wrapAISDK(ai);

const result = streamText({
  model: openai("gpt-5.4-nano"),
  messages: await ai.convertToModelMessages(messages),
});
return result.toUIMessageStreamResponse();
环境变量、元数据标记及无服务器环境刷新相关内容,请查看各服务商的参考文件。

Helicone (proxy, no OTel)

Helicone(代理模式,无需OTel)

Helicone needs no telemetry flag. Point the provider at the proxy
baseURL
and pass the auth header:
ts
import { createOpenAI } from "@ai-sdk/openai";

const openai = createOpenAI({
  baseURL: "https://oai.helicone.ai/v1",
  headers: { "Helicone-Auth": `Bearer ${process.env.HELICONE_API_KEY}` },
});
Use this
openai
instance with
streamText
as usual; streaming, tools, and attachments are unchanged.
Helicone无需遥测标识,只需将服务商指向代理
baseURL
并传入认证头:
ts
import { createOpenAI } from "@ai-sdk/openai";

const openai = createOpenAI({
  baseURL: "https://oai.helicone.ai/v1",
  headers: { "Helicone-Auth": `Bearer ${process.env.HELICONE_API_KEY}` },
});
将此
openai
实例与
streamText
配合使用即可;流传输、工具及附件功能不受影响。

Visualizing spans with react-o11y

使用react-o11y可视化链路

@assistant-ui/react-o11y
gives headless primitives to render collected spans as a trace waterfall. Feed it
SpanData[]
(id, parentSpanId, name, type, status, startedAt, endedAt, latencyMs) via
SpanResource
, mount with
useAui
, and render
SpanPrimitive
parts.
bash
npm install @assistant-ui/react-o11y
tsx
import {
  SpanResource,
  SpanPrimitive,
  type SpanData,
} from "@assistant-ui/react-o11y";
import { AuiProvider, useAui } from "@assistant-ui/store";

function SpanRow() {
  return (
    <SpanPrimitive.Root>
      <SpanPrimitive.Indent />
      <SpanPrimitive.CollapseToggle />
      <SpanPrimitive.StatusIndicator />
      <SpanPrimitive.TypeBadge />
      <SpanPrimitive.Name />
    </SpanPrimitive.Root>
  );
}

export function TraceView({ spans }: { spans: SpanData[] }) {
  const aui = useAui({ resource: SpanResource({ spans }) });
  return (
    <AuiProvider value={aui}>
      <SpanPrimitive.Children components={{ Span: SpanRow }} />
    </AuiProvider>
  );
}
SpanPrimitive.Children
flattens the tree to a visible list and wraps each item in
SpanByIndexProvider
.
Root
exposes
data-span-status
,
data-span-type
,
data-span-depth
, and
data-collapsed
for styling. See react-o11y.md for the full part list and
SpanState
shape.
@assistant-ui/react-o11y
提供无头组件,可将收集到的链路渲染为追踪瀑布流。通过
SpanResource
传入
SpanData[]
(id、parentSpanId、name、type、status、startedAt、endedAt、latencyMs),通过
useAui
挂载,并渲染
SpanPrimitive
组件。
bash
npm install @assistant-ui/react-o11y
tsx
import {
  SpanResource,
  SpanPrimitive,
  type SpanData,
} from "@assistant-ui/react-o11y";
import { AuiProvider, useAui } from "@assistant-ui/store";

function SpanRow() {
  return (
    <SpanPrimitive.Root>
      <SpanPrimitive.Indent />
      <SpanPrimitive.CollapseToggle />
      <SpanPrimitive.StatusIndicator />
      <SpanPrimitive.TypeBadge />
      <SpanPrimitive.Name />
    </SpanPrimitive.Root>
  );
}

export function TraceView({ spans }: { spans: SpanData[] }) {
  const aui = useAui({ resource: SpanResource({ spans }) });
  return (
    <AuiProvider value={aui}>
      <SpanPrimitive.Children components={{ Span: SpanRow }} />
    </AuiProvider>
  );
}
SpanPrimitive.Children
会将树形结构展平为可见列表,并将每个项包裹在
SpanByIndexProvider
中。
Root
会暴露
data-span-status
data-span-type
data-span-depth
data-collapsed
属性用于样式定制。完整组件列表及
SpanState
结构请查看react-o11y.md

Common Gotchas

常见问题

No traces on Vercel/Lambda
  • The function exits before OTel flushes its buffer. Langfuse:
    await langfuseSpanProcessor.forceFlush()
    before responding. LangSmith:
    await new Client().awaitPendingTraceBatches()
    .
Langfuse traces empty
  • experimental_telemetry: { isEnabled: true }
    must be set on each
    streamText
    /
    generateText
    call.
  • The span processor only registers when
    process.env.NEXT_RUNTIME === "nodejs"
    ; OTel does not run on the edge runtime.
LangSmith not tracing
  • Use the destructured methods from
    wrapAISDK(ai)
    , not the originals from
    ai
    .
    LANGSMITH_TRACING=true
    must be set.
Helicone requests still hit OpenAI directly
  • Confirm requests go to
    oai.helicone.ai
    , not
    api.openai.com
    , and carry both
    Helicone-Auth
    and
    Authorization
    headers.
react-o11y renders nothing
  • Primitives must render inside
    AuiProvider
    ; the resource mounts through
    useAui({ resource: SpanResource({ spans }) })
    .
Vercel/Lambda上无追踪数据
  • 函数在OTel刷新缓冲区前已退出。Langfuse解决方案:在响应前调用
    await langfuseSpanProcessor.forceFlush()
    ;LangSmith解决方案:调用
    await new Client().awaitPendingTraceBatches()
Langfuse追踪为空
  • 每次调用
    streamText
    /
    generateText
    时必须设置
    experimental_telemetry: { isEnabled: true }
  • 链路处理器仅在
    process.env.NEXT_RUNTIME === "nodejs"
    时注册;OTel无法在边缘运行时运行。
LangSmith未生成追踪
  • 使用
    wrapAISDK(ai)
    解构出的方法,而非
    ai
    中的原始方法。必须设置
    LANGSMITH_TRACING=true
Helicone请求仍直接调用OpenAI
  • 确认请求地址为
    oai.helicone.ai
    而非
    api.openai.com
    ,且同时携带
    Helicone-Auth
    Authorization
    头。
react-o11y无渲染内容
  • 组件必须在
    AuiProvider
    内部渲染;资源需通过
    useAui({ resource: SpanResource({ spans }) })
    挂载。

Related Skills

相关技能

  • /streaming
    - The route handler and stream response telemetry attaches to
  • /setup
    - Backend wiring (
    ai-sdk
    ,
    custom-backend
    ) where the route lives
  • /cloud
    - Persistence; pair
    userId
    /
    sessionId
    /
    threadId
    with trace attributes
  • /streaming
    - 遥测附加的路由处理器及流响应
  • /setup
    - 路由所在的后端配置(
    ai-sdk
    custom-backend
  • /cloud
    - 持久化;将
    userId
    /
    sessionId
    /
    threadId
    与追踪属性关联