diff --git a/frontend/src/components/workspace/message-list/message-list.tsx b/frontend/src/components/workspace/message-list/message-list.tsx index f947e38..862b4d2 100644 --- a/frontend/src/components/workspace/message-list/message-list.tsx +++ b/frontend/src/components/workspace/message-list/message-list.tsx @@ -4,7 +4,12 @@ import { Conversation, ConversationContent, } from "@/components/ai-elements/conversation"; -import { groupMessages, hasContent } from "@/core/messages/utils"; +import { + extractPresentFilesFromMessage, + groupMessages, + hasContent, + hasPresentFiles, +} from "@/core/messages/utils"; import type { AgentThreadState } from "@/core/threads"; import { cn } from "@/lib/utils"; @@ -12,6 +17,7 @@ import { StreamingIndicator } from "../streaming-indicator"; import { MessageGroup } from "./message-group"; import { MessageListItem } from "./message-list-item"; +import { PresentFileList } from "./present-file-list"; import { MessageListSkeleton } from "./skeleton"; export function MessageList({ @@ -43,6 +49,17 @@ export function MessageList({ /> ); } + if (groupedMessages[0] && hasPresentFiles(groupedMessages[0])) { + const files = []; + for (const message of groupedMessages) { + if (hasPresentFiles(message)) { + files.push(...extractPresentFilesFromMessage(message)); + } + } + return ( + + ); + } return ( + {files.map((file) => ( + + + {getFileName(file)} + {getFileExtension(file)} file + + + + + + ))} + + ); +} diff --git a/frontend/src/core/messages/utils.ts b/frontend/src/core/messages/utils.ts index d0fded7..2d35c88 100644 --- a/frontend/src/core/messages/utils.ts +++ b/frontend/src/core/messages/utils.ts @@ -46,9 +46,14 @@ export function groupMessages( messageIndex === messages.length - 1 && isLoading) ) { - // Assistant messages without any content are folded into the previous group - // Normally, these are tool calls (with or without thinking) - currentGroup.push(message); + if (message.tool_calls?.[0]?.name === "present_files") { + yieldCurrentGroup(); + currentGroup.push(message); + } else { + // Assistant messages without any content are folded into the previous group + // Normally, these are tool calls (with or without thinking) + currentGroup.push(message); + } } else { // Assistant messages with content (text or images) are shown as a group if they have content // No matter whether it has tool calls or not @@ -144,6 +149,25 @@ export function hasToolCalls(message: Message) { ); } +export function hasPresentFiles(message: Message) { + return ( + message.type === "ai" && message.tool_calls?.[0]?.name === "present_files" + ); +} + +export function extractPresentFilesFromMessage(message: Message) { + if (message.type !== "ai" || !hasPresentFiles(message)) { + return []; + } + const files = []; + for (const toolCall of message.tool_calls ?? []) { + if (toolCall.name === "present_files") { + files.push(...(toolCall.args.filepaths as string[])); + } + } + return files; +} + export function findToolCallResult(toolCallId: string, messages: Message[]) { for (const message of messages) { if (message.type === "tool" && message.tool_call_id === toolCallId) { diff --git a/frontend/src/core/utils/files.ts b/frontend/src/core/utils/files.ts new file mode 100644 index 0000000..ec65b10 --- /dev/null +++ b/frontend/src/core/utils/files.ts @@ -0,0 +1,25 @@ +export function getFileName(filepath: string) { + return filepath.split("/").pop()!; +} + +export function getFileExtension(filepath: string) { + const fileName = getFileName(filepath); + const extension = fileName.split(".").pop()!.toLocaleLowerCase(); + switch (extension) { + case "doc": + case "docx": + return "Word"; + case "md": + return "Markdown"; + case "txt": + return "Text"; + case "ppt": + case "pptx": + return "PowerPoint"; + case "xls": + case "xlsx": + return "Excel"; + default: + return extension.toUpperCase(); + } +}