'use client'; import { DefaultChatTransport } from 'ai'; import { useChat } from '@ai-sdk/react'; import { useEffect, useState, useRef } from 'react'; import useSWR, { useSWRConfig } from 'swr'; import { ChatHeader } from '@/components/chat-header'; import type { Vote } from '@/lib/db/schema'; import { fetcher, fetchWithErrorHandlers, generateUUID } from '@/lib/utils'; import { Artifact } from './artifact'; import { MultimodalInput } from './multimodal-input'; import { Messages } from './messages'; import type { VisibilityType } from './visibility-selector'; import { useArtifactSelector } from '@/hooks/use-artifact'; import { unstable_serialize } from 'swr/infinite'; import { getChatHistoryPaginationKey } from './sidebar-history'; import { toast } from './toast'; import type { Session } from 'next-auth'; import { useSearchParams } from 'next/navigation'; import { useChatVisibility } from '@/hooks/use-chat-visibility'; import { useAutoResume } from '@/hooks/use-auto-resume'; import { ChatSDKError } from '@/lib/errors'; import type { Attachment, ChatMessage } from '@/lib/types'; import type { AppUsage } from '@/lib/usage'; import { useDataStream } from './data-stream-provider'; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from '@/components/ui/alert-dialog'; export function Chat({ id, initialMessages, initialChatModel, initialVisibilityType, isReadonly, session, autoResume, initialLastContext, }: { id: string; initialMessages: ChatMessage[]; initialChatModel: string; initialVisibilityType: VisibilityType; isReadonly: boolean; session: Session; autoResume: boolean; initialLastContext?: AppUsage; }) { const { visibilityType } = useChatVisibility({ chatId: id, initialVisibilityType, }); const { mutate } = useSWRConfig(); const { setDataStream } = useDataStream(); const [input, setInput] = useState(''); const [usage, setUsage] = useState(initialLastContext); const [showCreditCardAlert, setShowCreditCardAlert] = useState(false); const [currentModelId, setCurrentModelId] = useState(initialChatModel); const currentModelIdRef = useRef(currentModelId); useEffect(() => { currentModelIdRef.current = currentModelId; }, [currentModelId]); const { messages, setMessages, sendMessage, status, stop, regenerate, resumeStream, } = useChat({ id, messages: initialMessages, experimental_throttle: 100, generateId: generateUUID, transport: new DefaultChatTransport({ api: '/api/chat', fetch: fetchWithErrorHandlers, prepareSendMessagesRequest({ messages, id, body }) { return { body: { id, message: messages.at(-1), selectedChatModel: currentModelIdRef.current, selectedVisibilityType: visibilityType, ...body, }, }; }, }), onData: (dataPart) => { setDataStream((ds) => (ds ? [...ds, dataPart] : [])); if (dataPart.type === 'data-usage') setUsage(dataPart.data); }, onFinish: () => { mutate(unstable_serialize(getChatHistoryPaginationKey)); }, onError: (error) => { if (error instanceof ChatSDKError) { // Check if it's a credit card error if ( error.message?.includes('AI Gateway requires a valid credit card') ) { setShowCreditCardAlert(true); } else { toast({ type: 'error', description: error.message, }); } } }, }); const searchParams = useSearchParams(); const query = searchParams.get('query'); const [hasAppendedQuery, setHasAppendedQuery] = useState(false); useEffect(() => { if (query && !hasAppendedQuery) { sendMessage({ role: 'user' as const, parts: [{ type: 'text', text: query }], }); setHasAppendedQuery(true); window.history.replaceState({}, '', `/chat/${id}`); } }, [query, sendMessage, hasAppendedQuery, id]); const { data: votes } = useSWR>( messages.length >= 2 ? `/api/vote?chatId=${id}` : null, fetcher, ); const [attachments, setAttachments] = useState>([]); const isArtifactVisible = useArtifactSelector((state) => state.isVisible); useAutoResume({ autoResume, initialMessages, resumeStream, setMessages, }); return ( <>
{!isReadonly && ( )}
Activate AI Gateway This application requires{' '} {process.env.NODE_ENV === 'production' ? 'the owner' : 'you'} to activate Vercel AI Gateway. Cancel { window.open( 'https://vercel.com/d?to=%2F%5Bteam%5D%2F%7E%2Fai%3Fmodal%3Dadd-credit-card', '_blank', ); window.location.href = '/'; }} > Activate ); }