chore: update to ai sdk v5 beta (#1074)
This commit is contained in:
parent
7d8e71383f
commit
4c281fe09d
54 changed files with 1372 additions and 1060 deletions
|
|
@ -1,20 +1,20 @@
|
|||
import { PreviewMessage, ThinkingMessage } from './message';
|
||||
import type { Vote } from '@/lib/db/schema';
|
||||
import type { UIMessage } from 'ai';
|
||||
import { memo } from 'react';
|
||||
import equal from 'fast-deep-equal';
|
||||
import type { UIArtifact } from './artifact';
|
||||
import type { UseChatHelpers } from '@ai-sdk/react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { useMessages } from '@/hooks/use-messages';
|
||||
import type { ChatMessage } from '@/lib/types';
|
||||
|
||||
interface ArtifactMessagesProps {
|
||||
chatId: string;
|
||||
status: UseChatHelpers['status'];
|
||||
status: UseChatHelpers<ChatMessage>['status'];
|
||||
votes: Array<Vote> | undefined;
|
||||
messages: Array<UIMessage>;
|
||||
setMessages: UseChatHelpers['setMessages'];
|
||||
reload: UseChatHelpers['reload'];
|
||||
messages: ChatMessage[];
|
||||
setMessages: UseChatHelpers<ChatMessage>['setMessages'];
|
||||
regenerate: UseChatHelpers<ChatMessage>['regenerate'];
|
||||
isReadonly: boolean;
|
||||
artifactStatus: UIArtifact['status'];
|
||||
}
|
||||
|
|
@ -25,7 +25,7 @@ function PureArtifactMessages({
|
|||
votes,
|
||||
messages,
|
||||
setMessages,
|
||||
reload,
|
||||
regenerate,
|
||||
isReadonly,
|
||||
}: ArtifactMessagesProps) {
|
||||
const {
|
||||
|
|
@ -56,7 +56,7 @@ function PureArtifactMessages({
|
|||
: undefined
|
||||
}
|
||||
setMessages={setMessages}
|
||||
reload={reload}
|
||||
regenerate={regenerate}
|
||||
isReadonly={isReadonly}
|
||||
requiresScrollPadding={
|
||||
hasSentMessage && index === messages.length - 1
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import type { Attachment, UIMessage } from 'ai';
|
||||
import { formatDistance } from 'date-fns';
|
||||
import { AnimatePresence, motion } from 'framer-motion';
|
||||
import {
|
||||
|
|
@ -28,6 +27,7 @@ import { textArtifact } from '@/artifacts/text/client';
|
|||
import equal from 'fast-deep-equal';
|
||||
import type { UseChatHelpers } from '@ai-sdk/react';
|
||||
import type { VisibilityType } from './visibility-selector';
|
||||
import type { Attachment, ChatMessage } from '@/lib/types';
|
||||
|
||||
export const artifactDefinitions = [
|
||||
textArtifact,
|
||||
|
|
@ -56,32 +56,30 @@ function PureArtifact({
|
|||
chatId,
|
||||
input,
|
||||
setInput,
|
||||
handleSubmit,
|
||||
status,
|
||||
stop,
|
||||
attachments,
|
||||
setAttachments,
|
||||
append,
|
||||
sendMessage,
|
||||
messages,
|
||||
setMessages,
|
||||
reload,
|
||||
regenerate,
|
||||
votes,
|
||||
isReadonly,
|
||||
selectedVisibilityType,
|
||||
}: {
|
||||
chatId: string;
|
||||
input: string;
|
||||
setInput: UseChatHelpers['setInput'];
|
||||
status: UseChatHelpers['status'];
|
||||
stop: UseChatHelpers['stop'];
|
||||
attachments: Array<Attachment>;
|
||||
setAttachments: Dispatch<SetStateAction<Array<Attachment>>>;
|
||||
messages: Array<UIMessage>;
|
||||
setMessages: UseChatHelpers['setMessages'];
|
||||
setInput: Dispatch<SetStateAction<string>>;
|
||||
status: UseChatHelpers<ChatMessage>['status'];
|
||||
stop: UseChatHelpers<ChatMessage>['stop'];
|
||||
attachments: Attachment[];
|
||||
setAttachments: Dispatch<SetStateAction<Attachment[]>>;
|
||||
messages: ChatMessage[];
|
||||
setMessages: UseChatHelpers<ChatMessage>['setMessages'];
|
||||
votes: Array<Vote> | undefined;
|
||||
append: UseChatHelpers['append'];
|
||||
handleSubmit: UseChatHelpers['handleSubmit'];
|
||||
reload: UseChatHelpers['reload'];
|
||||
sendMessage: UseChatHelpers<ChatMessage>['sendMessage'];
|
||||
regenerate: UseChatHelpers<ChatMessage>['regenerate'];
|
||||
isReadonly: boolean;
|
||||
selectedVisibilityType: VisibilityType;
|
||||
}) {
|
||||
|
|
@ -319,7 +317,7 @@ function PureArtifact({
|
|||
votes={votes}
|
||||
messages={messages}
|
||||
setMessages={setMessages}
|
||||
reload={reload}
|
||||
regenerate={regenerate}
|
||||
isReadonly={isReadonly}
|
||||
artifactStatus={artifact.status}
|
||||
/>
|
||||
|
|
@ -329,13 +327,12 @@ function PureArtifact({
|
|||
chatId={chatId}
|
||||
input={input}
|
||||
setInput={setInput}
|
||||
handleSubmit={handleSubmit}
|
||||
status={status}
|
||||
stop={stop}
|
||||
attachments={attachments}
|
||||
setAttachments={setAttachments}
|
||||
messages={messages}
|
||||
append={append}
|
||||
sendMessage={sendMessage}
|
||||
className="bg-background dark:bg-muted"
|
||||
setMessages={setMessages}
|
||||
selectedVisibilityType={selectedVisibilityType}
|
||||
|
|
@ -476,7 +473,7 @@ function PureArtifact({
|
|||
<Toolbar
|
||||
isToolbarVisible={isToolbarVisible}
|
||||
setIsToolbarVisible={setIsToolbarVisible}
|
||||
append={append}
|
||||
sendMessage={sendMessage}
|
||||
status={status}
|
||||
stop={stop}
|
||||
setMessages={setMessages}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
'use client';
|
||||
|
||||
import type { Attachment, UIMessage } from 'ai';
|
||||
import { DefaultChatTransport } from 'ai';
|
||||
import { useChat } from '@ai-sdk/react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import useSWR, { useSWRConfig } from 'swr';
|
||||
|
|
@ -20,6 +20,8 @@ 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 { useDataStream } from './data-stream-provider';
|
||||
|
||||
export function Chat({
|
||||
id,
|
||||
|
|
@ -31,45 +33,54 @@ export function Chat({
|
|||
autoResume,
|
||||
}: {
|
||||
id: string;
|
||||
initialMessages: Array<UIMessage>;
|
||||
initialMessages: ChatMessage[];
|
||||
initialChatModel: string;
|
||||
initialVisibilityType: VisibilityType;
|
||||
isReadonly: boolean;
|
||||
session: Session;
|
||||
autoResume: boolean;
|
||||
}) {
|
||||
const { mutate } = useSWRConfig();
|
||||
|
||||
const { visibilityType } = useChatVisibility({
|
||||
chatId: id,
|
||||
initialVisibilityType,
|
||||
});
|
||||
|
||||
const { mutate } = useSWRConfig();
|
||||
const { setDataStream } = useDataStream();
|
||||
|
||||
const [input, setInput] = useState<string>('');
|
||||
|
||||
const {
|
||||
messages,
|
||||
setMessages,
|
||||
handleSubmit,
|
||||
input,
|
||||
setInput,
|
||||
append,
|
||||
sendMessage,
|
||||
status,
|
||||
stop,
|
||||
reload,
|
||||
experimental_resume,
|
||||
data,
|
||||
} = useChat({
|
||||
regenerate,
|
||||
resumeStream,
|
||||
} = useChat<ChatMessage>({
|
||||
id,
|
||||
initialMessages,
|
||||
messages: initialMessages,
|
||||
experimental_throttle: 100,
|
||||
sendExtraMessageFields: true,
|
||||
generateId: generateUUID,
|
||||
fetch: fetchWithErrorHandlers,
|
||||
experimental_prepareRequestBody: (body) => ({
|
||||
id,
|
||||
message: body.messages.at(-1),
|
||||
selectedChatModel: initialChatModel,
|
||||
selectedVisibilityType: visibilityType,
|
||||
transport: new DefaultChatTransport({
|
||||
api: '/api/chat',
|
||||
fetch: fetchWithErrorHandlers,
|
||||
prepareSendMessagesRequest({ messages, id, body }) {
|
||||
return {
|
||||
body: {
|
||||
id,
|
||||
message: messages.at(-1),
|
||||
selectedChatModel: initialChatModel,
|
||||
selectedVisibilityType: visibilityType,
|
||||
...body,
|
||||
},
|
||||
};
|
||||
},
|
||||
}),
|
||||
onData: (dataPart) => {
|
||||
setDataStream((ds) => (ds ? [...ds, dataPart] : []));
|
||||
},
|
||||
onFinish: () => {
|
||||
mutate(unstable_serialize(getChatHistoryPaginationKey));
|
||||
},
|
||||
|
|
@ -90,15 +101,15 @@ export function Chat({
|
|||
|
||||
useEffect(() => {
|
||||
if (query && !hasAppendedQuery) {
|
||||
append({
|
||||
role: 'user',
|
||||
content: query,
|
||||
sendMessage({
|
||||
role: 'user' as const,
|
||||
parts: [{ type: 'text', text: query }],
|
||||
});
|
||||
|
||||
setHasAppendedQuery(true);
|
||||
window.history.replaceState({}, '', `/chat/${id}`);
|
||||
}
|
||||
}, [query, append, hasAppendedQuery, id]);
|
||||
}, [query, sendMessage, hasAppendedQuery, id]);
|
||||
|
||||
const { data: votes } = useSWR<Array<Vote>>(
|
||||
messages.length >= 2 ? `/api/vote?chatId=${id}` : null,
|
||||
|
|
@ -111,8 +122,7 @@ export function Chat({
|
|||
useAutoResume({
|
||||
autoResume,
|
||||
initialMessages,
|
||||
experimental_resume,
|
||||
data,
|
||||
resumeStream,
|
||||
setMessages,
|
||||
});
|
||||
|
||||
|
|
@ -133,7 +143,7 @@ export function Chat({
|
|||
votes={votes}
|
||||
messages={messages}
|
||||
setMessages={setMessages}
|
||||
reload={reload}
|
||||
regenerate={regenerate}
|
||||
isReadonly={isReadonly}
|
||||
isArtifactVisible={isArtifactVisible}
|
||||
/>
|
||||
|
|
@ -144,14 +154,13 @@ export function Chat({
|
|||
chatId={id}
|
||||
input={input}
|
||||
setInput={setInput}
|
||||
handleSubmit={handleSubmit}
|
||||
status={status}
|
||||
stop={stop}
|
||||
attachments={attachments}
|
||||
setAttachments={setAttachments}
|
||||
messages={messages}
|
||||
setMessages={setMessages}
|
||||
append={append}
|
||||
sendMessage={sendMessage}
|
||||
selectedVisibilityType={visibilityType}
|
||||
/>
|
||||
)}
|
||||
|
|
@ -162,15 +171,14 @@ export function Chat({
|
|||
chatId={id}
|
||||
input={input}
|
||||
setInput={setInput}
|
||||
handleSubmit={handleSubmit}
|
||||
status={status}
|
||||
stop={stop}
|
||||
attachments={attachments}
|
||||
setAttachments={setAttachments}
|
||||
append={append}
|
||||
sendMessage={sendMessage}
|
||||
messages={messages}
|
||||
setMessages={setMessages}
|
||||
reload={reload}
|
||||
regenerate={regenerate}
|
||||
votes={votes}
|
||||
isReadonly={isReadonly}
|
||||
selectedVisibilityType={visibilityType}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import { Suggestion } from '@/lib/db/schema';
|
||||
import { UseChatHelpers } from '@ai-sdk/react';
|
||||
import { ComponentType, Dispatch, ReactNode, SetStateAction } from 'react';
|
||||
import { DataStreamDelta } from './data-stream-handler';
|
||||
import { UIArtifact } from './artifact';
|
||||
import type { Suggestion } from '@/lib/db/schema';
|
||||
import type { UseChatHelpers } from '@ai-sdk/react';
|
||||
import type { ComponentType, Dispatch, ReactNode, SetStateAction } from 'react';
|
||||
import type { UIArtifact } from './artifact';
|
||||
import type { ChatMessage, CustomUIDataTypes } from '@/lib/types';
|
||||
import type { DataUIPart } from 'ai';
|
||||
|
||||
export type ArtifactActionContext<M = any> = {
|
||||
content: string;
|
||||
|
|
@ -23,7 +24,7 @@ type ArtifactAction<M = any> = {
|
|||
};
|
||||
|
||||
export type ArtifactToolbarContext = {
|
||||
appendMessage: UseChatHelpers['append'];
|
||||
sendMessage: UseChatHelpers<ChatMessage>['sendMessage'];
|
||||
};
|
||||
|
||||
export type ArtifactToolbarItem = {
|
||||
|
|
@ -63,7 +64,7 @@ type ArtifactConfig<T extends string, M = any> = {
|
|||
onStreamPart: (args: {
|
||||
setMetadata: Dispatch<SetStateAction<M>>;
|
||||
setArtifact: Dispatch<SetStateAction<UIArtifact>>;
|
||||
streamPart: DataStreamDelta;
|
||||
streamPart: DataUIPart<CustomUIDataTypes>;
|
||||
}) => void;
|
||||
};
|
||||
|
||||
|
|
@ -77,7 +78,7 @@ export class Artifact<T extends string, M = any> {
|
|||
readonly onStreamPart: (args: {
|
||||
setMetadata: Dispatch<SetStateAction<M>>;
|
||||
setArtifact: Dispatch<SetStateAction<UIArtifact>>;
|
||||
streamPart: DataStreamDelta;
|
||||
streamPart: DataUIPart<CustomUIDataTypes>;
|
||||
}) => void;
|
||||
|
||||
constructor(config: ArtifactConfig<T, M>) {
|
||||
|
|
|
|||
|
|
@ -1,28 +1,13 @@
|
|||
'use client';
|
||||
|
||||
import { useChat } from '@ai-sdk/react';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { artifactDefinitions, ArtifactKind } from './artifact';
|
||||
import { Suggestion } from '@/lib/db/schema';
|
||||
import { artifactDefinitions } from './artifact';
|
||||
import { initialArtifactData, useArtifact } from '@/hooks/use-artifact';
|
||||
import { useDataStream } from './data-stream-provider';
|
||||
|
||||
export type DataStreamDelta = {
|
||||
type:
|
||||
| 'text-delta'
|
||||
| 'code-delta'
|
||||
| 'sheet-delta'
|
||||
| 'image-delta'
|
||||
| 'title'
|
||||
| 'id'
|
||||
| 'suggestion'
|
||||
| 'clear'
|
||||
| 'finish'
|
||||
| 'kind';
|
||||
content: string | Suggestion;
|
||||
};
|
||||
export function DataStreamHandler() {
|
||||
const { dataStream } = useDataStream();
|
||||
|
||||
export function DataStreamHandler({ id }: { id: string }) {
|
||||
const { data: dataStream } = useChat({ id });
|
||||
const { artifact, setArtifact, setMetadata } = useArtifact();
|
||||
const lastProcessedIndex = useRef(-1);
|
||||
|
||||
|
|
@ -32,7 +17,7 @@ export function DataStreamHandler({ id }: { id: string }) {
|
|||
const newDeltas = dataStream.slice(lastProcessedIndex.current + 1);
|
||||
lastProcessedIndex.current = dataStream.length - 1;
|
||||
|
||||
(newDeltas as DataStreamDelta[]).forEach((delta: DataStreamDelta) => {
|
||||
newDeltas.forEach((delta) => {
|
||||
const artifactDefinition = artifactDefinitions.find(
|
||||
(artifactDefinition) => artifactDefinition.kind === artifact.kind,
|
||||
);
|
||||
|
|
@ -51,35 +36,35 @@ export function DataStreamHandler({ id }: { id: string }) {
|
|||
}
|
||||
|
||||
switch (delta.type) {
|
||||
case 'id':
|
||||
case 'data-id':
|
||||
return {
|
||||
...draftArtifact,
|
||||
documentId: delta.content as string,
|
||||
documentId: delta.data,
|
||||
status: 'streaming',
|
||||
};
|
||||
|
||||
case 'title':
|
||||
case 'data-title':
|
||||
return {
|
||||
...draftArtifact,
|
||||
title: delta.content as string,
|
||||
title: delta.data,
|
||||
status: 'streaming',
|
||||
};
|
||||
|
||||
case 'kind':
|
||||
case 'data-kind':
|
||||
return {
|
||||
...draftArtifact,
|
||||
kind: delta.content as ArtifactKind,
|
||||
kind: delta.data,
|
||||
status: 'streaming',
|
||||
};
|
||||
|
||||
case 'clear':
|
||||
case 'data-clear':
|
||||
return {
|
||||
...draftArtifact,
|
||||
content: '',
|
||||
status: 'streaming',
|
||||
};
|
||||
|
||||
case 'finish':
|
||||
case 'data-finish':
|
||||
return {
|
||||
...draftArtifact,
|
||||
status: 'idle',
|
||||
|
|
|
|||
40
components/data-stream-provider.tsx
Normal file
40
components/data-stream-provider.tsx
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
'use client';
|
||||
|
||||
import React, { createContext, useContext, useMemo, useState } from 'react';
|
||||
import type { DataUIPart } from 'ai';
|
||||
import type { CustomUIDataTypes } from '@/lib/types';
|
||||
|
||||
interface DataStreamContextValue {
|
||||
dataStream: DataUIPart<CustomUIDataTypes>[];
|
||||
setDataStream: React.Dispatch<
|
||||
React.SetStateAction<DataUIPart<CustomUIDataTypes>[]>
|
||||
>;
|
||||
}
|
||||
|
||||
const DataStreamContext = createContext<DataStreamContextValue | null>(null);
|
||||
|
||||
export function DataStreamProvider({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
const [dataStream, setDataStream] = useState<DataUIPart<CustomUIDataTypes>[]>(
|
||||
[],
|
||||
);
|
||||
|
||||
const value = useMemo(() => ({ dataStream, setDataStream }), [dataStream]);
|
||||
|
||||
return (
|
||||
<DataStreamContext.Provider value={value}>
|
||||
{children}
|
||||
</DataStreamContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useDataStream() {
|
||||
const context = useContext(DataStreamContext);
|
||||
if (!context) {
|
||||
throw new Error('useDataStream must be used within a DataStreamProvider');
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
|
@ -2,16 +2,16 @@
|
|||
|
||||
import {
|
||||
memo,
|
||||
MouseEvent,
|
||||
type MouseEvent,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
} from 'react';
|
||||
import { ArtifactKind, UIArtifact } from './artifact';
|
||||
import type { ArtifactKind, UIArtifact } from './artifact';
|
||||
import { FileIcon, FullscreenIcon, ImageIcon, LoaderIcon } from './icons';
|
||||
import { cn, fetcher } from '@/lib/utils';
|
||||
import { Document } from '@/lib/db/schema';
|
||||
import type { Document } from '@/lib/db/schema';
|
||||
import { InlineDocumentSkeleton } from './document-skeleton';
|
||||
import useSWR from 'swr';
|
||||
import { Editor } from './text-editor';
|
||||
|
|
@ -73,7 +73,7 @@ export function DocumentPreview({
|
|||
return (
|
||||
<DocumentToolCall
|
||||
type="create"
|
||||
args={{ title: args.title }}
|
||||
args={{ title: args.title, kind: args.kind }}
|
||||
isReadonly={isReadonly}
|
||||
/>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -88,7 +88,10 @@ export const DocumentToolResult = memo(PureDocumentToolResult, () => true);
|
|||
|
||||
interface DocumentToolCallProps {
|
||||
type: 'create' | 'update' | 'request-suggestions';
|
||||
args: { title: string };
|
||||
args:
|
||||
| { title: string; kind: ArtifactKind } // for create
|
||||
| { id: string; description: string } // for update
|
||||
| { documentId: string }; // for request-suggestions
|
||||
isReadonly: boolean;
|
||||
}
|
||||
|
||||
|
|
@ -139,7 +142,15 @@ function PureDocumentToolCall({
|
|||
</div>
|
||||
|
||||
<div className="text-left">
|
||||
{`${getActionText(type, 'present')} ${args.title ? `"${args.title}"` : ''}`}
|
||||
{`${getActionText(type, 'present')} ${
|
||||
type === 'create' && 'title' in args && args.title
|
||||
? `"${args.title}"`
|
||||
: type === 'update' && 'description' in args
|
||||
? `"${args.description}"`
|
||||
: type === 'request-suggestions'
|
||||
? 'for document'
|
||||
: ''
|
||||
}`}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import type { Message } from 'ai';
|
||||
import { useSWRConfig } from 'swr';
|
||||
import { useCopyToClipboard } from 'usehooks-ts';
|
||||
|
||||
|
|
@ -15,6 +14,7 @@ import {
|
|||
import { memo } from 'react';
|
||||
import equal from 'fast-deep-equal';
|
||||
import { toast } from 'sonner';
|
||||
import type { ChatMessage } from '@/lib/types';
|
||||
|
||||
export function PureMessageActions({
|
||||
chatId,
|
||||
|
|
@ -23,7 +23,7 @@ export function PureMessageActions({
|
|||
isLoading,
|
||||
}: {
|
||||
chatId: string;
|
||||
message: Message;
|
||||
message: ChatMessage;
|
||||
vote: Vote | undefined;
|
||||
isLoading: boolean;
|
||||
}) {
|
||||
|
|
|
|||
|
|
@ -1,28 +1,37 @@
|
|||
'use client';
|
||||
|
||||
import { ChatRequestOptions, Message } from 'ai';
|
||||
import { Button } from './ui/button';
|
||||
import { Dispatch, SetStateAction, useEffect, useRef, useState } from 'react';
|
||||
import {
|
||||
type Dispatch,
|
||||
type SetStateAction,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { Textarea } from './ui/textarea';
|
||||
import { deleteTrailingMessages } from '@/app/(chat)/actions';
|
||||
import { UseChatHelpers } from '@ai-sdk/react';
|
||||
import type { UseChatHelpers } from '@ai-sdk/react';
|
||||
import type { ChatMessage } from '@/lib/types';
|
||||
import { getTextFromMessage } from '@/lib/utils';
|
||||
|
||||
export type MessageEditorProps = {
|
||||
message: Message;
|
||||
message: ChatMessage;
|
||||
setMode: Dispatch<SetStateAction<'view' | 'edit'>>;
|
||||
setMessages: UseChatHelpers['setMessages'];
|
||||
reload: UseChatHelpers['reload'];
|
||||
setMessages: UseChatHelpers<ChatMessage>['setMessages'];
|
||||
regenerate: UseChatHelpers<ChatMessage>['regenerate'];
|
||||
};
|
||||
|
||||
export function MessageEditor({
|
||||
message,
|
||||
setMode,
|
||||
setMessages,
|
||||
reload,
|
||||
regenerate,
|
||||
}: MessageEditorProps) {
|
||||
const [isSubmitting, setIsSubmitting] = useState<boolean>(false);
|
||||
|
||||
const [draftContent, setDraftContent] = useState<string>(message.content);
|
||||
const [draftContent, setDraftContent] = useState<string>(
|
||||
getTextFromMessage(message),
|
||||
);
|
||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -75,14 +84,12 @@ export function MessageEditor({
|
|||
id: message.id,
|
||||
});
|
||||
|
||||
// @ts-expect-error todo: support UIMessage in setMessages
|
||||
setMessages((messages) => {
|
||||
const index = messages.findIndex((m) => m.id === message.id);
|
||||
|
||||
if (index !== -1) {
|
||||
const updatedMessage = {
|
||||
const updatedMessage: ChatMessage = {
|
||||
...message,
|
||||
content: draftContent,
|
||||
parts: [{ type: 'text', text: draftContent }],
|
||||
};
|
||||
|
||||
|
|
@ -93,7 +100,7 @@ export function MessageEditor({
|
|||
});
|
||||
|
||||
setMode('view');
|
||||
reload();
|
||||
regenerate();
|
||||
}}
|
||||
>
|
||||
{isSubmitting ? 'Sending...' : 'Send'}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
'use client';
|
||||
|
||||
import type { UIMessage } from 'ai';
|
||||
import cx from 'classnames';
|
||||
import { AnimatePresence, motion } from 'framer-motion';
|
||||
import { memo, useState } from 'react';
|
||||
|
|
@ -19,6 +17,11 @@ import { MessageEditor } from './message-editor';
|
|||
import { DocumentPreview } from './document-preview';
|
||||
import { MessageReasoning } from './message-reasoning';
|
||||
import type { UseChatHelpers } from '@ai-sdk/react';
|
||||
import type { ChatMessage } from '@/lib/types';
|
||||
import { useDataStream } from './data-stream-provider';
|
||||
|
||||
// Type narrowing is handled by TypeScript's control flow analysis
|
||||
// The AI SDK provides proper discriminated unions for tool calls
|
||||
|
||||
const PurePreviewMessage = ({
|
||||
chatId,
|
||||
|
|
@ -26,21 +29,27 @@ const PurePreviewMessage = ({
|
|||
vote,
|
||||
isLoading,
|
||||
setMessages,
|
||||
reload,
|
||||
regenerate,
|
||||
isReadonly,
|
||||
requiresScrollPadding,
|
||||
}: {
|
||||
chatId: string;
|
||||
message: UIMessage;
|
||||
message: ChatMessage;
|
||||
vote: Vote | undefined;
|
||||
isLoading: boolean;
|
||||
setMessages: UseChatHelpers['setMessages'];
|
||||
reload: UseChatHelpers['reload'];
|
||||
setMessages: UseChatHelpers<ChatMessage>['setMessages'];
|
||||
regenerate: UseChatHelpers<ChatMessage>['regenerate'];
|
||||
isReadonly: boolean;
|
||||
requiresScrollPadding: boolean;
|
||||
}) => {
|
||||
const [mode, setMode] = useState<'view' | 'edit'>('view');
|
||||
|
||||
const attachmentsFromMessage = message.parts.filter(
|
||||
(part) => part.type === 'file',
|
||||
);
|
||||
|
||||
useDataStream();
|
||||
|
||||
return (
|
||||
<AnimatePresence>
|
||||
<motion.div
|
||||
|
|
@ -72,20 +81,23 @@ const PurePreviewMessage = ({
|
|||
'min-h-96': message.role === 'assistant' && requiresScrollPadding,
|
||||
})}
|
||||
>
|
||||
{message.experimental_attachments &&
|
||||
message.experimental_attachments.length > 0 && (
|
||||
<div
|
||||
data-testid={`message-attachments`}
|
||||
className="flex flex-row justify-end gap-2"
|
||||
>
|
||||
{message.experimental_attachments.map((attachment) => (
|
||||
<PreviewAttachment
|
||||
key={attachment.url}
|
||||
attachment={attachment}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{attachmentsFromMessage.length > 0 && (
|
||||
<div
|
||||
data-testid={`message-attachments`}
|
||||
className="flex flex-row justify-end gap-2"
|
||||
>
|
||||
{attachmentsFromMessage.map((attachment) => (
|
||||
<PreviewAttachment
|
||||
key={attachment.url}
|
||||
attachment={{
|
||||
name: attachment.filename ?? 'file',
|
||||
contentType: attachment.mediaType,
|
||||
url: attachment.url,
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{message.parts?.map((part, index) => {
|
||||
const { type } = part;
|
||||
|
|
@ -96,7 +108,7 @@ const PurePreviewMessage = ({
|
|||
<MessageReasoning
|
||||
key={key}
|
||||
isLoading={isLoading}
|
||||
reasoning={part.reasoning}
|
||||
reasoning={part.text}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
@ -146,75 +158,151 @@ const PurePreviewMessage = ({
|
|||
message={message}
|
||||
setMode={setMode}
|
||||
setMessages={setMessages}
|
||||
reload={reload}
|
||||
regenerate={regenerate}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (type === 'tool-invocation') {
|
||||
const { toolInvocation } = part;
|
||||
const { toolName, toolCallId, state } = toolInvocation;
|
||||
|
||||
if (state === 'call') {
|
||||
const { args } = toolInvocation;
|
||||
if (type === 'tool-getWeather') {
|
||||
const { toolCallId, state } = part;
|
||||
|
||||
if (state === 'input-available') {
|
||||
return (
|
||||
<div
|
||||
key={toolCallId}
|
||||
className={cx({
|
||||
skeleton: ['getWeather'].includes(toolName),
|
||||
})}
|
||||
>
|
||||
{toolName === 'getWeather' ? (
|
||||
<Weather />
|
||||
) : toolName === 'createDocument' ? (
|
||||
<DocumentPreview isReadonly={isReadonly} args={args} />
|
||||
) : toolName === 'updateDocument' ? (
|
||||
<DocumentToolCall
|
||||
type="update"
|
||||
args={args}
|
||||
isReadonly={isReadonly}
|
||||
/>
|
||||
) : toolName === 'requestSuggestions' ? (
|
||||
<DocumentToolCall
|
||||
type="request-suggestions"
|
||||
args={args}
|
||||
isReadonly={isReadonly}
|
||||
/>
|
||||
) : null}
|
||||
<div key={toolCallId} className="skeleton">
|
||||
<Weather />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (state === 'result') {
|
||||
const { result } = toolInvocation;
|
||||
if (state === 'output-available') {
|
||||
const { output } = part;
|
||||
return (
|
||||
<div key={toolCallId}>
|
||||
<Weather weatherAtLocation={output} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (type === 'tool-createDocument') {
|
||||
const { toolCallId, state } = part;
|
||||
|
||||
if (state === 'input-available') {
|
||||
const { input } = part;
|
||||
return (
|
||||
<div key={toolCallId}>
|
||||
<DocumentPreview isReadonly={isReadonly} args={input} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (state === 'output-available') {
|
||||
const { output } = part;
|
||||
|
||||
if ('error' in output) {
|
||||
return (
|
||||
<div
|
||||
key={toolCallId}
|
||||
className="text-red-500 p-2 border rounded"
|
||||
>
|
||||
Error: {String(output.error)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div key={toolCallId}>
|
||||
{toolName === 'getWeather' ? (
|
||||
<Weather weatherAtLocation={result} />
|
||||
) : toolName === 'createDocument' ? (
|
||||
<DocumentPreview
|
||||
isReadonly={isReadonly}
|
||||
result={result}
|
||||
/>
|
||||
) : toolName === 'updateDocument' ? (
|
||||
<DocumentToolResult
|
||||
type="update"
|
||||
result={result}
|
||||
isReadonly={isReadonly}
|
||||
/>
|
||||
) : toolName === 'requestSuggestions' ? (
|
||||
<DocumentToolResult
|
||||
type="request-suggestions"
|
||||
result={result}
|
||||
isReadonly={isReadonly}
|
||||
/>
|
||||
) : (
|
||||
<pre>{JSON.stringify(result, null, 2)}</pre>
|
||||
)}
|
||||
<DocumentPreview
|
||||
isReadonly={isReadonly}
|
||||
result={output}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (type === 'tool-updateDocument') {
|
||||
const { toolCallId, state } = part;
|
||||
|
||||
if (state === 'input-available') {
|
||||
const { input } = part;
|
||||
|
||||
return (
|
||||
<div key={toolCallId}>
|
||||
<DocumentToolCall
|
||||
type="update"
|
||||
args={input}
|
||||
isReadonly={isReadonly}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (state === 'output-available') {
|
||||
const { output } = part;
|
||||
|
||||
if ('error' in output) {
|
||||
return (
|
||||
<div
|
||||
key={toolCallId}
|
||||
className="text-red-500 p-2 border rounded"
|
||||
>
|
||||
Error: {String(output.error)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div key={toolCallId}>
|
||||
<DocumentToolResult
|
||||
type="update"
|
||||
result={output}
|
||||
isReadonly={isReadonly}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (type === 'tool-requestSuggestions') {
|
||||
const { toolCallId, state } = part;
|
||||
|
||||
if (state === 'input-available') {
|
||||
const { input } = part;
|
||||
return (
|
||||
<div key={toolCallId}>
|
||||
<DocumentToolCall
|
||||
type="request-suggestions"
|
||||
args={input}
|
||||
isReadonly={isReadonly}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (state === 'output-available') {
|
||||
const { output } = part;
|
||||
|
||||
if ('error' in output) {
|
||||
return (
|
||||
<div
|
||||
key={toolCallId}
|
||||
className="text-red-500 p-2 border rounded"
|
||||
>
|
||||
Error: {String(output.error)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div key={toolCallId}>
|
||||
<DocumentToolResult
|
||||
type="request-suggestions"
|
||||
result={output}
|
||||
isReadonly={isReadonly}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -247,7 +335,7 @@ export const PreviewMessage = memo(
|
|||
if (!equal(prevProps.message.parts, nextProps.message.parts)) return false;
|
||||
if (!equal(prevProps.vote, nextProps.vote)) return false;
|
||||
|
||||
return true;
|
||||
return false;
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import type { UIMessage } from 'ai';
|
||||
import { PreviewMessage, ThinkingMessage } from './message';
|
||||
import { Greeting } from './greeting';
|
||||
import { memo } from 'react';
|
||||
|
|
@ -7,14 +6,16 @@ import equal from 'fast-deep-equal';
|
|||
import type { UseChatHelpers } from '@ai-sdk/react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { useMessages } from '@/hooks/use-messages';
|
||||
import type { ChatMessage } from '@/lib/types';
|
||||
import { useDataStream } from './data-stream-provider';
|
||||
|
||||
interface MessagesProps {
|
||||
chatId: string;
|
||||
status: UseChatHelpers['status'];
|
||||
status: UseChatHelpers<ChatMessage>['status'];
|
||||
votes: Array<Vote> | undefined;
|
||||
messages: Array<UIMessage>;
|
||||
setMessages: UseChatHelpers['setMessages'];
|
||||
reload: UseChatHelpers['reload'];
|
||||
messages: ChatMessage[];
|
||||
setMessages: UseChatHelpers<ChatMessage>['setMessages'];
|
||||
regenerate: UseChatHelpers<ChatMessage>['regenerate'];
|
||||
isReadonly: boolean;
|
||||
isArtifactVisible: boolean;
|
||||
}
|
||||
|
|
@ -25,7 +26,7 @@ function PureMessages({
|
|||
votes,
|
||||
messages,
|
||||
setMessages,
|
||||
reload,
|
||||
regenerate,
|
||||
isReadonly,
|
||||
}: MessagesProps) {
|
||||
const {
|
||||
|
|
@ -39,6 +40,8 @@ function PureMessages({
|
|||
status,
|
||||
});
|
||||
|
||||
useDataStream();
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={messagesContainerRef}
|
||||
|
|
@ -58,7 +61,7 @@ function PureMessages({
|
|||
: undefined
|
||||
}
|
||||
setMessages={setMessages}
|
||||
reload={reload}
|
||||
regenerate={regenerate}
|
||||
isReadonly={isReadonly}
|
||||
requiresScrollPadding={
|
||||
hasSentMessage && index === messages.length - 1
|
||||
|
|
@ -84,10 +87,9 @@ export const Messages = memo(PureMessages, (prevProps, nextProps) => {
|
|||
if (prevProps.isArtifactVisible && nextProps.isArtifactVisible) return true;
|
||||
|
||||
if (prevProps.status !== nextProps.status) return false;
|
||||
if (prevProps.status && nextProps.status) return false;
|
||||
if (prevProps.messages.length !== nextProps.messages.length) return false;
|
||||
if (!equal(prevProps.messages, nextProps.messages)) return false;
|
||||
if (!equal(prevProps.votes, nextProps.votes)) return false;
|
||||
|
||||
return true;
|
||||
return false;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
'use client';
|
||||
|
||||
import type { Attachment, UIMessage } from 'ai';
|
||||
import type { UIMessage } from 'ai';
|
||||
import cx from 'classnames';
|
||||
import type React from 'react';
|
||||
import {
|
||||
|
|
@ -27,6 +27,7 @@ import { AnimatePresence, motion } from 'framer-motion';
|
|||
import { ArrowDown } from 'lucide-react';
|
||||
import { useScrollToBottom } from '@/hooks/use-scroll-to-bottom';
|
||||
import type { VisibilityType } from './visibility-selector';
|
||||
import type { Attachment, ChatMessage } from '@/lib/types';
|
||||
|
||||
function PureMultimodalInput({
|
||||
chatId,
|
||||
|
|
@ -38,22 +39,20 @@ function PureMultimodalInput({
|
|||
setAttachments,
|
||||
messages,
|
||||
setMessages,
|
||||
append,
|
||||
handleSubmit,
|
||||
sendMessage,
|
||||
className,
|
||||
selectedVisibilityType,
|
||||
}: {
|
||||
chatId: string;
|
||||
input: UseChatHelpers['input'];
|
||||
setInput: UseChatHelpers['setInput'];
|
||||
status: UseChatHelpers['status'];
|
||||
input: string;
|
||||
setInput: Dispatch<SetStateAction<string>>;
|
||||
status: UseChatHelpers<ChatMessage>['status'];
|
||||
stop: () => void;
|
||||
attachments: Array<Attachment>;
|
||||
setAttachments: Dispatch<SetStateAction<Array<Attachment>>>;
|
||||
messages: Array<UIMessage>;
|
||||
setMessages: UseChatHelpers['setMessages'];
|
||||
append: UseChatHelpers['append'];
|
||||
handleSubmit: UseChatHelpers['handleSubmit'];
|
||||
setMessages: UseChatHelpers<ChatMessage>['setMessages'];
|
||||
sendMessage: UseChatHelpers<ChatMessage>['sendMessage'];
|
||||
className?: string;
|
||||
selectedVisibilityType: VisibilityType;
|
||||
}) {
|
||||
|
|
@ -112,20 +111,35 @@ function PureMultimodalInput({
|
|||
const submitForm = useCallback(() => {
|
||||
window.history.replaceState({}, '', `/chat/${chatId}`);
|
||||
|
||||
handleSubmit(undefined, {
|
||||
experimental_attachments: attachments,
|
||||
sendMessage({
|
||||
role: 'user',
|
||||
parts: [
|
||||
...attachments.map((attachment) => ({
|
||||
type: 'file' as const,
|
||||
url: attachment.url,
|
||||
name: attachment.name,
|
||||
mediaType: attachment.contentType,
|
||||
})),
|
||||
{
|
||||
type: 'text',
|
||||
text: input,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
setAttachments([]);
|
||||
setLocalStorageInput('');
|
||||
resetHeight();
|
||||
setInput('');
|
||||
|
||||
if (width && width > 768) {
|
||||
textareaRef.current?.focus();
|
||||
}
|
||||
}, [
|
||||
input,
|
||||
setInput,
|
||||
attachments,
|
||||
handleSubmit,
|
||||
sendMessage,
|
||||
setAttachments,
|
||||
setLocalStorageInput,
|
||||
width,
|
||||
|
|
@ -224,7 +238,7 @@ function PureMultimodalInput({
|
|||
attachments.length === 0 &&
|
||||
uploadQueue.length === 0 && (
|
||||
<SuggestedActions
|
||||
append={append}
|
||||
sendMessage={sendMessage}
|
||||
chatId={chatId}
|
||||
selectedVisibilityType={selectedVisibilityType}
|
||||
/>
|
||||
|
|
@ -328,7 +342,7 @@ function PureAttachmentsButton({
|
|||
status,
|
||||
}: {
|
||||
fileInputRef: React.MutableRefObject<HTMLInputElement | null>;
|
||||
status: UseChatHelpers['status'];
|
||||
status: UseChatHelpers<ChatMessage>['status'];
|
||||
}) {
|
||||
return (
|
||||
<Button
|
||||
|
|
@ -353,7 +367,7 @@ function PureStopButton({
|
|||
setMessages,
|
||||
}: {
|
||||
stop: () => void;
|
||||
setMessages: UseChatHelpers['setMessages'];
|
||||
setMessages: UseChatHelpers<ChatMessage>['setMessages'];
|
||||
}) {
|
||||
return (
|
||||
<Button
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import type { Attachment } from 'ai';
|
||||
|
||||
import type { Attachment } from '@/lib/types';
|
||||
import { LoaderIcon } from './icons';
|
||||
|
||||
export const PreviewAttachment = ({
|
||||
|
|
|
|||
|
|
@ -5,16 +5,17 @@ import { Button } from './ui/button';
|
|||
import { memo } from 'react';
|
||||
import type { UseChatHelpers } from '@ai-sdk/react';
|
||||
import type { VisibilityType } from './visibility-selector';
|
||||
import type { ChatMessage } from '@/lib/types';
|
||||
|
||||
interface SuggestedActionsProps {
|
||||
chatId: string;
|
||||
append: UseChatHelpers['append'];
|
||||
sendMessage: UseChatHelpers<ChatMessage>['sendMessage'];
|
||||
selectedVisibilityType: VisibilityType;
|
||||
}
|
||||
|
||||
function PureSuggestedActions({
|
||||
chatId,
|
||||
append,
|
||||
sendMessage,
|
||||
selectedVisibilityType,
|
||||
}: SuggestedActionsProps) {
|
||||
const suggestedActions = [
|
||||
|
|
@ -59,9 +60,9 @@ function PureSuggestedActions({
|
|||
onClick={async () => {
|
||||
window.history.replaceState({}, '', `/chat/${chatId}`);
|
||||
|
||||
append({
|
||||
sendMessage({
|
||||
role: 'user',
|
||||
content: suggestedAction.action,
|
||||
parts: [{ type: 'text', text: suggestedAction.action }],
|
||||
});
|
||||
}}
|
||||
className="text-left border rounded-xl px-4 py-3.5 text-sm flex-1 gap-1 sm:flex-col w-full h-auto justify-start items-start"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
'use client';
|
||||
|
||||
import type { Message } from 'ai';
|
||||
import cx from 'classnames';
|
||||
import {
|
||||
AnimatePresence,
|
||||
|
|
@ -11,7 +9,7 @@ import {
|
|||
import {
|
||||
type Dispatch,
|
||||
memo,
|
||||
ReactNode,
|
||||
type ReactNode,
|
||||
type SetStateAction,
|
||||
useEffect,
|
||||
useRef,
|
||||
|
|
@ -27,9 +25,10 @@ import {
|
|||
} from '@/components/ui/tooltip';
|
||||
|
||||
import { ArrowUpIcon, StopIcon, SummarizeIcon } from './icons';
|
||||
import { artifactDefinitions, ArtifactKind } from './artifact';
|
||||
import { ArtifactToolbarItem } from './create-artifact';
|
||||
import { UseChatHelpers } from '@ai-sdk/react';
|
||||
import { artifactDefinitions, type ArtifactKind } from './artifact';
|
||||
import type { ArtifactToolbarItem } from './create-artifact';
|
||||
import type { UseChatHelpers } from '@ai-sdk/react';
|
||||
import type { ChatMessage } from '@/lib/types';
|
||||
|
||||
type ToolProps = {
|
||||
description: string;
|
||||
|
|
@ -39,11 +38,11 @@ type ToolProps = {
|
|||
isToolbarVisible?: boolean;
|
||||
setIsToolbarVisible?: Dispatch<SetStateAction<boolean>>;
|
||||
isAnimating: boolean;
|
||||
append: UseChatHelpers['append'];
|
||||
sendMessage: UseChatHelpers<ChatMessage>['sendMessage'];
|
||||
onClick: ({
|
||||
appendMessage,
|
||||
sendMessage,
|
||||
}: {
|
||||
appendMessage: UseChatHelpers['append'];
|
||||
sendMessage: UseChatHelpers<ChatMessage>['sendMessage'];
|
||||
}) => void;
|
||||
};
|
||||
|
||||
|
|
@ -55,7 +54,7 @@ const Tool = ({
|
|||
isToolbarVisible,
|
||||
setIsToolbarVisible,
|
||||
isAnimating,
|
||||
append,
|
||||
sendMessage,
|
||||
onClick,
|
||||
}: ToolProps) => {
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
|
|
@ -82,7 +81,7 @@ const Tool = ({
|
|||
setSelectedTool(description);
|
||||
} else {
|
||||
setSelectedTool(null);
|
||||
onClick({ appendMessage: append });
|
||||
onClick({ sendMessage });
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -135,12 +134,12 @@ const randomArr = [...Array(6)].map((x) => nanoid(5));
|
|||
|
||||
const ReadingLevelSelector = ({
|
||||
setSelectedTool,
|
||||
append,
|
||||
sendMessage,
|
||||
isAnimating,
|
||||
}: {
|
||||
setSelectedTool: Dispatch<SetStateAction<string | null>>;
|
||||
isAnimating: boolean;
|
||||
append: UseChatHelpers['append'];
|
||||
sendMessage: UseChatHelpers<ChatMessage>['sendMessage'];
|
||||
}) => {
|
||||
const LEVELS = [
|
||||
'Elementary',
|
||||
|
|
@ -214,9 +213,14 @@ const ReadingLevelSelector = ({
|
|||
}}
|
||||
onClick={() => {
|
||||
if (currentLevel !== 2 && hasUserSelectedLevel) {
|
||||
append({
|
||||
sendMessage({
|
||||
role: 'user',
|
||||
content: `Please adjust the reading level to ${LEVELS[currentLevel]} level.`,
|
||||
parts: [
|
||||
{
|
||||
type: 'text',
|
||||
text: `Please adjust the reading level to ${LEVELS[currentLevel]} level.`,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
setSelectedTool(null);
|
||||
|
|
@ -243,7 +247,7 @@ export const Tools = ({
|
|||
isToolbarVisible,
|
||||
selectedTool,
|
||||
setSelectedTool,
|
||||
append,
|
||||
sendMessage,
|
||||
isAnimating,
|
||||
setIsToolbarVisible,
|
||||
tools,
|
||||
|
|
@ -251,7 +255,7 @@ export const Tools = ({
|
|||
isToolbarVisible: boolean;
|
||||
selectedTool: string | null;
|
||||
setSelectedTool: Dispatch<SetStateAction<string | null>>;
|
||||
append: UseChatHelpers['append'];
|
||||
sendMessage: UseChatHelpers<ChatMessage>['sendMessage'];
|
||||
isAnimating: boolean;
|
||||
setIsToolbarVisible: Dispatch<SetStateAction<boolean>>;
|
||||
tools: Array<ArtifactToolbarItem>;
|
||||
|
|
@ -274,7 +278,7 @@ export const Tools = ({
|
|||
icon={secondaryTool.icon}
|
||||
selectedTool={selectedTool}
|
||||
setSelectedTool={setSelectedTool}
|
||||
append={append}
|
||||
sendMessage={sendMessage}
|
||||
isAnimating={isAnimating}
|
||||
onClick={secondaryTool.onClick}
|
||||
/>
|
||||
|
|
@ -288,7 +292,7 @@ export const Tools = ({
|
|||
setSelectedTool={setSelectedTool}
|
||||
isToolbarVisible={isToolbarVisible}
|
||||
setIsToolbarVisible={setIsToolbarVisible}
|
||||
append={append}
|
||||
sendMessage={sendMessage}
|
||||
isAnimating={isAnimating}
|
||||
onClick={primaryTool.onClick}
|
||||
/>
|
||||
|
|
@ -299,7 +303,7 @@ export const Tools = ({
|
|||
const PureToolbar = ({
|
||||
isToolbarVisible,
|
||||
setIsToolbarVisible,
|
||||
append,
|
||||
sendMessage,
|
||||
status,
|
||||
stop,
|
||||
setMessages,
|
||||
|
|
@ -307,10 +311,10 @@ const PureToolbar = ({
|
|||
}: {
|
||||
isToolbarVisible: boolean;
|
||||
setIsToolbarVisible: Dispatch<SetStateAction<boolean>>;
|
||||
status: UseChatHelpers['status'];
|
||||
append: UseChatHelpers['append'];
|
||||
stop: UseChatHelpers['stop'];
|
||||
setMessages: UseChatHelpers['setMessages'];
|
||||
status: UseChatHelpers<ChatMessage>['status'];
|
||||
sendMessage: UseChatHelpers<ChatMessage>['sendMessage'];
|
||||
stop: UseChatHelpers<ChatMessage>['stop'];
|
||||
setMessages: UseChatHelpers<ChatMessage>['setMessages'];
|
||||
artifactKind: ArtifactKind;
|
||||
}) => {
|
||||
const toolbarRef = useRef<HTMLDivElement>(null);
|
||||
|
|
@ -431,14 +435,14 @@ const PureToolbar = ({
|
|||
) : selectedTool === 'adjust-reading-level' ? (
|
||||
<ReadingLevelSelector
|
||||
key="reading-level-selector"
|
||||
append={append}
|
||||
sendMessage={sendMessage}
|
||||
setSelectedTool={setSelectedTool}
|
||||
isAnimating={isAnimating}
|
||||
/>
|
||||
) : (
|
||||
<Tools
|
||||
key="tools"
|
||||
append={append}
|
||||
sendMessage={sendMessage}
|
||||
isAnimating={isAnimating}
|
||||
isToolbarVisible={isToolbarVisible}
|
||||
selectedTool={selectedTool}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue