feat(vercel-ai-sdk): add AI SDK v7 integration#794
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
824ace2 to
53f0072
Compare
|
@claude review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 53f0072fe4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 83394e9706
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 22c6d54403
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b51f773f96
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5f88c8c15d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 81bfa1f0aa
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: beb0ff7c01
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1f4d6a9d4a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| This integration targets AI SDK v7 GA. Install it together with `ai@^7`; the package depends on the matching `@ai-sdk/otel` integration internally. | ||
|
|
||
| ```sh | ||
| pnpm add @langfuse/vercel-ai-sdk ai |
There was a problem hiding this comment.
Add required Langfuse tracing packages to install step
pnpm add @langfuse/vercel-ai-sdk ai is not enough for the Usage example immediately below: it imports @langfuse/tracing, which this package does not depend on, and the spans also need a LangfuseSpanProcessor from @langfuse/otel to reach Langfuse. Users who follow these docs in a fresh project will hit a module-resolution error or end up with no exported spans, so include the required Langfuse/OpenTelemetry packages or make the sample not depend on them.
Useful? React with 👍 / 👎.
| // Handle media from Vercel AI SDK v6 and AI SDK v7. | ||
| if (["ai", "gen_ai"].includes(span.instrumentationScope.name)) { |
There was a problem hiding this comment.
🔴 AI SDK v6/v7 media handling is skipped whenever a user passes a custom tracer to LangfuseVercelAiSdkIntegration, because MediaService.ts:101 gates the entire media block on ['ai','gen_ai'].includes(span.instrumentationScope.name) — but a user-supplied tracer (a publicly supported option in LangfuseVercelAiSdkIntegrationOptions) sets the scope name to whatever the user picked. The spans still get exported (the rest of the pipeline correctly classifies them by attribute presence via isGenAISpan), but base64 file/image payloads stay inline in gen_ai.input.messages / gen_ai.output.messages instead of being uploaded to Langfuse media storage. Fix: gate by attribute presence (e.g. reuse isGenAISpan from span-filter.ts, or check any gen_ai.*/ai.* key) so the classification matches the rest of the export pipeline.
Extended reasoning...
What the bug is
LangfuseVercelAiSdkIntegrationOptions (packages/vercel-ai-sdk/src/types.ts:9) exposes a public tracer?: Tracer option, which LangfuseVercelAiSdkIntegration.ts:37 forwards verbatim to the @ai-sdk/otel OpenTelemetry constructor. In the installed @ai-sdk/otel@1.0.0-beta.125, the constructor resolves this.tracer = options.tracer ?? trace.getTracer('gen_ai') — so every span produced by every this.tracer.startSpan(...) call inherits the user's chosen instrumentation scope name when a custom tracer is supplied.
The AI SDK semantic-convention attribute keys (gen_ai.input.messages, gen_ai.output.messages, gen_ai.operation.name, etc.) are hardcoded string literals on those span attributes, so they are emitted regardless of which tracer created the span. LangfuseSpanProcessor correctly exports those spans because span-filter.ts:32-36 isGenAISpan checks attribute presence (Object.keys(span.attributes).some(k => k.startsWith('gen_ai.'))), not the scope name.
But packages/otel/src/MediaService.ts:100-101 gates the entire v6 + v7 media-handling block on ["ai", "gen_ai"].includes(span.instrumentationScope.name). With a custom tracer like new LangfuseVercelAiSdkIntegration({ tracer: trace.getTracer("my-app") }), the scope is my-app, the check fails, and the whole v6 (ai.prompt.messages/ai.prompt) AND v7 (gen_ai.input.messages/gen_ai.output.messages) media replacement block is skipped. Base64 file/image payloads stay inline in the exported attributes — never uploaded to Langfuse media storage, never replaced with @@@langfuseMedia:...@@@ tags. The Langfuse UI then renders huge raw base64 strings instead of the inlined-media tag.
Step-by-step proof
- User writes
new LangfuseVercelAiSdkIntegration({ tracer: trace.getTracer("my-app") })(a documented option onLangfuseVercelAiSdkIntegrationOptions). - That tracer reaches
@ai-sdk/otelOpenTelemetry. Every span it creates carriesinstrumentationScope.name === "my-app". - The user calls
generateText({ messages: [{ role: "user", content: [{ type: "file", mediaType: "application/pdf", data: "<base64-pdf>" }, ...] }] }).@ai-sdk/otelserializes the messages intogen_ai.input.messagesas JSON[{ role: "user", parts: [{ type: "blob", content: "<base64-pdf>", mime_type: "application/pdf" }, ...] }]. LangfuseSpanProcessorcallsisDefaultExportSpan(span)→isGenAISpan(span)returns true (attributes start withgen_ai.), span is exported.MediaService.process(span)reaches line 100:["ai", "gen_ai"].includes("my-app")→ false. The entire block at lines 100-232 is skipped.- The exported attribute still contains the raw base64. No upload happens. Langfuse UI shows the base64 blob inline.
Replacing the check with isGenAISpan(span) (or any gen_ai.* attribute presence test) keeps the existing default behavior and fixes the custom-tracer case — and matches the asymmetry the verifiers flagged: the rest of the pipeline already classifies these spans by attribute presence; only this one site uses scope name.
Why it survived review
The tracer option is not exercised by the README examples, the package tests, the integration test, or the v7 e2e suite — every test path uses the default gen_ai scope. So the gate check works in the default flow and never triggers in CI. The codex bot raised the same issue in this PR's inline comment 3435755845 (2026-06-18T12:29:46Z), without maintainer response.
Suggested fix
In packages/otel/src/MediaService.ts, replace the scope-name check with attribute-presence detection. Either inline:
const isAiSdkSpan = Object.keys(span.attributes).some(
(k) => k.startsWith("gen_ai.") || k.startsWith("ai."),
);
if (isAiSdkSpan) { ... }or reuse the existing helper from span-filter.ts:
import { isGenAISpan } from "./span-filter.js";
// ...
if (isGenAISpan(span)) { ... }The latter has the bonus of staying consistent with the rest of the export pipeline's classification.
🔬 also observed by chatgpt-codex-connector
Problem
AI SDK v7 moved telemetry to the callback-based
TelemetryAPI and removed the old integration path used by the previous Langfuse/Vercel AI SDK integration. Langfuse needs a maintained package that works with v7, keeps the AI SDK OpenTelemetry span shape aligned with Vercel's reference implementation, and still lets users attach Langfuse prompt and observation metadata.Changes
@langfuse/vercel-ai-sdkworkspace package for AI SDK v7.LangfuseVercelAiSdkIntegrationas a thinTelemetrywrapper around Vercel's@ai-sdk/otelOpenTelemetryintegration.enrichSpanhook to add only Langfuse observation attributes, instead of recreating AI SDK span lifecycle behavior ourselves.runtimeContextkey to Langfuse observation metadata, exceptlangfusePrompt, which links Langfuse prompt name/version and is not added as metadata.propagateAttributesfrom@langfuse/tracing.@langfuse/otelmedia parsing to handle AI SDK v7 OpenTelemetry message parts, including file/image attachments and empty blob-content guards.propagateAttributesand span processing, and e2e coverage for AI SDK v7 server-side ingestion.Verification
pnpm run buildpnpm run check:lintpnpm run check:typepnpm run check:formatpnpm --filter @langfuse/vercel-ai-sdk testpnpm exec vitest run --project=integration tests/integration/vercel-ai-sdk.integration.test.tslint,test-integration,test-e2e, andall-tests-passedare green on148389c.Release info
Bump level
Libraries affected
Changelog notes
@langfuse/vercel-ai-sdkwith AI SDK v7 telemetry support.