2024-10-15 10:50:50 +05:30
|
|
|
import {
|
2024-10-30 16:01:24 +05:30
|
|
|
CoreAssistantMessage,
|
2024-10-15 10:50:50 +05:30
|
|
|
CoreMessage,
|
|
|
|
|
CoreToolMessage,
|
|
|
|
|
Message,
|
|
|
|
|
ToolInvocation,
|
2024-10-30 16:01:24 +05:30
|
|
|
} from 'ai';
|
|
|
|
|
import { clsx, type ClassValue } from 'clsx';
|
|
|
|
|
import { twMerge } from 'tailwind-merge';
|
2023-05-19 12:33:56 -04:00
|
|
|
|
2024-11-05 17:15:51 +03:00
|
|
|
import { Message as DBMessage, Document } from '@/db/schema';
|
2024-10-15 10:50:50 +05:30
|
|
|
|
2023-05-19 12:33:56 -04:00
|
|
|
export function cn(...inputs: ClassValue[]) {
|
2024-10-11 18:00:22 +05:30
|
|
|
return twMerge(clsx(inputs));
|
2023-05-19 12:33:56 -04:00
|
|
|
}
|
|
|
|
|
|
2024-10-11 18:00:22 +05:30
|
|
|
interface ApplicationError extends Error {
|
|
|
|
|
info: string;
|
|
|
|
|
status: number;
|
2023-05-19 12:33:56 -04:00
|
|
|
}
|
2023-06-16 15:20:42 +04:00
|
|
|
|
2024-10-11 18:00:22 +05:30
|
|
|
export const fetcher = async (url: string) => {
|
|
|
|
|
const res = await fetch(url);
|
2024-03-14 20:00:52 +03:00
|
|
|
|
2024-10-11 18:00:22 +05:30
|
|
|
if (!res.ok) {
|
|
|
|
|
const error = new Error(
|
2024-10-30 16:01:24 +05:30
|
|
|
'An error occurred while fetching the data.'
|
2024-10-11 18:00:22 +05:30
|
|
|
) as ApplicationError;
|
2024-03-19 04:37:30 +03:00
|
|
|
|
2024-10-11 18:00:22 +05:30
|
|
|
error.info = await res.json();
|
|
|
|
|
error.status = res.status;
|
2024-03-19 04:37:30 +03:00
|
|
|
|
2024-10-11 18:00:22 +05:30
|
|
|
throw error;
|
2024-03-19 04:37:30 +03:00
|
|
|
}
|
2024-08-24 23:23:37 -05:00
|
|
|
|
2024-10-11 18:00:22 +05:30
|
|
|
return res.json();
|
|
|
|
|
};
|
2024-08-24 23:23:37 -05:00
|
|
|
|
2024-10-11 18:00:22 +05:30
|
|
|
export function getLocalStorage(key: string) {
|
2024-10-30 16:01:24 +05:30
|
|
|
if (typeof window !== 'undefined') {
|
|
|
|
|
return JSON.parse(localStorage.getItem(key) || '[]');
|
2024-10-11 18:00:22 +05:30
|
|
|
}
|
|
|
|
|
return [];
|
2024-08-24 23:23:37 -05:00
|
|
|
}
|
|
|
|
|
|
2024-10-11 18:00:22 +05:30
|
|
|
export function generateUUID(): string {
|
2024-10-30 16:01:24 +05:30
|
|
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
2024-10-11 18:00:22 +05:30
|
|
|
const r = (Math.random() * 16) | 0;
|
2024-10-30 16:01:24 +05:30
|
|
|
const v = c === 'x' ? r : (r & 0x3) | 0x8;
|
2024-10-11 18:00:22 +05:30
|
|
|
return v.toString(16);
|
|
|
|
|
});
|
2024-08-24 23:23:37 -05:00
|
|
|
}
|
2024-10-15 10:50:50 +05:30
|
|
|
|
|
|
|
|
function addToolMessageToChat({
|
|
|
|
|
toolMessage,
|
|
|
|
|
messages,
|
|
|
|
|
}: {
|
|
|
|
|
toolMessage: CoreToolMessage;
|
|
|
|
|
messages: Array<Message>;
|
|
|
|
|
}): Array<Message> {
|
|
|
|
|
return messages.map((message) => {
|
|
|
|
|
if (message.toolInvocations) {
|
|
|
|
|
return {
|
|
|
|
|
...message,
|
|
|
|
|
toolInvocations: message.toolInvocations.map((toolInvocation) => {
|
|
|
|
|
const toolResult = toolMessage.content.find(
|
2024-10-30 16:01:24 +05:30
|
|
|
(tool) => tool.toolCallId === toolInvocation.toolCallId
|
2024-10-15 10:50:50 +05:30
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (toolResult) {
|
|
|
|
|
return {
|
|
|
|
|
...toolInvocation,
|
2024-10-30 16:01:24 +05:30
|
|
|
state: 'result',
|
2024-10-15 10:50:50 +05:30
|
|
|
result: toolResult.result,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return toolInvocation;
|
|
|
|
|
}),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return message;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function convertToUIMessages(
|
2024-11-05 17:15:51 +03:00
|
|
|
messages: Array<DBMessage>
|
2024-10-15 10:50:50 +05:30
|
|
|
): Array<Message> {
|
|
|
|
|
return messages.reduce((chatMessages: Array<Message>, message) => {
|
2024-10-30 16:01:24 +05:30
|
|
|
if (message.role === 'tool') {
|
2024-10-15 10:50:50 +05:30
|
|
|
return addToolMessageToChat({
|
|
|
|
|
toolMessage: message as CoreToolMessage,
|
|
|
|
|
messages: chatMessages,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-30 16:01:24 +05:30
|
|
|
let textContent = '';
|
2024-10-15 10:50:50 +05:30
|
|
|
let toolInvocations: Array<ToolInvocation> = [];
|
|
|
|
|
|
2024-10-30 16:01:24 +05:30
|
|
|
if (typeof message.content === 'string') {
|
2024-10-15 10:50:50 +05:30
|
|
|
textContent = message.content;
|
|
|
|
|
} else if (Array.isArray(message.content)) {
|
|
|
|
|
for (const content of message.content) {
|
2024-10-30 16:01:24 +05:30
|
|
|
if (content.type === 'text') {
|
2024-10-15 10:50:50 +05:30
|
|
|
textContent += content.text;
|
2024-10-30 16:01:24 +05:30
|
|
|
} else if (content.type === 'tool-call') {
|
2024-10-15 10:50:50 +05:30
|
|
|
toolInvocations.push({
|
2024-10-30 16:01:24 +05:30
|
|
|
state: 'call',
|
2024-10-15 10:50:50 +05:30
|
|
|
toolCallId: content.toolCallId,
|
|
|
|
|
toolName: content.toolName,
|
|
|
|
|
args: content.args,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
chatMessages.push({
|
2024-11-05 17:15:51 +03:00
|
|
|
id: message.id,
|
|
|
|
|
role: message.role as Message['role'],
|
2024-10-15 10:50:50 +05:30
|
|
|
content: textContent,
|
|
|
|
|
toolInvocations,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return chatMessages;
|
|
|
|
|
}, []);
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-30 16:01:24 +05:30
|
|
|
export function sanitizeResponseMessages(
|
|
|
|
|
messages: Array<CoreToolMessage | CoreAssistantMessage>
|
|
|
|
|
): Array<CoreToolMessage | CoreAssistantMessage> {
|
|
|
|
|
let toolResultIds: Array<string> = [];
|
|
|
|
|
|
|
|
|
|
for (const message of messages) {
|
|
|
|
|
if (message.role === 'tool') {
|
|
|
|
|
for (const content of message.content) {
|
|
|
|
|
if (content.type === 'tool-result') {
|
|
|
|
|
toolResultIds.push(content.toolCallId);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const messagesBySanitizedContent = messages.map((message) => {
|
|
|
|
|
if (message.role !== 'assistant') return message;
|
|
|
|
|
|
|
|
|
|
if (typeof message.content === 'string') return message;
|
|
|
|
|
|
|
|
|
|
const sanitizedContent = message.content.filter((content) =>
|
|
|
|
|
content.type === 'tool-call'
|
|
|
|
|
? toolResultIds.includes(content.toolCallId)
|
|
|
|
|
: content.type === 'text'
|
|
|
|
|
? content.text.length > 0
|
|
|
|
|
: true
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
...message,
|
|
|
|
|
content: sanitizedContent,
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return messagesBySanitizedContent.filter(
|
|
|
|
|
(message) => message.content.length > 0
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function sanitizeUIMessages(messages: Array<Message>): Array<Message> {
|
|
|
|
|
const messagesBySanitizedToolInvocations = messages.map((message) => {
|
|
|
|
|
if (message.role !== 'assistant') return message;
|
|
|
|
|
|
|
|
|
|
if (!message.toolInvocations) return message;
|
|
|
|
|
|
|
|
|
|
let 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)
|
|
|
|
|
);
|
|
|
|
|
}
|
2024-11-01 15:31:54 +05:30
|
|
|
|
2024-11-05 17:15:51 +03:00
|
|
|
export function getMostRecentUserMessage(messages: Array<CoreMessage>) {
|
|
|
|
|
const userMessages = messages.filter((message) => message.role === 'user');
|
|
|
|
|
return userMessages.at(-1);
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-01 15:31:54 +05:30
|
|
|
export function getDocumentTimestampByIndex(
|
|
|
|
|
documents: Array<Document>,
|
|
|
|
|
index: number
|
|
|
|
|
) {
|
|
|
|
|
if (!documents) return new Date();
|
|
|
|
|
if (index > documents.length) return new Date();
|
|
|
|
|
|
|
|
|
|
return documents[index].createdAt;
|
|
|
|
|
}
|
2024-11-05 17:15:51 +03:00
|
|
|
|
|
|
|
|
export function getMessageIdFromAnnotations(message: Message) {
|
|
|
|
|
if (!message.annotations) return message.id;
|
|
|
|
|
|
|
|
|
|
const [annotation] = message.annotations;
|
|
|
|
|
if (!annotation) return message.id;
|
|
|
|
|
|
|
|
|
|
// @ts-expect-error messageIdFromServer is not defined in MessageAnnotation
|
|
|
|
|
return annotation.messageIdFromServer;
|
|
|
|
|
}
|