This commit is contained in:
marko-kraemer 2025-08-10 22:01:21 -07:00
parent a73adb3c98
commit 23514db06d
9 changed files with 34 additions and 363 deletions

View File

@ -1,297 +0,0 @@
'use client';
import React, { useState } from 'react';
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Button } from '@/components/ui/button';
import { Settings2, Brain, Database, Zap, Workflow, Bot } from 'lucide-react';
import { AgentMCPConfiguration } from './agent-mcp-configuration';
import { AgentTriggersConfiguration } from './triggers/agent-triggers-configuration';
import { AgentWorkflowsConfiguration } from './workflows/agent-workflows-configuration';
import { AgentKnowledgeBaseManager } from './knowledge-base/agent-knowledge-base-manager';
import { AgentToolsConfiguration } from './agent-tools-configuration';
import { AgentSelector } from '../thread/chat-input/agent-selector';
import { useAgent, useUpdateAgent } from '@/hooks/react-query/agents/use-agents';
import { Input } from '@/components/ui/input';
import { Textarea } from '@/components/ui/textarea';
import { Label } from '@/components/ui/label';
import { toast } from 'sonner';
import { useRouter } from 'next/navigation';
import { DEFAULT_AGENTPRESS_TOOLS } from './tools';
interface AgentConfigModalProps {
isOpen: boolean;
onOpenChange: (open: boolean) => void;
selectedAgentId?: string;
onAgentSelect?: (agentId: string | undefined) => void;
initialTab?: string;
isSunaAgent?: boolean;
}
export const AgentConfigModal: React.FC<AgentConfigModalProps> = ({
isOpen,
onOpenChange,
selectedAgentId,
onAgentSelect,
initialTab = 'tools',
isSunaAgent
}) => {
const [activeTab, setActiveTab] = useState(initialTab);
const [editingInstructions, setEditingInstructions] = useState(false);
const [instructionsValue, setInstructionsValue] = useState('');
const [agentName, setAgentName] = useState('');
const [agentDescription, setAgentDescription] = useState('');
const { data: agent, isLoading } = useAgent(selectedAgentId || '');
const updateAgentMutation = useUpdateAgent();
const router = useRouter();
// 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(() => {
if (agent) {
setAgentName(agent.name || '');
setAgentDescription(agent.description || '');
setInstructionsValue(agent.system_prompt || '');
}
}, [agent]);
const handleSaveInstructions = async () => {
if (!selectedAgentId) return;
try {
await updateAgentMutation.mutateAsync({
agentId: selectedAgentId,
name: agentName,
description: agentDescription,
system_prompt: instructionsValue
});
toast.success('Agent updated successfully');
setEditingInstructions(false);
} catch (error) {
toast.error('Failed to update agent');
}
};
const handleToolsChange = async (tools: Record<string, { enabled: boolean; description: string }>) => {
if (!selectedAgentId) return;
try {
await updateAgentMutation.mutateAsync({
agentId: selectedAgentId,
agentpress_tools: tools
});
toast.success('Tools updated successfully');
} catch (error) {
toast.error('Failed to update tools');
}
};
const handleMCPChange = async (mcps: any) => {
if (!selectedAgentId) return;
try {
await updateAgentMutation.mutateAsync({
agentId: selectedAgentId,
configured_mcps: mcps.configured_mcps || [],
custom_mcps: mcps.custom_mcps || []
});
toast.success('Integrations updated successfully');
} catch (error) {
toast.error('Failed to update integrations');
}
};
const displayName = agent?.name || 'Suna';
return (
<Dialog open={isOpen} onOpenChange={onOpenChange}>
<DialogContent className="max-w-4xl h-[85vh] p-0 flex flex-col">
<DialogHeader className="flex-shrink-0 border-b px-6 py-4">
<DialogTitle className="flex items-center justify-between">
<div className="flex items-center gap-2">
<Bot className="h-4 w-4" />
Agent Configuration
</div>
{selectedAgentId && (
<Button
variant="ghost"
size="sm"
onClick={() => router.push(`/agents/config/${selectedAgentId}`)}
className="text-xs"
>
<Settings2 className="h-3 w-3 mr-1" />
Advanced
</Button>
)}
</DialogTitle>
</DialogHeader>
<div className="flex-shrink-0 px-6 py-3 border-b">
<AgentSelector
selectedAgentId={selectedAgentId}
onAgentSelect={onAgentSelect}
isSunaAgent={isSunaAgent}
/>
</div>
<div className="flex-1 min-h-0 px-6 py-4">
<Tabs value={activeTab} onValueChange={setActiveTab} className="h-full flex flex-col">
<TabsList className="grid w-full grid-cols-5 flex-shrink-0 h-9 mb-4">
<TabsTrigger value="tools" className="text-xs">
<Settings2 className="h-3 w-3 mr-1" />
Tools
</TabsTrigger>
<TabsTrigger value="instructions" className="text-xs">
<Brain className="h-3 w-3 mr-1" />
Instructions
</TabsTrigger>
<TabsTrigger value="knowledge" className="text-xs">
<Database className="h-3 w-3 mr-1" />
Knowledge
</TabsTrigger>
<TabsTrigger value="triggers" className="text-xs">
<Zap className="h-3 w-3 mr-1" />
Triggers
</TabsTrigger>
<TabsTrigger value="workflows" className="text-xs">
<Workflow className="h-3 w-3 mr-1" />
Playbooks
</TabsTrigger>
</TabsList>
<TabsContent value="tools" className="flex-1 m-0 mt-0 overflow-y-auto overflow-hidden">
<div className="h-full">
{selectedAgentId ? (
<AgentToolsConfiguration
tools={agent?.agentpress_tools || DEFAULT_AGENTPRESS_TOOLS}
onToolsChange={handleToolsChange}
/>
) : (
<div className="flex items-center justify-center h-32">
<p className="text-sm text-muted-foreground">Select an agent to configure tools</p>
</div>
)}
</div>
</TabsContent>
<TabsContent value="instructions" className="flex-1 m-0 mt-0 overflow-y-auto overflow-hidden">
<div className="h-full flex flex-col">
{selectedAgentId ? (
<>
<div className="grid grid-cols-2 gap-4 mb-4">
<div className="space-y-2">
<Label htmlFor="agent-name" className="text-sm">Name</Label>
<Input
id="agent-name"
value={agentName}
onChange={(e) => setAgentName(e.target.value)}
placeholder="Agent name"
className="h-8"
/>
</div>
<div className="space-y-2">
<Label htmlFor="agent-description" className="text-sm">Description</Label>
<Input
id="agent-description"
value={agentDescription}
onChange={(e) => setAgentDescription(e.target.value)}
placeholder="Brief description"
className="h-8"
/>
</div>
</div>
<div className="space-y-2 flex-1 flex flex-col">
<Label htmlFor="system-instructions" className="text-sm">System Instructions</Label>
<Textarea
id="system-instructions"
value={instructionsValue}
onChange={(e) => setInstructionsValue(e.target.value)}
placeholder="Define the agent's role, behavior, and expertise..."
className="flex-1 resize-none"
/>
</div>
<div className="flex gap-2 pt-4">
<Button
onClick={handleSaveInstructions}
disabled={updateAgentMutation.isPending}
size="sm"
>
{updateAgentMutation.isPending ? 'Saving...' : 'Save'}
</Button>
<Button
variant="outline"
size="sm"
onClick={() => {
setAgentName(agent?.name || '');
setAgentDescription(agent?.description || '');
setInstructionsValue(agent?.system_prompt || '');
}}
>
Reset
</Button>
</div>
</>
) : (
<div className="flex items-center justify-center h-32">
<p className="text-sm text-muted-foreground">Select an agent to configure instructions</p>
</div>
)}
</div>
</TabsContent>
<TabsContent value="knowledge" className="flex-1 m-0 mt-0 overflow-y-auto overflow-hidden">
<div className="h-full">
{selectedAgentId ? (
<AgentKnowledgeBaseManager
agentId={selectedAgentId}
agentName={agentName}
/>
) : (
<div className="flex items-center justify-center h-32">
<p className="text-sm text-muted-foreground">Select an agent to manage knowledge base</p>
</div>
)}
</div>
</TabsContent>
<TabsContent value="triggers" className="flex-1 m-0 mt-0 overflow-y-auto overflow-hidden">
<div className="h-full">
{selectedAgentId ? (
<AgentTriggersConfiguration agentId={selectedAgentId} />
) : (
<div className="flex items-center justify-center h-32">
<p className="text-sm text-muted-foreground">Select an agent to configure triggers</p>
</div>
)}
</div>
</TabsContent>
<TabsContent value="workflows" className="flex-1 m-0 mt-0 overflow-y-auto overflow-hidden">
<div className="h-full">
{selectedAgentId ? (
<AgentWorkflowsConfiguration
agentId={selectedAgentId}
agentName={agentName}
/>
) : (
<div className="flex items-center justify-center h-32">
<p className="text-sm text-muted-foreground">Select an agent to configure playbooks</p>
</div>
)}
</div>
</TabsContent>
</Tabs>
</div>
</DialogContent>
</Dialog>
);
};

