diff --git a/e2e/scenarios/ai-sdk-instrumentation/assertions.ts b/e2e/scenarios/ai-sdk-instrumentation/assertions.ts index 8ddd56cd5..18d727115 100644 --- a/e2e/scenarios/ai-sdk-instrumentation/assertions.ts +++ b/e2e/scenarios/ai-sdk-instrumentation/assertions.ts @@ -638,6 +638,7 @@ function expectOperationParentedByRoot( function expectAISDKParentSpan(span: CapturedLogEvent | undefined) { expect(span).toBeDefined(); + expect(span?.span.type).toBe("function"); expect(span?.row.metadata).toMatchObject({ braintrust: { integration_name: "ai-sdk", @@ -655,6 +656,12 @@ function expectAISDKParentSpan(span: CapturedLogEvent | undefined) { ).toBe("string"); } +function expectAISDKModelChildSpan(span: CapturedLogEvent | undefined) { + expect(span).toBeDefined(); + expect(span?.span.type).toBe("llm"); + expect(["doGenerate", "doStream"]).toContain(span?.span.name); +} + function expectEmbeddingTokenMetrics(span: CapturedLogEvent | undefined) { const metrics = span?.metrics as Record | undefined; const totalTokens = metrics?.tokens; @@ -730,6 +737,7 @@ export function defineAISDKInstrumentationAssertions(options: { expectOperationParentedByRoot(trace.operation, root); expectAISDKParentSpan(trace.parent); expect(trace.child).toBeDefined(); + expectAISDKModelChildSpan(trace.child); expect(trace.child?.metrics).toMatchObject({ completion_tokens: expect.any(Number), prompt_tokens: expect.any(Number), @@ -755,6 +763,7 @@ export function defineAISDKInstrumentationAssertions(options: { expectOperationParentedByRoot(trace.operation, root); expectAISDKParentSpan(trace.parent); + expectAISDKModelChildSpan(trace.child); expect(trace.parent?.metrics?.time_to_first_token).toEqual( expect.any(Number), ); @@ -895,6 +904,7 @@ export function defineAISDKInstrumentationAssertions(options: { if (options.supportsToolExecution) { expect(trace.modelChildren.length).toBeGreaterThanOrEqual(2); + trace.modelChildren.forEach(expectAISDKModelChildSpan); expect(trace.toolSpans.length).toBeGreaterThanOrEqual(1); expect(trace.toolSpans[0]?.input).toBeDefined(); expect(trace.toolSpans[0]?.output).toBeDefined(); @@ -903,6 +913,7 @@ export function defineAISDKInstrumentationAssertions(options: { ); } else { expect(trace.modelChildren.length).toBeGreaterThanOrEqual(1); + trace.modelChildren.forEach(expectAISDKModelChildSpan); expect(collectToolCallNames(trace.parent?.output)).toContain( "get_weather", ); @@ -929,6 +940,7 @@ export function defineAISDKInstrumentationAssertions(options: { object: { city: "Paris" }, }); if (trace.child) { + expectAISDKModelChildSpan(trace.child); expect(trace.child.output).toBeDefined(); } }); @@ -966,6 +978,7 @@ export function defineAISDKInstrumentationAssertions(options: { expect(trace.parent?.output).toBeDefined(); } if (trace.child) { + expectAISDKModelChildSpan(trace.child); expect(trace.child.output).toBeDefined(); } }); @@ -1008,6 +1021,7 @@ export function defineAISDKInstrumentationAssertions(options: { expect(hasPromptLikeInput(trace.parent?.input)).toBe(true); expect(trace.parent?.output).toBeDefined(); expect(trace.modelChildren.length).toBeGreaterThanOrEqual(1); + trace.modelChildren.forEach(expectAISDKModelChildSpan); expect(trace.latestChild?.output).toBeDefined(); }); @@ -1023,6 +1037,7 @@ export function defineAISDKInstrumentationAssertions(options: { expect.any(Number), ); expect(trace.modelChildren.length).toBeGreaterThanOrEqual(1); + trace.modelChildren.forEach(expectAISDKModelChildSpan); expect(trace.latestChild?.output).toBeDefined(); }); diff --git a/js/src/instrumentation/plugins/ai-sdk-plugin.ts b/js/src/instrumentation/plugins/ai-sdk-plugin.ts index 8d9022983..c68094856 100644 --- a/js/src/instrumentation/plugins/ai-sdk-plugin.ts +++ b/js/src/instrumentation/plugins/ai-sdk-plugin.ts @@ -111,7 +111,7 @@ export class AISDKPlugin extends BasePlugin { this.unsubscribers.push( traceStreamingChannel(aiSDKChannels.generateText, { name: "generateText", - type: SpanTypeAttribute.LLM, + type: SpanTypeAttribute.FUNCTION, extractInput: ([params], event, span) => prepareAISDKCallInput(params, event, span, denyOutputPaths), extractOutput: (result, endEvent) => { @@ -131,7 +131,7 @@ export class AISDKPlugin extends BasePlugin { this.unsubscribers.push( traceStreamingChannel(aiSDKChannels.streamText, { name: "streamText", - type: SpanTypeAttribute.LLM, + type: SpanTypeAttribute.FUNCTION, extractInput: ([params], event, span) => prepareAISDKCallInput(params, event, span, denyOutputPaths), extractOutput: (result, endEvent) => @@ -157,7 +157,7 @@ export class AISDKPlugin extends BasePlugin { this.unsubscribers.push( traceSyncStreamChannel(aiSDKChannels.streamTextSync, { name: "streamText", - type: SpanTypeAttribute.LLM, + type: SpanTypeAttribute.FUNCTION, extractInput: ([params], event, span) => prepareAISDKCallInput(params, event, span, denyOutputPaths), patchResult: ({ endEvent, result, span, startTime }) => @@ -175,7 +175,7 @@ export class AISDKPlugin extends BasePlugin { this.unsubscribers.push( traceStreamingChannel(aiSDKChannels.generateObject, { name: "generateObject", - type: SpanTypeAttribute.LLM, + type: SpanTypeAttribute.FUNCTION, extractInput: ([params], event, span) => prepareAISDKCallInput(params, event, span, denyOutputPaths), extractOutput: (result, endEvent) => { @@ -195,7 +195,7 @@ export class AISDKPlugin extends BasePlugin { this.unsubscribers.push( traceStreamingChannel(aiSDKChannels.streamObject, { name: "streamObject", - type: SpanTypeAttribute.LLM, + type: SpanTypeAttribute.FUNCTION, extractInput: ([params], event, span) => prepareAISDKCallInput(params, event, span, denyOutputPaths), extractOutput: (result, endEvent) => @@ -221,7 +221,7 @@ export class AISDKPlugin extends BasePlugin { this.unsubscribers.push( traceSyncStreamChannel(aiSDKChannels.streamObjectSync, { name: "streamObject", - type: SpanTypeAttribute.LLM, + type: SpanTypeAttribute.FUNCTION, extractInput: ([params], event, span) => prepareAISDKCallInput(params, event, span, denyOutputPaths), patchResult: ({ endEvent, result, span, startTime }) => @@ -239,7 +239,7 @@ export class AISDKPlugin extends BasePlugin { this.unsubscribers.push( traceAsyncChannel(aiSDKChannels.embed, { name: "embed", - type: SpanTypeAttribute.LLM, + type: SpanTypeAttribute.FUNCTION, extractInput: ([params], event) => prepareAISDKEmbedInput(params, event.self), extractOutput: (result, endEvent) => @@ -256,7 +256,7 @@ export class AISDKPlugin extends BasePlugin { this.unsubscribers.push( traceAsyncChannel(aiSDKChannels.embedMany, { name: "embedMany", - type: SpanTypeAttribute.LLM, + type: SpanTypeAttribute.FUNCTION, extractInput: ([params], event) => prepareAISDKEmbedInput(params, event.self), extractOutput: (result, endEvent) => @@ -273,7 +273,7 @@ export class AISDKPlugin extends BasePlugin { this.unsubscribers.push( traceStreamingChannel(aiSDKChannels.agentGenerate, { name: "Agent.generate", - type: SpanTypeAttribute.LLM, + type: SpanTypeAttribute.FUNCTION, extractInput: ([params], event, span) => prepareAISDKCallInput(params, event, span, denyOutputPaths), extractOutput: (result, endEvent) => { @@ -293,7 +293,7 @@ export class AISDKPlugin extends BasePlugin { this.unsubscribers.push( traceStreamingChannel(aiSDKChannels.agentStream, { name: "Agent.stream", - type: SpanTypeAttribute.LLM, + type: SpanTypeAttribute.FUNCTION, extractInput: ([params], event, span) => prepareAISDKCallInput(params, event, span, denyOutputPaths), extractOutput: (result, endEvent) => @@ -319,7 +319,7 @@ export class AISDKPlugin extends BasePlugin { this.unsubscribers.push( traceSyncStreamChannel(aiSDKChannels.agentStreamSync, { name: "Agent.stream", - type: SpanTypeAttribute.LLM, + type: SpanTypeAttribute.FUNCTION, extractInput: ([params], event, span) => prepareAISDKCallInput(params, event, span, denyOutputPaths), patchResult: ({ endEvent, result, span, startTime }) => @@ -337,7 +337,7 @@ export class AISDKPlugin extends BasePlugin { this.unsubscribers.push( traceStreamingChannel(aiSDKChannels.toolLoopAgentGenerate, { name: "ToolLoopAgent.generate", - type: SpanTypeAttribute.LLM, + type: SpanTypeAttribute.FUNCTION, extractInput: ([params], event, span) => prepareAISDKCallInput(params, event, span, denyOutputPaths), extractOutput: (result, endEvent) => { @@ -357,7 +357,7 @@ export class AISDKPlugin extends BasePlugin { this.unsubscribers.push( traceStreamingChannel(aiSDKChannels.toolLoopAgentStream, { name: "ToolLoopAgent.stream", - type: SpanTypeAttribute.LLM, + type: SpanTypeAttribute.FUNCTION, extractInput: ([params], event, span) => prepareAISDKCallInput(params, event, span, denyOutputPaths), extractOutput: (result, endEvent) => diff --git a/js/src/wrappers/ai-sdk/ai-sdk.test.ts b/js/src/wrappers/ai-sdk/ai-sdk.test.ts index 90be978b5..c6cc49fd5 100644 --- a/js/src/wrappers/ai-sdk/ai-sdk.test.ts +++ b/js/src/wrappers/ai-sdk/ai-sdk.test.ts @@ -106,7 +106,7 @@ describe("ai sdk client unit tests", TEST_SUITE_OPTIONS, () => { span_id: expect.any(String), root_span_id: expect.any(String), span_attributes: { - type: "llm", + type: "function", name: "generateText", }, metadata: expect.objectContaining({ @@ -244,7 +244,7 @@ describe("ai sdk client unit tests", TEST_SUITE_OPTIONS, () => { span_id: expect.any(String), root_span_id: expect.any(String), span_attributes: { - type: "llm", + type: "function", name: "generateText", }, metadata: expect.objectContaining({ @@ -360,7 +360,7 @@ describe("ai sdk client unit tests", TEST_SUITE_OPTIONS, () => { span_id: expect.any(String), root_span_id: expect.any(String), span_attributes: { - type: "llm", + type: "function", name: "generateText", }, metadata: expect.objectContaining({ @@ -473,7 +473,7 @@ describe("ai sdk client unit tests", TEST_SUITE_OPTIONS, () => { ) as any; expect(span.span_attributes.name).toBe("streamText"); - expect(span.span_attributes.type).toBe("llm"); + expect(span.span_attributes.type).toBe("function"); const { metrics } = span; expect(start).toBeLessThanOrEqual(metrics.start);