@loomtrace/vercel-ai-sdk
v0.1.0
Published
Zero-config loomtrace tracing for agents built directly on the Vercel AI SDK.
Maintainers
Readme
@loomtrace/vercel-ai-sdk
Zero-config loomtrace tracing for agents built directly on the Vercel AI SDK.
One traceAgent() call stands up the OpenTelemetry wiring the AI SDK needs —
a NodeTracerProvider, a LoomTraceSpanProcessor (@loomtrace/otel)
in front of a destination, and @ai-sdk/otel's telemetry registered against
it — and hands back a flush() / shutdown() handle. After it runs, every AI
SDK call in the process becomes a loomtrace trace, with the model calls and
tool invocations as its spans.
This is the standalone door into @loomtrace/core, for an app
built directly on ai. If loomtrace lives inside a framework you maintain,
or you already own a TracerProvider, wire @loomtrace/core /
@loomtrace/otel by hand instead — same core, same trace format,
same destinations, lower-level seam. See
examples/otel-vercel-ai for that path.
Install
pnpm add @loomtrace/vercel-ai-sdkai@^7 is a peer dependency — v7 is where registerTelemetry and
@ai-sdk/otel landed; earlier majors drove telemetry through
experimental_telemetry and are not supported. Everything else
(@ai-sdk/otel, the @opentelemetry/* packages) is bundled. Node.js only —
for Next.js / edge, register @ai-sdk/otel against @vercel/otel's provider
in instrumentation.ts and add LoomTraceSpanProcessor there via
@loomtrace/otel.
Usage
import { traceAgent } from "@loomtrace/vercel-ai-sdk";
import { generateText } from "ai";
import { openai } from "@ai-sdk/openai";
const tracing = traceAgent({ serviceName: "trip-planner" });
const { text } = await generateText({
model: openai("gpt-5.4"),
prompt: "Plan a 3-day trip to Lisbon.",
// optional — labels the trace; calls are traced with or without it
telemetry: { functionId: "plan-trip" },
});
await tracing.shutdown();That writes a trace to .loomtrace/traces/<traceId>.json, readable with
@loomtrace/cli:
npx loomtrace inspect .loomtrace/traces/<traceId>.jsonOr watch the whole directory and let each new trace render as your program
writes it — point it at .loomtrace/traces and leave it running:
npx loomtrace watch .loomtrace/tracesFor a runnable version — mock model, no API key, no network — see
examples/vercel-ai-sdk-standalone.
traceAgent(options?)
Synchronous: setup needs no await (and no top-level await in your entry
file), only teardown does. Returns an AgentTracing handle:
flush()— resolves once every trace buffered so far has reached the destination. Safe to call repeatedly (e.g. between test cases).shutdown()—flush(), then release resources: shut down the destination and theTracerProvider. Idempotent.provider— theNodeTracerProviderloomtrace registered. Escape hatch for advanced wiring; most users never touch it.
| Option | Default | |
| --- | --- | --- |
| destination | "local" | "silent" \| "local" \| LoomDestination. "local" writes .loomtrace/traces/ under the cwd. The default differs from @loomtrace/core's "silent" on purpose: you installed this package and called it, so traces on disk are the expected outcome, not a surprise. |
| serviceName | "unknown_service" | OTel resource service.name. |
| metadata | — | Key/values merged onto every trace's metadata — environment, release, region. The same field LoomTraceConfig.metadata populates on the embed door. |
| enabled | true | false → registers nothing and returns a no-op handle, so an app can forward its own --trace flag straight through without branching the call site. |
| flushOnExit | true | Flush buffered traces on process "beforeExit" — which fires when the event loop drains on its own (the common case for a script-shaped agent) and never on process.exit() or a signal. Signal handlers stay yours to own. |
| onError | one console.warn | Reports loomtrace's own failures — a rejected destination write, a span that will not convert, a TracerProvider already registered globally. Never thrown into the traced program. |
| provider | — | Attach to a NodeTracerProvider you already built and registered (e.g. you also export to Datadog / OTLP). You own its processors — include a LoomTraceSpanProcessor yourself, OTel 2.x has no addSpanProcessor — and its lifecycle; destination, metadata and serviceName are ignored. |
One traceAgent() per process: a second call reports through onError and
returns the existing handle.
