Skip to content
Merged
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
@@ -0,0 +1,36 @@
import * as Sentry from '@sentry/node';
import { streamText } from 'ai';
import { convertArrayToReadableStream, MockLanguageModelV1 } from 'ai/test';

async function run() {
await Sentry.startSpan({ op: 'function', name: 'main' }, async () => {
const result = streamText({
experimental_telemetry: { isEnabled: true, recordInputs: true, recordOutputs: true },
model: new MockLanguageModelV1({
doStream: async () => ({
stream: convertArrayToReadableStream([
{ type: 'response-metadata', id: 'resp-1', modelId: 'mock-model-id' },
{ type: 'text-delta', textDelta: 'Stream ' },
{ type: 'text-delta', textDelta: 'response!' },
{
type: 'finish',
finishReason: 'stop',
usage: { promptTokens: 10, completionTokens: 20 },
},
]),
rawCall: { rawPrompt: null, rawSettings: {} },
}),
}),
prompt: 'Stream me a response',
});

// streamText returns synchronously; drive the lazy stream to completion so the spans finish.
for await (const _part of result.textStream) {
void _part;
}
});

await Sentry.flush(2000);
}

run();
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,15 @@ function attr(value: unknown) {
return expect.objectContaining({ value });
}

describe.skipIf(isOrchestrionEnabled())('Vercel AI integration (streaming v4)', () => {
const expectedOrigin = isOrchestrionEnabled() ? 'auto.vercelai.channel' : 'auto.vercelai.otel';

// v4's OTel path serializes tool-call arguments from the provider's raw JSON string (whitespace
// preserved); the channel path serializes the SDK-parsed args object (compact). Same data, different spacing.
const toolCallArgs = isOrchestrionEnabled()
? '{\\"location\\":\\"San Francisco\\"}'
: '{ \\"location\\": \\"San Francisco\\" }';

describe('Vercel AI integration (streaming v4)', () => {
afterAll(() => {
cleanupChildProcesses();
});
Expand All @@ -49,9 +57,7 @@ describe.skipIf(isOrchestrionEnabled())('Vercel AI integration (streaming v4)',
[GEN_AI_USAGE_TOTAL_TOKENS_ATTRIBUTE]: attr(30),
[GEN_AI_OPERATION_NAME_ATTRIBUTE]: attr('invoke_agent'),
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: attr('gen_ai.invoke_agent'),
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: attr('auto.vercelai.otel'),
'vercel.ai.pipeline.name': attr('generateText'),
'vercel.ai.streaming': attr(false),
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: attr(expectedOrigin),
}),
}),
// Second span - generate_content for simple generateText
Expand All @@ -66,9 +72,7 @@ describe.skipIf(isOrchestrionEnabled())('Vercel AI integration (streaming v4)',
[GEN_AI_USAGE_TOTAL_TOKENS_ATTRIBUTE]: attr(30),
[GEN_AI_OPERATION_NAME_ATTRIBUTE]: attr('generate_content'),
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: attr('gen_ai.generate_content'),
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: attr('auto.vercelai.otel'),
'vercel.ai.pipeline.name': attr('generateText.doGenerate'),
'vercel.ai.streaming': attr(false),
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: attr(expectedOrigin),
}),
}),
// Third span - invoke_agent for explicit telemetry generateText
Expand All @@ -82,7 +86,7 @@ describe.skipIf(isOrchestrionEnabled())('Vercel AI integration (streaming v4)',
[GEN_AI_USAGE_TOTAL_TOKENS_ATTRIBUTE]: attr(30),
[GEN_AI_OPERATION_NAME_ATTRIBUTE]: attr('invoke_agent'),
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: attr('gen_ai.invoke_agent'),
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: attr('auto.vercelai.otel'),
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: attr(expectedOrigin),
}),
}),
// Fourth span - tool call invoke_agent
Expand All @@ -96,7 +100,7 @@ describe.skipIf(isOrchestrionEnabled())('Vercel AI integration (streaming v4)',
[GEN_AI_USAGE_TOTAL_TOKENS_ATTRIBUTE]: attr(40),
[GEN_AI_OPERATION_NAME_ATTRIBUTE]: attr('invoke_agent'),
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: attr('gen_ai.invoke_agent'),
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: attr('auto.vercelai.otel'),
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: attr(expectedOrigin),
}),
}),
// Fifth span - tool call generate_content
Expand All @@ -110,7 +114,7 @@ describe.skipIf(isOrchestrionEnabled())('Vercel AI integration (streaming v4)',
[GEN_AI_USAGE_TOTAL_TOKENS_ATTRIBUTE]: attr(40),
[GEN_AI_OPERATION_NAME_ATTRIBUTE]: attr('generate_content'),
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: attr('gen_ai.generate_content'),
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: attr('auto.vercelai.otel'),
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: attr(expectedOrigin),
}),
}),
// Sixth span - execute_tool
Expand All @@ -124,7 +128,7 @@ describe.skipIf(isOrchestrionEnabled())('Vercel AI integration (streaming v4)',
[GEN_AI_TOOL_TYPE_ATTRIBUTE]: attr('function'),
[GEN_AI_OPERATION_NAME_ATTRIBUTE]: attr('execute_tool'),
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: attr('gen_ai.execute_tool'),
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: attr('auto.vercelai.otel'),
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: attr(expectedOrigin),
}),
}),
]),
Expand All @@ -149,9 +153,7 @@ describe.skipIf(isOrchestrionEnabled())('Vercel AI integration (streaming v4)',
[GEN_AI_USAGE_TOTAL_TOKENS_ATTRIBUTE]: attr(30),
[GEN_AI_OPERATION_NAME_ATTRIBUTE]: attr('invoke_agent'),
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: attr('gen_ai.invoke_agent'),
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: attr('auto.vercelai.otel'),
'vercel.ai.pipeline.name': attr('generateText'),
'vercel.ai.streaming': attr(false),
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: attr(expectedOrigin),
}),
}),
// Second span - generate_content with input/output messages
Expand All @@ -170,7 +172,7 @@ describe.skipIf(isOrchestrionEnabled())('Vercel AI integration (streaming v4)',
[GEN_AI_USAGE_TOTAL_TOKENS_ATTRIBUTE]: attr(30),
[GEN_AI_OPERATION_NAME_ATTRIBUTE]: attr('generate_content'),
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: attr('gen_ai.generate_content'),
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: attr('auto.vercelai.otel'),
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: attr(expectedOrigin),
}),
}),
// Third span - explicit telemetry invoke_agent with messages
Expand All @@ -188,7 +190,7 @@ describe.skipIf(isOrchestrionEnabled())('Vercel AI integration (streaming v4)',
[GEN_AI_USAGE_TOTAL_TOKENS_ATTRIBUTE]: attr(30),
[GEN_AI_OPERATION_NAME_ATTRIBUTE]: attr('invoke_agent'),
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: attr('gen_ai.invoke_agent'),
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: attr('auto.vercelai.otel'),
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: attr(expectedOrigin),
}),
}),
// Fourth span - tool call invoke_agent with messages
Expand All @@ -200,15 +202,15 @@ describe.skipIf(isOrchestrionEnabled())('Vercel AI integration (streaming v4)',
'[{"role":"user","content":"What is the weather in San Francisco?"}]',
),
[GEN_AI_OUTPUT_MESSAGES_ATTRIBUTE]: attr(
'[{"role":"assistant","parts":[{"type":"text","content":"Tool call completed!"},{"type":"tool_call","id":"call-1","name":"getWeather","arguments":"{ \\"location\\": \\"San Francisco\\" }"}],"finish_reason":"tool_call"}]',
`[{"role":"assistant","parts":[{"type":"text","content":"Tool call completed!"},{"type":"tool_call","id":"call-1","name":"getWeather","arguments":"${toolCallArgs}"}],"finish_reason":"tool_call"}]`,
),
[GEN_AI_REQUEST_MODEL_ATTRIBUTE]: attr('mock-model-id'),
[GEN_AI_USAGE_INPUT_TOKENS_ATTRIBUTE]: attr(15),
[GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE]: attr(25),
[GEN_AI_USAGE_TOTAL_TOKENS_ATTRIBUTE]: attr(40),
[GEN_AI_OPERATION_NAME_ATTRIBUTE]: attr('invoke_agent'),
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: attr('gen_ai.invoke_agent'),
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: attr('auto.vercelai.otel'),
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: attr(expectedOrigin),
}),
}),
// Fifth span - tool call generate_content with available_tools
Expand All @@ -225,7 +227,7 @@ describe.skipIf(isOrchestrionEnabled())('Vercel AI integration (streaming v4)',
[GEN_AI_USAGE_TOTAL_TOKENS_ATTRIBUTE]: attr(40),
[GEN_AI_OPERATION_NAME_ATTRIBUTE]: attr('generate_content'),
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: attr('gen_ai.generate_content'),
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: attr('auto.vercelai.otel'),
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: attr(expectedOrigin),
}),
}),
// Sixth span - execute_tool with description and input/output
Expand All @@ -241,7 +243,7 @@ describe.skipIf(isOrchestrionEnabled())('Vercel AI integration (streaming v4)',
[GEN_AI_TOOL_TYPE_ATTRIBUTE]: attr('function'),
[GEN_AI_OPERATION_NAME_ATTRIBUTE]: attr('execute_tool'),
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: attr('gen_ai.execute_tool'),
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: attr('auto.vercelai.otel'),
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: attr(expectedOrigin),
}),
}),
]),
Expand All @@ -255,7 +257,7 @@ describe.skipIf(isOrchestrionEnabled())('Vercel AI integration (streaming v4)',
attributes: expect.objectContaining({
[GEN_AI_OPERATION_NAME_ATTRIBUTE]: attr('invoke_agent'),
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: attr('gen_ai.invoke_agent'),
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: attr('auto.vercelai.otel'),
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: attr(expectedOrigin),
}),
}),
expect.objectContaining({
Expand All @@ -268,7 +270,7 @@ describe.skipIf(isOrchestrionEnabled())('Vercel AI integration (streaming v4)',
[GEN_AI_USAGE_TOTAL_TOKENS_ATTRIBUTE]: attr(40),
[GEN_AI_OPERATION_NAME_ATTRIBUTE]: attr('generate_content'),
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: attr('gen_ai.generate_content'),
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: attr('auto.vercelai.otel'),
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: attr(expectedOrigin),
}),
}),
expect.objectContaining({
Expand All @@ -280,7 +282,7 @@ describe.skipIf(isOrchestrionEnabled())('Vercel AI integration (streaming v4)',
[GEN_AI_TOOL_TYPE_ATTRIBUTE]: attr('function'),
[GEN_AI_OPERATION_NAME_ATTRIBUTE]: attr('execute_tool'),
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: attr('gen_ai.execute_tool'),
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: attr('auto.vercelai.otel'),
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: attr(expectedOrigin),
}),
}),
]),
Expand Down

This file was deleted.

Loading
Loading