From fa1db87b74871979f9139e721b476159778e2c37 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Fri, 20 Mar 2026 13:18:46 +0100 Subject: [PATCH 1/7] fix --- .../core/src/tracing/ai/messageTruncation.ts | 76 ++++++++++--------- packages/core/src/tracing/vercel-ai/index.ts | 8 ++ packages/core/src/tracing/vercel-ai/utils.ts | 1 + .../lib/tracing/ai-message-truncation.test.ts | 61 +++++++++++++++ 4 files changed, 112 insertions(+), 34 deletions(-) diff --git a/packages/core/src/tracing/ai/messageTruncation.ts b/packages/core/src/tracing/ai/messageTruncation.ts index 16df3c298466..cb1bfb47a4e7 100644 --- a/packages/core/src/tracing/ai/messageTruncation.ts +++ b/packages/core/src/tracing/ai/messageTruncation.ts @@ -95,31 +95,36 @@ function truncateTextByBytes(text: string, maxBytes: number): string { } /** - * Extract text content from a Google GenAI message part. - * Parts are either plain strings or objects with a text property. + * Extract text content from a message part/item. + * Handles plain strings and objects with a text property. * * @returns The text content */ -function getPartText(part: TextPart | MediaPart): string { +function getPartText(part: unknown): string { if (typeof part === 'string') { return part; } - if ('text' in part) return part.text; + if (typeof part === 'object' && part !== null && 'text' in part && typeof part.text === 'string') { + return part.text; + } return ''; } /** - * Create a new part with updated text content while preserving the original structure. + * Create a new part/item with updated text content while preserving the original structure. * * @param part - Original part (string or object) * @param text - New text content * @returns New part with updated text */ -function withPartText(part: TextPart | MediaPart, text: string): TextPart { +function withPartText(part: unknown, text: string): unknown { if (typeof part === 'string') { return text; } - return { ...part, text }; + if (typeof part === 'object' && part !== null) { + return { ...part, text }; + } + return text; } /** @@ -176,56 +181,63 @@ function truncateContentMessage(message: ContentMessage, maxBytes: number): unkn } /** - * Truncate a message with `parts: [...]` format (Google GenAI). - * Keeps as many complete parts as possible, only truncating the first part if needed. + * Truncate a message with an array-based format. + * Handles both `parts: [...]` (Google GenAI) and `content: [...]` (OpenAI/Anthropic multimodal). + * Keeps as many complete items as possible, only truncating the first item if needed. * - * @param message - Message with parts array + * @param message - Message with parts or content array * @param maxBytes - Maximum byte limit * @returns Array with truncated message, or empty array if it doesn't fit */ -function truncatePartsMessage(message: PartsMessage, maxBytes: number): unknown[] { - const { parts } = message; +function truncateArrayMessage(message: PartsMessage | ContentArrayMessage, maxBytes: number): unknown[] { + const key = 'parts' in message ? 'parts' : 'content'; + const rawItems = 'parts' in message ? message.parts : message.content; + const items: unknown[] = Array.isArray(rawItems) ? rawItems : []; - // Calculate overhead by creating empty text parts - const emptyParts = parts.map(part => withPartText(part, '')); - const overhead = jsonBytes({ ...message, parts: emptyParts }); + if (items.length === 0) { + return []; + } + + // Calculate overhead by creating empty text items + const emptyItems = items.map(item => withPartText(item, '')); + const overhead = jsonBytes({ ...message, [key]: emptyItems }); let remainingBytes = maxBytes - overhead; if (remainingBytes <= 0) { return []; } - // Include parts until we run out of space - const includedParts: (TextPart | MediaPart)[] = []; + // Include items until we run out of space + const includedItems: unknown[] = []; - for (const part of parts) { - const text = getPartText(part); + for (const item of items) { + const text = getPartText(item); const textSize = utf8Bytes(text); if (textSize <= remainingBytes) { - // Part fits: include it as-is - includedParts.push(part); + // Item fits: include it as-is + includedItems.push(item); remainingBytes -= textSize; - } else if (includedParts.length === 0) { - // First part doesn't fit: truncate it + } else if (includedItems.length === 0) { + // First item doesn't fit: truncate it const truncated = truncateTextByBytes(text, remainingBytes); if (truncated) { - includedParts.push(withPartText(part, truncated)); + includedItems.push(withPartText(item, truncated)); } break; } else { - // Subsequent part doesn't fit: stop here + // Subsequent item doesn't fit: stop here break; } } /* c8 ignore start * for type safety only, algorithm guarantees SOME text included */ - if (includedParts.length <= 0) { + if (includedItems.length <= 0) { return []; } else { /* c8 ignore stop */ - return [{ ...message, parts: includedParts }]; + return [{ ...message, [key]: includedItems }]; } } @@ -258,13 +270,8 @@ function truncateSingleMessage(message: unknown, maxBytes: number): unknown[] { return truncateContentMessage(message, maxBytes); } - if (isContentArrayMessage(message)) { - // Content array messages are returned as-is without truncation - return [message]; - } - - if (isPartsMessage(message)) { - return truncatePartsMessage(message, maxBytes); + if (isContentArrayMessage(message) || isPartsMessage(message)) { + return truncateArrayMessage(message, maxBytes); } // Unknown message format: cannot truncate safely @@ -348,6 +355,7 @@ function truncateMessagesByBytes(messages: unknown[], maxBytes: number): unknown const strippedMessage = stripped[0]; // Check if it fits + console.log('strippedMessage', strippedMessage); const messageBytes = jsonBytes(strippedMessage); if (messageBytes <= effectiveMaxBytes) { return stripped; diff --git a/packages/core/src/tracing/vercel-ai/index.ts b/packages/core/src/tracing/vercel-ai/index.ts index 1d8a27e8e3aa..4cc622730824 100644 --- a/packages/core/src/tracing/vercel-ai/index.ts +++ b/packages/core/src/tracing/vercel-ai/index.ts @@ -95,12 +95,15 @@ function mapVercelAiOperationName(operationName: string): string { * This is supposed to be used in `client.on('spanStart', ...) */ function onVercelAiSpanStart(span: Span): void { + console.log('onVercelAiSpanStart'); const { data: attributes, description: name } = spanToJSON(span); if (!name) { return; } + console.log('name present', name); + // Tool call spans // https://ai-sdk.dev/docs/ai-sdk-core/telemetry#tool-call-spans if (attributes[AI_TOOL_CALL_NAME_ATTRIBUTE] && attributes[AI_TOOL_CALL_ID_ATTRIBUTE] && name === 'ai.toolCall') { @@ -108,6 +111,8 @@ function onVercelAiSpanStart(span: Span): void { return; } + console.log('not a tool call') + // V6+ Check if this is a Vercel AI span by checking if the operation ID attribute is present. // V5+ Check if this is a Vercel AI span by name pattern. if (!attributes[AI_OPERATION_ID_ATTRIBUTE] && !name.startsWith('ai.')) { @@ -391,6 +396,7 @@ function processToolCallSpan(span: Span, attributes: SpanAttributes): void { } function processGenerateSpan(span: Span, name: string, attributes: SpanAttributes): void { + console.log('processGenerateSpan', name); span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, 'auto.vercelai.otel'); const nameWthoutAi = name.replace('ai.', ''); @@ -402,7 +408,9 @@ function processGenerateSpan(span: Span, name: string, attributes: SpanAttribute span.setAttribute('gen_ai.function_id', functionId); } + console.log('before requestMessagesFromPrompt', attributes); requestMessagesFromPrompt(span, attributes); + console.log('attributes', attributes); if (attributes[AI_MODEL_ID_ATTRIBUTE] && !attributes[GEN_AI_RESPONSE_MODEL_ATTRIBUTE]) { span.setAttribute(GEN_AI_RESPONSE_MODEL_ATTRIBUTE, attributes[AI_MODEL_ID_ATTRIBUTE]); diff --git a/packages/core/src/tracing/vercel-ai/utils.ts b/packages/core/src/tracing/vercel-ai/utils.ts index e72efde75e18..ecf335262ae1 100644 --- a/packages/core/src/tracing/vercel-ai/utils.ts +++ b/packages/core/src/tracing/vercel-ai/utils.ts @@ -271,6 +271,7 @@ export function requestMessagesFromPrompt(span: Span, attributes: SpanAttributes } const filteredLength = Array.isArray(filteredMessages) ? filteredMessages.length : 0; + console.log('filteredMessages', filteredMessages); const truncatedMessages = getTruncatedJsonString(filteredMessages); span.setAttributes({ diff --git a/packages/core/test/lib/tracing/ai-message-truncation.test.ts b/packages/core/test/lib/tracing/ai-message-truncation.test.ts index c7f8e0043622..f1f318e02128 100644 --- a/packages/core/test/lib/tracing/ai-message-truncation.test.ts +++ b/packages/core/test/lib/tracing/ai-message-truncation.test.ts @@ -547,5 +547,66 @@ describe('message truncation utilities', () => { }, ]); }); + + it('truncates content array message when first text item does not fit', () => { + const messages = [ + { + role: 'user', + content: [{ type: 'text', text: `2 ${humongous}` }], + }, + ]; + const result = truncateGenAiMessages(messages); + const truncLen = + 20_000 - + 2 - + JSON.stringify({ + role: 'user', + content: [{ type: 'text', text: '' }], + }).length; + expect(result).toStrictEqual([ + { + role: 'user', + content: [{ type: 'text', text: `2 ${humongous}`.substring(0, truncLen) }], + }, + ]); + }); + + it('drops subsequent content array items that do not fit', () => { + const messages = [ + { + role: 'assistant', + content: [ + { type: 'text', text: `1 ${big}` }, + { type: 'image_url', url: 'https://example.com/img.png' }, + { type: 'text', text: `2 ${big}` }, + { type: 'text', text: `3 ${big}` }, + { type: 'text', text: `4 ${giant}` }, + { type: 'text', text: `5 ${giant}` }, + ], + }, + ]; + const result = truncateGenAiMessages(messages); + expect(result).toStrictEqual([ + { + role: 'assistant', + content: [ + { type: 'text', text: `1 ${big}` }, + { type: 'image_url', url: 'https://example.com/img.png' }, + { type: 'text', text: `2 ${big}` }, + { type: 'text', text: `3 ${big}` }, + ], + }, + ]); + }); + + it('drops content array message if overhead is too large', () => { + const messages = [ + { + some_other_field: humongous, + content: [{ type: 'text', text: 'hello' }], + }, + ]; + expect(truncateGenAiMessages(messages)).toStrictEqual([]); + }); }); }); From 5a0b03b09b1369bba03befce7ac56cc9209fe841 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Fri, 20 Mar 2026 13:37:14 +0100 Subject: [PATCH 2/7] remove consoles --- packages/core/src/tracing/ai/messageTruncation.ts | 1 - packages/core/src/tracing/vercel-ai/index.ts | 8 -------- packages/core/src/tracing/vercel-ai/utils.ts | 1 - 3 files changed, 10 deletions(-) diff --git a/packages/core/src/tracing/ai/messageTruncation.ts b/packages/core/src/tracing/ai/messageTruncation.ts index cb1bfb47a4e7..4f81662db094 100644 --- a/packages/core/src/tracing/ai/messageTruncation.ts +++ b/packages/core/src/tracing/ai/messageTruncation.ts @@ -355,7 +355,6 @@ function truncateMessagesByBytes(messages: unknown[], maxBytes: number): unknown const strippedMessage = stripped[0]; // Check if it fits - console.log('strippedMessage', strippedMessage); const messageBytes = jsonBytes(strippedMessage); if (messageBytes <= effectiveMaxBytes) { return stripped; diff --git a/packages/core/src/tracing/vercel-ai/index.ts b/packages/core/src/tracing/vercel-ai/index.ts index 4cc622730824..1d8a27e8e3aa 100644 --- a/packages/core/src/tracing/vercel-ai/index.ts +++ b/packages/core/src/tracing/vercel-ai/index.ts @@ -95,15 +95,12 @@ function mapVercelAiOperationName(operationName: string): string { * This is supposed to be used in `client.on('spanStart', ...) */ function onVercelAiSpanStart(span: Span): void { - console.log('onVercelAiSpanStart'); const { data: attributes, description: name } = spanToJSON(span); if (!name) { return; } - console.log('name present', name); - // Tool call spans // https://ai-sdk.dev/docs/ai-sdk-core/telemetry#tool-call-spans if (attributes[AI_TOOL_CALL_NAME_ATTRIBUTE] && attributes[AI_TOOL_CALL_ID_ATTRIBUTE] && name === 'ai.toolCall') { @@ -111,8 +108,6 @@ function onVercelAiSpanStart(span: Span): void { return; } - console.log('not a tool call') - // V6+ Check if this is a Vercel AI span by checking if the operation ID attribute is present. // V5+ Check if this is a Vercel AI span by name pattern. if (!attributes[AI_OPERATION_ID_ATTRIBUTE] && !name.startsWith('ai.')) { @@ -396,7 +391,6 @@ function processToolCallSpan(span: Span, attributes: SpanAttributes): void { } function processGenerateSpan(span: Span, name: string, attributes: SpanAttributes): void { - console.log('processGenerateSpan', name); span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, 'auto.vercelai.otel'); const nameWthoutAi = name.replace('ai.', ''); @@ -408,9 +402,7 @@ function processGenerateSpan(span: Span, name: string, attributes: SpanAttribute span.setAttribute('gen_ai.function_id', functionId); } - console.log('before requestMessagesFromPrompt', attributes); requestMessagesFromPrompt(span, attributes); - console.log('attributes', attributes); if (attributes[AI_MODEL_ID_ATTRIBUTE] && !attributes[GEN_AI_RESPONSE_MODEL_ATTRIBUTE]) { span.setAttribute(GEN_AI_RESPONSE_MODEL_ATTRIBUTE, attributes[AI_MODEL_ID_ATTRIBUTE]); diff --git a/packages/core/src/tracing/vercel-ai/utils.ts b/packages/core/src/tracing/vercel-ai/utils.ts index ecf335262ae1..e72efde75e18 100644 --- a/packages/core/src/tracing/vercel-ai/utils.ts +++ b/packages/core/src/tracing/vercel-ai/utils.ts @@ -271,7 +271,6 @@ export function requestMessagesFromPrompt(span: Span, attributes: SpanAttributes } const filteredLength = Array.isArray(filteredMessages) ? filteredMessages.length : 0; - console.log('filteredMessages', filteredMessages); const truncatedMessages = getTruncatedJsonString(filteredMessages); span.setAttributes({ From 3069aad595d9fa811c9b41f24bf5c77ed143387f Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Fri, 20 Mar 2026 13:42:37 +0100 Subject: [PATCH 3/7] . --- .../core/src/tracing/ai/messageTruncation.ts | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/packages/core/src/tracing/ai/messageTruncation.ts b/packages/core/src/tracing/ai/messageTruncation.ts index 4f81662db094..4c27377cf503 100644 --- a/packages/core/src/tracing/ai/messageTruncation.ts +++ b/packages/core/src/tracing/ai/messageTruncation.ts @@ -47,6 +47,11 @@ type MediaPart = { content: string; }; +/** + * Union of all item types that can appear in array-based messages. + */ +type ArrayMessageItem = TextPart | MediaPart | ContentArrayMessage['content'][number]; + /** * Calculate the UTF-8 byte length of a string. */ @@ -100,11 +105,11 @@ function truncateTextByBytes(text: string, maxBytes: number): string { * * @returns The text content */ -function getPartText(part: unknown): string { +function getPartText(part: ArrayMessageItem): string { if (typeof part === 'string') { return part; } - if (typeof part === 'object' && part !== null && 'text' in part && typeof part.text === 'string') { + if ('text' in part && typeof part.text === 'string') { return part.text; } return ''; @@ -117,14 +122,11 @@ function getPartText(part: unknown): string { * @param text - New text content * @returns New part with updated text */ -function withPartText(part: unknown, text: string): unknown { +function withPartText(part: ArrayMessageItem, text: string): ArrayMessageItem { if (typeof part === 'string') { return text; } - if (typeof part === 'object' && part !== null) { - return { ...part, text }; - } - return text; + return { ...part, text }; } /** @@ -191,8 +193,8 @@ function truncateContentMessage(message: ContentMessage, maxBytes: number): unkn */ function truncateArrayMessage(message: PartsMessage | ContentArrayMessage, maxBytes: number): unknown[] { const key = 'parts' in message ? 'parts' : 'content'; - const rawItems = 'parts' in message ? message.parts : message.content; - const items: unknown[] = Array.isArray(rawItems) ? rawItems : []; + const items: ArrayMessageItem[] = + 'parts' in message && Array.isArray(message.parts) ? message.parts : Array.isArray(message.content) ? message.content : []; if (items.length === 0) { return []; @@ -208,7 +210,7 @@ function truncateArrayMessage(message: PartsMessage | ContentArrayMessage, maxBy } // Include items until we run out of space - const includedItems: unknown[] = []; + const includedItems: ArrayMessageItem[] = []; for (const item of items) { const text = getPartText(item); From 1f0a9e8bf4cfddf560097ecfcfdc207fd4c11baa Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Fri, 20 Mar 2026 13:47:36 +0100 Subject: [PATCH 4/7] . --- .../core/src/tracing/ai/messageTruncation.ts | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/packages/core/src/tracing/ai/messageTruncation.ts b/packages/core/src/tracing/ai/messageTruncation.ts index 4c27377cf503..0792b2ad7799 100644 --- a/packages/core/src/tracing/ai/messageTruncation.ts +++ b/packages/core/src/tracing/ai/messageTruncation.ts @@ -100,33 +100,33 @@ function truncateTextByBytes(text: string, maxBytes: number): string { } /** - * Extract text content from a message part/item. + * Extract text content from a message item. * Handles plain strings and objects with a text property. * * @returns The text content */ -function getPartText(part: ArrayMessageItem): string { - if (typeof part === 'string') { - return part; +function getItemText(item: ArrayMessageItem): string { + if (typeof item === 'string') { + return item; } - if ('text' in part && typeof part.text === 'string') { - return part.text; + if ('text' in item && typeof item.text === 'string') { + return item.text; } return ''; } /** - * Create a new part/item with updated text content while preserving the original structure. + * Create a new item with updated text content while preserving the original structure. * - * @param part - Original part (string or object) + * @param item - Original item (string or object) * @param text - New text content - * @returns New part with updated text + * @returns New item with updated text */ -function withPartText(part: ArrayMessageItem, text: string): ArrayMessageItem { - if (typeof part === 'string') { +function withItemText(item: ArrayMessageItem, text: string): ArrayMessageItem { + if (typeof item === 'string') { return text; } - return { ...part, text }; + return { ...item, text }; } /** @@ -201,7 +201,7 @@ function truncateArrayMessage(message: PartsMessage | ContentArrayMessage, maxBy } // Calculate overhead by creating empty text items - const emptyItems = items.map(item => withPartText(item, '')); + const emptyItems = items.map(item => withItemText(item, '')); const overhead = jsonBytes({ ...message, [key]: emptyItems }); let remainingBytes = maxBytes - overhead; @@ -213,7 +213,7 @@ function truncateArrayMessage(message: PartsMessage | ContentArrayMessage, maxBy const includedItems: ArrayMessageItem[] = []; for (const item of items) { - const text = getPartText(item); + const text = getItemText(item); const textSize = utf8Bytes(text); if (textSize <= remainingBytes) { @@ -224,7 +224,7 @@ function truncateArrayMessage(message: PartsMessage | ContentArrayMessage, maxBy // First item doesn't fit: truncate it const truncated = truncateTextByBytes(text, remainingBytes); if (truncated) { - includedItems.push(withPartText(item, truncated)); + includedItems.push(withItemText(item, truncated)); } break; } else { From e5d1ae76b511df423fe57f712b676635981a0a97 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Fri, 20 Mar 2026 13:57:39 +0100 Subject: [PATCH 5/7] readability --- .../core/src/tracing/ai/messageTruncation.ts | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/packages/core/src/tracing/ai/messageTruncation.ts b/packages/core/src/tracing/ai/messageTruncation.ts index 0792b2ad7799..b758591f32ff 100644 --- a/packages/core/src/tracing/ai/messageTruncation.ts +++ b/packages/core/src/tracing/ai/messageTruncation.ts @@ -14,15 +14,20 @@ type ContentMessage = { content: string; }; +/** + * One block inside OpenAI / Anthropic `content: [...]` arrays (text, image_url, etc.). + */ +type ContentArrayBlock = { + [key: string]: unknown; + type: string; +}; + /** * Message format used by OpenAI and Anthropic APIs for media. */ type ContentArrayMessage = { [key: string]: unknown; - content: { - [key: string]: unknown; - type: string; - }[]; + content: ContentArrayBlock[]; }; /** @@ -48,9 +53,9 @@ type MediaPart = { }; /** - * Union of all item types that can appear in array-based messages. + * One element of an array-based message: OpenAI/Anthropic `content[]` or Google `parts`. */ -type ArrayMessageItem = TextPart | MediaPart | ContentArrayMessage['content'][number]; +type ArrayMessageItem = TextPart | MediaPart | ContentArrayBlock; /** * Calculate the UTF-8 byte length of a string. From e8e454c81df568d5ee45f8b1ff846d6d8b12ed02 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Fri, 20 Mar 2026 14:19:34 +0100 Subject: [PATCH 6/7] . --- .../core/src/tracing/ai/messageTruncation.ts | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/packages/core/src/tracing/ai/messageTruncation.ts b/packages/core/src/tracing/ai/messageTruncation.ts index b758591f32ff..f8bc6ae5a202 100644 --- a/packages/core/src/tracing/ai/messageTruncation.ts +++ b/packages/core/src/tracing/ai/messageTruncation.ts @@ -187,6 +187,23 @@ function truncateContentMessage(message: ContentMessage, maxBytes: number): unkn return [{ ...message, content: truncatedContent }]; } +/** + * Picks content array on the message that should be truncated (`parts` or `content`). + * Returns `null` if neither field has a truncateable array. + */ +function resolvePartsOrContentForTruncation(message: PartsMessage | ContentArrayMessage): { + key: 'parts' | 'content' | null; + items: ArrayMessageItem[]; +} { + if ('parts' in message && Array.isArray(message.parts)) { + return { key: 'parts', items: message.parts }; + } + if ('content' in message && Array.isArray(message.content)) { + return { key: 'content', items: message.content }; + } + return { key: null, items: [] }; +} + /** * Truncate a message with an array-based format. * Handles both `parts: [...]` (Google GenAI) and `content: [...]` (OpenAI/Anthropic multimodal). @@ -197,11 +214,9 @@ function truncateContentMessage(message: ContentMessage, maxBytes: number): unkn * @returns Array with truncated message, or empty array if it doesn't fit */ function truncateArrayMessage(message: PartsMessage | ContentArrayMessage, maxBytes: number): unknown[] { - const key = 'parts' in message ? 'parts' : 'content'; - const items: ArrayMessageItem[] = - 'parts' in message && Array.isArray(message.parts) ? message.parts : Array.isArray(message.content) ? message.content : []; + const { key, items } = resolvePartsOrContentForTruncation(message); - if (items.length === 0) { + if (key === null || items.length === 0) { return []; } From 52d5b899048595cb935b1a8cf1c730b1165df3d8 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Fri, 20 Mar 2026 14:42:29 +0100 Subject: [PATCH 7/7] rename --- packages/core/src/tracing/ai/messageTruncation.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/core/src/tracing/ai/messageTruncation.ts b/packages/core/src/tracing/ai/messageTruncation.ts index f8bc6ae5a202..779cf332855b 100644 --- a/packages/core/src/tracing/ai/messageTruncation.ts +++ b/packages/core/src/tracing/ai/messageTruncation.ts @@ -188,10 +188,10 @@ function truncateContentMessage(message: ContentMessage, maxBytes: number): unkn } /** - * Picks content array on the message that should be truncated (`parts` or `content`). - * Returns `null` if neither field has a truncateable array. + * Extracts the array items and their key from an array-based message. + * Returns `null` key if neither `parts` nor `content` is a valid array. */ -function resolvePartsOrContentForTruncation(message: PartsMessage | ContentArrayMessage): { +function getArrayItems(message: PartsMessage | ContentArrayMessage): { key: 'parts' | 'content' | null; items: ArrayMessageItem[]; } { @@ -214,7 +214,7 @@ function resolvePartsOrContentForTruncation(message: PartsMessage | ContentArray * @returns Array with truncated message, or empty array if it doesn't fit */ function truncateArrayMessage(message: PartsMessage | ContentArrayMessage, maxBytes: number): unknown[] { - const { key, items } = resolvePartsOrContentForTruncation(message); + const { key, items } = getArrayItems(message); if (key === null || items.length === 0) { return [];