suna/frontend/src/components/agents/composio/composio-tools-manager.tsx

139 lines
4.6 KiB
TypeScript
Raw Normal View History

import React, { useState, useEffect } from 'react';
2025-08-03 04:10:11 +08:00
import { Button } from '@/components/ui/button';
2025-08-03 13:16:07 +08:00
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
2025-08-03 04:10:11 +08:00
import { useComposioProfiles } from '@/hooks/react-query/composio/use-composio-profiles';
2025-08-06 13:48:06 +08:00
import { useComposioToolkitIcon } from '@/hooks/react-query/composio/use-composio';
2025-08-03 04:10:11 +08:00
import { backendApi } from '@/lib/api-client';
import { toast } from 'sonner';
import { useQueryClient } from '@tanstack/react-query';
2025-08-03 13:16:07 +08:00
import { cn } from '@/lib/utils';
import { composioApi } from '@/hooks/react-query/composio/utils';
import { ComposioToolsSelector } from './composio-tools-selector';
2025-08-03 04:10:11 +08:00
interface ComposioToolsManagerProps {
agentId: string;
open: boolean;
onOpenChange: (open: boolean) => void;
2025-08-04 11:43:20 +08:00
profileId?: string;
profileInfo?: {
2025-08-03 13:16:07 +08:00
profile_id: string;
profile_name: string;
toolkit_name: string;
toolkit_slug: string;
};
2025-08-04 11:43:20 +08:00
appLogo?: string;
2025-08-03 13:16:07 +08:00
onToolsUpdate?: () => void;
2025-08-03 04:10:11 +08:00
}
2025-08-03 13:16:07 +08:00
2025-08-03 04:10:11 +08:00
export const ComposioToolsManager: React.FC<ComposioToolsManagerProps> = ({
agentId,
open,
onOpenChange,
profileId,
2025-08-03 13:16:07 +08:00
profileInfo,
onToolsUpdate,
appLogo,
2025-08-03 04:10:11 +08:00
}) => {
2025-08-03 13:16:07 +08:00
const [selectedTools, setSelectedTools] = useState<string[]>([]);
2025-08-03 04:10:11 +08:00
const queryClient = useQueryClient();
2025-08-03 13:16:07 +08:00
const { data: profiles } = useComposioProfiles();
2025-08-03 04:10:11 +08:00
2025-08-03 13:16:07 +08:00
const currentProfile = profileInfo || profiles?.find(p => p.profile_id === profileId);
2025-08-06 13:48:06 +08:00
const { data: iconData } = useComposioToolkitIcon(currentProfile?.toolkit_slug || '', {
enabled: !!currentProfile?.toolkit_slug
});
2025-08-03 13:16:07 +08:00
// Load current tools when dialog opens
useEffect(() => {
const loadCurrentTools = async () => {
if (!open || !agentId || !profileId) return;
try {
const response = await backendApi.get(`/agents/${agentId}`);
if (response.success && response.data) {
const agent = response.data;
const composioMcps = agent.custom_mcps?.filter((mcp: any) =>
mcp.type === 'composio' && mcp.config?.profile_id === profileId
) || [];
const enabledTools = composioMcps.flatMap((mcp: any) => mcp.enabledTools || []);
setSelectedTools(enabledTools);
}
} catch (error) {
console.error('Failed to load current tools:', error);
setSelectedTools([]);
}
};
if (open) {
loadCurrentTools();
} else {
// Reset when dialog closes
setSelectedTools([]);
}
}, [open, agentId, profileId]);
const handleSave = async () => {
2025-08-03 13:16:07 +08:00
if (!currentProfile) return;
const mcpConfigResponse = await composioApi.getMcpConfigForProfile(currentProfile.profile_id);
const response = await backendApi.put(`/agents/${agentId}/custom-mcp-tools`, {
custom_mcps: [{
...mcpConfigResponse.mcp_config,
enabledTools: selectedTools
}]
2025-08-03 13:16:07 +08:00
});
if (response.data.success) {
toast.success(`Added ${selectedTools.length} ${currentProfile.toolkit_name} tools to your agent!`);
onToolsUpdate?.();
onOpenChange(false);
2025-08-03 04:10:11 +08:00
}
};
if (!currentProfile) return null;
2025-08-03 04:10:11 +08:00
return (
<Dialog open={open} onOpenChange={onOpenChange}>
2025-08-03 13:16:07 +08:00
<DialogContent className="max-w-2xl h-[80vh] p-0 gap-0 flex flex-col">
<DialogHeader className="p-6 pb-4 border-b flex-shrink-0">
<div className="flex items-center gap-3">
2025-08-06 13:48:06 +08:00
{iconData?.icon_url || appLogo ? (
<img
src={iconData?.icon_url || appLogo}
alt={currentProfile?.toolkit_name}
2025-08-06 13:48:06 +08:00
className="w-10 h-10 rounded-lg border object-contain bg-muted p-1"
2025-08-03 13:16:07 +08:00
/>
) : (
<div className="w-10 h-10 rounded-lg bg-gradient-to-br from-primary/20 to-primary/10 flex items-center justify-center text-primary font-semibold">
{currentProfile?.toolkit_name?.charAt(0) || 'T'}
2025-08-03 04:10:11 +08:00
</div>
2025-08-03 13:16:07 +08:00
)}
<div className="flex-1">
<DialogTitle className="text-lg font-semibold">
Configure {currentProfile?.toolkit_name} Tools
</DialogTitle>
<p className="text-sm text-muted-foreground mt-1">
Select tools to add to your agent
</p>
2025-08-03 04:10:11 +08:00
</div>
2025-08-03 13:16:07 +08:00
</div>
</DialogHeader>
<ComposioToolsSelector
profileId={currentProfile.profile_id}
agentId={agentId}
toolkitName={currentProfile.toolkit_name}
toolkitSlug={currentProfile.toolkit_slug}
selectedTools={selectedTools}
onToolsChange={setSelectedTools}
onSave={handleSave}
onCancel={() => onOpenChange(false)}
className="flex-1 min-h-0"
/>
2025-08-03 04:10:11 +08:00
</DialogContent>
</Dialog>
);
};