refactor: replace message.content with message.parts (#868)

This commit is contained in:
Jeremy 2025-03-16 18:42:29 -07:00 committed by GitHub
parent 553a3d825a
commit 47a630fd53
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 1311 additions and 311 deletions

View file

@ -5,11 +5,12 @@ import type {
TextStreamPart,
ToolInvocation,
ToolSet,
UIMessage,
} from 'ai';
import { type ClassValue, clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';
import type { Message as DBMessage, Document } from '@/lib/db/schema';
import type { DBMessage, Document } from '@/lib/db/schema';
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
@ -85,52 +86,6 @@ function addToolMessageToChat({
});
}
export function convertToUIMessages(
messages: Array<DBMessage>,
): Array<Message> {
return messages.reduce((chatMessages: Array<Message>, message) => {
if (message.role === 'tool') {
return addToolMessageToChat({
toolMessage: message as CoreToolMessage,
messages: chatMessages,
});
}
let textContent = '';
let reasoning: string | undefined = undefined;
const toolInvocations: Array<ToolInvocation> = [];
if (typeof message.content === 'string') {
textContent = message.content;
} else if (Array.isArray(message.content)) {
for (const content of message.content) {
if (content.type === 'text') {
textContent += content.text;
} else if (content.type === 'tool-call') {
toolInvocations.push({
state: 'call',
toolCallId: content.toolCallId,
toolName: content.toolName,
args: content.args,
});
} else if (content.type === 'reasoning') {
reasoning = content.reasoning;
}
}
}
chatMessages.push({
id: message.id,
role: message.role as Message['role'],
content: textContent,
reasoning,
toolInvocations,
});
return chatMessages;
}, []);
}
type ResponseMessageWithoutId = CoreToolMessage | CoreAssistantMessage;
type ResponseMessage = ResponseMessageWithoutId & { id: string };
@ -182,40 +137,7 @@ export function sanitizeResponseMessages({
);
}
export function sanitizeUIMessages(messages: Array<Message>): Array<Message> {
const messagesBySanitizedToolInvocations = messages.map((message) => {
if (message.role !== 'assistant') return message;
if (!message.toolInvocations) return message;
const toolResultIds: Array<string> = [];
for (const toolInvocation of message.toolInvocations) {
if (toolInvocation.state === 'result') {
toolResultIds.push(toolInvocation.toolCallId);
}
}
const sanitizedToolInvocations = message.toolInvocations.filter(
(toolInvocation) =>
toolInvocation.state === 'result' ||
toolResultIds.includes(toolInvocation.toolCallId),
);
return {
...message,
toolInvocations: sanitizedToolInvocations,
};
});
return messagesBySanitizedToolInvocations.filter(
(message) =>
message.content.length > 0 ||
(message.toolInvocations && message.toolInvocations.length > 0),
);
}
export function getMostRecentUserMessage(messages: Array<Message>) {
export function getMostRecentUserMessage(messages: Array<UIMessage>) {
const userMessages = messages.filter((message) => message.role === 'user');
return userMessages.at(-1);
}
@ -229,3 +151,15 @@ export function getDocumentTimestampByIndex(
return documents[index].createdAt;
}
export function getTrailingMessageId({
messages,
}: {
messages: Array<ResponseMessage>;
}): string | null {
const trailingMessage = messages.at(-1);
if (!trailingMessage) return null;
return trailingMessage.id;
}