diff --git a/apps/desktop/src/components/right-panel/components/chat/message-content.tsx b/apps/desktop/src/components/right-panel/components/chat/message-content.tsx index 6d7acb4547..d752738a0b 100644 --- a/apps/desktop/src/components/right-panel/components/chat/message-content.tsx +++ b/apps/desktop/src/components/right-panel/components/chat/message-content.tsx @@ -1,5 +1,6 @@ import { commands as miscCommands } from "@hypr/plugin-misc"; import Renderer from "@hypr/tiptap/renderer"; +import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@hypr/ui/components/ui/accordion"; import { PencilRuler } from "lucide-react"; import { useEffect, useState } from "react"; import { MarkdownCard } from "./markdown-card"; @@ -12,6 +13,52 @@ interface MessageContentProps { onApplyMarkdown?: (markdownContent: string) => void; } +function ToolDetailsRenderer({ details }: { details: any }) { + if (!details) { + return ( +
+ No details available... +
+ ); + } + + return ( +
+
+        {typeof details === 'object' ? JSON.stringify(details, null, 2) : String(details)}
+      
+
+ ); +} + function MarkdownText({ content }: { content: string }) { const [htmlContent, setHtmlContent] = useState(""); @@ -124,31 +171,71 @@ function MarkdownText({ content }: { content: string }) { export function MessageContent({ message, sessionTitle, hasEnhancedNote, onApplyMarkdown }: MessageContentProps) { if (message.type === "tool-start") { - return ( -
+ const hasToolDetails = message.toolDetails; + + if (hasToolDetails) { + return (
- - - Called tool: {message.content} - + + + +
+ + + Called tool: {message.content} + +
+
+ + + +
+
-
- ); + ); + } else { + return ( +
+
+ + + Called tool: {message.content} + +
+
+ ); + } } if (message.type === "tool-result") { diff --git a/apps/desktop/src/components/right-panel/components/chat/types.ts b/apps/desktop/src/components/right-panel/components/chat/types.ts index 21b4d7c7fb..cd3a9cca64 100644 --- a/apps/desktop/src/components/right-panel/components/chat/types.ts +++ b/apps/desktop/src/components/right-panel/components/chat/types.ts @@ -11,6 +11,7 @@ export interface Message { isUser: boolean; timestamp: Date; type: "text-delta" | "tool-start" | "tool-result" | "tool-error" | "generating"; + toolDetails?: any; } export type ChatSession = { diff --git a/apps/desktop/src/components/right-panel/hooks/useChatLogic.ts b/apps/desktop/src/components/right-panel/hooks/useChatLogic.ts index 35a298fca9..8ed740b08e 100644 --- a/apps/desktop/src/components/right-panel/hooks/useChatLogic.ts +++ b/apps/desktop/src/components/right-panel/hooks/useChatLogic.ts @@ -160,6 +160,7 @@ export function useChatLogic({ role: "User", content: userMessage.content.trim(), type: "text-delta", + tool_details: null, }); const aiMessageId = crypto.randomUUID(); @@ -340,7 +341,7 @@ export function useChatLogic({ userId, apiBase, ), - stopWhen: stepCountIs(3), + stopWhen: stepCountIs(5), tools: { ...(type === "HyprLocal" && { update_progress: tool({ inputSchema: z.any() }) }), ...(shouldUseTools && { ...newMcpTools, search_sessions_multi_keywords: searchTool, ...hyprMcpTools }), @@ -428,6 +429,7 @@ export function useChatLogic({ role: "Assistant", type: "text-delta", content: aiResponse.trim(), + tool_details: null, }); } catch (error) { console.error("Failed to save AI text:", error); @@ -445,6 +447,7 @@ export function useChatLogic({ isUser: false, timestamp: new Date(), type: "tool-start", + toolDetails: chunk.input, }; setMessages((prev) => [...prev, toolStartMessage]); @@ -456,6 +459,7 @@ export function useChatLogic({ role: "Assistant", content: toolStartMessage.content, type: "tool-start", + tool_details: JSON.stringify(chunk.input), }); } @@ -479,6 +483,7 @@ export function useChatLogic({ role: "Assistant", content: toolResultMessage.content, type: "tool-result", + tool_details: null, }); } @@ -500,6 +505,7 @@ export function useChatLogic({ role: "Assistant", content: toolErrorMessage.content, type: "tool-error", + tool_details: null, }); } @@ -514,6 +520,7 @@ export function useChatLogic({ role: "Assistant", type: "text-delta", content: aiResponse.trim(), + tool_details: null, }); } @@ -570,6 +577,7 @@ export function useChatLogic({ role: "Assistant", content: finalErrorMessage, type: "text-delta", + tool_details: null, }); } }; diff --git a/apps/desktop/src/components/right-panel/hooks/useChatQueries.ts b/apps/desktop/src/components/right-panel/hooks/useChatQueries.ts index 38ef252506..636a741902 100644 --- a/apps/desktop/src/components/right-panel/hooks/useChatQueries.ts +++ b/apps/desktop/src/components/right-panel/hooks/useChatQueries.ts @@ -86,6 +86,16 @@ export function useChatQueries({ timestamp: new Date(msg.created_at), type: msg.type || "text-delta", parts: msg.role === "Assistant" ? parseMarkdownBlocks(msg.content) : undefined, + toolDetails: msg.type === "tool-start" && msg.tool_details + ? (() => { + try { + return JSON.parse(msg.tool_details); + } catch (error) { + console.error("Failed to parse tool_details for tool-start:", msg.id, error); + return undefined; + } + })() + : undefined, })); }, }); diff --git a/crates/db-user/src/chat_messages_migration_2.sql b/crates/db-user/src/chat_messages_migration_2.sql new file mode 100644 index 0000000000..3f30423c84 --- /dev/null +++ b/crates/db-user/src/chat_messages_migration_2.sql @@ -0,0 +1,4 @@ +ALTER TABLE + chat_messages +ADD + COLUMN tool_details TEXT DEFAULT NULL; diff --git a/crates/db-user/src/chat_messages_ops.rs b/crates/db-user/src/chat_messages_ops.rs index 560eda9a68..569f4a5407 100644 --- a/crates/db-user/src/chat_messages_ops.rs +++ b/crates/db-user/src/chat_messages_ops.rs @@ -15,8 +15,9 @@ impl UserDatabase { created_at, role, content, - type - ) VALUES (?, ?, ?, ?, ?, ?) + type, + tool_details + ) VALUES (?, ?, ?, ?, ?, ?, ?) RETURNING *", vec![ message.id, @@ -25,6 +26,7 @@ impl UserDatabase { message.role.to_string(), message.content, message.r#type.to_string(), + message.tool_details.unwrap_or_default(), ], ) .await?; diff --git a/crates/db-user/src/chat_messages_types.rs b/crates/db-user/src/chat_messages_types.rs index 1665fda4ea..a8a1eb7a3e 100644 --- a/crates/db-user/src/chat_messages_types.rs +++ b/crates/db-user/src/chat_messages_types.rs @@ -34,5 +34,6 @@ user_common_derives! { pub role: ChatMessageRole, pub content: String, pub r#type: ChatMessageType, + pub tool_details: Option, } } diff --git a/crates/db-user/src/init.rs b/crates/db-user/src/init.rs index c797772a9c..5210d107de 100644 --- a/crates/db-user/src/init.rs +++ b/crates/db-user/src/init.rs @@ -785,6 +785,7 @@ pub async fn seed(db: &UserDatabase, user_id: impl Into) -> Result<(), c content: "Hello, how are you?".to_string(), created_at: now, r#type: ChatMessageType::TextDelta, + tool_details: None, }; let _ = db.upsert_chat_message(chat_message_1).await?; diff --git a/crates/db-user/src/lib.rs b/crates/db-user/src/lib.rs index abb73c2056..01aa7d722c 100644 --- a/crates/db-user/src/lib.rs +++ b/crates/db-user/src/lib.rs @@ -129,7 +129,7 @@ impl std::ops::Deref for UserDatabase { } // Append only. Do not reorder. -const MIGRATIONS: [&str; 23] = [ +const MIGRATIONS: [&str; 24] = [ include_str!("./calendars_migration.sql"), include_str!("./configs_migration.sql"), include_str!("./events_migration.sql"), @@ -153,6 +153,7 @@ const MIGRATIONS: [&str; 23] = [ include_str!("./session_participants_migration_1.sql"), include_str!("./events_migration_2.sql"), include_str!("./chat_messages_migration_1.sql"), + include_str!("./chat_messages_migration_2.sql"), ]; pub async fn migrate(db: &UserDatabase) -> Result<(), crate::Error> { diff --git a/plugins/db/js/bindings.gen.ts b/plugins/db/js/bindings.gen.ts index e38f4ee566..11df378f0f 100644 --- a/plugins/db/js/bindings.gen.ts +++ b/plugins/db/js/bindings.gen.ts @@ -162,7 +162,7 @@ async deleteTag(tagId: string) : Promise { export type Calendar = { id: string; tracking_id: string; user_id: string; platform: Platform; name: string; selected: boolean; source: string | null } export type ChatGroup = { id: string; user_id: string; name: string | null; created_at: string; session_id: string } -export type ChatMessage = { id: string; group_id: string; created_at: string; role: ChatMessageRole; content: string; type: ChatMessageType } +export type ChatMessage = { id: string; group_id: string; created_at: string; role: ChatMessageRole; content: string; type: ChatMessageType; tool_details: string | null } export type ChatMessageRole = "User" | "Assistant" export type ChatMessageType = "text-delta" | "tool-start" | "tool-result" | "tool-error" export type Config = { id: string; user_id: string; general: ConfigGeneral; notification: ConfigNotification; ai: ConfigAI }