observability
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chineseassistant-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 client primitives for rendering spans.
@assistant-ui/react-o11y请始终参考 assistant-ui.com/llms.txt 获取最新API。
为assistant-ui后端提供追踪与遥测能力。大部分内容属于通用AI SDK遥测;assistant-ui专属部分为路由处理器,以及用于渲染链路的客户端组件。
@assistant-ui/react-o11yContents
目录
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 /, not to the React runtime. The frontend (, ) is unchanged. is a separate, optional client layer for drawing the trace waterfall in your own UI.
streamTextgenerateTextuseChatRuntimeThreadreact-o11yThread (frontend) ──> /api/chat (streamText) ──> tracing backend
└─> react-o11y (optional UI)遥测功能需附加在调用/的服务器路由上,而非React运行时。前端(、)无需修改。是独立的可选客户端层,用于在自定义UI中绘制追踪瀑布流。
streamTextgenerateTextuseChatRuntimeThreadreact-o11yThread (前端) ──> /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 collectedLangfuse → 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 flag. Enable it per call:
experimental_telemetryts
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 and wrap the call so traces carry /:
instrumentation.tsuserIdsessionIdts
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 module instead. is not wrapped, so import it from directly:
aiconvertToModelMessagesaits
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_telemetryts
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,需在中注册OTel链路处理器,并包装调用以携带/:
instrumentation.tsuserIdsessionIdts
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,直接包装模块。无需包装,直接从导入:
aiconvertToModelMessagesaits
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 and pass the auth header:
baseURLts
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 instance with as usual; streaming, tools, and attachments are unchanged.
openaistreamTextHelicone无需遥测标识,只需将服务商指向代理并传入认证头:
baseURLts
import { createOpenAI } from "@ai-sdk/openai";
const openai = createOpenAI({
baseURL: "https://oai.helicone.ai/v1",
headers: { "Helicone-Auth": `Bearer ${process.env.HELICONE_API_KEY}` },
});将此实例与配合使用即可;流传输、工具及附件功能不受影响。
openaistreamTextVisualizing spans with react-o11y
使用react-o11y可视化链路
@assistant-ui/react-o11ySpanData[]SpanResourceuseAuiSpanPrimitivebash
npm install @assistant-ui/react-o11ytsx
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.ChildrenSpanByIndexProviderRootdata-span-statusdata-span-typedata-span-depthdata-collapsedSpanState@assistant-ui/react-o11ySpanResourceSpanData[]useAuiSpanPrimitivebash
npm install @assistant-ui/react-o11ytsx
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.ChildrenSpanByIndexProviderRootdata-span-statusdata-span-typedata-span-depthdata-collapsedSpanStateCommon Gotchas
常见问题
No traces on Vercel/Lambda
- The function exits before OTel flushes its buffer. Langfuse: before responding. LangSmith:
await langfuseSpanProcessor.forceFlush().await new Client().awaitPendingTraceBatches()
Langfuse traces empty
- must be set on each
experimental_telemetry: { isEnabled: true }/streamTextcall.generateText - The span processor only registers when ; OTel does not run on the edge runtime.
process.env.NEXT_RUNTIME === "nodejs"
LangSmith not tracing
- Use the destructured methods from , not the originals from
wrapAISDK(ai).aimust be set.LANGSMITH_TRACING=true
Helicone requests still hit OpenAI directly
- Confirm requests go to , not
oai.helicone.ai, and carry bothapi.openai.comandHelicone-Authheaders.Authorization
react-o11y renders nothing
- Primitives must render inside ; the resource mounts through
AuiProvider.useAui({ resource: SpanResource({ spans }) })
Vercel/Lambda上无追踪数据
- 函数在OTel刷新缓冲区前已退出。Langfuse解决方案:在响应前调用;LangSmith解决方案:调用
await langfuseSpanProcessor.forceFlush()。await new Client().awaitPendingTraceBatches()
Langfuse追踪为空
- 每次调用/
streamText时必须设置generateText。experimental_telemetry: { isEnabled: true } - 链路处理器仅在时注册;OTel无法在边缘运行时运行。
process.env.NEXT_RUNTIME === "nodejs"
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
相关技能
- - The route handler and stream response telemetry attaches to
/streaming - - Backend wiring (
/setup,ai-sdk) where the route livescustom-backend - - Persistence; pair
/cloud/userId/sessionIdwith trace attributesthreadId
- - 遥测附加的路由处理器及流响应
/streaming - - 路由所在的后端配置(
/setup、ai-sdk)custom-backend - - 持久化;将
/cloud/userId/sessionId与追踪属性关联threadId