Vercel AI SDK
The Vercel AI SDK (the ai package) has first-party, built-in OpenTelemetry telemetry — you flip it on per call with experimental_telemetry, and the SDK emits OTel spans for every generation. Neens ingests those spans through standard OTLP with zero Neens-specific code: you set up an OpenTelemetry exporter in your TypeScript/Node app and point it at Neens.
At a glance
| Language | TypeScript / Node.js |
| Instrumentation | Built-in (experimental_telemetry) + OpenTelemetry Node SDK |
| Endpoint | POST /v1/traces (OTLP/HTTP) |
| Auth | Authorization: Bearer nk_live_… agent key |
Instrument your agent
Install the OpenTelemetry packages
The AI SDK produces the spans; you supply an OpenTelemetry SDK to export them. This assumes you already have the AI SDK itself installed (npm install ai).
npm install @opentelemetry/sdk-node \
@opentelemetry/exporter-trace-otlp-http \
@opentelemetry/auto-instrumentations-nodeFor the Next.js path below, install @vercel/otel instead:
npm install @vercel/otel @opentelemetry/apiPoint the exporter at Neens
Create an instrumentation.ts and start the Node SDK before you import or call the AI SDK — load it with node --import ./instrumentation.js your-app.js (or require('./instrumentation') at the very top of your entry file).
// instrumentation.ts
import { NodeSDK } from '@opentelemetry/sdk-node'
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'
const sdk = new NodeSDK({
traceExporter: new OTLPTraceExporter({
url: 'https://<your-neens-host>/v1/traces',
headers: { Authorization: 'Bearer nk_live_your_key_here' },
}),
instrumentations: [getNodeAutoInstrumentations()],
})
sdk.start()
// Flush spans on shutdown so nothing is lost.
process.on('SIGTERM', () => {
sdk.shutdown().finally(() => process.exit(0))
})Prefer env vars? The OTLP exporter reads them, so you can drop the explicit url/headers and construct new OTLPTraceExporter(). The exporter auto-appends /v1/traces to the endpoint:
export OTEL_EXPORTER_OTLP_ENDPOINT="https://<your-neens-host>"
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer nk_live_your_key_here"Enable telemetry on your calls
Telemetry is opt-in per call. Pass experimental_telemetry: { isEnabled: true } to any generateText, streamText, generateObject, or streamObject call. A functionId labels the span so you can tell your agent’s steps apart.
import { openai } from '@ai-sdk/openai'
import { generateText } from 'ai'
const { text } = await generateText({
model: openai('gpt-4o'),
prompt: 'Summarize the latest support ticket.',
experimental_telemetry: {
isEnabled: true,
functionId: 'summarize-ticket',
// recordInputs / recordOutputs default to true;
// set false to keep prompts/responses out of spans.
},
})What Neens captures
With telemetry enabled, the AI SDK emits a span per generation carrying the model, token usage, the prompt, the response text, and any tool calls — so you see the full trajectory. The SDK writes its own ai.* attributes (e.g. ai.prompt, ai.response.text, ai.toolCall.*) and the OpenTelemetry GenAI gen_ai.* attributes; Neens reads the standard gen_ai.* model and token fields (gen_ai.request.model, gen_ai.usage.input_tokens, gen_ai.usage.output_tokens) alongside the ai.* prompt/response and tool-call attributes. For multi-turn agents, propagate a conversation/session id across calls so Neens groups them into one session (see Traces & sessions).
Neens speaks standard OTLP — this is not a Neens SDK fork. Any OpenTelemetry-instrumented app exports to Neens by pointing its exporter at the endpoint above. See Send traces for the full attribute reference, size limits, and response codes.
Verify
Open Traces in Neens; your generations appear within a few seconds. Continue to Traces & sessions.