suna/frontend/src/components/agents/agent-config-modal.tsx

351 lines
15 KiB
TypeScript

'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 { X, 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';
interface AgentConfigModalProps {
isOpen: boolean;
onOpenChange: (open: boolean) => void;
selectedAgentId?: string;
onAgentSelect?: (agentId: string | undefined) => void;
initialTab?: string;
}
export const AgentConfigModal: React.FC<AgentConfigModalProps> = ({
isOpen,
onOpenChange,
selectedAgentId,
onAgentSelect,
initialTab = 'integrations'
}) => {
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-5xl max-h-[90vh] overflow-hidden flex flex-col">
<DialogHeader className="flex-shrink-0 pb-2">
<DialogTitle className="flex items-center gap-2">
<Settings2 className="h-5 w-5" />
Agent Configuration
</DialogTitle>
</DialogHeader>
<div className="flex-1 overflow-hidden flex flex-col">
{/* Agent Selector */}
<div className="flex-shrink-0 border-b pb-4 mb-4">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<AgentSelector
selectedAgentId={selectedAgentId}
onAgentSelect={onAgentSelect}
/>
</div>
{selectedAgentId && (
<div className="flex items-center gap-2">
<Button
variant="outline"
size="sm"
onClick={() => router.push(`/agents/config/${selectedAgentId}`)}
className="flex items-center gap-2"
>
<Settings2 className="h-4 w-4" />
Edit Agent
</Button>
</div>
)}
</div>
</div>
{/* Configuration Tabs */}
<div className="flex-1 overflow-hidden">
<Tabs value={activeTab} onValueChange={setActiveTab} className="h-full flex flex-col">
<TabsList className="grid w-full grid-cols-6 flex-shrink-0">
<TabsTrigger value="integrations" className="flex items-center gap-2">
<span className="hidden sm:inline">Integrations</span>
</TabsTrigger>
<TabsTrigger value="tools" className="flex items-center gap-2">
<Settings2 className="h-4 w-4" />
<span className="hidden sm:inline">Tools</span>
</TabsTrigger>
<TabsTrigger value="instructions" className="flex items-center gap-2">
<Brain className="h-4 w-4" />
<span className="hidden sm:inline">Instructions</span>
</TabsTrigger>
<TabsTrigger value="knowledge" className="flex items-center gap-2">
<Database className="h-4 w-4" />
<span className="hidden sm:inline">Knowledge Base</span>
</TabsTrigger>
<TabsTrigger value="triggers" className="flex items-center gap-2">
<Zap className="h-4 w-4" />
<span className="hidden sm:inline">Triggers</span>
</TabsTrigger>
<TabsTrigger value="workflows" className="flex items-center gap-2">
<Workflow className="h-4 w-4" />
<span className="hidden sm:inline">Workflows</span>
</TabsTrigger>
</TabsList>
<div className="flex-1 overflow-hidden mt-4">
<TabsContent value="integrations" className="h-full m-0 overflow-y-auto">
<div className="space-y-4">
<div className="text-center py-8">
<h3 className="text-lg font-semibold mb-2">MCP Integrations</h3>
<p className="text-muted-foreground mb-4">
Connect your agent to external services and tools
</p>
{selectedAgentId && (
<AgentMCPConfiguration
configuredMCPs={agent?.configured_mcps || []}
customMCPs={agent?.custom_mcps || []}
onMCPChange={handleMCPChange}
/>
)}
</div>
</div>
</TabsContent>
<TabsContent value="tools" className="h-full m-0 overflow-y-auto">
<div className="space-y-4 p-4">
{selectedAgentId ? (
<div className="max-w-4xl">
<div className="mb-4">
<h3 className="text-lg font-semibold mb-2">Standard Tools</h3>
<p className="text-muted-foreground mb-4">
Configure the built-in tools available to your agent
</p>
</div>
<AgentToolsConfiguration
tools={agent?.agentpress_tools || {}}
onToolsChange={handleToolsChange}
/>
</div>
) : (
<div className="text-center py-8">
<Settings2 className="h-12 w-12 mx-auto mb-4 text-muted-foreground" />
<h3 className="text-lg font-semibold mb-2">Standard Tools</h3>
<p className="text-muted-foreground mb-4">
Select an agent to configure its tools
</p>
</div>
)}
</div>
</TabsContent>
<TabsContent value="instructions" className="h-full m-0 overflow-y-auto">
<div className="space-y-4 p-4">
{selectedAgentId ? (
<div className="max-w-2xl">
<div className="space-y-4">
<div className="space-y-2">
<Label htmlFor="agent-name">Agent Name</Label>
<Input
id="agent-name"
value={agentName}
onChange={(e) => setAgentName(e.target.value)}
placeholder="Enter agent name"
/>
</div>
<div className="space-y-2">
<Label htmlFor="agent-description">Description</Label>
<Input
id="agent-description"
value={agentDescription}
onChange={(e) => setAgentDescription(e.target.value)}
placeholder="Brief description of the agent"
/>
</div>
<div className="space-y-2">
<Label htmlFor="system-instructions">System Instructions</Label>
<Textarea
id="system-instructions"
value={instructionsValue}
onChange={(e) => setInstructionsValue(e.target.value)}
placeholder="Describe the agent's role, behavior, and expertise..."
className="min-h-[200px]"
/>
</div>
<div className="flex gap-2">
<Button
onClick={handleSaveInstructions}
disabled={updateAgentMutation.isPending}
>
{updateAgentMutation.isPending ? 'Saving...' : 'Save Changes'}
</Button>
<Button
variant="outline"
onClick={() => {
setAgentName(agent?.name || '');
setAgentDescription(agent?.description || '');
setInstructionsValue(agent?.system_prompt || '');
}}
>
Reset
</Button>
</div>
</div>
</div>
) : (
<div className="text-center py-8">
<Brain className="h-12 w-12 mx-auto mb-4 text-muted-foreground" />
<h3 className="text-lg font-semibold mb-2">System Instructions</h3>
<p className="text-muted-foreground mb-4">
Select an agent to configure its instructions
</p>
</div>
)}
</div>
</TabsContent>
<TabsContent value="knowledge" className="h-full m-0 overflow-y-auto">
<div className="space-y-4">
{selectedAgentId ? (
<AgentKnowledgeBaseManager
agentId={selectedAgentId}
agentName={agentName}
/>
) : (
<div className="text-center py-8">
<Database className="h-12 w-12 mx-auto mb-4 text-muted-foreground" />
<h3 className="text-lg font-semibold mb-2">Knowledge Base</h3>
<p className="text-muted-foreground mb-4">
Select an agent to manage its knowledge base
</p>
</div>
)}
</div>
</TabsContent>
<TabsContent value="triggers" className="h-full m-0 overflow-y-auto">
<div className="space-y-4">
{selectedAgentId ? (
<AgentTriggersConfiguration agentId={selectedAgentId} />
) : (
<div className="text-center py-8">
<Zap className="h-12 w-12 mx-auto mb-4 text-muted-foreground" />
<h3 className="text-lg font-semibold mb-2">Triggers</h3>
<p className="text-muted-foreground mb-4">
Select an agent to configure its triggers
</p>
</div>
)}
</div>
</TabsContent>
<TabsContent value="workflows" className="h-full m-0 overflow-y-auto">
<div className="space-y-4">
{selectedAgentId ? (
<AgentWorkflowsConfiguration
agentId={selectedAgentId}
agentName={agentName}
/>
) : (
<div className="text-center py-8">
<Workflow className="h-12 w-12 mx-auto mb-4 text-muted-foreground" />
<h3 className="text-lg font-semibold mb-2">Workflows</h3>
<p className="text-muted-foreground mb-4">
Select an agent to configure its workflows
</p>
</div>
)}
</div>
</TabsContent>
</div>
</Tabs>
</div>
</div>
</DialogContent>
</Dialog>
</>
);
};