Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ export default Sentry.withSentry(
(env: Env) => ({
dsn: env.SENTRY_DSN,
tracesSampleRate: 1.0,
streamGenAiSpans: true,
}),
{
async fetch(_request, _env, _ctx) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ export default Sentry.withSentry(
(env: Env) => ({
dsn: env.SENTRY_DSN,
tracesSampleRate: 1.0,
streamGenAiSpans: true,
}),
{
async fetch(_request, _env, _ctx) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ export default Sentry.withSentry(
(env: Env) => ({
dsn: env.SENTRY_DSN,
tracesSampleRate: 1.0,
streamGenAiSpans: true,
}),
{
async fetch(_request, _env, _ctx) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ export default Sentry.withSentry(
dsn: env.SENTRY_DSN,
tracesSampleRate: 1.0,
dataCollection: { genAI: { inputs: true, outputs: true } },
streamGenAiSpans: true,
}),
{
async fetch(_request, _env, _ctx) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ export default Sentry.withSentry(
(env: Env) => ({
dsn: env.SENTRY_DSN,
tracesSampleRate: 1.0,
streamGenAiSpans: true,
}),
{
async fetch(_request, _env, _ctx) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@ export default Sentry.withSentry(
(env: Env) => ({
dsn: env.SENTRY_DSN,
tracesSampleRate: 1.0,
// Keep gen_ai spans embedded in the transaction (instead of streamed as a
// separate envelope container) so they can be asserted on `transaction.spans`.
streamGenAiSpans: false,
}),
{
async fetch(request) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,40 +22,29 @@ it('traces a basic Workers AI text generation request', async ({ signal }) => {
.ignore('event')
.expect(envelope => {
const transactionEvent = envelope[1]?.[0]?.[1] as any;
expect(transactionEvent.transaction).toBe('GET /');

// The transaction event is framework-generated and carries non-deterministic fields
// (random ports, ids, timestamps, sdk version), so we assert the stable subset.
expect(transactionEvent).toEqual(
const container = envelope[1]?.[1]?.[1] as any;
expect(container).toBeDefined();
expect(container.items).toHaveLength(1);

expect(container.items[0]).toEqual(
expect.objectContaining({
type: 'transaction',
transaction: 'GET /',
transaction_info: { source: 'route' },
contexts: expect.objectContaining({
trace: expect.objectContaining({
op: 'http.server',
origin: 'auto.http.cloudflare',
status: 'ok',
}),
}),
spans: [
expect.objectContaining({
description: 'chat @cf/meta/llama-3.1-8b-instruct',
op: 'gen_ai.chat',
origin: 'auto.ai.cloudflare.workers_ai',
data: {
'sentry.origin': 'auto.ai.cloudflare.workers_ai',
'sentry.op': 'gen_ai.chat',
[GEN_AI_PROVIDER_NAME]: 'cloudflare.workers_ai',
[GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'chat',
[GEN_AI_REQUEST_MODEL_ATTRIBUTE]: '@cf/meta/llama-3.1-8b-instruct',
[GEN_AI_REQUEST_TEMPERATURE_ATTRIBUTE]: 0.7,
[GEN_AI_REQUEST_MAX_TOKENS_ATTRIBUTE]: 100,
[GEN_AI_USAGE_INPUT_TOKENS_ATTRIBUTE]: 12,
[GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE]: 7,
[GEN_AI_USAGE_TOTAL_TOKENS_ATTRIBUTE]: 19,
},
}),
],
name: 'chat @cf/meta/llama-3.1-8b-instruct',
status: 'ok',
is_segment: false,
attributes: {
'sentry.origin': { value: 'auto.ai.cloudflare.workers_ai', type: 'string' },
'sentry.op': { value: 'gen_ai.chat', type: 'string' },
[GEN_AI_PROVIDER_NAME]: { value: 'cloudflare.workers_ai', type: 'string' },
[GEN_AI_OPERATION_NAME_ATTRIBUTE]: { value: 'chat', type: 'string' },
[GEN_AI_REQUEST_MODEL_ATTRIBUTE]: { value: '@cf/meta/llama-3.1-8b-instruct', type: 'string' },
[GEN_AI_REQUEST_TEMPERATURE_ATTRIBUTE]: { value: 0.7, type: 'double' },
[GEN_AI_REQUEST_MAX_TOKENS_ATTRIBUTE]: { value: 100, type: 'integer' },
[GEN_AI_USAGE_INPUT_TOKENS_ATTRIBUTE]: { value: 12, type: 'integer' },
[GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE]: { value: 7, type: 'integer' },
[GEN_AI_USAGE_TOTAL_TOKENS_ATTRIBUTE]: { value: 19, type: 'integer' },
},
}),
);
})
Expand All @@ -69,38 +58,29 @@ it('traces a streaming Workers AI text generation request', async ({ signal }) =
.ignore('event')
.expect(envelope => {
const transactionEvent = envelope[1]?.[0]?.[1] as any;
expect(transactionEvent.transaction).toBe('GET /stream');

expect(transactionEvent).toEqual(
const container = envelope[1]?.[1]?.[1] as any;
expect(container).toBeDefined();
expect(container.items).toHaveLength(1);

expect(container.items[0]).toEqual(
expect.objectContaining({
type: 'transaction',
transaction: 'GET /stream',
transaction_info: { source: 'url' },
contexts: expect.objectContaining({
trace: expect.objectContaining({
op: 'http.server',
origin: 'auto.http.cloudflare',
status: 'ok',
}),
}),
spans: [
expect.objectContaining({
description: 'chat @cf/meta/llama-3.1-8b-instruct',
op: 'gen_ai.chat',
origin: 'auto.ai.cloudflare.workers_ai',
data: {
'sentry.origin': 'auto.ai.cloudflare.workers_ai',
'sentry.op': 'gen_ai.chat',
[GEN_AI_PROVIDER_NAME]: 'cloudflare.workers_ai',
[GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'chat',
[GEN_AI_REQUEST_MODEL_ATTRIBUTE]: '@cf/meta/llama-3.1-8b-instruct',
[GEN_AI_REQUEST_STREAM_ATTRIBUTE]: true,
[GEN_AI_RESPONSE_STREAMING_ATTRIBUTE]: true,
[GEN_AI_USAGE_INPUT_TOKENS_ATTRIBUTE]: 12,
[GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE]: 7,
[GEN_AI_USAGE_TOTAL_TOKENS_ATTRIBUTE]: 19,
},
}),
],
name: 'chat @cf/meta/llama-3.1-8b-instruct',
status: 'ok',
is_segment: false,
attributes: {
'sentry.origin': { value: 'auto.ai.cloudflare.workers_ai', type: 'string' },
'sentry.op': { value: 'gen_ai.chat', type: 'string' },
[GEN_AI_PROVIDER_NAME]: { value: 'cloudflare.workers_ai', type: 'string' },
[GEN_AI_OPERATION_NAME_ATTRIBUTE]: { value: 'chat', type: 'string' },
[GEN_AI_REQUEST_MODEL_ATTRIBUTE]: { value: '@cf/meta/llama-3.1-8b-instruct', type: 'string' },
[GEN_AI_REQUEST_STREAM_ATTRIBUTE]: { value: true, type: 'boolean' },
[GEN_AI_RESPONSE_STREAMING_ATTRIBUTE]: { value: true, type: 'boolean' },
[GEN_AI_USAGE_INPUT_TOKENS_ATTRIBUTE]: { value: 12, type: 'integer' },
[GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE]: { value: 7, type: 'integer' },
[GEN_AI_USAGE_TOTAL_TOKENS_ATTRIBUTE]: { value: 19, type: 'integer' },
},
}),
);
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ export default Sentry.withSentry(
dsn: env.E2E_TEST_DSN,
environment: 'qa',
tunnel: 'http://localhost:3031/',
streamGenAiSpans: false,
Comment thread
cursor[bot] marked this conversation as resolved.
tracesSampleRate: 1.0,
integrations: [Sentry.vercelAIIntegration()],
}),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,33 @@
import { expect, test } from '@playwright/test';
import { waitForTransaction } from '@sentry-internal/test-utils';
import { getSpanOp, waitForRequest } from '@sentry-internal/test-utils';

test('does not capture Vercel AI v7 spans without nodejs_compat', async ({ baseURL }) => {
const transactionPromise = waitForTransaction('cloudflare-vercelai-v7-als', txn => {
return txn.transaction === 'GET /generate';
// The transaction envelope also carries any extracted gen_ai spans as a span v2 container item,
// so we wait for the whole envelope and assert neither place contains AI spans.
const envelopePromise = waitForRequest('cloudflare-vercelai-v7-als', ({ envelope }) => {
const transactionItem = envelope[1].find(([header]) => header.type === 'transaction');
return (transactionItem?.[1] as any)?.transaction === 'GET /generate';
});

const response = await fetch(`${baseURL}/generate`);
expect(response.status).toBe(200);

const transaction = await transactionPromise;
const { envelope } = await envelopePromise;

const transaction = envelope[1].find(([header]) => header.type === 'transaction')?.[1] as any;
expect(transaction.transaction).toBe('GET /generate');
expect(transaction.contexts?.trace?.op).toBe('http.server');

// v7 uses diagnostics_channel which is not available with nodejs_als,
// so no AI spans should be present.
const aiSpans = (transaction.spans || []).filter(
// v7 uses diagnostics_channel which is not available with nodejs_als, so no AI spans should be
// present — neither embedded in the transaction nor streamed as a span v2 container item.
const embeddedAiSpans = (transaction.spans || []).filter(
(span: any) => span.op?.startsWith('gen_ai.') || span.description?.includes('generateText'),
);
expect(aiSpans).toHaveLength(0);
expect(embeddedAiSpans).toHaveLength(0);

const streamedGenAiSpans = envelope[1]
.filter(([header]) => header.type === 'span')
.flatMap(([, payload]) => (payload as any).items ?? [])
.filter((span: any) => getSpanOp(span)?.startsWith('gen_ai.'));
expect(streamedGenAiSpans).toHaveLength(0);
});
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ export default Sentry.withSentry(
environment: 'qa',
tunnel: 'http://localhost:3031/',
tracesSampleRate: 1.0,
streamGenAiSpans: false,
integrations: [Sentry.vercelAIIntegration()],
}),
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,57 +1,40 @@
import { expect, test } from '@playwright/test';
import { waitForTransaction } from '@sentry-internal/test-utils';
import { getSpanOp, waitForStreamedSpans } from '@sentry-internal/test-utils';

test('captures Vercel AI v7 spans with nodejs_compat using tracing channels', async ({ baseURL }) => {
const transactionPromise = waitForTransaction('cloudflare-vercelai-v7-compat', txn => {
return txn.transaction === 'GET /generate';
});
// gen_ai spans are extracted into a separate span v2 envelope item
const genAiSpansPromise = waitForStreamedSpans('cloudflare-vercelai-v7-compat', spans =>
spans.some(span => getSpanOp(span) === 'gen_ai.invoke_agent'),
);

const response = await fetch(`${baseURL}/generate`);

expect(response.status).toBe(200);

const transaction = await transactionPromise;

expect(transaction.transaction).toBe('GET /generate');
expect(transaction.contexts?.trace?.op).toBe('http.server');
expect(transaction.spans).toHaveLength(2);
const genAiSpans = await genAiSpansPromise;

expect(transaction.spans).toEqual(
expect(genAiSpans).toEqual(
expect.arrayContaining([
expect.objectContaining({
description: 'invoke_agent',
op: 'gen_ai.invoke_agent',
origin: 'auto.vercelai.channel',
parent_span_id: expect.any(String),
span_id: expect.any(String),
start_timestamp: expect.any(Number),
timestamp: expect.any(Number),
trace_id: expect.any(String),
data: expect.objectContaining({
'gen_ai.operation.name': 'invoke_agent',
'gen_ai.usage.input_tokens': 10,
'gen_ai.usage.output_tokens': 20,
'gen_ai.usage.total_tokens': 30,
'sentry.op': 'gen_ai.invoke_agent',
'sentry.origin': 'auto.vercelai.channel',
name: 'invoke_agent',
attributes: expect.objectContaining({
'gen_ai.operation.name': { value: 'invoke_agent', type: 'string' },
'gen_ai.usage.input_tokens': { value: 10, type: 'integer' },
'gen_ai.usage.output_tokens': { value: 20, type: 'integer' },
'gen_ai.usage.total_tokens': { value: 30, type: 'integer' },
'sentry.op': { value: 'gen_ai.invoke_agent', type: 'string' },
'sentry.origin': { value: 'auto.vercelai.channel', type: 'string' },
}),
}),
expect.objectContaining({
description: 'generate_content mock-model-id',
op: 'gen_ai.generate_content',
origin: 'auto.vercelai.channel',
parent_span_id: expect.any(String),
span_id: expect.any(String),
start_timestamp: expect.any(Number),
timestamp: expect.any(Number),
trace_id: expect.any(String),
data: expect.objectContaining({
'gen_ai.operation.name': 'generate_content',
'gen_ai.usage.input_tokens': 10,
'gen_ai.usage.output_tokens': 20,
'gen_ai.usage.total_tokens': 30,
'sentry.op': 'gen_ai.generate_content',
'sentry.origin': 'auto.vercelai.channel',
name: 'generate_content mock-model-id',
attributes: expect.objectContaining({
'gen_ai.operation.name': { value: 'generate_content', type: 'string' },
'gen_ai.usage.input_tokens': { value: 10, type: 'integer' },
'gen_ai.usage.output_tokens': { value: 20, type: 'integer' },
'gen_ai.usage.total_tokens': { value: 30, type: 'integer' },
'sentry.op': { value: 'gen_ai.generate_content', type: 'string' },
'sentry.origin': { value: 'auto.vercelai.channel', type: 'string' },
}),
}),
]),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,4 @@ Sentry.init({
bufferSize: 1000,
},
integrations: [Sentry.vercelAIIntegration()],
streamGenAiSpans: true,
});
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ Sentry.init({
dataCollection: { userInfo: true },
// debug: true,
integrations: [Sentry.vercelAIIntegration(), Sentry.nodeRuntimeMetricsIntegration({ collectionIntervalMs: 1_000 })],
streamGenAiSpans: true,
// Verify Log type is available
beforeSendLog(log: Log) {
return log;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,4 @@ Sentry.init({
}
return event;
},
streamGenAiSpans: true,
});
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,4 @@ Sentry.init({
enableTruncation: true,
}),
],
streamGenAiSpans: true,
});
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,4 @@ Sentry.init({
dataCollection: { genAI: { inputs: true, outputs: true } },
transport: loggingTransport,
traceLifecycle: 'stream',
streamGenAiSpans: true,
});
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,4 @@ Sentry.init({
}
return event;
},
streamGenAiSpans: true,
});
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,4 @@ Sentry.init({
}
return event;
},
streamGenAiSpans: true,
});
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,4 @@ Sentry.init({
}
return event;
},
streamGenAiSpans: true,
});
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,4 @@ Sentry.init({
}
return event;
},
streamGenAiSpans: true,
});
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,4 @@ Sentry.init({
}
return event;
},
streamGenAiSpans: true,
});
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,4 @@ Sentry.init({
enableTruncation: true,
}),
],
streamGenAiSpans: true,
});
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,4 @@ Sentry.init({
dataCollection: { genAI: { inputs: true, outputs: true } },
transport: loggingTransport,
traceLifecycle: 'stream',
streamGenAiSpans: true,
});
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,4 @@ Sentry.init({
}
return event;
},
streamGenAiSpans: true,
});
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,4 @@ Sentry.init({
}
return event;
},
streamGenAiSpans: true,
});
Loading
Loading