fix granular tool

This commit is contained in:
marko-kraemer 2025-10-09 17:17:17 +02:00
parent a2b30e85bb
commit 77d2d04a03
8 changed files with 150 additions and 144 deletions

View File

@ -71,12 +71,12 @@ class MethodMetadata:
display_name (str): Human-readable method name display_name (str): Human-readable method name
description (str): Method description description (str): Method description
is_core (bool): Whether this is a core method (always enabled) is_core (bool): Whether this is a core method (always enabled)
visible (bool): Whether method is visible in frontend UI (default False) visible (bool): Whether method is visible in frontend UI (default True)
""" """
display_name: str display_name: str
description: str description: str
is_core: bool = False is_core: bool = False
visible: bool = False visible: bool = True
class Tool(ABC): class Tool(ABC):
"""Abstract base class for all tools. """Abstract base class for all tools.
@ -254,7 +254,7 @@ def method_metadata(
display_name: str, display_name: str,
description: str, description: str,
is_core: bool = False, is_core: bool = False,
visible: bool = False visible: bool = True
): ):
"""Decorator to add metadata to a tool method. """Decorator to add metadata to a tool method.

View File

@ -19,7 +19,7 @@ from core.services.supabase import DBConnection
icon="FileText", icon="FileText",
color="bg-emerald-100 dark:bg-emerald-800/50", color="bg-emerald-100 dark:bg-emerald-800/50",
weight=270, weight=270,
visible=True visible=False
) )
class PaperSearchTool(Tool): class PaperSearchTool(Tool):
def __init__(self, thread_manager: ThreadManager): def __init__(self, thread_manager: ThreadManager):

View File

@ -268,7 +268,9 @@ def ask(self, question: str):
## 👁️ Visible in UI ## 👁️ Visible in UI
**Default:** `visible = True` (both tool and method level) **Default:**
- **Tool level:** `visible = False` (tools hidden by default)
- **Method level:** `visible = True` (methods visible by default)
Controls whether a tool/method is shown in the frontend UI. Controls whether a tool/method is shown in the frontend UI.

View File

