| import type { |
| CoreAssistantMessage, |
| CoreToolMessage, |
| UIMessage, |
| UIMessagePart, |
| } from 'ai'; |
| import { type ClassValue, clsx } from 'clsx'; |
| import { twMerge } from 'tailwind-merge'; |
| import type { DBMessage, Document } from '@/lib/db/schema'; |
| import { ChatSDKError, type ErrorCode } from './errors'; |
| import type { ChatMessage, ChatTools, CustomUIDataTypes } from './types'; |
| import { formatISO } from 'date-fns'; |
|
|
| export function cn(...inputs: ClassValue[]) { |
| return twMerge(clsx(inputs)); |
| } |
|
|
| export const fetcher = async (url: string) => { |
| const response = await fetch(url); |
|
|
| if (!response.ok) { |
| const { code, cause } = await response.json(); |
| throw new ChatSDKError(code as ErrorCode, cause); |
| } |
|
|
| return response.json(); |
| }; |
|
|
| export async function fetchWithErrorHandlers( |
| input: RequestInfo | URL, |
| init?: RequestInit, |
| ) { |
| try { |
| const response = await fetch(input, init); |
|
|
| if (!response.ok) { |
| const { code, cause } = await response.json(); |
| throw new ChatSDKError(code as ErrorCode, cause); |
| } |
|
|
| return response; |
| } catch (error: unknown) { |
| if (typeof navigator !== 'undefined' && !navigator.onLine) { |
| throw new ChatSDKError('offline:chat'); |
| } |
|
|
| throw error; |
| } |
| } |
|
|
| export function getLocalStorage(key: string) { |
| if (typeof window !== 'undefined') { |
| return JSON.parse(localStorage.getItem(key) || '[]'); |
| } |
| return []; |
| } |
|
|
| export function generateUUID(): string { |
| return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { |
| const r = (Math.random() * 16) | 0; |
| const v = c === 'x' ? r : (r & 0x3) | 0x8; |
| return v.toString(16); |
| }); |
| } |
|
|
| type ResponseMessageWithoutId = CoreToolMessage | CoreAssistantMessage; |
| type ResponseMessage = ResponseMessageWithoutId & { id: string }; |
|
|
| export function getMostRecentUserMessage(messages: Array<UIMessage>) { |
| const userMessages = messages.filter((message) => message.role === 'user'); |
| return userMessages.at(-1); |
| } |
|
|
| export function getDocumentTimestampByIndex( |
| documents: Array<Document>, |
| index: number, |
| ) { |
| if (!documents) return new Date(); |
| if (index > documents.length) return new Date(); |
|
|
| 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; |
| } |
|
|
| export function sanitizeText(text: string) { |
| return text.replace('<has_function_call>', ''); |
| } |
|
|
| export function convertToUIMessages(messages: DBMessage[]): ChatMessage[] { |
| return messages.map((message) => ({ |
| id: message.id, |
| role: message.role as 'user' | 'assistant' | 'system', |
| parts: message.parts as UIMessagePart<CustomUIDataTypes, ChatTools>[], |
| metadata: { |
| createdAt: formatISO(message.createdAt), |
| }, |
| })); |
| } |
|
|
| export function getTextFromMessage(message: ChatMessage): string { |
| return message.parts |
| .filter((part) => part.type === 'text') |
| .map((part) => part.text) |
| .join(''); |
| } |
|
|