Skip to content

Commit 29f97f5

Browse files
nicohrubecclaude
andcommitted
feat(core)!: Remove streamGenAiSpans flag
Removes the `streamGenAiSpans` client option. The behavior it gated (extracting gen_ai spans from transactions into a v2 span envelope container) was already the default and is now unconditional — there is no longer a way to opt out. For static transactions, `extractGenAiSpansFromEvent` always pulls gen_ai spans into a v2 span container. For full span streaming (`traceLifecycle: 'stream'`), the existing `hasSpanStreamingEnabled` guard still short-circuits so spans are not extracted twice. Because gen_ai spans now always take the v2 path (not subject to the transaction payload-size limits that truncation works around), `shouldEnableTruncation` simplifies to `!getClient()`. The `enableTruncation` escape hatch is intentionally left in place for a follow-up. Test suites that set `streamGenAiSpans: false` to assert on `transaction.spans` are migrated to assert on the v2 span container; redundant `streamGenAiSpans: true` config lines are removed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent f6f3330 commit 29f97f5

68 files changed

Lines changed: 147 additions & 261 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

dev-packages/cloudflare-integration-tests/suites/tracing/anthropic-ai/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ export default Sentry.withSentry(
2727
(env: Env) => ({
2828
dsn: env.SENTRY_DSN,
2929
tracesSampleRate: 1.0,
30-
streamGenAiSpans: true,
3130
}),
3231
{
3332
async fetch(_request, _env, _ctx) {

dev-packages/cloudflare-integration-tests/suites/tracing/google-genai/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ export default Sentry.withSentry(
4545
(env: Env) => ({
4646
dsn: env.SENTRY_DSN,
4747
tracesSampleRate: 1.0,
48-
streamGenAiSpans: true,
4948
}),
5049
{
5150
async fetch(_request, _env, _ctx) {

dev-packages/cloudflare-integration-tests/suites/tracing/langchain/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ export default Sentry.withSentry(
3030
(env: Env) => ({
3131
dsn: env.SENTRY_DSN,
3232
tracesSampleRate: 1.0,
33-
streamGenAiSpans: true,
3433
}),
3534
{
3635
async fetch(_request, _env, _ctx) {

dev-packages/cloudflare-integration-tests/suites/tracing/langgraph/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ export default Sentry.withSentry(
1010
dsn: env.SENTRY_DSN,
1111
tracesSampleRate: 1.0,
1212
dataCollection: { genAI: { inputs: true, outputs: true } },
13-
streamGenAiSpans: true,
1413
}),
1514
{
1615
async fetch(_request, _env, _ctx) {

dev-packages/cloudflare-integration-tests/suites/tracing/openai/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ export default Sentry.withSentry(
3232
(env: Env) => ({
3333
dsn: env.SENTRY_DSN,
3434
tracesSampleRate: 1.0,
35-
streamGenAiSpans: true,
3635
}),
3736
{
3837
async fetch(_request, _env, _ctx) {

dev-packages/cloudflare-integration-tests/suites/tracing/workers-ai/index.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,6 @@ export default Sentry.withSentry(
1212
(env: Env) => ({
1313
dsn: env.SENTRY_DSN,
1414
tracesSampleRate: 1.0,
15-
// Keep gen_ai spans embedded in the transaction (instead of streamed as a
16-
// separate envelope container) so they can be asserted on `transaction.spans`.
17-
streamGenAiSpans: false,
1815
}),
1916
{
2017
async fetch(request) {

dev-packages/cloudflare-integration-tests/suites/tracing/workers-ai/test.ts

Lines changed: 42 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -22,40 +22,29 @@ it('traces a basic Workers AI text generation request', async ({ signal }) => {
2222
.ignore('event')
2323
.expect(envelope => {
2424
const transactionEvent = envelope[1]?.[0]?.[1] as any;
25+
expect(transactionEvent.transaction).toBe('GET /');
2526

26-
// The transaction event is framework-generated and carries non-deterministic fields
27-
// (random ports, ids, timestamps, sdk version), so we assert the stable subset.
28-
expect(transactionEvent).toEqual(
27+
const container = envelope[1]?.[1]?.[1] as any;
28+
expect(container).toBeDefined();
29+
expect(container.items).toHaveLength(1);
30+
31+
expect(container.items[0]).toEqual(
2932
expect.objectContaining({
30-
type: 'transaction',
31-
transaction: 'GET /',
32-
transaction_info: { source: 'route' },
33-
contexts: expect.objectContaining({
34-
trace: expect.objectContaining({
35-
op: 'http.server',
36-
origin: 'auto.http.cloudflare',
37-
status: 'ok',
38-
}),
39-
}),
40-
spans: [
41-
expect.objectContaining({
42-
description: 'chat @cf/meta/llama-3.1-8b-instruct',
43-
op: 'gen_ai.chat',
44-
origin: 'auto.ai.cloudflare.workers_ai',
45-
data: {
46-
'sentry.origin': 'auto.ai.cloudflare.workers_ai',
47-
'sentry.op': 'gen_ai.chat',
48-
[GEN_AI_PROVIDER_NAME]: 'cloudflare.workers_ai',
49-
[GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'chat',
50-
[GEN_AI_REQUEST_MODEL_ATTRIBUTE]: '@cf/meta/llama-3.1-8b-instruct',
51-
[GEN_AI_REQUEST_TEMPERATURE_ATTRIBUTE]: 0.7,
52-
[GEN_AI_REQUEST_MAX_TOKENS_ATTRIBUTE]: 100,
53-
[GEN_AI_USAGE_INPUT_TOKENS_ATTRIBUTE]: 12,
54-
[GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE]: 7,
55-
[GEN_AI_USAGE_TOTAL_TOKENS_ATTRIBUTE]: 19,
56-
},
57-
}),
58-
],
33+
name: 'chat @cf/meta/llama-3.1-8b-instruct',
34+
status: 'ok',
35+
is_segment: false,
36+
attributes: {
37+
'sentry.origin': { value: 'auto.ai.cloudflare.workers_ai', type: 'string' },
38+
'sentry.op': { value: 'gen_ai.chat', type: 'string' },
39+
[GEN_AI_PROVIDER_NAME]: { value: 'cloudflare.workers_ai', type: 'string' },
40+
[GEN_AI_OPERATION_NAME_ATTRIBUTE]: { value: 'chat', type: 'string' },
41+
[GEN_AI_REQUEST_MODEL_ATTRIBUTE]: { value: '@cf/meta/llama-3.1-8b-instruct', type: 'string' },
42+
[GEN_AI_REQUEST_TEMPERATURE_ATTRIBUTE]: { value: 0.7, type: 'double' },
43+
[GEN_AI_REQUEST_MAX_TOKENS_ATTRIBUTE]: { value: 100, type: 'integer' },
44+
[GEN_AI_USAGE_INPUT_TOKENS_ATTRIBUTE]: { value: 12, type: 'integer' },
45+
[GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE]: { value: 7, type: 'integer' },
46+
[GEN_AI_USAGE_TOTAL_TOKENS_ATTRIBUTE]: { value: 19, type: 'integer' },
47+
},
5948
}),
6049
);
6150
})
@@ -69,38 +58,29 @@ it('traces a streaming Workers AI text generation request', async ({ signal }) =
6958
.ignore('event')
7059
.expect(envelope => {
7160
const transactionEvent = envelope[1]?.[0]?.[1] as any;
61+
expect(transactionEvent.transaction).toBe('GET /stream');
7262

73-
expect(transactionEvent).toEqual(
63+
const container = envelope[1]?.[1]?.[1] as any;
64+
expect(container).toBeDefined();
65+
expect(container.items).toHaveLength(1);
66+
67+
expect(container.items[0]).toEqual(
7468
expect.objectContaining({
75-
type: 'transaction',
76-
transaction: 'GET /stream',
77-
transaction_info: { source: 'url' },
78-
contexts: expect.objectContaining({
79-
trace: expect.objectContaining({
80-
op: 'http.server',
81-
origin: 'auto.http.cloudflare',
82-
status: 'ok',
83-
}),
84-
}),
85-
spans: [
86-
expect.objectContaining({
87-
description: 'chat @cf/meta/llama-3.1-8b-instruct',
88-
op: 'gen_ai.chat',
89-
origin: 'auto.ai.cloudflare.workers_ai',
90-
data: {
91-
'sentry.origin': 'auto.ai.cloudflare.workers_ai',
92-
'sentry.op': 'gen_ai.chat',
93-
[GEN_AI_PROVIDER_NAME]: 'cloudflare.workers_ai',
94-
[GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'chat',
95-
[GEN_AI_REQUEST_MODEL_ATTRIBUTE]: '@cf/meta/llama-3.1-8b-instruct',
96-
[GEN_AI_REQUEST_STREAM_ATTRIBUTE]: true,
97-
[GEN_AI_RESPONSE_STREAMING_ATTRIBUTE]: true,
98-
[GEN_AI_USAGE_INPUT_TOKENS_ATTRIBUTE]: 12,
99-
[GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE]: 7,
100-
[GEN_AI_USAGE_TOTAL_TOKENS_ATTRIBUTE]: 19,
101-
},
102-
}),
103-
],
69+
name: 'chat @cf/meta/llama-3.1-8b-instruct',
70+
status: 'ok',
71+
is_segment: false,
72+
attributes: {
73+
'sentry.origin': { value: 'auto.ai.cloudflare.workers_ai', type: 'string' },
74+
'sentry.op': { value: 'gen_ai.chat', type: 'string' },
75+
[GEN_AI_PROVIDER_NAME]: { value: 'cloudflare.workers_ai', type: 'string' },
76+
[GEN_AI_OPERATION_NAME_ATTRIBUTE]: { value: 'chat', type: 'string' },
77+
[GEN_AI_REQUEST_MODEL_ATTRIBUTE]: { value: '@cf/meta/llama-3.1-8b-instruct', type: 'string' },
78+
[GEN_AI_REQUEST_STREAM_ATTRIBUTE]: { value: true, type: 'boolean' },
79+
[GEN_AI_RESPONSE_STREAMING_ATTRIBUTE]: { value: true, type: 'boolean' },
80+
[GEN_AI_USAGE_INPUT_TOKENS_ATTRIBUTE]: { value: 12, type: 'integer' },
81+
[GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE]: { value: 7, type: 'integer' },
82+
[GEN_AI_USAGE_TOTAL_TOKENS_ATTRIBUTE]: { value: 19, type: 'integer' },
83+
},
10484
}),
10585
);
10686
})

dev-packages/e2e-tests/test-applications/cloudflare-vercelai-v7-als/src/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ export default Sentry.withSentry(
77
dsn: env.E2E_TEST_DSN,
88
environment: 'qa',
99
tunnel: 'http://localhost:3031/',
10-
streamGenAiSpans: false,
1110
tracesSampleRate: 1.0,
1211
integrations: [Sentry.vercelAIIntegration()],
1312
}),
Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,33 @@
11
import { expect, test } from '@playwright/test';
2-
import { waitForTransaction } from '@sentry-internal/test-utils';
2+
import { getSpanOp, waitForRequest } from '@sentry-internal/test-utils';
33

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

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

12-
const transaction = await transactionPromise;
15+
const { envelope } = await envelopePromise;
1316

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

17-
// v7 uses diagnostics_channel which is not available with nodejs_als,
18-
// so no AI spans should be present.
19-
const aiSpans = (transaction.spans || []).filter(
21+
// v7 uses diagnostics_channel which is not available with nodejs_als, so no AI spans should be
22+
// present — neither embedded in the transaction nor streamed as a span v2 container item.
23+
const embeddedAiSpans = (transaction.spans || []).filter(
2024
(span: any) => span.op?.startsWith('gen_ai.') || span.description?.includes('generateText'),
2125
);
22-
expect(aiSpans).toHaveLength(0);
26+
expect(embeddedAiSpans).toHaveLength(0);
27+
28+
const streamedGenAiSpans = envelope[1]
29+
.filter(([header]) => header.type === 'span')
30+
.flatMap(([, payload]) => (payload as any).items ?? [])
31+
.filter((span: any) => getSpanOp(span)?.startsWith('gen_ai.'));
32+
expect(streamedGenAiSpans).toHaveLength(0);
2333
});

dev-packages/e2e-tests/test-applications/cloudflare-vercelai-v7-compat/src/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ export default Sentry.withSentry(
88
environment: 'qa',
99
tunnel: 'http://localhost:3031/',
1010
tracesSampleRate: 1.0,
11-
streamGenAiSpans: false,
1211
integrations: [Sentry.vercelAIIntegration()],
1312
}),
1413
{

0 commit comments

Comments
 (0)