View File

@ -61,14 +61,7 @@ export const AgentToolsConfiguration = ({ tools, onToolsChange, disabled = false
return (
<div className="space-y-4">
<div className="flex items-center justify-between">
<div>
<h3 className="text-sm font-medium text-foreground">Default Tools</h3>
<p className="text-xs text-muted-foreground">Configure default agentpress tools {getSelectedToolsCount()} selected</p>
</div>
</div>
<div>
<div className="space-y-2">
<div className="space-y-2">
{getFilteredTools().map(([toolName, toolInfo]) => (
<div
key={toolName}
@ -98,22 +91,21 @@ export const AgentToolsConfiguration = ({ tools, onToolsChange, disabled = false
</div>
</div>
))}
</div>
{getFilteredTools().length === 0 && (
<div className="text-center py-12 px-6 bg-muted/30 rounded-xl border-2 border-dashed border-border">
<div className="mx-auto w-12 h-12 bg-muted rounded-full flex items-center justify-center mb-4 border">
<Search className="h-6 w-6 text-muted-foreground" />
</div>
<h4 className="text-sm font-semibold text-foreground mb-2">
No tools found
</h4>
<p className="text-sm text-muted-foreground mb-6 max-w-sm mx-auto">
Try adjusting your search criteria
</p>
</div>
)}
</div>
{getFilteredTools().length === 0 && (
<div className="text-center py-12 px-6 bg-muted/30 rounded-xl border-2 border-dashed border-border">
<div className="mx-auto w-12 h-12 bg-muted rounded-full flex items-center justify-center mb-4 border">
<Search className="h-6 w-6 text-muted-foreground" />
</div>
<h4 className="text-sm font-semibold text-foreground mb-2">
No tools found
</h4>
<p className="text-sm text-muted-foreground mb-6 max-w-sm mx-auto">
Try adjusting your search criteria
</p>
</div>
)}
</div>
);
};

View File

@ -14,7 +14,7 @@ import type { ComposioToolkit, ComposioProfile } from '@/hooks/react-query/compo
import { toast } from 'sonner';
import { cn } from '@/lib/utils';
import { useQueryClient } from '@tanstack/react-query';
import { AgentSelector } from '../../thread/chat-input/agent-selector';
// import { AgentSelector } from '../../thread/chat-input/agent-selector';
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible';
import { CustomMCPDialog } from '../mcp/custom-mcp-dialog';
@ -512,13 +512,13 @@ export const ComposioRegistry: React.FC<ComposioRegistryProps> = ({
</p>
</div>
<div className="flex items-center gap-3">
{showAgentSelector && (
{/* {showAgentSelector && (
<AgentSelector
selectedAgentId={currentAgentId}
onAgentSelect={handleAgentSelect}
isSunaAgent={agent?.metadata?.is_suna_default}
/>
)}
)} */}
{mode !== 'profile-only' && currentAgentId && (
<Button
variant="outline"

View File

@ -164,7 +164,7 @@ export function ConfigurationTab({
}`}
>
<div className="px-6 pb-6 pt-2">
<div className="border-t border-border/30 pt-4">
<div className="pt-4">
<ExpandableMarkdownEditor
value={displayData.system_prompt}
onSave={handleSystemPromptChange}
@ -203,7 +203,7 @@ export function ConfigurationTab({
}`}
>
<div className="px-6 pb-6 pt-2">
<div className="border-t border-border/30 pt-4">
<div className="pt-4">
<AgentModelSelector
value={displayData.model}
onChange={(model) => {
@ -246,7 +246,7 @@ export function ConfigurationTab({
}`}
>
<div className="px-6 pb-6 pt-2">
<div className="border-t border-border/30 pt-4">
<div className="pt-4">
<AgentToolsConfiguration
tools={displayData.agentpress_tools}
onToolsChange={areToolsEditable ? handleToolsChange : () => { }}
@ -283,7 +283,7 @@ export function ConfigurationTab({
}`}
>
<div className="px-6 pb-6 pt-2">
<div className="border-t border-border/30 pt-4">
<div className="pt-4">
<AgentMCPConfiguration
configuredMCPs={displayData.configured_mcps}
customMCPs={displayData.custom_mcps}
@ -327,7 +327,7 @@ export function ConfigurationTab({
}`}
>
<div className="px-6 pb-6 pt-2">
<div className="border-t border-border/30 pt-4">
<div className="pt-4">
<AgentKnowledgeBaseManager
agentId={agentId}
agentName={displayData.name || 'Agent'}
@ -361,7 +361,7 @@ export function ConfigurationTab({
}`}
>
<div className="px-6 pb-6 pt-2">
<div className="border-t border-border/30 pt-4">
<div className="pt-4">
<AgentPlaybooksConfiguration
agentId={agentId}
agentName={displayData.name || 'Agent'}
@ -395,7 +395,7 @@ export function ConfigurationTab({
}`}
>
<div className="px-6 pb-6 pt-2">
<div className="border-t border-border/30 pt-4">
<div className="pt-4">
<AgentTriggersConfiguration agentId={agentId} />
</div>
</div>

View File

@ -628,22 +628,18 @@ export const AgentKnowledgeBaseManager = ({ agentId, agentName }: AgentKnowledge
</div>
</div>
)}
<div className="flex items-center justify-between">
<div>
<h3 className="text-sm font-medium text-foreground">Knowledge Base</h3>
<p className="text-xs text-muted-foreground">Upload and manage knowledge for the agent</p>
</div>
<div className="flex items-center gap-3">
<div className="relative">
<div className="flex items-center justify-between w-full">
<div className="flex items-center gap-3 flex-1">
<div className="relative flex-1 max-w-md">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-muted-foreground" />
<Input
placeholder="Search..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="pl-9 h-9 w-48"
className="pl-9 h-9 w-full"
/>
</div>
<Button onClick={() => handleOpenAddDialog()} size="sm" className="gap-2">
<Button onClick={() => handleOpenAddDialog()} size="sm" className="gap-2 flex-shrink-0">
<Plus className="h-4 w-4" />
Add Knowledge
</Button>

View File

@ -105,10 +105,6 @@ export const MCPConfigurationNew: React.FC<MCPConfigurationProps> = ({
return (
<div className="space-y-4">
<div className="flex items-center justify-between">
<div>
<h3 className="text-sm font-medium text-foreground">Integrations</h3>
<p className="text-xs text-muted-foreground">Connect external services via MCPs</p>
</div>
<div className="flex gap-2">
<Button onClick={() => setShowRegistryDialog(true)} size="sm" variant="default" className="gap-2">
<Store className="h-4 w-4" />

View File

@ -58,11 +58,7 @@ export function AgentPlaybooksConfiguration({ agentId, agentName }: AgentPlayboo
return (
<div className="space-y-4">
<div className="flex items-center justify-between">
<div>
<h3 className="text-sm font-medium text-foreground">Playbooks</h3>
<p className="text-xs text-muted-foreground">Simple variable-driven runs</p>
</div>
<div className="flex items-center justify-start">
<Button onClick={() => { setEditing(null); setIsCreateOpen(true); }} size="sm" className="gap-2">
<Plus className="h-4 w-4" />
New Playbook

View File

@ -128,11 +128,6 @@ export const AgentTriggersConfiguration: React.FC<AgentTriggersConfigurationProp
return (
<div className="space-y-4">
<div>
<h3 className="text-sm font-medium text-foreground">Triggers</h3>
<p className="text-xs text-muted-foreground mb-4">Set up automated agent runs</p>
</div>
<OneClickIntegrations agentId={agentId} />
{triggers.length > 0 && (

View File

@ -22,7 +22,7 @@ import { ChatSnack } from './chat-snack';
import { Brain, Zap, Workflow, Database, ArrowDown } from 'lucide-react';
import { useComposioToolkitIcon } from '@/hooks/react-query/composio/use-composio';
import { Skeleton } from '@/components/ui/skeleton';
import { AgentConfigModal } from '@/components/agents/agent-config-modal';
import { IntegrationsRegistry } from '@/components/agents/integrations-registry';
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
import { useSubscriptionWithStreaming } from '@/hooks/react-query/subscriptions/use-subscriptions';
@ -133,8 +133,7 @@ export const ChatInput = forwardRef<ChatInputHandles, ChatInputProps>(
const [pendingFiles, setPendingFiles] = useState<File[]>([]);
const [isUploading, setIsUploading] = useState(false);
const [isDraggingOver, setIsDraggingOver] = useState(false);
const [configModalOpen, setConfigModalOpen] = useState(false);
const [configModalTab, setConfigModalTab] = useState('integrations');
const [registryDialogOpen, setRegistryDialogOpen] = useState(false);
const [showSnackbar, setShowSnackbar] = useState(defaultShowSnackbar);
const [userDismissedUsage, setUserDismissedUsage] = useState(false);
@ -495,13 +494,7 @@ export const ChatInput = forwardRef<ChatInputHandles, ChatInputProps>(
</div>
</div>
)}
<AgentConfigModal
isOpen={configModalOpen}
onOpenChange={setConfigModalOpen}
selectedAgentId={selectedAgentId}
onAgentSelect={onAgentSelect}
initialTab={configModalTab}
/>
<Dialog open={registryDialogOpen} onOpenChange={setRegistryDialogOpen}>
<DialogContent className="p-0 max-w-6xl h-[90vh] overflow-hidden">
<DialogHeader className="sr-only">