-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
fix(core): Fix truncation to only keep last message in vercel #19080
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 7 commits
a0fabf0
a14a741
0a8a976
c1d8eea
85f963b
d746121
ed69ebd
93e43f3
94e70da
be3b7cd
50fcded
df96710
d81a9a5
a049b0f
ffb7817
af9bd7e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| import * as Sentry from '@sentry/node'; | ||
| import { generateText } from 'ai'; | ||
| import { MockLanguageModelV1 } from 'ai/test'; | ||
|
|
||
| async function run() { | ||
| await Sentry.startSpan({ op: 'function', name: 'main' }, async () => { | ||
| const largeContent1 = 'A'.repeat(15000); // ~15KB | ||
| const largeContent2 = 'B'.repeat(15000); // ~15KB | ||
| const largeContent3 = 'C'.repeat(25000) + 'D'.repeat(25000); // ~50KB (will be truncated) | ||
|
|
||
| // Test 1: Messages array with large last message that gets truncated | ||
| // Only the last message should be kept, and it should be truncated to only Cs | ||
| await generateText({ | ||
| experimental_telemetry: { isEnabled: true }, | ||
| model: new MockLanguageModelV1({ | ||
| doGenerate: async () => ({ | ||
| rawCall: { rawPrompt: null, rawSettings: {} }, | ||
| finishReason: 'stop', | ||
| usage: { promptTokens: 10, completionTokens: 5 }, | ||
| text: 'Response to truncated messages', | ||
| }), | ||
| }), | ||
| messages: [ | ||
| { role: 'user', content: largeContent1 }, | ||
| { role: 'assistant', content: largeContent2 }, | ||
| { role: 'user', content: largeContent3 }, | ||
| ], | ||
| }); | ||
|
|
||
| // Test 2: Messages array where last message is small and kept intact | ||
| const smallContent = 'This is a small message that fits within the limit'; | ||
| await generateText({ | ||
| experimental_telemetry: { isEnabled: true }, | ||
| model: new MockLanguageModelV1({ | ||
| doGenerate: async () => ({ | ||
| rawCall: { rawPrompt: null, rawSettings: {} }, | ||
| finishReason: 'stop', | ||
| usage: { promptTokens: 10, completionTokens: 5 }, | ||
| text: 'Response to small message', | ||
| }), | ||
| }), | ||
| messages: [ | ||
| { role: 'user', content: largeContent1 }, | ||
| { role: 'assistant', content: largeContent2 }, | ||
| { role: 'user', content: smallContent }, | ||
| ], | ||
| }); | ||
| }); | ||
| } | ||
|
|
||
| run(); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -111,6 +111,16 @@ export function convertPromptToMessages(prompt: string): { role: string; content | |
| try { | ||
| const p = JSON.parse(prompt); | ||
| if (!!p && typeof p === 'object') { | ||
| // Handle messages array format: { messages: [...] } | ||
| const { messages } = p as { messages?: unknown }; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. m-h: Function expects prompt to be of type string, but we cast it here to unknown, which could cause some unexpected behavior. q: is it ever a stringified messages array?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I removed the cast and reorganized this method a bit because I noticed that in the previous version I would not add the system instruction in case we get a messages array
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think it should be a stringified messages array at that point but I added an additional explicit check to be safe |
||
| if (Array.isArray(messages)) { | ||
| return messages.filter( | ||
| (m: unknown): m is { role: string; content: string } => | ||
| !!m && typeof m === 'object' && 'role' in m && 'content' in m, | ||
| ); | ||
| } | ||
|
|
||
| // Handle prompt/system string format: { prompt: "...", system: "..." } | ||
| const { prompt, system } = p; | ||
| if (typeof prompt === 'string' || typeof system === 'string') { | ||
| const messages: { role: string; content: string }[] = []; | ||
|
|
@@ -133,16 +143,13 @@ export function convertPromptToMessages(prompt: string): { role: string; content | |
| * invoke_agent op | ||
| */ | ||
| export function requestMessagesFromPrompt(span: Span, attributes: SpanAttributes): void { | ||
| if (attributes[AI_PROMPT_ATTRIBUTE]) { | ||
| const truncatedPrompt = getTruncatedJsonString(attributes[AI_PROMPT_ATTRIBUTE] as string | string[]); | ||
| span.setAttribute('gen_ai.prompt', truncatedPrompt); | ||
| } | ||
| const prompt = attributes[AI_PROMPT_ATTRIBUTE]; | ||
| if ( | ||
| typeof prompt === 'string' && | ||
| typeof attributes[AI_PROMPT_ATTRIBUTE] === 'string' && | ||
| !attributes[GEN_AI_INPUT_MESSAGES_ATTRIBUTE] && | ||
| !attributes[AI_PROMPT_MESSAGES_ATTRIBUTE] | ||
| ) { | ||
| // No messages array is present, so we need to convert the prompt to the proper messages format | ||
| const prompt = attributes[AI_PROMPT_ATTRIBUTE]; | ||
| const messages = convertPromptToMessages(prompt); | ||
| if (messages.length) { | ||
| const { systemInstructions, filteredMessages } = extractSystemInstructions(messages); | ||
|
|
@@ -152,12 +159,16 @@ export function requestMessagesFromPrompt(span: Span, attributes: SpanAttributes | |
| } | ||
|
|
||
| const filteredLength = Array.isArray(filteredMessages) ? filteredMessages.length : 0; | ||
| const truncatedMessages = getTruncatedJsonString(filteredMessages); | ||
|
|
||
| span.setAttributes({ | ||
| [GEN_AI_INPUT_MESSAGES_ATTRIBUTE]: getTruncatedJsonString(filteredMessages), | ||
| [AI_PROMPT_ATTRIBUTE]: truncatedMessages, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i thought we're going to git rid of AI_PROMPT_ATTRIBUTE as it's deprecated, right?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we are getting rid of |
||
| [GEN_AI_INPUT_MESSAGES_ATTRIBUTE]: truncatedMessages, | ||
|
nicohrubec marked this conversation as resolved.
|
||
| [GEN_AI_INPUT_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE]: filteredLength, | ||
| }); | ||
| } | ||
| } else if (typeof attributes[AI_PROMPT_MESSAGES_ATTRIBUTE] === 'string') { | ||
| // In this case we already get a properly formatted messages array, this is the preferred way to get the messages | ||
| try { | ||
| const messages = JSON.parse(attributes[AI_PROMPT_MESSAGES_ATTRIBUTE]); | ||
| if (Array.isArray(messages)) { | ||
|
|
@@ -168,9 +179,11 @@ export function requestMessagesFromPrompt(span: Span, attributes: SpanAttributes | |
| } | ||
|
|
||
| const filteredLength = Array.isArray(filteredMessages) ? filteredMessages.length : 0; | ||
| const truncatedMessages = getTruncatedJsonString(filteredMessages); | ||
|
|
||
| span.setAttributes({ | ||
| [AI_PROMPT_MESSAGES_ATTRIBUTE]: undefined, | ||
| [GEN_AI_INPUT_MESSAGES_ATTRIBUTE]: getTruncatedJsonString(filteredMessages), | ||
| [AI_PROMPT_MESSAGES_ATTRIBUTE]: truncatedMessages, | ||
| [GEN_AI_INPUT_MESSAGES_ATTRIBUTE]: truncatedMessages, | ||
| [GEN_AI_INPUT_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE]: filteredLength, | ||
| }); | ||
|
sentry[bot] marked this conversation as resolved.
|
||
| } | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.