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,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;
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue