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
description (str): Method description
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
description: str
is_core: bool = False
visible: bool = False
visible: bool = True
class Tool(ABC):
"""Abstract base class for all tools.
@ -254,7 +254,7 @@ def method_metadata(
display_name: str,
description: str,
is_core: bool = False,
visible: bool = False
visible: bool = True
):
"""Decorator to add metadata to a tool method.

View File

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

View File

@ -268,7 +268,9 @@ def ask(self, question: str):
## 👁️ 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.

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["is_core"] = False
method_info["visible"] = False
method_info["visible"] = True
metadata["methods"].append(method_info)
except Exception as e:

View File

@ -206,7 +206,10 @@ export const GranularToolConfiguration = ({
const toolGroup = getToolGroup(toolName, toolsData);
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();
@ -251,7 +254,7 @@ export const GranularToolConfiguration = ({
const isGroupEnabled = isToolGroupEnabled(toolGroup.name);
const isExpanded = expandedGroups.has(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 hasGranular = hasGranularControl(toolGroup.name, toolsData);
@ -323,7 +326,9 @@ export const GranularToolConfiguration = ({
</span>
</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);
return (

View File

@ -57,13 +57,13 @@ export function normalizeToolGroup(apiToolGroup: any): ToolGroup {
enabled: m.enabled ?? true,
isCore: m.is_core || m.isCore,
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,
isCore: apiToolGroup.is_core || apiToolGroup.isCore,
is_core: apiToolGroup.is_core,
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 {
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(

View File

@ -11,7 +11,6 @@ import {
FileText,
Search,
Users,
Code2,
RefreshCw,
} from 'lucide-react';
import { Button } from '@/components/ui/button';
@ -25,7 +24,7 @@ interface SunaModesPanelProps {
isMobile?: boolean;
}
type ModeType = 'research' | 'people' | 'code' | 'docs' | 'data' | 'slides' | 'image';
type ModeType = 'image' | 'slides' | 'data' | 'docs' | 'people' | 'research';
interface Mode {
id: ModeType;
@ -53,83 +52,74 @@ interface Mode {
const modes: Mode[] = [
{
id: 'research',
label: 'Research',
icon: <Search className="w-4 h-4" />,
id: 'image',
label: 'Image',
icon: <ImageIcon 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',
'A majestic golden eagle soaring through misty mountain peaks at sunrise with dramatic lighting',
'Close-up portrait of a fashion model with avant-garde makeup, studio lighting, high contrast shadows',
'Cozy Scandinavian living room with natural wood furniture, indoor plants, and soft morning sunlight',
'Futuristic cyberpunk street market at night with neon signs, rain-slicked pavement, and holographic displays',
'Elegant product photography of luxury perfume bottle on marble surface with soft reflections',
'Whimsical floating islands connected by rope bridges in a pastel sky with dreamy clouds',
'Macro close-up of morning dew drops on vibrant flower petals with bokeh background',
'Modern workspace desk setup with laptop, coffee, notebook, and succulent plants from above',
'Mystical forest path with ancient trees, glowing fireflies, and ethereal light beams through fog',
'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',
label: 'People',
icon: <Users className="w-4 h-4" />,
id: 'slides',
label: 'Slides',
icon: <Presentation className="w-4 h-4" />,
samplePrompts: [
'Find VP of Engineering candidates at Series B+ AI companies in NYC',
'Build lead list of CMOs at SaaS companies with 50-200 employees',
'Research blockchain developers with Solidity experience open to relocation',
'Generate prospect list of tech founders who raised funding in the last 6 months',
'Identify Product Managers at fintech startups with 5+ years experience',
'Find decision-makers at mid-market companies in healthcare IT',
'Research sales leaders at B2B companies with recent ARR growth',
'Build list of CTOs at enterprise companies adopting AI infrastructure',
'Find UX designers with experience in mobile-first e-commerce',
'Identify DevOps engineers at cloud-native startups in Austin',
],
},
{
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',
'Create a Series A pitch deck with market size, traction, and financial projections',
'Build a Q4 business review showcasing KPIs, wins, and strategic initiatives',
'Design a product launch presentation with demo videos and customer testimonials',
'Develop a sales enablement deck explaining our value prop and competitive advantages',
'Create an investor update highlighting key metrics and upcoming milestones',
'Build a customer case study presentation showing ROI and success metrics',
'Design an all-hands presentation covering company updates and vision',
'Develop a training deck for new product features and workflows',
'Create a conference talk about scaling engineering teams',
'Build a board meeting presentation with strategic recommendations',
],
options: {
title: 'Choose a template',
items: [
{ id: 'prd', name: 'PRD', description: 'Product requirements document' },
{ id: 'technical', name: 'Technical', description: 'Technical documentation' },
{ id: 'proposal', name: 'Proposal', description: 'Business proposal' },
{ id: 'report', name: 'Report', description: 'Detailed report format' },
{ id: 'guide', name: 'Guide', description: 'Step-by-step guide' },
{ id: 'wiki', name: 'Wiki', description: 'Knowledge base article' },
{ id: 'policy', name: 'Policy', description: 'Policy document' },
{ id: 'meeting-notes', name: 'Meeting Notes', description: 'Meeting minutes' },
{ id: 'modern', name: 'Modern', description: 'Clean and professional' },
{ id: 'bold', name: 'Bold', description: 'High impact design' },
{ id: 'elegant', name: 'Elegant', description: 'Sophisticated style' },
{ id: 'tech', name: 'Tech', description: 'Technology focused' },
{ id: 'creative', name: 'Creative', description: 'Artistic and unique' },
{ id: 'minimal', name: 'Minimal', description: 'Simple and clear' },
{ id: 'corporate', name: 'Corporate', description: 'Business standard' },
{ id: 'vibrant', name: 'Vibrant', description: 'Colorful and energetic' },
],
},
},
@ -175,76 +165,68 @@ const modes: Mode[] = [
},
},
{
id: 'slides',
label: 'Slides',
icon: <Presentation className="w-4 h-4" />,
id: 'docs',
label: 'Docs',
icon: <FileText className="w-4 h-4" />,
samplePrompts: [
'Create a Series A pitch deck with market size, traction, and financial projections',
'Build a Q4 business review showcasing KPIs, wins, and strategic initiatives',
'Design a product launch presentation with demo videos and customer testimonials',
'Develop a sales enablement deck explaining our value prop and competitive advantages',
'Create an investor update highlighting key metrics and upcoming milestones',
'Build a customer case study presentation showing ROI and success metrics',
'Design an all-hands presentation covering company updates and vision',
'Develop a training deck for new product features and workflows',
'Create a conference talk about scaling engineering teams',
'Build a board meeting presentation with strategic recommendations',
'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: {
title: 'Choose a template',
items: [
{ id: 'modern', name: 'Modern', description: 'Clean and professional' },
{ id: 'bold', name: 'Bold', description: 'High impact design' },
{ id: 'elegant', name: 'Elegant', description: 'Sophisticated style' },
{ id: 'tech', name: 'Tech', description: 'Technology focused' },
{ id: 'creative', name: 'Creative', description: 'Artistic and unique' },
{ id: 'minimal', name: 'Minimal', description: 'Simple and clear' },
{ id: 'corporate', name: 'Corporate', description: 'Business standard' },
{ id: 'vibrant', name: 'Vibrant', description: 'Colorful and energetic' },
{ id: 'prd', name: 'PRD', description: 'Product requirements document' },
{ id: 'technical', name: 'Technical', description: 'Technical documentation' },
{ id: 'proposal', name: 'Proposal', description: 'Business proposal' },
{ id: 'report', name: 'Report', description: 'Detailed report format' },
{ id: 'guide', name: 'Guide', description: 'Step-by-step guide' },
{ id: 'wiki', name: 'Wiki', description: 'Knowledge base article' },
{ id: 'policy', name: 'Policy', description: 'Policy document' },
{ id: 'meeting-notes', name: 'Meeting Notes', description: 'Meeting minutes' },
],
},
},
{
id: 'image',
label: 'Image',
icon: <ImageIcon className="w-4 h-4" />,
id: 'people',
label: 'People',
icon: <Users className="w-4 h-4" />,
samplePrompts: [
'A majestic golden eagle soaring through misty mountain peaks at sunrise with dramatic lighting',
'Close-up portrait of a fashion model with avant-garde makeup, studio lighting, high contrast shadows',
'Cozy Scandinavian living room with natural wood furniture, indoor plants, and soft morning sunlight',
'Futuristic cyberpunk street market at night with neon signs, rain-slicked pavement, and holographic displays',
'Elegant product photography of luxury perfume bottle on marble surface with soft reflections',
'Whimsical floating islands connected by rope bridges in a pastel sky with dreamy clouds',
'Macro close-up of morning dew drops on vibrant flower petals with bokeh background',
'Modern workspace desk setup with laptop, coffee, notebook, and succulent plants from above',
'Mystical forest path with ancient trees, glowing fireflies, and ethereal light beams through fog',
'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',
'Find VP of Engineering candidates at Series B+ AI companies in NYC',
'Build lead list of CMOs at SaaS companies with 50-200 employees',
'Research blockchain developers with Solidity experience open to relocation',
'Generate prospect list of tech founders who raised funding in the last 6 months',
'Identify Product Managers at fintech startups with 5+ years experience',
'Find decision-makers at mid-market companies in healthcare IT',
'Research sales leaders at B2B companies with recent ARR growth',
'Build list of CTOs at enterprise companies adopting AI infrastructure',
'Find UX designers with experience in mobile-first e-commerce',
'Identify DevOps engineers at cloud-native startups in Austin',
],
},
{
id: 'research',
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>
)}
{/* 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) && (
<div className="space-y-3 animate-in fade-in-0 zoom-in-95 duration-300">
<div className="flex items-center justify-between">

View File

@ -37,11 +37,23 @@ export function useToolsMetadata() {
return useQuery<ToolsMetadataResponse>({
queryKey: ['tools', 'metadata'],
queryFn: async () => {
const response = await backendApi.get<ToolsMetadataResponse>('/tools');
const response = await backendApi.get<{ success: boolean; tools: ToolMetadata[] }>('/tools');
if (!response.success || !response.data) {
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
gcTime: 1000 * 60 * 60 * 24, // Keep in cache for 24 hours