Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,8 @@ The LangGraph instrumentation no longer emits `gen_ai.create_agent` spans when a

**Tracing removed from generated templates:** Tracing was removed from the generated Pages Router API handler, Edge API handler, and Middleware wrapper templates. Route handlers and middleware are still instrumented automatically, so no action is required for most users.

**Vercel AI no longer supported on Edge runtime:** We now rely on diagnostics channels for our Vercel AI instrumentation, which does not work on the Edge runtime. Because of this, monitoring of the `ai` package is no longer supported on Edge. Note that Edge is deprecated.

### Cloudflare: `nodejs_compat` compatibility flag is now required

Affected SDKs: `@sentry/cloudflare`.
Expand Down Expand Up @@ -871,7 +873,10 @@ Sentry.init({
- The internal `sentry.sdk_meta.gen_ai.input.messages.original_length` span attribute was removed.
- (Vercel AI) The internal JSON-stringify workaround for array span attributes was removed.
- AI integrations are no longer available in the browser SDK. They remain available in the server-side SDKs.
- The AI instrumentation code moved out of `@sentry/core` into `@sentry/server-utils`. If you imported any AI helper **directly from `@sentry/core`**, import it from `@sentry/server-utils` instead (or keep importing it from your platform SDK, e.g. `@sentry/node`, if it re-exported that helper before — platform SDK availability is unchanged from v10). Affected helpers: `instrumentOpenAiClient`, `instrumentAnthropicAiClient`, `instrumentGoogleGenAIClient`, `instrumentWorkersAiClient`, `createLangChainCallbackHandler`, `instrumentLangChainEmbeddings`, `instrumentStateGraph`, `instrumentStateGraphCompile`, `instrumentCreateReactAgent`, `addVercelAiProcessors`.
- The AI instrumentation code moved out of `@sentry/core` into `@sentry/server-utils`. If you imported any AI helper **directly from `@sentry/core`**, import it from `@sentry/server-utils` instead (or keep importing it from your platform SDK, e.g. `@sentry/node`, if it re-exported that helper before — platform SDK availability is unchanged from v10). Affected helpers: `instrumentOpenAiClient`, `instrumentAnthropicAiClient`, `instrumentGoogleGenAIClient`, `instrumentWorkersAiClient`, `createLangChainCallbackHandler`, `instrumentLangChainEmbeddings`, `instrumentStateGraph`, `instrumentStateGraphCompile`, `instrumentCreateReactAgent`.
- The `addVercelAiProcessors` helper was removed. It was an internal building block for setting up Vercel AI span processing by hand; `vercelAIIntegration()` now wires this up on its own, so add that integration instead of calling `addVercelAiProcessors` directly.
- (Vercel Edge) `vercelAIIntegration` was removed from `@sentry/vercel-edge`; Vercel AI is not instrumented on the Edge runtime. `@sentry/nextjs` keeps the export on its Edge build as a no-op (so `import { vercelAIIntegration }` from `@sentry/nextjs` still resolves in edge-compiled instrumentation files), with the real instrumentation running only in the Node runtime.
- (Cloudflare & Deno) `vercelAIIntegration` no longer post-processes the OpenTelemetry spans emitted by the `ai` SDK. Instrumentation now goes solely through the channel-based instrumentation, the same as the other server SDKs.
- The following low-level AI exports are no longer part of the public API (they were provider-instrumentation internals exported from `@sentry/core`):
- Attribute/stream/util helpers: `extractOpenAiRequestAttributes`, `addOpenAiRequestAttributes`, `addOpenAiResponseAttributes`, `extractOpenAiRequestParameters`, `instrumentOpenAiStream`, `extractAnthropicRequestAttributes`, `addAnthropicRequestAttributes`, `addAnthropicResponseAttributes`, `instrumentAsyncIterableStream`, `instrumentMessageStream`, `extractGoogleGenAIRequestAttributes`, `addGoogleGenAIRequestAttributes`, `addGoogleGenAIResponseAttributes`, `instrumentGoogleGenAIStream`, `getProviderMetadataAttributes`, `getTruncatedJsonString`, `shouldEnableTruncation`, `resolveAIRecordingOptions`, `wrapToolsWithSpans`, `extractLLMFromParams`, `extractAgentNameFromParams`, `instrumentCompiledGraphInvoke`.
- Integration-name constants: `OPENAI_INTEGRATION_NAME`, `ANTHROPIC_AI_INTEGRATION_NAME`, `GOOGLE_GENAI_INTEGRATION_NAME`, `LANGCHAIN_INTEGRATION_NAME`, `LANGGRAPH_INTEGRATION_NAME`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,10 @@ export default Sentry.withSentry(
dsn: env.SENTRY_DSN,
traceLifecycle: 'static',
tracesSampleRate: 1,
// The Vercel AI SDK emits its spans through `@opentelemetry/api`, so they are only picked up when
// the Cloudflare OpenTelemetry tracer provider is set up.
enableOpenTelemetrySetup: true,
}),
{
async fetch(_request, _env, _ctx) {
await generateText({
experimental_telemetry: { isEnabled: true },
model: new MockLanguageModelV3({
doGenerate: async () => ({
finishReason: { unified: 'stop', raw: 'stop' },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ it('captures a transaction with Vercel AI v6 spans via @sentry/cloudflare vercel
is_segment: false,
attributes: expect.objectContaining({
'sentry.op': { type: 'string', value: 'gen_ai.invoke_agent' },
'sentry.origin': { type: 'string', value: 'auto.vercelai.otel' },
'sentry.origin': { type: 'string', value: 'auto.vercelai.channel' },
[GEN_AI_OPERATION_NAME]: { type: 'string', value: 'invoke_agent' },
[GEN_AI_USAGE_INPUT_TOKENS]: { type: 'integer', value: 10 },
[GEN_AI_USAGE_OUTPUT_TOKENS]: { type: 'integer', value: 20 },
Expand All @@ -49,7 +49,7 @@ it('captures a transaction with Vercel AI v6 spans via @sentry/cloudflare vercel
is_segment: false,
attributes: expect.objectContaining({
'sentry.op': { type: 'string', value: 'gen_ai.generate_content' },
'sentry.origin': { type: 'string', value: 'auto.vercelai.otel' },
'sentry.origin': { type: 'string', value: 'auto.vercelai.channel' },
[GEN_AI_OPERATION_NAME]: { type: 'string', value: 'generate_content' },
}),
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { cloudflare } from '@cloudflare/vite-plugin';
import { sentryCloudflareVitePlugin } from '@sentry/cloudflare/vite';
import { defineConfig } from 'vite';

export default defineConfig({
plugins: [cloudflare(), sentryCloudflareVitePlugin()],
});
2 changes: 1 addition & 1 deletion packages/cloudflare/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,11 +121,11 @@ export { getDefaultIntegrations } from './sdk';
export { httpServerIntegration } from './integrations/httpServer';
export { fetchIntegration } from './integrations/fetch';
export { spotlightIntegration } from './integrations/spotlight';
export { vercelAIIntegration } from './integrations/tracing/vercelai';
export {
otlpIntegration,
getOtlpTracesEndpoint,
prismaIntegration,
vercelAIIntegration,
Comment thread
cursor[bot] marked this conversation as resolved.
instrumentOpenAiClient,
instrumentAnthropicAiClient,
instrumentGoogleGenAIClient,
Expand Down
56 changes: 0 additions & 56 deletions packages/cloudflare/src/integrations/tracing/vercelai.ts

This file was deleted.

12 changes: 6 additions & 6 deletions packages/cloudflare/src/sdk.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import type { Integration } from '@sentry/core';
import { vercelAIIntegration } from './integrations/tracing/vercelai';
import { getBaseDefaultIntegrations, initWithDefaultIntegrations } from './baseSdk';
import type { CloudflareClient, CloudflareOptions } from './client';
import { vercelAIIntegration } from '@sentry/server-utils/orchestrion';

/**
* Get the default integrations for the Cloudflare SDK.
*
* This is the full set and requires the `nodejs_compat` compatibility flag. Runtimes that cannot
* enable it (e.g. Shopify Oxygen) go through `wrapRequestHandler`, which only sets up
* `getBaseDefaultIntegrations`.
*/
export function getDefaultIntegrations(options: CloudflareOptions): Integration[] {
return [
...getBaseDefaultIntegrations(options),
// Subscribes to the `ai` SDK's native `node:diagnostics_channel` telemetry channel.
Comment thread
cursor[bot] marked this conversation as resolved.
// Note: For now we add this directly here, in order for this to be here for vercel AI v7
// Generally, we auto-inject integrations based on what orchestrion is using
// however, for things with native channel support (like ai v7) we do not know about this
// we'll fix this in a follow up, but for the time being vercelAIIntegration is added here
// TODO: Remove this once we auto-inject integrations for native channels as well
vercelAIIntegration(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Default and public integrations diverge

Medium Severity

The default vercelAIIntegration is loaded from @sentry/server-utils/orchestrion (v4–v7, including orchestrion channels), but @sentry/cloudflare still re-exports the v7-only factory from @sentry/server-utils. Passing vercelAIIntegration() from the public API replaces the default by name, so v4–v6 channel instrumentation is dropped even when the Vite plugin injected those channels.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit a3f614e. Configure here.

];
}
Expand Down
51 changes: 1 addition & 50 deletions packages/deno/src/integrations/tracing/vercelai.ts
Original file line number Diff line number Diff line change
@@ -1,50 +1 @@
/**
* This is a copy of the Vercel AI integration from the cloudflare SDK.
*/

import type { IntegrationFn } from '@sentry/core';
import { defineIntegration, extendIntegration } from '@sentry/core';
import {
addVercelAiProcessors,
vercelAIIntegration as serverUtilsVercelAiIntegration,
type VercelAiOptions,
} from '@sentry/server-utils';

const _vercelAIIntegration = ((options: VercelAiOptions = {}) => {
const inner = serverUtilsVercelAiIntegration(options);

return extendIntegration(inner, {
options,
setup(client) {
addVercelAiProcessors(client);
},
});
}) satisfies IntegrationFn;

/**
* Adds Sentry tracing instrumentation for the [ai](https://www.npmjs.com/package/ai) library.
* This integration is not enabled by default, you need to manually add it.
*
* For more information, see the [`ai` documentation](https://sdk.vercel.ai/docs/ai-sdk-core/telemetry).
*
* You need to enable collecting spans for a specific call by setting
* `experimental_telemetry.isEnabled` to `true` in the first argument of the function call.
*
* ```javascript
* const result = await generateText({
* model: openai('gpt-4-turbo'),
* experimental_telemetry: { isEnabled: true },
* });
* ```
*
* If you want to collect inputs and outputs for a specific call, you must specifically opt-in to each
* function call by setting `experimental_telemetry.recordInputs` and `experimental_telemetry.recordOutputs`
* to `true`.
*
* ```javascript
* const result = await generateText({
* model: openai('gpt-4-turbo'),
* experimental_telemetry: { isEnabled: true, recordInputs: true, recordOutputs: true },
* });
*/
export const vercelAIIntegration = defineIntegration(_vercelAIIntegration);
export { vercelAIIntegration } from '@sentry/server-utils/orchestrion';
12 changes: 12 additions & 0 deletions packages/nextjs/src/common/vercelAIIntegrationShim.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { defineIntegration } from '@sentry/core';

/**
* Shim for the edge build so named imports from `@sentry/nextjs` stay resolvable in
* edge-compiled instrumentation modules. The real implementation ships in the server build;
* Vercel AI is not instrumented on the edge runtime.
*/
export const vercelAIIntegration = defineIntegration(() => {
return {
name: 'VercelAI',
};
});
1 change: 1 addition & 0 deletions packages/nextjs/src/edge/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export * from '../common';
export { captureUnderscoreErrorException } from '../common/pages-router-instrumentation/_error';

export { pinoIntegration } from '../common/pinoIntegrationShim';
export { vercelAIIntegration } from '../common/vercelAIIntegrationShim';

// Override core span methods with Next.js-specific implementations that support Cache Components
export { startSpan, startSpanManual, startInactiveSpan } from '../common/utils/nextSpan';
Expand Down
4 changes: 2 additions & 2 deletions packages/nextjs/src/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ import { maybeCleanupQueueSpan } from './vercelQueuesMonitoring';

export * from '@sentry/node';

// Explicitly re-export so it is statically detectable by turbopack
export { pinoIntegration } from '@sentry/node';
// Explicitly re-export so these are statically detectable by turbopack
export { pinoIntegration, vercelAIIntegration } from '@sentry/node';

export { captureUnderscoreErrorException } from '../common/pages-router-instrumentation/_error';

Expand Down
15 changes: 10 additions & 5 deletions packages/nextjs/test/serverExports.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,19 @@ import { beforeAll, describe, expect, it } from 'vitest';
/**
* Node-only exports must be statically resolvable from the server AND edge builds, since Next.js compiles
* instrumentation modules for the edge runtime too. Otherwise named imports from `@sentry/nextjs` fail to compile
* under Turbopack/webpack.
* under Turbopack/webpack. Exports that are Node-only get a no-op shim on the edge build (e.g. `vercelAIIntegration`).
*
*
* Regression test for https://github.com/getsentry/sentry-javascript/issues/21317
*/
describe('`pinoIntegration` is a statically detectable export from every runtime build', () => {
describe('Node-only integrations are statically detectable exports from every runtime build', () => {
const builds = {
server: resolve(__dirname, '../build/cjs/index.server.js'),
edge: resolve(__dirname, '../build/cjs/edge/index.js'),
};

const dualRuntimeExports = ['pinoIntegration', 'vercelAIIntegration'];

const staticExports: Record<string, string[]> = {};

beforeAll(async () => {
Expand All @@ -26,7 +28,10 @@ describe('`pinoIntegration` is a statically detectable export from every runtime
}
});

it.each(Object.keys(builds))('statically exports `pinoIntegration` from the %s build', runtime => {
expect(staticExports[runtime]).toContain('pinoIntegration');
});
it.each(Object.keys(builds).flatMap(runtime => dualRuntimeExports.map(name => ({ runtime, name }))))(
'statically exports `$name` from the $runtime build',
({ runtime, name }) => {
expect(staticExports[runtime]).toContain(name);
},
);
});
1 change: 0 additions & 1 deletion packages/server-utils/src/ai/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,3 @@ export { instrumentGoogleGenAIClient } from './google-genai';
export { instrumentWorkersAiClient } from './workers-ai';
export { createLangChainCallbackHandler, instrumentLangChainEmbeddings } from './langchain';
export { instrumentStateGraph, instrumentStateGraphCompile, instrumentCreateReactAgent } from './langgraph';
export { addVercelAiProcessors } from './vercel-ai';
22 changes: 0 additions & 22 deletions packages/server-utils/src/ai/vercel-ai/constants.ts

This file was deleted.

Loading