diff --git a/surfsense_web/components/assistant-ui/thread.tsx b/surfsense_web/components/assistant-ui/thread.tsx index 904ee8c8b..f87f74d30 100644 --- a/surfsense_web/components/assistant-ui/thread.tsx +++ b/surfsense_web/components/assistant-ui/thread.tsx @@ -614,6 +614,32 @@ const ComposerAction: FC = ({ isBlockedByOtherUser = false ); }, [filteredTools, disabledTools]); + const groupedTools = useMemo(() => { + if (!filteredTools) return []; + const toolsByName = new Map(filteredTools.map((t) => [t.name, t])); + const result: { label: string; tools: typeof filteredTools }[] = []; + const placed = new Set(); + + for (const group of TOOL_GROUPS) { + const matched = group.tools.flatMap((name) => { + const tool = toolsByName.get(name); + if (!tool) return []; + placed.add(name); + return [tool]; + }); + if (matched.length > 0) { + result.push({ label: group.label, tools: matched }); + } + } + + const ungrouped = filteredTools.filter((t) => !placed.has(t.name)); + if (ungrouped.length > 0) { + result.push({ label: "Other", tools: ungrouped }); + } + + return result; + }, [filteredTools]); + useEffect(() => { hydrateDisabled(); }, [hydrateDisabled]); @@ -668,33 +694,40 @@ const ComposerAction: FC = ({ isBlockedByOtherUser = false {filteredEnabledCount}/{filteredTools?.length ?? 0} enabled -
- {filteredTools?.map((tool) => { - const isDisabled = disabledTools.includes(tool.name); - const ToolIcon = getToolIcon(tool.name); - return ( -
- - - {formatToolName(tool.name)} - - toggleTool(tool.name)} - className="shrink-0" - /> -
- ); - })} - {!filteredTools?.length && ( -
- Loading tools... +
+ {groupedTools.map((group) => ( +
+
+ {group.label}
- )} -
+ {group.tools.map((tool) => { + const isDisabled = disabledTools.includes(tool.name); + const ToolIcon = getToolIcon(tool.name); + return ( +
+ + + {formatToolName(tool.name)} + + toggleTool(tool.name)} + className="shrink-0" + /> +
+ ); + })} +
+ ))} + {!filteredTools?.length && ( +
+ Loading tools... +
+ )} +
@@ -783,7 +823,7 @@ const ComposerAction: FC = ({ isBlockedByOtherUser = false type="button" onClick={() => toggleTool("web_search")} className={cn( - "rounded-full transition-all flex items-center gap-1 px-2 py-1 border h-8", + "rounded-full transition-all flex items-center gap-1 px-2 py-1 border h-8 select-none", isWebSearchEnabled ? "bg-sky-500/15 border-sky-500/60 text-sky-500" : "bg-transparent border-transparent text-muted-foreground hover:text-foreground" @@ -891,6 +931,22 @@ function formatToolName(name: string): string { .join(" "); } +const TOOL_GROUPS: { label: string; tools: string[] }[] = [ + { + label: "Research", + tools: ["search_knowledge_base", "search_surfsense_docs", "scrape_webpage", "link_preview"], + }, + { + label: "Generate", + tools: ["generate_podcast", "generate_report", "generate_image", "display_image"], + }, + { + label: "Memory", + tools: ["save_memory", "recall_memory"], + }, +]; + + const MessageError: FC = () => { return (