Skip to content

Commit ea3183f

Browse files
authored
fix(core): Stringify available tools sent from vercelai (#18197)
[Linear Ticket](https://linear.app/getsentry/issue/JS-657/available-tools-json-should-be-a-stringified-json-array-of-objects-not) The available tools sent from our SDKs should generally be in the format of a stringified array of objects (where an object stores information about a particular tool). This is true for all AI SDKs except Vercel, where we send an array of strings. This PR fixes this by parsing the available tool array and converting the whole array into a proper string representation.
1 parent b947281 commit ea3183f

File tree

4 files changed

+33
-3
lines changed

4 files changed

+33
-3
lines changed

dev-packages/node-integration-tests/suites/tracing/vercelai/test.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,9 @@ describe('Vercel AI integration', () => {
197197
]),
198198
};
199199

200+
const EXPECTED_AVAILABLE_TOOLS_JSON =
201+
'[{"type":"function","name":"getWeather","parameters":{"type":"object","properties":{"location":{"type":"string"}},"required":["location"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"}}]';
202+
200203
const EXPECTED_TRANSACTION_DEFAULT_PII_TRUE = {
201204
transaction: 'main',
202205
spans: expect.arrayContaining([
@@ -358,7 +361,7 @@ describe('Vercel AI integration', () => {
358361
'vercel.ai.prompt.format': expect.any(String),
359362
'gen_ai.request.messages': expect.any(String),
360363
'vercel.ai.prompt.toolChoice': expect.any(String),
361-
'gen_ai.request.available_tools': expect.any(Array),
364+
'gen_ai.request.available_tools': EXPECTED_AVAILABLE_TOOLS_JSON,
362365
'vercel.ai.response.finishReason': 'tool-calls',
363366
'vercel.ai.response.id': expect.any(String),
364367
'vercel.ai.response.model': 'mock-model-id',

dev-packages/node-integration-tests/suites/tracing/vercelai/v5/test.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,9 @@ describe('Vercel AI integration (V5)', () => {
193193
]),
194194
};
195195

196+
const EXPECTED_AVAILABLE_TOOLS_JSON =
197+
'[{"type":"function","name":"getWeather","inputSchema":{"$schema":"http://json-schema.org/draft-07/schema#","type":"object","properties":{"location":{"type":"string"}},"required":["location"],"additionalProperties":false}}]';
198+
196199
const EXPECTED_TRANSACTION_DEFAULT_PII_TRUE = {
197200
transaction: 'main',
198201
spans: expect.arrayContaining([
@@ -348,7 +351,7 @@ describe('Vercel AI integration (V5)', () => {
348351
'vercel.ai.pipeline.name': 'generateText.doGenerate',
349352
'gen_ai.request.messages': expect.any(String),
350353
'vercel.ai.prompt.toolChoice': expect.any(String),
351-
'gen_ai.request.available_tools': expect.any(Array),
354+
'gen_ai.request.available_tools': EXPECTED_AVAILABLE_TOOLS_JSON,
352355
'vercel.ai.response.finishReason': 'tool-calls',
353356
'vercel.ai.response.id': expect.any(String),
354357
'vercel.ai.response.model': 'mock-model-id',

packages/core/src/tracing/vercel-ai/index.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
import { getTruncatedJsonString } from '../ai/utils';
1111
import { toolCallSpanMap } from './constants';
1212
import type { TokenSummary } from './types';
13-
import { accumulateTokensForParent, applyAccumulatedTokens } from './utils';
13+
import { accumulateTokensForParent, applyAccumulatedTokens, convertAvailableToolsToJsonString } from './utils';
1414
import type { ProviderMetadata } from './vercel-ai-attributes';
1515
import {
1616
AI_MODEL_ID_ATTRIBUTE,
@@ -123,6 +123,13 @@ function processEndedVercelAiSpan(span: SpanJSON): void {
123123
attributes[GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE] + attributes[GEN_AI_USAGE_INPUT_TOKENS_ATTRIBUTE];
124124
}
125125

126+
// Convert the available tools array to a JSON string
127+
if (attributes[AI_PROMPT_TOOLS_ATTRIBUTE] && Array.isArray(attributes[AI_PROMPT_TOOLS_ATTRIBUTE])) {
128+
attributes[AI_PROMPT_TOOLS_ATTRIBUTE] = convertAvailableToolsToJsonString(
129+
attributes[AI_PROMPT_TOOLS_ATTRIBUTE] as unknown[],
130+
);
131+
}
132+
126133
// Rename AI SDK attributes to standardized gen_ai attributes
127134
renameAttributeKey(attributes, AI_PROMPT_MESSAGES_ATTRIBUTE, 'gen_ai.request.messages');
128135
renameAttributeKey(attributes, AI_RESPONSE_TEXT_ATTRIBUTE, 'gen_ai.response.text');

packages/core/src/tracing/vercel-ai/utils.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,3 +70,20 @@ export function _INTERNAL_getSpanForToolCallId(toolCallId: string): Span | undef
7070
export function _INTERNAL_cleanupToolCallSpan(toolCallId: string): void {
7171
toolCallSpanMap.delete(toolCallId);
7272
}
73+
74+
/**
75+
* Convert an array of tool strings to a JSON string
76+
*/
77+
export function convertAvailableToolsToJsonString(tools: unknown[]): string {
78+
const toolObjects = tools.map(tool => {
79+
if (typeof tool === 'string') {
80+
try {
81+
return JSON.parse(tool);
82+
} catch {
83+
return tool;
84+
}
85+
}
86+
return tool;
87+
});
88+
return JSON.stringify(toolObjects);
89+
}

0 commit comments

Comments
 (0)