import { useAuth } from "@lib/auth-context" import { Avatar, AvatarFallback, AvatarImage } from "@ui/components/avatar" import type { UIMessage } from "@ai-sdk/react" import { type ChatMemoryCard, memoryResultsFromSearchToolOutput, } from "@/lib/chat-search-memory-results" import { isWebSearchToolName } from "@/lib/chat-web-search-tools" import { MemorySearchResultCard } from "../message/memory-search-result-card" interface ReasoningStep { type: string state?: string message: string } export function ChainOfThought({ messages }: { messages: UIMessage[] }) { const { user } = useAuth() // Group messages into user-assistant pairs const messagePairs: Array<{ userMessage: UIMessage agentMessage?: UIMessage }> = [] let lastUserPair: { userMessage: UIMessage agentMessage?: UIMessage } | null = null for (let i = 0; i < messages.length; i++) { const message = messages[i] if (!message) continue if (message.role === "user") { lastUserPair = { userMessage: message } messagePairs.push(lastUserPair) } else if ( message.role === "assistant" && lastUserPair && !lastUserPair.agentMessage ) { lastUserPair.agentMessage = message } } return (
{messagePairs.map((pair, pairIdx) => { const userMessageText = pair.userMessage.parts.find((part) => part.type === "text")?.text ?? "" const reasoningSteps: ReasoningStep[] = [] if (pair.agentMessage) { pair.agentMessage.parts.forEach((part) => { if (part.type === "tool-searchMemories") { if ( part.state === "input-available" || part.state === "input-streaming" ) { reasoningSteps.push({ type: part.type, state: part.state, message: "Searching memories...", }) } else if (part.state === "output-available") { reasoningSteps.push({ type: part.type, state: part.state, message: "Found relevant memories", }) } else if (part.state === "output-error") { reasoningSteps.push({ type: part.type, state: part.state, message: "Error searching memories", }) } return } const webSearchPart = part.type === "dynamic-tool" && isWebSearchToolName( (part as { toolName?: string }).toolName ?? "", ) ? (part as { type: "dynamic-tool" toolName: string state: string }) : part.type === "tool-web_search" || part.type === "tool-google_search" ? (part as { type: string; state: string }) : null if (webSearchPart) { if ( webSearchPart.state === "input-available" || webSearchPart.state === "input-streaming" ) { reasoningSteps.push({ type: "web-search", state: webSearchPart.state, message: "Searching the web...", }) } else if (webSearchPart.state === "output-available") { reasoningSteps.push({ type: "web-search", state: webSearchPart.state, message: "Explored the web", }) } else if (webSearchPart.state === "output-error") { reasoningSteps.push({ type: "web-search", state: webSearchPart.state, message: "Web search failed", }) } } }) const webSourceCount = pair.agentMessage.parts.filter( (p) => p.type === "source-url", ).length if (webSourceCount > 0) { const hasToolWebDone = reasoningSteps.some( (s) => s.type === "web-search" && s.state === "output-available", ) if (!hasToolWebDone) { reasoningSteps.push({ type: "web-sources", state: "done", message: webSourceCount === 1 ? "Found 1 web source" : `Found ${webSourceCount} web sources`, }) } } } const memoryResults: ChatMemoryCard[] = [] if (pair.agentMessage) { pair.agentMessage.parts.forEach((part) => { if ( part.type === "tool-searchMemories" && part.state === "output-available" ) { memoryResults.push( ...memoryResultsFromSearchToolOutput(part.output), ) } }) } return (
{user && ( {user?.name?.charAt(0)} )}

{userMessageText}

{(reasoningSteps.length > 0 || memoryResults.length > 0) && (
{reasoningSteps.length > 0 && (
{reasoningSteps.map((step, idx) => (
{step.message}
))}
)} {memoryResults.length > 0 && (
{memoryResults.map((result, idx) => ( ))}
)}
)}
) })}
) }