chatbot-template/lib/utils.ts

218 lines
5.4 KiB
TypeScript
Raw Normal View History

2024-11-15 12:18:17 -05:00
import type {
2024-10-30 16:01:24 +05:30
CoreAssistantMessage,
CoreMessage,
CoreToolMessage,
Message,
ToolInvocation,
2024-10-30 16:01:24 +05:30
} from 'ai';
2024-11-15 12:18:17 -05:00
import { type ClassValue, clsx } from 'clsx';
2024-10-30 16:01:24 +05:30
import { twMerge } from 'tailwind-merge';
2023-05-19 12:33:56 -04:00
2024-11-15 12:18:17 -05:00
import type { Message as DBMessage, Document } from '@/lib/db/schema';
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-11-15 13:00:15 -05:00
'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-11-15 12:18:17 -05:00
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (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
}
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-11-15 13:00:15 -05:00
(tool) => tool.toolCallId === toolInvocation.toolCallId,
);
if (toolResult) {
return {
...toolInvocation,
2024-10-30 16:01:24 +05:30
state: 'result',
result: toolResult.result,
};
}
return toolInvocation;
}),
};
}
return message;
});
}
export function convertToUIMessages(
2024-11-15 13:00:15 -05:00
messages: Array<DBMessage>,
): Array<Message> {
return messages.reduce((chatMessages: Array<Message>, message) => {
2024-10-30 16:01:24 +05:30
if (message.role === 'tool') {
return addToolMessageToChat({
toolMessage: message as CoreToolMessage,
messages: chatMessages,
});
}
2024-10-30 16:01:24 +05:30
let textContent = '';
2024-11-15 12:18:17 -05:00
const toolInvocations: Array<ToolInvocation> = [];
2024-10-30 16:01:24 +05:30
if (typeof message.content === 'string') {
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') {
textContent += content.text;
2024-10-30 16:01:24 +05:30
} else if (content.type === 'tool-call') {
toolInvocations.push({
2024-10-30 16:01:24 +05:30
state: 'call',
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'],
content: textContent,
toolInvocations,
});
return chatMessages;
}, []);
}
type ResponseMessageWithoutId = CoreToolMessage | CoreAssistantMessage;
type ResponseMessage = ResponseMessageWithoutId & { id: string };
2024-10-30 16:01:24 +05:30
export function sanitizeResponseMessages(
messages: Array<ResponseMessage>,
): Array<ResponseMessage> {
2024-11-15 12:18:17 -05:00
const toolResultIds: Array<string> = [];
2024-10-30 16:01:24 +05:30
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
2024-11-15 13:00:15 -05:00
: true,
2024-10-30 16:01:24 +05:30
);
return {
...message,
content: sanitizedContent,
};
});
return messagesBySanitizedContent.filter(
2024-11-15 13:00:15 -05:00
(message) => message.content.length > 0,
2024-10-30 16:01:24 +05:30
);
}
export function sanitizeUIMessages(messages: Array<Message>): Array<Message> {
const messagesBySanitizedToolInvocations = messages.map((message) => {
if (message.role !== 'assistant') return message;
if (!message.toolInvocations) return message;
2024-11-15 12:18:17 -05:00
const toolResultIds: Array<string> = [];
2024-10-30 16:01:24 +05:30
for (const toolInvocation of message.toolInvocations) {
if (toolInvocation.state === 'result') {
toolResultIds.push(toolInvocation.toolCallId);
}
}
const sanitizedToolInvocations = message.toolInvocations.filter(
(toolInvocation) =>
toolInvocation.state === 'result' ||
2024-11-15 13:00:15 -05:00
toolResultIds.includes(toolInvocation.toolCallId),
2024-10-30 16:01:24 +05:30
);
return {
...message,
toolInvocations: sanitizedToolInvocations,
};
});
return messagesBySanitizedToolInvocations.filter(
(message) =>
message.content.length > 0 ||
2024-11-15 13:00:15 -05:00
(message.toolInvocations && message.toolInvocations.length > 0),
2024-10-30 16:01:24 +05:30
);
}
export function getMostRecentUserMessage(messages: Array<Message>) {
2024-11-05 17:15:51 +03:00
const userMessages = messages.filter((message) => message.role === 'user');
return userMessages.at(-1);
}
export function getDocumentTimestampByIndex(
documents: Array<Document>,
2024-11-15 13:00:15 -05:00
index: number,
) {
if (!documents) return new Date();
if (index > documents.length) return new Date();
return documents[index].createdAt;
}