Loading...
Loading...
Adds tracing, telemetry, and observability to an assistant-ui backend. Use when wiring an AI SDK route handler (streamText/generateText, toUIMessageStreamResponse) to a tracing backend: Langfuse via OpenTelemetry (LangfuseSpanProcessor and NodeSDK in instrumentation.ts, experimental_telemetry isEnabled, propagateAttributes with traceName/userId/sessionId, langfuseSpanProcessor.forceFlush on serverless), LangSmith via wrapAISDK(ai) from langsmith/experimental/vercel (createLangSmithProviderOptions, awaitPendingTraceBatches), or Helicone via createOpenAI baseURL https://oai.helicone.ai/v1 with the Helicone-Auth header. Also covers rendering collected spans with @assistant-ui/react-o11y headless primitives (SpanResource, SpanPrimitive Root/Indent/CollapseToggle/StatusIndicator/TypeBadge/Name/Children, SpanByIndexProvider, SpanData/SpanState) mounted via useAui/AuiProvider from @assistant-ui/store. Use for missing or empty traces, edge vs nodejs runtime telemetry, serverless flush issues, or trace waterfalls.
npx skill4agent add assistant-ui/skills observability@assistant-ui/react-o11ystreamTextgenerateTextuseChatRuntimeThreadreact-o11yThread (frontend) ──> /api/chat (streamText) ──> tracing backend
└─> react-o11y (optional UI)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 collectedexperimental_telemetryimport { 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();
}instrumentation.tsuserIdsessionIdimport { 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();
}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 },
}),
);aiconvertToModelMessagesaiimport * 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();baseURLimport { createOpenAI } from "@ai-sdk/openai";
const openai = createOpenAI({
baseURL: "https://oai.helicone.ai/v1",
headers: { "Helicone-Auth": `Bearer ${process.env.HELICONE_API_KEY}` },
});openaistreamText@assistant-ui/react-o11ySpanData[]SpanResourceuseAuiSpanPrimitivenpm install @assistant-ui/react-o11yimport {
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-collapsedSpanStateawait langfuseSpanProcessor.forceFlush()await new Client().awaitPendingTraceBatches()experimental_telemetry: { isEnabled: true }streamTextgenerateTextprocess.env.NEXT_RUNTIME === "nodejs"wrapAISDK(ai)aiLANGSMITH_TRACING=trueoai.helicone.aiapi.openai.comHelicone-AuthAuthorizationAuiProvideruseAui({ resource: SpanResource({ spans }) })/streaming/setupai-sdkcustom-backend/clouduserIdsessionIdthreadId