From 87da181fc56ac60e19517f98195d483e258db73e Mon Sep 17 00:00:00 2001 From: marko-kraemer Date: Thu, 10 Jul 2025 07:21:23 +0200 Subject: [PATCH] wip --- backend/pipedream/api.py | 2 +- frontend/src/app/(dashboard)/agents/page.tsx | 219 +----------------- .../components/agents/agent-config-modal.tsx | 36 ++- .../agents/mcp/mcp-configuration-new.tsx | 34 ++- .../src/components/agents/results-info.tsx | 35 --- frontend/src/components/agents/tools.ts | 16 +- ...ttings-dropdown.tsx => agent-selector.tsx} | 75 +++--- .../thread/chat-input/chat-input.tsx | 44 +++- .../chat-input/chat-settings-dialog.tsx | 110 --------- .../thread/chat-input/message-input.tsx | 60 ++--- .../thread/chat-input/model-selector.tsx | 52 ++--- .../thread/chat-input/voice-recorder.tsx | 51 ++-- .../hooks/react-query/agents/use-agents.ts | 42 ++++ 13 files changed, 255 insertions(+), 521 deletions(-) rename frontend/src/components/thread/chat-input/{chat-settings-dropdown.tsx => agent-selector.tsx} (85%) delete mode 100644 frontend/src/components/thread/chat-input/chat-settings-dialog.tsx diff --git a/backend/pipedream/api.py b/backend/pipedream/api.py index 2a2dba90..0b511add 100644 --- a/backend/pipedream/api.py +++ b/backend/pipedream/api.py @@ -377,7 +377,7 @@ async def get_pipedream_apps( data = response.json() - print(data) + # print(data) logger.info(f"Successfully fetched {len(data.get('data', []))} apps from Pipedream registry") return { diff --git a/frontend/src/app/(dashboard)/agents/page.tsx b/frontend/src/app/(dashboard)/agents/page.tsx index 6a6eefd3..d89ad0f9 100644 --- a/frontend/src/app/(dashboard)/agents/page.tsx +++ b/frontend/src/app/(dashboard)/agents/page.tsx @@ -15,7 +15,7 @@ import { Skeleton } from '@/components/ui/skeleton'; import { useRouter } from 'next/navigation'; // Import hooks and components from existing pages -import { useAgents, useUpdateAgent, useDeleteAgent, useOptimisticAgentUpdate, useCreateAgent } from '@/hooks/react-query/agents/use-agents'; +import { useAgents, useUpdateAgent, useDeleteAgent, useOptimisticAgentUpdate, useCreateNewAgent } from '@/hooks/react-query/agents/use-agents'; import { useMarketplaceTemplates, useInstallTemplate, useMyTemplates, useUnpublishTemplate, usePublishTemplate } from '@/hooks/react-query/secure-mcp/use-secure-mcp'; import { useCreateCredentialProfile, type CreateCredentialProfileRequest } from '@/hooks/react-query/mcp/use-credential-profiles'; import { useMCPServerDetails } from '@/hooks/react-query/mcp/use-mcp-servers'; @@ -108,13 +108,7 @@ interface MissingProfile { required_config: string[]; } -interface AgentPreviewSheetProps { - item: MarketplaceTemplate | null; - open: boolean; - onOpenChange: (open: boolean) => void; - onInstall: (item: MarketplaceTemplate) => void; - isInstalling: boolean; -} + interface InstallDialogProps { item: MarketplaceTemplate | null; @@ -124,158 +118,7 @@ interface InstallDialogProps { isInstalling: boolean; } -const AgentPreviewSheet: React.FC = ({ - item, - open, - onOpenChange, - onInstall, - isInstalling -}) => { - if (!item) return null; - const { avatar, color } = item.avatar && item.avatar_color - ? { avatar: item.avatar, color: item.avatar_color } - : getAgentAvatar(item.id); - - const formatDate = (dateString: string) => { - return new Date(dateString).toLocaleDateString('en-US', { - year: 'numeric', - month: 'long', - day: 'numeric' - }); - }; - - return ( - - - -
-
-
{avatar}
-
-
-
- - {item.name} - -
-
-
- - {item.creator_name} -
-
- - {item.download_count} downloads -
-
-
-
- -
-
-
-

- Description -

-

- {item.description || 'No description available for this agent.'} -

-
- - {item.tags && item.tags.length > 0 && ( -
-

- Tags -

-
- {item.tags.map(tag => ( - - - {tag} - - ))} -
-
- )} - {item.mcp_requirements && item.mcp_requirements.length > 0 && ( -
-

- Required Tools & MCPs -

-
- {item.mcp_requirements.map((mcp, index) => ( -
-
-
- -
-
-
{mcp.display_name}
- {mcp.enabled_tools && mcp.enabled_tools.length > 0 && ( -
- {mcp.enabled_tools.length} tool{mcp.enabled_tools.length !== 1 ? 's' : ''} -
- )} -
-
- {mcp.custom_type && ( - - {mcp.custom_type.toUpperCase()} - - )} -
- ))} -
-
- )} - {item.metadata?.source_version_name && ( -
-

- Version -

-
- - {item.metadata.source_version_name} -
-
- )} - {item.marketplace_published_at && ( -
-

- Published -

-
- - {formatDate(item.marketplace_published_at)} -
-
- )} -
-
-
- ); -}; const InstallDialog: React.FC = ({ item, @@ -939,7 +782,6 @@ export default function AgentsPage() { const [installingItemId, setInstallingItemId] = useState(null); const [selectedItem, setSelectedItem] = useState(null); const [showInstallDialog, setShowInstallDialog] = useState(false); - const [showPreviewSheet, setShowPreviewSheet] = useState(false); // Templates state const [templatesActioningId, setTemplatesActioningId] = useState(null); @@ -987,7 +829,7 @@ export default function AgentsPage() { const updateAgentMutation = useUpdateAgent(); const deleteAgentMutation = useDeleteAgent(); - const createAgentMutation = useCreateAgent(); + const createNewAgentMutation = useCreateNewAgent(); const { optimisticallyUpdateAgent, revertOptimisticUpdate } = useOptimisticAgentUpdate(); const installTemplateMutation = useInstallTemplate(); const unpublishMutation = useUnpublishTemplate(); @@ -1138,44 +980,11 @@ export default function AgentsPage() { setEditDialogOpen(true); }; - const handleCreateNewAgent = async () => { - try { - const { avatar, avatar_color } = generateRandomAvatar(); - - const defaultAgentData = { - name: 'New Agent', - description: 'A newly created agent', - system_prompt: 'You are a helpful assistant. Provide clear, accurate, and helpful responses to user queries.', - avatar, - avatar_color, - configured_mcps: [], - agentpress_tools: Object.fromEntries( - Object.entries(DEFAULT_AGENTPRESS_TOOLS).map(([key, value]) => [ - key, - { enabled: value.enabled, description: value.description } - ]) - ), - is_default: false, - }; - - const newAgent = await createAgentMutation.mutateAsync(defaultAgentData); - router.push(`/agents/config/${newAgent.agent_id}`); - } catch (error) { - console.error('Error creating agent:', error); - } + const handleCreateNewAgent = () => { + createNewAgentMutation.mutate(); }; // Marketplace handlers - const handleItemClick = (item: MarketplaceTemplate) => { - setSelectedItem(item); - setShowPreviewSheet(true); - }; - - const handlePreviewInstall = (item: MarketplaceTemplate) => { - setShowPreviewSheet(false); - setShowInstallDialog(true); - }; - const handleInstallClick = (item: MarketplaceTemplate, e?: React.MouseEvent) => { if (e) { e.stopPropagation(); @@ -1288,13 +1097,6 @@ export default function AgentsPage() { } }; - const handleTagFilter = (tag: string) => { - setMarketplaceSelectedTags(prev => - prev.includes(tag) - ? prev.filter(t => t !== tag) - : [...prev, tag] - ); - }; const getItemStyling = (item: MarketplaceTemplate) => { if (item.avatar && item.avatar_color) { @@ -1560,7 +1362,7 @@ export default function AgentsPage() {
handleItemClick(item)} + onClick={() => handleInstallClick(item)} >
@@ -1658,7 +1460,7 @@ export default function AgentsPage() {
handleItemClick(item)} + onClick={() => handleInstallClick(item)} >
@@ -1975,13 +1777,6 @@ export default function AgentsPage() { {/* Marketplace Dialogs */} - = ({ const updateAgentMutation = useUpdateAgent(); const router = useRouter(); - const handleAgentSelect = (agentId: string | undefined) => { - onAgentSelect?.(agentId); - }; + // Update active tab when initialTab changes or modal opens + React.useEffect(() => { + if (isOpen && initialTab) { + setActiveTab(initialTab); + } + }, [initialTab, isOpen]); // Update local state when agent data changes React.useEffect(() => { @@ -108,19 +112,10 @@ export const AgentConfigModal: React.FC = ({ -
- - - Agent Configuration - - -
+ + + Agent Configuration +
@@ -128,11 +123,10 @@ export const AgentConfigModal: React.FC = ({
- {/* */} +
{selectedAgentId && (
diff --git a/frontend/src/components/agents/mcp/mcp-configuration-new.tsx b/frontend/src/components/agents/mcp/mcp-configuration-new.tsx index d01c6c29..786db325 100644 --- a/frontend/src/components/agents/mcp/mcp-configuration-new.tsx +++ b/frontend/src/components/agents/mcp/mcp-configuration-new.tsx @@ -93,18 +93,30 @@ export const MCPConfigurationNew: React.FC = ({ )} {configuredMCPs.length > 0 && ( -
-
-

- Configured Integrations -

+
+
+
+

+ Configured Integrations +

+
+
+ +
-
- +
+ +
)} diff --git a/frontend/src/components/agents/results-info.tsx b/frontend/src/components/agents/results-info.tsx index 09be1735..47e62939 100644 --- a/frontend/src/components/agents/results-info.tsx +++ b/frontend/src/components/agents/results-info.tsx @@ -1,10 +1,4 @@ import React from 'react'; -import { Button } from '@/components/ui/button'; -import { Plus, FileText, Loader2 } from 'lucide-react'; -import { useRouter } from 'next/navigation'; -import { useCreateAgent } from '@/hooks/react-query/agents/use-agents'; -import { DEFAULT_AGENTPRESS_TOOLS } from './tools'; -import { generateRandomAvatar } from '../../lib/utils/_avatar-generator'; interface ResultsInfoProps { isLoading: boolean; @@ -21,35 +15,6 @@ export const ResultsInfo = ({ currentPage, totalPages }: ResultsInfoProps) => { - const router = useRouter(); - const createAgentMutation = useCreateAgent(); - - const handleCreateNewAgent = async () => { - try { - const { avatar, avatar_color } = generateRandomAvatar(); - - const defaultAgentData = { - name: 'New Agent', - description: 'A newly created agent', - system_prompt: 'You are a helpful assistant. Provide clear, accurate, and helpful responses to user queries.', - avatar, - avatar_color, - configured_mcps: [], - agentpress_tools: Object.fromEntries( - Object.entries(DEFAULT_AGENTPRESS_TOOLS).map(([key, value]) => [ - key, - { enabled: value.enabled, description: value.description } - ]) - ), - is_default: false, - }; - - const newAgent = await createAgentMutation.mutateAsync(defaultAgentData); - router.push(`/agents/config/${newAgent.agent_id}`); - } catch (error) { - console.error('Error creating agent:', error); - } - }; if (isLoading || totalAgents === 0) { return null; diff --git a/frontend/src/components/agents/tools.ts b/frontend/src/components/agents/tools.ts index f9c5a29f..d251894b 100644 --- a/frontend/src/components/agents/tools.ts +++ b/frontend/src/components/agents/tools.ts @@ -1,12 +1,12 @@ export const DEFAULT_AGENTPRESS_TOOLS: Record = { - 'sb_shell_tool': { enabled: false, description: 'Execute shell commands in tmux sessions for terminal operations, CLI tools, and system management', icon: '💻', color: 'bg-slate-100 dark:bg-slate-800' }, - 'sb_files_tool': { enabled: false, description: 'Create, read, update, and delete files in the workspace with comprehensive file management', icon: '📁', color: 'bg-blue-100 dark:bg-blue-800/50' }, - 'sb_browser_tool': { enabled: false, description: 'Browser automation for web navigation, clicking, form filling, and page interaction', icon: '🌐', color: 'bg-indigo-100 dark:bg-indigo-800/50' }, - 'sb_deploy_tool': { enabled: false, description: 'Deploy applications and services with automated deployment capabilities', icon: '🚀', color: 'bg-green-100 dark:bg-green-800/50' }, - 'sb_expose_tool': { enabled: false, description: 'Expose services and manage ports for application accessibility', icon: '🔌', color: 'bg-orange-100 dark:bg-orange-800/20' }, - 'web_search_tool': { enabled: false, description: 'Search the web using Tavily API and scrape webpages with Firecrawl for research', icon: '🔍', color: 'bg-yellow-100 dark:bg-yellow-800/50' }, - 'sb_vision_tool': { enabled: false, description: 'Vision and image processing capabilities for visual content analysis', icon: '👁️', color: 'bg-pink-100 dark:bg-pink-800/50' }, - 'data_providers_tool': { enabled: false, description: 'Access to data providers and external APIs (requires RapidAPI key)', icon: '🔗', color: 'bg-cyan-100 dark:bg-cyan-800/50' }, + 'sb_shell_tool': { enabled: true, description: 'Execute shell commands in tmux sessions for terminal operations, CLI tools, and system management', icon: '💻', color: 'bg-slate-100 dark:bg-slate-800' }, + 'sb_files_tool': { enabled: true, description: 'Create, read, update, and delete files in the workspace with comprehensive file management', icon: '📁', color: 'bg-blue-100 dark:bg-blue-800/50' }, + 'sb_browser_tool': { enabled: true, description: 'Browser automation for web navigation, clicking, form filling, and page interaction', icon: '🌐', color: 'bg-indigo-100 dark:bg-indigo-800/50' }, + 'sb_deploy_tool': { enabled: true, description: 'Deploy applications and services with automated deployment capabilities', icon: '🚀', color: 'bg-green-100 dark:bg-green-800/50' }, + 'sb_expose_tool': { enabled: true, description: 'Expose services and manage ports for application accessibility', icon: '🔌', color: 'bg-orange-100 dark:bg-orange-800/20' }, + 'web_search_tool': { enabled: true, description: 'Search the web using Tavily API and scrape webpages with Firecrawl for research', icon: '🔍', color: 'bg-yellow-100 dark:bg-yellow-800/50' }, + 'sb_vision_tool': { enabled: true, description: 'Vision and image processing capabilities for visual content analysis', icon: '👁️', color: 'bg-pink-100 dark:bg-pink-800/50' }, + 'data_providers_tool': { enabled: true, description: 'Access to data providers and external APIs (requires RapidAPI key)', icon: '🔗', color: 'bg-cyan-100 dark:bg-cyan-800/50' }, }; export const getToolDisplayName = (toolName: string): string => { diff --git a/frontend/src/components/thread/chat-input/chat-settings-dropdown.tsx b/frontend/src/components/thread/chat-input/agent-selector.tsx similarity index 85% rename from frontend/src/components/thread/chat-input/chat-settings-dropdown.tsx rename to frontend/src/components/thread/chat-input/agent-selector.tsx index 8ab69374..01e34d03 100644 --- a/frontend/src/components/thread/chat-input/chat-settings-dropdown.tsx +++ b/frontend/src/components/thread/chat-input/agent-selector.tsx @@ -17,8 +17,8 @@ import { TooltipProvider, TooltipTrigger, } from '@/components/ui/tooltip'; -import { useAgents } from '@/hooks/react-query/agents/use-agents'; -import { ChatSettingsDialog } from './chat-settings-dialog'; +import { useAgents, useCreateNewAgent } from '@/hooks/react-query/agents/use-agents'; + import { useRouter } from 'next/navigation'; import { cn, truncateString } from '@/lib/utils'; @@ -47,38 +47,26 @@ const PREDEFINED_AGENTS: PredefinedAgent[] = [ // } ]; -interface ChatSettingsDropdownProps { +interface AgentSelectorProps { selectedAgentId?: string; onAgentSelect?: (agentId: string | undefined) => void; - selectedModel: string; - onModelChange: (model: string) => void; - modelOptions: any[]; - subscriptionStatus: any; - canAccessModel: (modelId: string) => boolean; - refreshCustomModels?: () => void; disabled?: boolean; } -export const ChatSettingsDropdown: React.FC = ({ +export const AgentSelector: React.FC = ({ selectedAgentId, onAgentSelect, - selectedModel, - onModelChange, - modelOptions, - subscriptionStatus, - canAccessModel, - refreshCustomModels, disabled = false, }) => { const [isOpen, setIsOpen] = useState(false); const [searchQuery, setSearchQuery] = useState(''); const [highlightedIndex, setHighlightedIndex] = useState(-1); - const [dialogOpen, setDialogOpen] = useState(false); const searchInputRef = useRef(null); const router = useRouter(); const { data: agentsResponse, isLoading: agentsLoading } = useAgents(); const agents = agentsResponse?.agents || []; + const createNewAgentMutation = useCreateNewAgent(); // Combine all agents const allAgents = [ @@ -120,19 +108,31 @@ export const ChatSettingsDropdown: React.FC = ({ const getAgentDisplay = () => { const selectedAgent = allAgents.find(agent => agent.id === selectedAgentId); + if (selectedAgent) { + console.log('Selected agent found:', selectedAgent.name, 'with ID:', selectedAgent.id); return { name: selectedAgent.name, icon: selectedAgent.icon }; } + + // If selectedAgentId is not undefined but no agent is found, log a warning + if (selectedAgentId !== undefined) { + console.warn('Agent with ID', selectedAgentId, 'not found, falling back to Suna'); + } + + // Default to Suna (the first agent which has id: undefined) + const defaultAgent = allAgents[0]; + console.log('Using default agent:', defaultAgent.name); return { - name: 'Suna', - icon: Suna + name: defaultAgent.name, + icon: defaultAgent.icon }; }; const handleAgentSelect = (agentId: string | undefined) => { + console.log('Agent selected:', agentId === undefined ? 'Suna (default)' : agentId); onAgentSelect?.(agentId); setIsOpen(false); }; @@ -169,9 +169,9 @@ export const ChatSettingsDropdown: React.FC = ({ router.push('/agents'); }; - const handleMoreOptions = () => { + const handleCreateAgent = () => { setIsOpen(false); - setDialogOpen(true); + createNewAgentMutation.mutate(); }; const renderAgentItem = (agent: any, index: number) => { @@ -325,42 +325,33 @@ export const ChatSettingsDropdown: React.FC = ({ {/* Footer Actions */}
-
+
+
+
- ); }; \ No newline at end of file diff --git a/frontend/src/components/thread/chat-input/chat-input.tsx b/frontend/src/components/thread/chat-input/chat-input.tsx index 703ccfaf..c3519150 100644 --- a/frontend/src/components/thread/chat-input/chat-input.tsx +++ b/frontend/src/components/thread/chat-input/chat-input.tsx @@ -121,14 +121,54 @@ export const ChatInput = forwardRef( const deleteFileMutation = useFileDelete(); const queryClient = useQueryClient(); - const textareaRef = useRef(null); - const fileInputRef = useRef(null); + const textareaRef = useRef(null); + const fileInputRef = useRef(null); + const hasLoadedFromLocalStorage = useRef(false); useImperativeHandle(ref, () => ({ getPendingFiles: () => pendingFiles, clearPendingFiles: () => setPendingFiles([]), })); + // Load saved agent from localStorage on mount + useEffect(() => { + if (typeof window !== 'undefined' && onAgentSelect && !hasLoadedFromLocalStorage.current) { + // Don't load from localStorage if an agent is already selected + // or if there are URL parameters that might be setting the agent + const urlParams = new URLSearchParams(window.location.search); + const hasAgentIdInUrl = urlParams.has('agent_id'); + + if (!selectedAgentId && !hasAgentIdInUrl) { + const savedAgentId = localStorage.getItem('lastSelectedAgentId'); + if (savedAgentId) { + // Convert 'suna' back to undefined for the default agent + const agentIdToSelect = savedAgentId === 'suna' ? undefined : savedAgentId; + console.log('Loading saved agent from localStorage:', savedAgentId); + onAgentSelect(agentIdToSelect); + } else { + console.log('No saved agent found in localStorage'); + } + } else { + console.log('Skipping localStorage load:', { + hasSelectedAgent: !!selectedAgentId, + hasAgentIdInUrl, + selectedAgentId + }); + } + hasLoadedFromLocalStorage.current = true; + } + }, [onAgentSelect, selectedAgentId]); // Keep selectedAgentId to check current state + + // Save selected agent to localStorage whenever it changes + useEffect(() => { + if (typeof window !== 'undefined') { + // Use 'suna' as a special key for the default agent (undefined) + const keyToStore = selectedAgentId === undefined ? 'suna' : selectedAgentId; + console.log('Saving selected agent to localStorage:', keyToStore); + localStorage.setItem('lastSelectedAgentId', keyToStore); + } + }, [selectedAgentId]); + useEffect(() => { if (autoFocus && textareaRef.current) { textareaRef.current.focus(); diff --git a/frontend/src/components/thread/chat-input/chat-settings-dialog.tsx b/frontend/src/components/thread/chat-input/chat-settings-dialog.tsx deleted file mode 100644 index 49ab99d4..00000000 --- a/frontend/src/components/thread/chat-input/chat-settings-dialog.tsx +++ /dev/null @@ -1,110 +0,0 @@ -'use client'; - -import React, { useState } from 'react'; -import { Settings, X } from 'lucide-react'; -import { Button } from '@/components/ui/button'; -import { - Dialog, - DialogContent, - DialogHeader, - DialogTitle, - DialogTrigger, -} from '@/components/ui/dialog'; -import { Label } from '@/components/ui/label'; -import { ModelSelector } from './model-selector'; -import { SubscriptionStatus } from './_use-model-selection'; -import { cn } from '@/lib/utils'; -import { BillingModal } from '@/components/billing/billing-modal'; - -interface ChatSettingsDialogProps { - selectedModel: string; - onModelChange: (model: string) => void; - modelOptions: any[]; - subscriptionStatus: SubscriptionStatus; - canAccessModel: (modelId: string) => boolean; - refreshCustomModels?: () => void; - disabled?: boolean; - className?: string; - open?: boolean; - onOpenChange?: (open: boolean) => void; -} - -export function ChatSettingsDialog({ - selectedModel, - onModelChange, - modelOptions, - subscriptionStatus, - canAccessModel, - refreshCustomModels, - disabled = false, - className, - open: controlledOpen, - onOpenChange: controlledOnOpenChange, -}: ChatSettingsDialogProps) { - const [internalOpen, setInternalOpen] = useState(false); - const [billingModalOpen, setBillingModalOpen] = useState(false); - - const open = controlledOpen !== undefined ? controlledOpen : internalOpen; - const setOpen = controlledOnOpenChange || setInternalOpen; - - return ( - - {controlledOpen === undefined && ( - - - - )} - - - - - - Chat Settings - - - -
-
- -
- -
- {/* Billing Modal */} - - -

- Choose the AI model that best fits your needs. Premium models offer better performance. -

-
-
-
-
- ); -} \ No newline at end of file diff --git a/frontend/src/components/thread/chat-input/message-input.tsx b/frontend/src/components/thread/chat-input/message-input.tsx index b55355e0..adb78b49 100644 --- a/frontend/src/components/thread/chat-input/message-input.tsx +++ b/frontend/src/components/thread/chat-input/message-input.tsx @@ -7,7 +7,7 @@ import { UploadedFile } from './chat-input'; import { FileUploadHandler } from './file-upload-handler'; import { VoiceRecorder } from './voice-recorder'; import { ModelSelector } from './model-selector'; -import { ChatSettingsDropdown } from './chat-settings-dropdown'; +import { AgentSelector } from './agent-selector'; import { canAccessModel, SubscriptionStatus } from './_use-model-selection'; import { isLocalMode } from '@/lib/config'; import { useFeatureFlag } from '@/lib/feature-flags'; @@ -131,41 +131,29 @@ export const MessageInput = forwardRef( const renderDropdown = () => { if (isLoggedIn) { - if (hideAgentSelection) { - return - } else if (enableAdvancedConfig || (customAgentsEnabled && !flagsLoading)) { - return - } else { - return - } + const showAdvancedFeatures = enableAdvancedConfig || (customAgentsEnabled && !flagsLoading); + + return ( +
+ {showAdvancedFeatures && !hideAgentSelection && ( + + )} + +
+ ); } return ; } diff --git a/frontend/src/components/thread/chat-input/model-selector.tsx b/frontend/src/components/thread/chat-input/model-selector.tsx index 5d425538..1ad2b0c0 100644 --- a/frontend/src/components/thread/chat-input/model-selector.tsx +++ b/frontend/src/components/thread/chat-input/model-selector.tsx @@ -14,7 +14,7 @@ import { TooltipTrigger, } from '@/components/ui/tooltip'; import { Button } from '@/components/ui/button'; -import { Check, ChevronDown, Search, AlertTriangle, Crown, ArrowUpRight, Brain, Plus, Edit, Trash } from 'lucide-react'; +import { Check, ChevronDown, Search, AlertTriangle, Crown, ArrowUpRight, Brain, Plus, Edit, Trash, Cpu } from 'lucide-react'; import { ModelOption, SubscriptionStatus, @@ -91,9 +91,6 @@ export const ModelSelector: React.FC = ({ } }, [customModels]); - // Get current custom models from state - const currentCustomModels = customModels || []; - // Enhance model options with capabilities - using a Map to ensure uniqueness const modelMap = new Map(); @@ -511,30 +508,29 @@ export const ModelSelector: React.FC = ({ return (
- - - + + + + + + + + +

Choose a model

+
+
+
= ({ const getButtonClass = () => { switch (state) { case 'recording': - return 'text-red-500 hover:bg-red-600'; + return 'text-red-500 hover:bg-red-50 hover:text-red-600'; case 'processing': - return 'hover:bg-gray-100'; + return ''; default: - return 'hover:bg-gray-100'; + return ''; } }; @@ -156,17 +162,32 @@ export const VoiceRecorder: React.FC = ({ }; return ( - + + + + + + +

+ {state === 'recording' + ? 'Click to stop recording' + : state === 'processing' + ? 'Processing...' + : 'Record voice message' + } +

+
+
+
); }; \ No newline at end of file diff --git a/frontend/src/hooks/react-query/agents/use-agents.ts b/frontend/src/hooks/react-query/agents/use-agents.ts index 10ca51b6..7bbac505 100644 --- a/frontend/src/hooks/react-query/agents/use-agents.ts +++ b/frontend/src/hooks/react-query/agents/use-agents.ts @@ -4,6 +4,9 @@ import { toast } from 'sonner'; import { agentKeys } from './keys'; import { Agent, AgentUpdateRequest, AgentsParams, createAgent, deleteAgent, getAgent, getAgents, getThreadAgent, updateAgent, AgentBuilderChatRequest, AgentBuilderStreamData, startAgentBuilderChat, getAgentBuilderChatHistory } from './utils'; import { useRef, useCallback } from 'react'; +import { useRouter } from 'next/navigation'; +import { generateRandomAvatar } from '@/lib/utils/_avatar-generator'; +import { DEFAULT_AGENTPRESS_TOOLS } from '@/components/agents/tools'; export const useAgents = (params: AgentsParams = {}) => { return createQueryHook( @@ -44,6 +47,45 @@ export const useCreateAgent = () => { )(); }; +export const useCreateNewAgent = () => { + const router = useRouter(); + const createAgentMutation = useCreateAgent(); + + return createMutationHook( + async (_: void) => { + const { avatar, avatar_color } = generateRandomAvatar(); + + const defaultAgentData = { + name: 'New Agent', + description: '', + system_prompt: 'You are a helpful assistant. Provide clear, accurate, and helpful responses to user queries.', + avatar, + avatar_color, + configured_mcps: [], + agentpress_tools: Object.fromEntries( + Object.entries(DEFAULT_AGENTPRESS_TOOLS).map(([key, value]) => [ + key, + { enabled: value.enabled, description: value.description } + ]) + ), + is_default: false, + }; + + const newAgent = await createAgentMutation.mutateAsync(defaultAgentData); + return newAgent; + }, + { + onSuccess: (newAgent) => { + router.push(`/agents/config/${newAgent.agent_id}`); + }, + onError: (error) => { + console.error('Error creating agent:', error); + toast.error('Failed to create agent. Please try again.'); + }, + } + )(); +}; + export const useUpdateAgent = () => { const queryClient = useQueryClient();