diff --git a/src/components/thread/messages/ai.tsx b/src/components/thread/messages/ai.tsx index e269a63..91fa365 100644 --- a/src/components/thread/messages/ai.tsx +++ b/src/components/thread/messages/ai.tsx @@ -6,7 +6,7 @@ import { Avatar, AvatarFallback } from "@/components/ui/avatar"; import { MarkdownText } from "../markdown-text"; import { LoadExternalComponent } from "@langchain/langgraph-sdk/react-ui/client"; import { cn } from "@/lib/utils"; -import { ToolCalls } from "./tool-calls"; +import { ToolCalls, ToolResult } from "./tool-calls"; function CustomComponent({ message, @@ -61,40 +61,45 @@ export function AssistantMessage({ "tool_calls" in message && message.tool_calls && message.tool_calls.length > 0; + const isToolResult = message.type === "tool"; return (
A -
- {hasToolCalls && } - - {contentString.length > 0 && ( -
- {contentString} -
- )} -
+ ) : ( +
+ {hasToolCalls && } + + {contentString.length > 0 && ( +
+ {contentString} +
)} - > - thread.setBranch(branch)} - isLoading={isLoading} - /> - handleRegenerate(parentCheckpoint)} - /> +
+ thread.setBranch(branch)} + isLoading={isLoading} + /> + handleRegenerate(parentCheckpoint)} + /> +
-
+ )}
); } diff --git a/src/components/thread/messages/tool-calls.tsx b/src/components/thread/messages/tool-calls.tsx index 741b297..bd768c3 100644 --- a/src/components/thread/messages/tool-calls.tsx +++ b/src/components/thread/messages/tool-calls.tsx @@ -1,4 +1,7 @@ -import { AIMessage } from "@langchain/langgraph-sdk"; +import { AIMessage, ToolMessage } from "@langchain/langgraph-sdk"; +import { useState } from "react"; +import { motion, AnimatePresence } from "framer-motion"; +import { ChevronDown, ChevronUp } from "lucide-react"; function isComplexValue(value: any): boolean { return Array.isArray(value) || (typeof value === "object" && value !== null); @@ -23,7 +26,14 @@ export function ToolCalls({ className="border border-gray-200 rounded-lg overflow-hidden" >
-

{tc.name}

+

+ {tc.name} + {tc.id && ( + + {tc.id} + + )} +

@@ -51,3 +61,123 @@ export function ToolCalls({ ); } + +export function ToolResult({ message }: { message: ToolMessage }) { + const [isExpanded, setIsExpanded] = useState(false); + + let parsedContent: any; + let isJsonContent = false; + + try { + if (typeof message.content === "string") { + parsedContent = JSON.parse(message.content); + isJsonContent = true; + } + } catch { + // Content is not JSON, use as is + parsedContent = message.content; + } + + const contentStr = isJsonContent + ? JSON.stringify(parsedContent, null, 2) + : String(message.content); + const contentLines = contentStr.split("\n"); + const shouldTruncate = contentLines.length > 4 || contentStr.length > 500; + const displayedContent = + shouldTruncate && !isExpanded + ? contentStr.length > 500 + ? contentStr.slice(0, 500) + "..." + : contentLines.slice(0, 4).join("\n") + "\n..." + : contentStr; + + return ( +
+
+
+ {message.name ? ( +

+ Tool Result:{" "} + + {message.name} + +

+ ) : ( +

Tool Result

+ )} + {message.tool_call_id && ( + + {message.tool_call_id} + + )} +
+
+ +
+ + + {isJsonContent ? ( +
+ + {(Array.isArray(parsedContent) + ? isExpanded + ? parsedContent + : parsedContent.slice(0, 5) + : Object.entries(parsedContent) + ).map((item, argIdx) => { + const [key, value] = Array.isArray(parsedContent) + ? [argIdx, item] + : [item[0], item[1]]; + return ( + + + + + ); + })} + +
+ {key} + + {isComplexValue(value) ? ( + + {JSON.stringify(value, null, 2)} + + ) : ( + String(value) + )} +
+ ) : ( + {displayedContent} + )} + + +
+ {((shouldTruncate && !isJsonContent) || + (isJsonContent && + Array.isArray(parsedContent) && + parsedContent.length > 5)) && ( + setIsExpanded(!isExpanded)} + className="w-full py-2 flex items-center justify-center border-t-[1px] border-gray-200 text-gray-500 hover:text-gray-600 hover:bg-gray-50 transition-all ease-in-out duration-200 cursor-pointer" + initial={{ scale: 1 }} + whileHover={{ scale: 1.02 }} + whileTap={{ scale: 0.98 }} + > + {isExpanded ? : } + + )} + + + ); +}