Template
Improve chat navigation, model selection, and streaming
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import type { UIMessage } from "ai";
|
||||
import { isToolUIPart, type ChatStatus, type UIMessage } from "ai";
|
||||
import {
|
||||
Conversation,
|
||||
ConversationContent,
|
||||
@@ -19,14 +19,14 @@ import {
|
||||
ReasoningTrigger,
|
||||
} from "@/components/ai-elements/reasoning";
|
||||
import { Loader } from "@/components/ai-elements/loader";
|
||||
import { ToolPartRenderer, getTextContent, getReasoningPart } from "./ChatMessageParts";
|
||||
import { ToolPartRenderer } from "@/components/tools/tool-part-renderer";
|
||||
import { getTextContent, getReasoningPart } from "@/lib/chat/messages";
|
||||
|
||||
interface ChatMessagesProps {
|
||||
messages: UIMessage[];
|
||||
isLoading?: boolean;
|
||||
status?: ChatStatus;
|
||||
}
|
||||
|
||||
|
||||
function LoadingIndicator() {
|
||||
return (
|
||||
<Message from="assistant">
|
||||
@@ -37,8 +37,7 @@ function LoadingIndicator() {
|
||||
);
|
||||
}
|
||||
|
||||
export function ChatMessages({ messages, isLoading }: ChatMessagesProps) {
|
||||
const isToolPart = (type: string) => type === "file" || type.startsWith("tool-");
|
||||
export function ChatMessages({ messages, status = "ready" }: ChatMessagesProps) {
|
||||
const hasReasoningPart = (parts: typeof messages[0]["parts"]) =>
|
||||
parts.some((p) => p.type === "reasoning" && p.text);
|
||||
|
||||
@@ -46,26 +45,31 @@ export function ChatMessages({ messages, isLoading }: ChatMessagesProps) {
|
||||
(message) =>
|
||||
message.role === "user" ||
|
||||
getTextContent(message.parts).trim() !== "" ||
|
||||
message.parts.some((p) => isToolPart(p.type)) ||
|
||||
message.parts.some((p) => p.type === "file" || isToolUIPart(p)) ||
|
||||
hasReasoningPart(message.parts)
|
||||
);
|
||||
|
||||
const lastMessage = messages[messages.length - 1];
|
||||
const hasContent =
|
||||
getTextContent(lastMessage?.parts || []).trim() !== "" ||
|
||||
lastMessage?.parts.some((p) => isToolPart(p.type)) ||
|
||||
lastMessage?.parts.some((p) => p.type === "file" || isToolUIPart(p)) ||
|
||||
hasReasoningPart(lastMessage?.parts || []);
|
||||
const isStreaming = lastMessage?.role === "assistant" && hasContent;
|
||||
const showLoading = isLoading && !isStreaming;
|
||||
const isLoading = status === "submitted" || status === "streaming";
|
||||
const showLoading =
|
||||
isLoading && !(lastMessage?.role === "assistant" && hasContent);
|
||||
|
||||
return (
|
||||
<Conversation>
|
||||
<ConversationContent className="mx-auto max-w-3xl px-4 pb-4">
|
||||
{visibleMessages.map((message) => {
|
||||
const isStreaming =
|
||||
status === "streaming" &&
|
||||
message.role === "assistant" &&
|
||||
message.id === lastMessage?.id;
|
||||
const textContent = getTextContent(message.parts);
|
||||
const reasoningPart = getReasoningPart(message.parts);
|
||||
const fileParts = message.parts.filter((p) => p.type === "file");
|
||||
const toolParts = message.parts.filter((p) => p.type.startsWith("tool-"));
|
||||
const toolParts = message.parts.filter((p) => isToolUIPart(p));
|
||||
return (
|
||||
<Message key={message.id} from={message.role}>
|
||||
{fileParts.length > 0 && (
|
||||
@@ -80,7 +84,9 @@ export function ChatMessages({ messages, isLoading }: ChatMessagesProps) {
|
||||
)}
|
||||
|
||||
{message.role === "assistant" && reasoningPart && (
|
||||
<Reasoning isStreaming={reasoningPart.state !== "done"}>
|
||||
<Reasoning
|
||||
isStreaming={isStreaming && reasoningPart.state === "streaming"}
|
||||
>
|
||||
<ReasoningTrigger />
|
||||
<ReasoningContent>{reasoningPart.text || ""}</ReasoningContent>
|
||||
</Reasoning>
|
||||
@@ -92,7 +98,16 @@ export function ChatMessages({ messages, isLoading }: ChatMessagesProps) {
|
||||
|
||||
{textContent && (
|
||||
<MessageContent from={message.role}>
|
||||
<MessageResponse>{textContent}</MessageResponse>
|
||||
<MessageResponse
|
||||
mode={message.role === "assistant" ? "streaming" : "static"}
|
||||
isAnimating={
|
||||
isStreaming && message.parts.some(
|
||||
(part) => part.type === "text" && part.state === "streaming"
|
||||
)
|
||||
}
|
||||
>
|
||||
{textContent}
|
||||
</MessageResponse>
|
||||
</MessageContent>
|
||||
)}
|
||||
</Message>
|
||||
|
||||
Reference in New Issue
Block a user