/** * Providers Settings Component * Manage AI provider configurations and API keys */ import { useState, useEffect } from 'react'; import { Plus, Trash2, Edit, Eye, EyeOff, Check, X, Loader2, Star, Key, } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { Separator } from '@/components/ui/separator'; import { Switch } from '@/components/ui/switch'; import { useProviderStore, type ProviderWithKeyInfo } from '@/stores/providers'; import { cn } from '@/lib/utils'; import { toast } from 'sonner'; // Provider type definitions const providerTypes = [ { id: 'anthropic', name: 'Anthropic', icon: '🤖', placeholder: 'sk-ant-api03-...' }, { id: 'openai', name: 'OpenAI', icon: '💚', placeholder: 'sk-proj-...' }, { id: 'google', name: 'Google', icon: '🔷', placeholder: 'AIza...' }, { id: 'ollama', name: 'Ollama', icon: '🦙', placeholder: 'Not required' }, { id: 'custom', name: 'Custom', icon: '⚙️', placeholder: 'API key...' }, ]; export function ProvidersSettings() { const { providers, defaultProviderId, loading, fetchProviders, addProvider, updateProvider, deleteProvider, setApiKey, setDefaultProvider, validateApiKey, } = useProviderStore(); const [showAddDialog, setShowAddDialog] = useState(false); const [editingProvider, setEditingProvider] = useState(null); // Fetch providers on mount useEffect(() => { fetchProviders(); }, [fetchProviders]); const handleAddProvider = async (type: string, name: string, apiKey: string) => { try { await addProvider({ id: `${type}-${Date.now()}`, type: type as 'anthropic' | 'openai' | 'google' | 'ollama' | 'custom', name, enabled: true, }, apiKey || undefined); setShowAddDialog(false); toast.success('Provider added successfully'); } catch (error) { toast.error(`Failed to add provider: ${error}`); } }; const handleDeleteProvider = async (providerId: string) => { try { await deleteProvider(providerId); toast.success('Provider deleted'); } catch (error) { toast.error(`Failed to delete provider: ${error}`); } }; const handleSetDefault = async (providerId: string) => { try { await setDefaultProvider(providerId); toast.success('Default provider updated'); } catch (error) { toast.error(`Failed to set default: ${error}`); } }; const handleToggleEnabled = async (provider: ProviderWithKeyInfo) => { try { await updateProvider(provider.id, { enabled: !provider.enabled }); } catch (error) { toast.error(`Failed to update provider: ${error}`); } }; return (

AI Providers

Configure your AI model providers and API keys

{loading ? (
) : providers.length === 0 ? (

No providers configured

Add an AI provider to start using ClawX

) : (
{providers.map((provider) => ( setEditingProvider(provider.id)} onCancelEdit={() => setEditingProvider(null)} onDelete={() => handleDeleteProvider(provider.id)} onSetDefault={() => handleSetDefault(provider.id)} onToggleEnabled={() => handleToggleEnabled(provider)} onUpdateKey={async (key) => { await setApiKey(provider.id, key); setEditingProvider(null); }} onValidateKey={(key) => validateApiKey(provider.id, key)} /> ))}
)} {/* Add Provider Dialog */} {showAddDialog && ( setShowAddDialog(false)} onAdd={handleAddProvider} /> )}
); } interface ProviderCardProps { provider: ProviderWithKeyInfo; isDefault: boolean; isEditing: boolean; onEdit: () => void; onCancelEdit: () => void; onDelete: () => void; onSetDefault: () => void; onToggleEnabled: () => void; onUpdateKey: (key: string) => Promise; onValidateKey: (key: string) => Promise<{ valid: boolean; error?: string }>; } function ProviderCard({ provider, isDefault, isEditing, onEdit, onCancelEdit, onDelete, onSetDefault, onToggleEnabled, onUpdateKey, onValidateKey, }: ProviderCardProps) { const [newKey, setNewKey] = useState(''); const [showKey, setShowKey] = useState(false); const [validating, setValidating] = useState(false); const [saving, setSaving] = useState(false); const typeInfo = providerTypes.find((t) => t.id === provider.type); const handleSaveKey = async () => { if (!newKey) return; setValidating(true); const result = await onValidateKey(newKey); setValidating(false); if (!result.valid) { toast.error(result.error || 'Invalid API key'); return; } setSaving(true); try { await onUpdateKey(newKey); setNewKey(''); toast.success('API key updated'); } catch (error) { toast.error(`Failed to save key: ${error}`); } finally { setSaving(false); } }; return (
{typeInfo?.icon || '⚙️'}
{provider.name} {isDefault && ( Default )}
{provider.type}
{isEditing ? (
setNewKey(e.target.value)} className="pr-10" />
) : (
{provider.hasKey ? provider.keyMasked : 'No API key set'} {provider.hasKey && ( Configured )}
{!isDefault && ( )}
)}
); } interface AddProviderDialogProps { onClose: () => void; onAdd: (type: string, name: string, apiKey: string) => Promise; } function AddProviderDialog({ onClose, onAdd }: AddProviderDialogProps) { const [selectedType, setSelectedType] = useState(null); const [name, setName] = useState(''); const [apiKey, setApiKey] = useState(''); const [showKey, setShowKey] = useState(false); const [saving, setSaving] = useState(false); const typeInfo = providerTypes.find((t) => t.id === selectedType); const handleAdd = async () => { if (!selectedType) return; setSaving(true); try { await onAdd(selectedType, name || typeInfo?.name || selectedType, apiKey); } finally { setSaving(false); } }; return (
Add AI Provider Configure a new AI model provider {!selectedType ? (
{providerTypes.map((type) => ( ))}
) : (
{typeInfo?.icon}

{typeInfo?.name}

setName(e.target.value)} />
setApiKey(e.target.value)} className="pr-10" />

Your API key will be securely encrypted and stored locally.

)}
); }