@ -222,7 +222,7 @@ def _extract_tool_metadata(tool_name: str, tool_class: Type[Tool]) -> Dict[str,
method_info["description"] = f"{method_name} function" method_info["description"] = f"{method_name} function"
method_info["is_core"] = False method_info["is_core"] = False
method_info["visible"] = False method_info["visible"] = True
metadata["methods"].append(method_info) metadata["methods"].append(method_info)
except Exception as e: except Exception as e:

View File

@ -206,7 +206,10 @@ export const GranularToolConfiguration = ({
const toolGroup = getToolGroup(toolName, toolsData); const toolGroup = getToolGroup(toolName, toolsData);
if (!toolGroup) return 0; if (!toolGroup) return 0;
return toolGroup.methods.filter(method => isMethodEnabled(toolName, method.name)).length; // Only count visible methods
return toolGroup.methods
.filter(method => method.visible !== false)
.filter(method => isMethodEnabled(toolName, method.name)).length;
}; };
const filteredGroups = getFilteredToolGroups(); const filteredGroups = getFilteredToolGroups();
@ -251,7 +254,7 @@ export const GranularToolConfiguration = ({
const isGroupEnabled = isToolGroupEnabled(toolGroup.name); const isGroupEnabled = isToolGroupEnabled(toolGroup.name);
const isExpanded = expandedGroups.has(toolGroup.name); const isExpanded = expandedGroups.has(toolGroup.name);
const enabledMethodsCount = getEnabledMethodsCount(toolGroup.name); const enabledMethodsCount = getEnabledMethodsCount(toolGroup.name);
const totalMethodsCount = toolGroup.methods.length; const totalMethodsCount = toolGroup.methods.filter(m => m.visible !== false).length;
const IconComponent = getIconComponent(toolGroup.icon); const IconComponent = getIconComponent(toolGroup.icon);
const hasGranular = hasGranularControl(toolGroup.name, toolsData); const hasGranular = hasGranularControl(toolGroup.name, toolsData);
@ -323,7 +326,9 @@ export const GranularToolConfiguration = ({
</span> </span>
</div> </div>
{toolGroup.methods.map((method) => { {toolGroup.methods
.filter(method => method.visible !== false) // Only show visible methods
.map((method) => {
const isMethodEnabledState = isMethodEnabled(toolGroup.name, method.name); const isMethodEnabledState = isMethodEnabled(toolGroup.name, method.name);
return ( return (

View File

@ -57,13 +57,13 @@ export function normalizeToolGroup(apiToolGroup: any): ToolGroup {
enabled: m.enabled ?? true, enabled: m.enabled ?? true,
isCore: m.is_core || m.isCore, isCore: m.is_core || m.isCore,
is_core: m.is_core, is_core: m.is_core,
visible: m.visible ?? false, // Default false - tools must explicitly set visible=True visible: m.visible, // Use API value directly - backend controls visibility
})) || [], })) || [],
enabled: apiToolGroup.enabled ?? true, enabled: apiToolGroup.enabled ?? true,
isCore: apiToolGroup.is_core || apiToolGroup.isCore, isCore: apiToolGroup.is_core || apiToolGroup.isCore,
is_core: apiToolGroup.is_core, is_core: apiToolGroup.is_core,
weight: apiToolGroup.weight ?? 100, weight: apiToolGroup.weight ?? 100,
visible: apiToolGroup.visible ?? false, // Default false - tools must explicitly set visible=True visible: apiToolGroup.visible, // Use API value directly - backend controls visibility
}; };
} }
@ -91,7 +91,12 @@ export function getAllToolGroups(toolsData?: Record<string, any>): Record<string
export function hasGranularControl(toolName: string, toolsData?: Record<string, any>): boolean { export function hasGranularControl(toolName: string, toolsData?: Record<string, any>): boolean {
const group = getToolGroup(toolName, toolsData); const group = getToolGroup(toolName, toolsData);
return group ? group.methods.length > 1 : false; if (!group) return false;
// Only count visible methods for granular control (visible=true or visible=undefined counts as visible)
const visibleMethods = group.methods.filter(m => m.visible !== false);
console.log(`[hasGranularControl] ${toolName}: ${group.methods.length} total methods, ${visibleMethods.length} visible`, group.methods.map(m => ({ name: m.name, visible: m.visible })));
return visibleMethods.length > 1;
} }
export function getEnabledMethodsForTool( export function getEnabledMethodsForTool(

View File

@ -11,7 +11,6 @@ import {
FileText, FileText,
Search, Search,
Users, Users,
Code2,
RefreshCw, RefreshCw,
} from 'lucide-react'; } from 'lucide-react';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
@ -25,7 +24,7 @@ interface SunaModesPanelProps {
isMobile?: boolean; isMobile?: boolean;
} }
type ModeType = 'research' | 'people' | 'code' | 'docs' | 'data' | 'slides' | 'image'; type ModeType = 'image' | 'slides' | 'data' | 'docs' | 'people' | 'research';
interface Mode { interface Mode {
id: ModeType; id: ModeType;
@ -53,83 +52,74 @@ interface Mode {
const modes: Mode[] = [ const modes: Mode[] = [
{ {
id: 'research', id: 'image',
label: 'Research', label: 'Image',
icon: <Search className="w-4 h-4" />, icon: <ImageIcon className="w-4 h-4" />,
samplePrompts: [ samplePrompts: [
'Analyze emerging trends in quantum computing and potential business applications', 'A majestic golden eagle soaring through misty mountain peaks at sunrise with dramatic lighting',
'Research top 10 competitors in the AI-powered CRM space with feature comparison', 'Close-up portrait of a fashion model with avant-garde makeup, studio lighting, high contrast shadows',
'Investigate regulatory requirements for launching a fintech app in the EU', 'Cozy Scandinavian living room with natural wood furniture, indoor plants, and soft morning sunlight',
'Compile market analysis on electric vehicle adoption rates across major markets', 'Futuristic cyberpunk street market at night with neon signs, rain-slicked pavement, and holographic displays',
'Study the impact of remote work on commercial real estate demand in major cities', 'Elegant product photography of luxury perfume bottle on marble surface with soft reflections',
'Research Web3 adoption patterns among Fortune 500 companies', 'Whimsical floating islands connected by rope bridges in a pastel sky with dreamy clouds',
'Analyze consumer sentiment towards sustainable fashion brands', 'Macro close-up of morning dew drops on vibrant flower petals with bokeh background',
'Investigate the latest developments in gene therapy for rare diseases', 'Modern workspace desk setup with laptop, coffee, notebook, and succulent plants from above',
'Study pricing strategies of successful D2C subscription box companies', 'Mystical forest path with ancient trees, glowing fireflies, and ethereal light beams through fog',
'Research the competitive landscape of AI-powered cybersecurity solutions', 'Architectural detail of contemporary glass building facade with geometric patterns and reflections',
'Vibrant street food vendor stall with colorful ingredients, steam rising, and warm lighting',
'Serene Japanese zen garden with raked sand, moss-covered stones, and cherry blossom petals',
'Dynamic action shot of athlete mid-jump against dramatic sunset sky, silhouette effect',
'Rustic farmhouse kitchen with copper pots, fresh herbs, wooden cutting boards, and natural textures',
'Abstract fluid art with swirling metallic gold, deep blue, and emerald green organic patterns',
], ],
options: {
title: 'Choose a style',
items: [
{ id: 'photorealistic', name: 'Photorealistic', image: '/images/image-styles/photorealistic_eagle-min.png' },
{ id: 'watercolor', name: 'Watercolor', image: '/images/image-styles/watercolor_garden-min.png' },
{ id: 'digital-art', name: 'Digital Art', image: '/images/image-styles/digital_art_cyberpunk-min.png' },
{ id: 'oil-painting', name: 'Oil Painting', image: '/images/image-styles/oil_painting_villa-min.png' },
{ id: 'minimalist', name: 'Minimalist', image: '/images/image-styles/minimalist_coffee-min.png' },
{ id: 'isometric', name: 'Isometric', image: '/images/image-styles/isometric_bedroom-min.png' },
{ id: 'vintage', name: 'Vintage', image: '/images/image-styles/vintage_diner-min.png' },
{ id: 'comic', name: 'Comic Book', image: '/images/image-styles/comic_book_robot-min.png' },
{ id: 'neon', name: 'Neon', image: '/images/image-styles/neon_jellyfish-min.png' },
{ id: 'pastel', name: 'Pastel', image: '/images/image-styles/pastel_landscape-min.png' },
{ id: 'geometric', name: 'Geometric', image: '/images/image-styles/geometric_crystal-min.png' },
{ id: 'abstract', name: 'Abstract', image: '/images/image-styles/abstract_organic-min.png' },
{ id: 'anime', name: 'Anime', image: '/images/image-styles/anime_forest-min.png' },
{ id: 'impressionist', name: 'Impressionist', image: '/images/image-styles/impressionist_garden-min.png' },
{ id: 'surreal', name: 'Surreal', image: '/images/image-styles/surreal_islands-min.png' },
],
},
}, },
{ {
id: 'people', id: 'slides',
label: 'People', label: 'Slides',
icon: <Users className="w-4 h-4" />, icon: <Presentation className="w-4 h-4" />,
samplePrompts: [ samplePrompts: [
'Find VP of Engineering candidates at Series B+ AI companies in NYC', 'Create a Series A pitch deck with market size, traction, and financial projections',
'Build lead list of CMOs at SaaS companies with 50-200 employees', 'Build a Q4 business review showcasing KPIs, wins, and strategic initiatives',
'Research blockchain developers with Solidity experience open to relocation', 'Design a product launch presentation with demo videos and customer testimonials',
'Generate prospect list of tech founders who raised funding in the last 6 months', 'Develop a sales enablement deck explaining our value prop and competitive advantages',
'Identify Product Managers at fintech startups with 5+ years experience', 'Create an investor update highlighting key metrics and upcoming milestones',
'Find decision-makers at mid-market companies in healthcare IT', 'Build a customer case study presentation showing ROI and success metrics',
'Research sales leaders at B2B companies with recent ARR growth', 'Design an all-hands presentation covering company updates and vision',
'Build list of CTOs at enterprise companies adopting AI infrastructure', 'Develop a training deck for new product features and workflows',
'Find UX designers with experience in mobile-first e-commerce', 'Create a conference talk about scaling engineering teams',
'Identify DevOps engineers at cloud-native startups in Austin', 'Build a board meeting presentation with strategic recommendations',
],
},
{
id: 'code',
label: 'Code',
icon: <Code2 className="w-4 h-4" />,
samplePrompts: [
'Build a modern landing page with React, Tailwind CSS, and smooth animations',
'Create a Python script to scrape product data and generate CSV reports',
'Develop a REST API with authentication and rate limiting',
'Build an interactive data dashboard with real-time charts and filters',
'Create a Chrome extension to track time spent on websites',
'Build a Node.js backend with PostgreSQL and Redis caching',
'Develop a real-time chat application with WebSocket support',
'Create an automated email campaign system with analytics',
'Build a portfolio website with dark mode and CMS integration',
'Develop a Python CLI tool for automating deployment workflows',
],
},
{
id: 'docs',
label: 'Docs',
icon: <FileText className="w-4 h-4" />,
samplePrompts: [
'Write a comprehensive PRD for an AI-powered recommendation engine',
'Draft a technical architecture document for a scalable microservices platform',
'Create a go-to-market strategy document for our Q2 product launch',
'Develop a 90-day onboarding playbook for engineering managers',
'Write an API documentation guide with examples and best practices',
'Create a company handbook covering culture, policies, and benefits',
'Draft a data privacy policy compliant with GDPR and CCPA',
'Develop a customer success playbook for SaaS enterprise accounts',
'Write a security incident response plan with escalation procedures',
'Create a comprehensive style guide for brand and content',
], ],
options: { options: {
title: 'Choose a template', title: 'Choose a template',
items: [ items: [
{ id: 'prd', name: 'PRD', description: 'Product requirements document' }, { id: 'modern', name: 'Modern', description: 'Clean and professional' },
{ id: 'technical', name: 'Technical', description: 'Technical documentation' }, { id: 'bold', name: 'Bold', description: 'High impact design' },
{ id: 'proposal', name: 'Proposal', description: 'Business proposal' }, { id: 'elegant', name: 'Elegant', description: 'Sophisticated style' },
{ id: 'report', name: 'Report', description: 'Detailed report format' }, { id: 'tech', name: 'Tech', description: 'Technology focused' },
{ id: 'guide', name: 'Guide', description: 'Step-by-step guide' }, { id: 'creative', name: 'Creative', description: 'Artistic and unique' },
{ id: 'wiki', name: 'Wiki', description: 'Knowledge base article' }, { id: 'minimal', name: 'Minimal', description: 'Simple and clear' },
{ id: 'policy', name: 'Policy', description: 'Policy document' }, { id: 'corporate', name: 'Corporate', description: 'Business standard' },
{ id: 'meeting-notes', name: 'Meeting Notes', description: 'Meeting minutes' }, { id: 'vibrant', name: 'Vibrant', description: 'Colorful and energetic' },
], ],
}, },
}, },
@ -175,76 +165,68 @@ const modes: Mode[] = [
}, },
}, },
{ {
id: 'slides', id: 'docs',
label: 'Slides', label: 'Docs',
icon: <Presentation className="w-4 h-4" />, icon: <FileText className="w-4 h-4" />,
samplePrompts: [ samplePrompts: [
'Create a Series A pitch deck with market size, traction, and financial projections', 'Write a comprehensive PRD for an AI-powered recommendation engine',
'Build a Q4 business review showcasing KPIs, wins, and strategic initiatives', 'Draft a technical architecture document for a scalable microservices platform',
'Design a product launch presentation with demo videos and customer testimonials', 'Create a go-to-market strategy document for our Q2 product launch',
'Develop a sales enablement deck explaining our value prop and competitive advantages', 'Develop a 90-day onboarding playbook for engineering managers',
'Create an investor update highlighting key metrics and upcoming milestones', 'Write an API documentation guide with examples and best practices',
'Build a customer case study presentation showing ROI and success metrics', 'Create a company handbook covering culture, policies, and benefits',
'Design an all-hands presentation covering company updates and vision', 'Draft a data privacy policy compliant with GDPR and CCPA',
'Develop a training deck for new product features and workflows', 'Develop a customer success playbook for SaaS enterprise accounts',
'Create a conference talk about scaling engineering teams', 'Write a security incident response plan with escalation procedures',
'Build a board meeting presentation with strategic recommendations', 'Create a comprehensive style guide for brand and content',
], ],
options: { options: {
title: 'Choose a template', title: 'Choose a template',
items: [ items: [
{ id: 'modern', name: 'Modern', description: 'Clean and professional' }, { id: 'prd', name: 'PRD', description: 'Product requirements document' },
{ id: 'bold', name: 'Bold', description: 'High impact design' }, { id: 'technical', name: 'Technical', description: 'Technical documentation' },
{ id: 'elegant', name: 'Elegant', description: 'Sophisticated style' }, { id: 'proposal', name: 'Proposal', description: 'Business proposal' },
{ id: 'tech', name: 'Tech', description: 'Technology focused' }, { id: 'report', name: 'Report', description: 'Detailed report format' },
{ id: 'creative', name: 'Creative', description: 'Artistic and unique' }, { id: 'guide', name: 'Guide', description: 'Step-by-step guide' },
{ id: 'minimal', name: 'Minimal', description: 'Simple and clear' }, { id: 'wiki', name: 'Wiki', description: 'Knowledge base article' },
{ id: 'corporate', name: 'Corporate', description: 'Business standard' }, { id: 'policy', name: 'Policy', description: 'Policy document' },
{ id: 'vibrant', name: 'Vibrant', description: 'Colorful and energetic' }, { id: 'meeting-notes', name: 'Meeting Notes', description: 'Meeting minutes' },
], ],
}, },
}, },
{ {
id: 'image', id: 'people',
label: 'Image', label: 'People',
icon: <ImageIcon className="w-4 h-4" />, icon: <Users className="w-4 h-4" />,
samplePrompts: [ samplePrompts: [
'A majestic golden eagle soaring through misty mountain peaks at sunrise with dramatic lighting', 'Find VP of Engineering candidates at Series B+ AI companies in NYC',
'Close-up portrait of a fashion model with avant-garde makeup, studio lighting, high contrast shadows', 'Build lead list of CMOs at SaaS companies with 50-200 employees',
'Cozy Scandinavian living room with natural wood furniture, indoor plants, and soft morning sunlight', 'Research blockchain developers with Solidity experience open to relocation',
'Futuristic cyberpunk street market at night with neon signs, rain-slicked pavement, and holographic displays', 'Generate prospect list of tech founders who raised funding in the last 6 months',
'Elegant product photography of luxury perfume bottle on marble surface with soft reflections', 'Identify Product Managers at fintech startups with 5+ years experience',
'Whimsical floating islands connected by rope bridges in a pastel sky with dreamy clouds', 'Find decision-makers at mid-market companies in healthcare IT',
'Macro close-up of morning dew drops on vibrant flower petals with bokeh background', 'Research sales leaders at B2B companies with recent ARR growth',
'Modern workspace desk setup with laptop, coffee, notebook, and succulent plants from above', 'Build list of CTOs at enterprise companies adopting AI infrastructure',
'Mystical forest path with ancient trees, glowing fireflies, and ethereal light beams through fog', 'Find UX designers with experience in mobile-first e-commerce',
'Architectural detail of contemporary glass building facade with geometric patterns and reflections', 'Identify DevOps engineers at cloud-native startups in Austin',
'Vibrant street food vendor stall with colorful ingredients, steam rising, and warm lighting', ],
'Serene Japanese zen garden with raked sand, moss-covered stones, and cherry blossom petals', },
'Dynamic action shot of athlete mid-jump against dramatic sunset sky, silhouette effect', {
'Rustic farmhouse kitchen with copper pots, fresh herbs, wooden cutting boards, and natural textures', id: 'research',
'Abstract fluid art with swirling metallic gold, deep blue, and emerald green organic patterns', label: 'Research',
icon: <Search className="w-4 h-4" />,
samplePrompts: [
'Analyze emerging trends in quantum computing and potential business applications',
'Research top 10 competitors in the AI-powered CRM space with feature comparison',
'Investigate regulatory requirements for launching a fintech app in the EU',
'Compile market analysis on electric vehicle adoption rates across major markets',
'Study the impact of remote work on commercial real estate demand in major cities',
'Research Web3 adoption patterns among Fortune 500 companies',
'Analyze consumer sentiment towards sustainable fashion brands',
'Investigate the latest developments in gene therapy for rare diseases',
'Study pricing strategies of successful D2C subscription box companies',
'Research the competitive landscape of AI-powered cybersecurity solutions',
], ],
options: {
title: 'Choose a style',
items: [
{ id: 'photorealistic', name: 'Photorealistic', image: '/images/image-styles/photorealistic_eagle-min.png' },
{ id: 'watercolor', name: 'Watercolor', image: '/images/image-styles/watercolor_garden-min.png' },
{ id: 'digital-art', name: 'Digital Art', image: '/images/image-styles/digital_art_cyberpunk-min.png' },
{ id: 'oil-painting', name: 'Oil Painting', image: '/images/image-styles/oil_painting_villa-min.png' },
{ id: 'minimalist', name: 'Minimalist', image: '/images/image-styles/minimalist_coffee-min.png' },
{ id: 'isometric', name: 'Isometric', image: '/images/image-styles/isometric_bedroom-min.png' },
{ id: 'vintage', name: 'Vintage', image: '/images/image-styles/vintage_diner-min.png' },
{ id: 'comic', name: 'Comic Book', image: '/images/image-styles/comic_book_robot-min.png' },
{ id: 'neon', name: 'Neon', image: '/images/image-styles/neon_jellyfish-min.png' },
{ id: 'pastel', name: 'Pastel', image: '/images/image-styles/pastel_landscape-min.png' },
{ id: 'geometric', name: 'Geometric', image: '/images/image-styles/geometric_crystal-min.png' },
{ id: 'abstract', name: 'Abstract', image: '/images/image-styles/abstract_organic-min.png' },
{ id: 'anime', name: 'Anime', image: '/images/image-styles/anime_forest-min.png' },
{ id: 'impressionist', name: 'Impressionist', image: '/images/image-styles/impressionist_garden-min.png' },
{ id: 'surreal', name: 'Surreal', image: '/images/image-styles/surreal_islands-min.png' },
],
},
}, },
]; ];
@ -347,7 +329,7 @@ export function SunaModesPanel({ selectedMode, onModeSelect, onSelectPrompt, isM
</div> </div>
)} )}
{/* Sample Prompts - Card Grid Style (for code, docs, data, slides, image) */} {/* Sample Prompts - Card Grid Style (for image, slides, data, docs) */}
{selectedMode && displayedPrompts && !['research', 'people'].includes(selectedMode) && ( {selectedMode && displayedPrompts && !['research', 'people'].includes(selectedMode) && (
<div className="space-y-3 animate-in fade-in-0 zoom-in-95 duration-300"> <div className="space-y-3 animate-in fade-in-0 zoom-in-95 duration-300">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">

View File

@ -37,11 +37,23 @@ export function useToolsMetadata() {
return useQuery<ToolsMetadataResponse>({ return useQuery<ToolsMetadataResponse>({
queryKey: ['tools', 'metadata'], queryKey: ['tools', 'metadata'],
queryFn: async () => { queryFn: async () => {
const response = await backendApi.get<ToolsMetadataResponse>('/tools'); const response = await backendApi.get<{ success: boolean; tools: ToolMetadata[] }>('/tools');
if (!response.success || !response.data) { if (!response.success || !response.data) {
throw new Error('Failed to fetch tools metadata'); throw new Error('Failed to fetch tools metadata');
} }
return response.data;
// Backend returns array, convert to object keyed by tool name
const toolsArray = response.data.tools;
const toolsObject: Record<string, ToolMetadata> = {};
for (const tool of toolsArray) {
toolsObject[tool.name] = tool;
}
return {
success: response.data.success,
tools: toolsObject
};
}, },
staleTime: 1000 * 60 * 60, // Cache for 1 hour since tools don't change frequently staleTime: 1000 * 60 * 60, // Cache for 1 hour since tools don't change frequently
gcTime: 1000 * 60 * 60 * 24, // Keep in cache for 24 hours gcTime: 1000 * 60 * 60 * 24, // Keep in cache for 24 hours