Skip to main content
Glama
Settings.tsx.backup42.7 kB
import { useEffect, useState } from 'react'; import { Save, RefreshCw, AlertCircle, CheckCircle2, Eye, EyeOff, Trash2, Plus } from 'lucide-react'; import api from '../lib/api'; type TabType = 'system' | 'providers' | 'layers' | 'tasks' | 'features'; interface SystemConfig { server_port?: number; server_host?: string; api_version?: string; log_level?: string; redis_host?: string; redis_port?: number; cache_ttl_seconds?: number; } interface Provider { provider: string; apiKey: string; endpoint: string; enabled: boolean; configuration?: Record<string, any>; } interface LayerConfig { layer: string; models: string[]; priority: number; enabled: boolean; description?: string; } interface TaskConfig { task: string; preferredModel: string; fallbackModels: string[]; enabled: boolean; } interface FeatureFlag { flag: string; enabled: boolean; description?: string; metadata?: Record<string, any>; } export default function Settings() { const [activeTab, setActiveTab] = useState<TabType>('system'); const [loading, setLoading] = useState(true); const [error, setError] = useState<string | null>(null); const [saveStatus, setSaveStatus] = useState<'idle' | 'success' | 'error'>('idle'); // Data states const [systemConfig, setSystemConfig] = useState<SystemConfig>({}); const [providers, setProviders] = useState<Provider[]>([]); const [layers, setLayers] = useState<LayerConfig[]>([]); const [tasks, setTasks] = useState<TaskConfig[]>([]); const [features, setFeatures] = useState<FeatureFlag[]>([]); // UI states const [showApiKeys, setShowApiKeys] = useState<Record<string, boolean>>({}); useEffect(() => { loadAllSettings(); }, []); async function loadAllSettings() { setLoading(true); try { await Promise.all([ loadSystemConfig(), loadProviders(), loadLayers(), loadTasks(), loadFeatures() ]); setError(null); } catch (err) { console.error('Failed to load settings:', err); setError(err instanceof Error ? err.message : 'Failed to load settings'); } finally { setLoading(false); } } async function loadSystemConfig() { try { const response = await api.get('/config/system'); setSystemConfig(response.data || {}); } catch (err) { console.error('Failed to load system config:', err); } } async function loadProviders() { try { const response = await api.get('/config/providers'); setProviders(response.data || []); } catch (err) { console.error('Failed to load providers:', err); } } async function loadLayers() { try { const response = await api.get('/config/layers'); setLayers(response.data || []); } catch (err) { console.error('Failed to load layers:', err); } } async function loadTasks() { try { const response = await api.get('/config/tasks'); setTasks(response.data || []); } catch (err) { console.error('Failed to load tasks:', err); } } async function loadFeatures() { try { const response = await api.get('/config/features'); setFeatures(response.data || []); } catch (err) { console.error('Failed to load features:', err); } } async function saveSystemConfig() { try { for (const [key, value] of Object.entries(systemConfig)) { await api.put(`/config/system/${key}`, { value }); } setSaveStatus('success'); setTimeout(() => setSaveStatus('idle'), 3000); } catch (err) { console.error('Failed to save system config:', err); setSaveStatus('error'); setTimeout(() => setSaveStatus('idle'), 3000); } } async function saveProvider(provider: Provider) { try { await api.post(`/config/providers/${provider.provider}`, { apiKey: provider.apiKey, endpoint: provider.endpoint, enabled: provider.enabled, configuration: provider.configuration }); setSaveStatus('success'); setTimeout(() => setSaveStatus('idle'), 3000); await loadProviders(); } catch (err) { console.error('Failed to save provider:', err); setSaveStatus('error'); setTimeout(() => setSaveStatus('idle'), 3000); } } async function deleteProvider(providerName: string) { if (!confirm(`Are you sure you want to delete ${providerName}?`)) return; try { await api.delete(`/config/providers/${providerName}`); await loadProviders(); } catch (err) { console.error('Failed to delete provider:', err); } } async function saveLayer(layer: LayerConfig) { try { await api.put(`/config/layers/${layer.layer}`, { models: layer.models, priority: layer.priority, enabled: layer.enabled, description: layer.description }); setSaveStatus('success'); setTimeout(() => setSaveStatus('idle'), 3000); await loadLayers(); } catch (err) { console.error('Failed to save layer:', err); setSaveStatus('error'); setTimeout(() => setSaveStatus('idle'), 3000); } } async function saveTask(task: TaskConfig) { try { await api.put(`/config/tasks/${task.task}`, { preferredModel: task.preferredModel, fallbackModels: task.fallbackModels, enabled: task.enabled }); setSaveStatus('success'); setTimeout(() => setSaveStatus('idle'), 3000); await loadTasks(); } catch (err) { console.error('Failed to save task:', err); setSaveStatus('error'); setTimeout(() => setSaveStatus('idle'), 3000); } } async function saveFeature(feature: FeatureFlag) { try { await api.put(`/config/features/${feature.flag}`, { enabled: feature.enabled, description: feature.description, metadata: feature.metadata }); setSaveStatus('success'); setTimeout(() => setSaveStatus('idle'), 3000); await loadFeatures(); } catch (err) { console.error('Failed to save feature:', err); setSaveStatus('error'); setTimeout(() => setSaveStatus('idle'), 3000); } } async function clearCache() { try { await api.post('/config/cache/clear'); alert('Cache cleared successfully'); } catch (err) { console.error('Failed to clear cache:', err); alert('Failed to clear cache'); } } if (loading) { return ( <div className="flex items-center justify-center h-96"> <div className="text-center"> <div className="w-12 h-12 border-4 border-blue-600 border-t-transparent rounded-full animate-spin mx-auto mb-4" /> <p className="text-slate-400">Loading settings...</p> </div> </div> ); } const tabs: { id: TabType; label: string }[] = [ { id: 'system', label: 'System' }, { id: 'providers', label: 'Providers' }, { id: 'layers', label: 'Layers' }, { id: 'tasks', label: 'Tasks' }, { id: 'features', label: 'Features' } ]; return ( <div className="space-y-6"> {/* Header */} <div className="flex items-center justify-between"> <h1 className="text-3xl font-bold text-white">Settings</h1> <div className="flex items-center gap-3"> {saveStatus === 'success' && ( <div className="flex items-center gap-2 text-green-400"> <CheckCircle2 className="w-5 h-5" /> <span>Saved successfully</span> </div> )} {saveStatus === 'error' && ( <div className="flex items-center gap-2 text-red-400"> <AlertCircle className="w-5 h-5" /> <span>Failed to save</span> </div> )} <button onClick={clearCache} className="btn-secondary flex items-center gap-2"> <RefreshCw className="w-4 h-4" /> Clear Cache </button> <button onClick={loadAllSettings} className="btn-secondary flex items-center gap-2"> <RefreshCw className="w-4 h-4" /> Reload </button> </div> </div> {error && ( <div className="bg-red-500/10 border border-red-500/50 rounded-lg p-4"> <div className="flex items-center gap-3"> <AlertCircle className="w-5 h-5 text-red-400" /> <div className="flex-1"> <h3 className="text-red-400 font-semibold">Error</h3> <p className="text-red-300/80">{error}</p> </div> </div> </div> )} {/* Tabs */} <div className="border-b border-slate-700"> <nav className="flex space-x-4"> {tabs.map(tab => ( <button key={tab.id} onClick={() => setActiveTab(tab.id)} className={`px-4 py-2 font-medium transition-colors border-b-2 ${ activeTab === tab.id ? 'text-blue-400 border-blue-400' : 'text-slate-400 border-transparent hover:text-slate-300' }`} > {tab.label} </button> ))} </nav> </div> {/* Tab Content */} <div className="min-h-[500px]"> {activeTab === 'system' && ( <SystemConfigTab config={systemConfig} onChange={setSystemConfig} onSave={saveSystemConfig} /> )} {activeTab === 'providers' && ( <ProvidersTab providers={providers} showApiKeys={showApiKeys} onToggleApiKey={(provider) => setShowApiKeys({ ...showApiKeys, [provider]: !showApiKeys[provider] })} onSave={saveProvider} onDelete={deleteProvider} /> )} {activeTab === 'layers' && ( <LayersTab layers={layers} onSave={saveLayer} /> )} {activeTab === 'tasks' && ( <TasksTab tasks={tasks} onSave={saveTask} /> )} {activeTab === 'features' && ( <FeaturesTab features={features} onSave={saveFeature} /> )} </div> </div> ); } // System Config Tab Component function SystemConfigTab({ config, onChange, onSave }: { config: SystemConfig; onChange: (config: SystemConfig) => void; onSave: () => void; }) { return ( <div className="card space-y-6"> <div className="flex items-center justify-between"> <h2 className="text-xl font-semibold text-white">System Configuration</h2> <button onClick={onSave} className="btn-primary flex items-center gap-2"> <Save className="w-4 h-4" /> Save Changes </button> </div> <div className="grid grid-cols-1 md:grid-cols-2 gap-6"> <div> <label className="block text-sm font-medium text-slate-300 mb-2"> Server Host </label> <input type="text" value={config.server_host || ''} onChange={(e) => onChange({ ...config, server_host: e.target.value })} className="input w-full" placeholder="0.0.0.0" /> </div> <div> <label className="block text-sm font-medium text-slate-300 mb-2"> Server Port </label> <input type="number" value={config.server_port || ''} onChange={(e) => onChange({ ...config, server_port: parseInt(e.target.value) || 3000 })} className="input w-full" placeholder="3000" /> </div> <div> <label className="block text-sm font-medium text-slate-300 mb-2"> API Version </label> <input type="text" value={config.api_version || ''} onChange={(e) => onChange({ ...config, api_version: e.target.value })} className="input w-full" placeholder="v1" /> </div> <div> <label className="block text-sm font-medium text-slate-300 mb-2"> Log Level </label> <select value={config.log_level || 'info'} onChange={(e) => onChange({ ...config, log_level: e.target.value })} className="input w-full" > <option value="debug">Debug</option> <option value="info">Info</option> <option value="warn">Warning</option> <option value="error">Error</option> </select> </div> <div> <label className="block text-sm font-medium text-slate-300 mb-2"> Redis Host </label> <input type="text" value={config.redis_host || ''} onChange={(e) => onChange({ ...config, redis_host: e.target.value })} className="input w-full" placeholder="localhost" /> </div> <div> <label className="block text-sm font-medium text-slate-300 mb-2"> Redis Port </label> <input type="number" value={config.redis_port || ''} onChange={(e) => onChange({ ...config, redis_port: parseInt(e.target.value) || 6379 })} className="input w-full" placeholder="6379" /> </div> <div> <label className="block text-sm font-medium text-slate-300 mb-2"> Cache TTL (seconds) </label> <input type="number" value={config.cache_ttl_seconds || ''} onChange={(e) => onChange({ ...config, cache_ttl_seconds: parseInt(e.target.value) || 60 })} className="input w-full" placeholder="60" /> </div> </div> </div> ); } // Providers Tab Component function ProvidersTab({ providers, showApiKeys, onToggleApiKey, onSave, onDelete }: { providers: Provider[]; showApiKeys: Record<string, boolean>; onToggleApiKey: (provider: string) => void; onSave: (provider: Provider) => void; onDelete: (provider: string) => void; }) { const [editingProvider, setEditingProvider] = useState<Provider | null>(null); const [newProvider, setNewProvider] = useState(false); const handleEdit = (provider: Provider) => { setEditingProvider({ ...provider }); setNewProvider(false); }; const handleNew = () => { setEditingProvider({ provider: '', apiKey: '', endpoint: '', enabled: true }); setNewProvider(true); }; const handleSave = () => { if (editingProvider) { onSave(editingProvider); setEditingProvider(null); setNewProvider(false); } }; return ( <div className="space-y-6"> <div className="flex items-center justify-between"> <h2 className="text-xl font-semibold text-white">Provider Configuration</h2> <button onClick={handleNew} className="btn-primary flex items-center gap-2"> <Plus className="w-4 h-4" /> Add Provider </button> </div> {editingProvider && ( <div className="card space-y-4 border-2 border-blue-500/50"> <h3 className="text-lg font-semibold text-white"> {newProvider ? 'New Provider' : `Edit ${editingProvider.provider}`} </h3> <div className="grid grid-cols-1 gap-4"> <div> <label className="block text-sm font-medium text-slate-300 mb-2"> Provider Name </label> <input type="text" value={editingProvider.provider} onChange={(e) => setEditingProvider({ ...editingProvider, provider: e.target.value })} className="input w-full" placeholder="openrouter" disabled={!newProvider} /> </div> <div> <label className="block text-sm font-medium text-slate-300 mb-2"> API Key </label> <input type="text" value={editingProvider.apiKey} onChange={(e) => setEditingProvider({ ...editingProvider, apiKey: e.target.value })} className="input w-full font-mono" placeholder="sk-or-v1-..." /> </div> <div> <label className="block text-sm font-medium text-slate-300 mb-2"> Endpoint URL </label> <input type="text" value={editingProvider.endpoint} onChange={(e) => setEditingProvider({ ...editingProvider, endpoint: e.target.value })} className="input w-full" placeholder="https://openrouter.ai/api/v1" /> </div> <div className="flex items-center gap-2"> <input type="checkbox" id="provider-enabled" checked={editingProvider.enabled} onChange={(e) => setEditingProvider({ ...editingProvider, enabled: e.target.checked })} className="w-4 h-4 text-blue-600 rounded focus:ring-blue-500" /> <label htmlFor="provider-enabled" className="text-sm font-medium text-slate-300"> Enabled </label> </div> </div> <div className="flex gap-3"> <button onClick={handleSave} className="btn-primary"> <Save className="w-4 h-4 mr-2" /> Save Provider </button> <button onClick={() => { setEditingProvider(null); setNewProvider(false); }} className="btn-secondary"> Cancel </button> </div> </div> )} <div className="grid grid-cols-1 gap-4"> {providers.map(provider => ( <div key={provider.provider} className="card"> <div className="flex items-start justify-between"> <div className="flex-1"> <div className="flex items-center gap-3 mb-2"> <h3 className="text-lg font-semibold text-white">{provider.provider}</h3> <span className={`px-2 py-1 rounded text-xs font-semibold ${ provider.enabled ? 'bg-green-500/20 text-green-400' : 'bg-red-500/20 text-red-400' }`}> {provider.enabled ? 'Enabled' : 'Disabled'} </span> </div> <div className="space-y-2 text-sm"> <div className="flex items-center gap-2"> <span className="text-slate-400">Endpoint:</span> <span className="text-slate-300 font-mono">{provider.endpoint}</span> </div> <div className="flex items-center gap-2"> <span className="text-slate-400">API Key:</span> <span className="text-slate-300 font-mono"> {showApiKeys[provider.provider] ? provider.apiKey : provider.apiKey.substring(0, 10) + '...' } </span> <button onClick={() => onToggleApiKey(provider.provider)} className="text-slate-400 hover:text-slate-300" > {showApiKeys[provider.provider] ? <EyeOff className="w-4 h-4" /> : <Eye className="w-4 h-4" />} </button> </div> </div> </div> <div className="flex gap-2"> <button onClick={() => handleEdit(provider)} className="btn-secondary text-sm" > Edit </button> <button onClick={() => onDelete(provider.provider)} className="btn-danger text-sm flex items-center gap-1" > <Trash2 className="w-4 h-4" /> </button> </div> </div> </div> ))} </div> {providers.length === 0 && !editingProvider && ( <div className="card text-center py-12"> <p className="text-slate-400 mb-4">No providers configured</p> <button onClick={handleNew} className="btn-primary"> <Plus className="w-4 h-4 mr-2" /> Add Your First Provider </button> </div> )} </div> ); } // Layers Tab Component function LayersTab({ layers, onSave }: { layers: LayerConfig[]; onSave: (layer: LayerConfig) => void; }) { const [editingLayer, setEditingLayer] = useState<LayerConfig | null>(null); const handleEdit = (layer: LayerConfig) => { setEditingLayer({ ...layer }); }; const handleSave = () => { if (editingLayer) { onSave(editingLayer); setEditingLayer(null); } }; const handleModelChange = (index: number, value: string) => { if (!editingLayer) return; const newModels = [...editingLayer.models]; newModels[index] = value; setEditingLayer({ ...editingLayer, models: newModels }); }; const handleAddModel = () => { if (!editingLayer) return; setEditingLayer({ ...editingLayer, models: [...editingLayer.models, ''] }); }; const handleRemoveModel = (index: number) => { if (!editingLayer) return; const newModels = editingLayer.models.filter((_, i) => i !== index); setEditingLayer({ ...editingLayer, models: newModels }); }; return ( <div className="space-y-6"> <h2 className="text-xl font-semibold text-white">Layer Configuration</h2> {editingLayer && ( <div className="card space-y-4 border-2 border-blue-500/50"> <h3 className="text-lg font-semibold text-white">Edit {editingLayer.layer}</h3> <div className="space-y-4"> <div> <label className="block text-sm font-medium text-slate-300 mb-2"> Description </label> <input type="text" value={editingLayer.description || ''} onChange={(e) => setEditingLayer({ ...editingLayer, description: e.target.value })} className="input w-full" placeholder="Layer description" /> </div> <div> <label className="block text-sm font-medium text-slate-300 mb-2"> Priority </label> <input type="number" value={editingLayer.priority} onChange={(e) => setEditingLayer({ ...editingLayer, priority: parseInt(e.target.value) || 0 })} className="input w-full" /> </div> <div> <div className="flex items-center justify-between mb-2"> <label className="block text-sm font-medium text-slate-300"> Models </label> <button onClick={handleAddModel} className="text-blue-400 hover:text-blue-300 text-sm"> + Add Model </button> </div> <div className="space-y-2"> {editingLayer.models.map((model, index) => ( <div key={index} className="flex gap-2"> <input type="text" value={model} onChange={(e) => handleModelChange(index, e.target.value)} className="input flex-1 font-mono text-sm" placeholder="provider/model-name" /> <button onClick={() => handleRemoveModel(index)} className="btn-danger" > <Trash2 className="w-4 h-4" /> </button> </div> ))} </div> </div> <div className="flex items-center gap-2"> <input type="checkbox" id="layer-enabled" checked={editingLayer.enabled} onChange={(e) => setEditingLayer({ ...editingLayer, enabled: e.target.checked })} className="w-4 h-4 text-blue-600 rounded focus:ring-blue-500" /> <label htmlFor="layer-enabled" className="text-sm font-medium text-slate-300"> Enabled </label> </div> </div> <div className="flex gap-3"> <button onClick={handleSave} className="btn-primary"> <Save className="w-4 h-4 mr-2" /> Save Layer </button> <button onClick={() => setEditingLayer(null)} className="btn-secondary"> Cancel </button> </div> </div> )} <div className="grid grid-cols-1 md:grid-cols-2 gap-4"> {layers.map(layer => ( <div key={layer.layer} className="card"> <div className="flex items-start justify-between mb-3"> <div> <div className="flex items-center gap-3 mb-1"> <h3 className="text-lg font-semibold text-white">{layer.layer}</h3> <span className={`px-2 py-1 rounded text-xs font-semibold ${ layer.enabled ? 'bg-green-500/20 text-green-400' : 'bg-red-500/20 text-red-400' }`}> {layer.enabled ? 'Enabled' : 'Disabled'} </span> </div> {layer.description && ( <p className="text-sm text-slate-400">{layer.description}</p> )} </div> <button onClick={() => handleEdit(layer)} className="btn-secondary text-sm" > Edit </button> </div> <div className="space-y-2"> <div className="text-sm"> <span className="text-slate-400">Priority:</span> <span className="text-slate-300 ml-2 font-semibold">{layer.priority}</span> </div> <div> <span className="text-sm text-slate-400">Models:</span> <div className="mt-1 space-y-1"> {layer.models.map((model, idx) => ( <div key={idx} className="text-sm font-mono text-slate-300 bg-slate-800 px-2 py-1 rounded"> {model} </div> ))} </div> </div> </div> </div> ))} </div> </div> ); } // Tasks Tab Component function TasksTab({ tasks, onSave }: { tasks: TaskConfig[]; onSave: (task: TaskConfig) => void; }) { const [editingTask, setEditingTask] = useState<TaskConfig | null>(null); const handleEdit = (task: TaskConfig) => { setEditingTask({ ...task }); }; const handleSave = () => { if (editingTask) { onSave(editingTask); setEditingTask(null); } }; const handleFallbackChange = (index: number, value: string) => { if (!editingTask) return; const newFallbacks = [...editingTask.fallbackModels]; newFallbacks[index] = value; setEditingTask({ ...editingTask, fallbackModels: newFallbacks }); }; const handleAddFallback = () => { if (!editingTask) return; setEditingTask({ ...editingTask, fallbackModels: [...editingTask.fallbackModels, ''] }); }; const handleRemoveFallback = (index: number) => { if (!editingTask) return; const newFallbacks = editingTask.fallbackModels.filter((_, i) => i !== index); setEditingTask({ ...editingTask, fallbackModels: newFallbacks }); }; return ( <div className="space-y-6"> <h2 className="text-xl font-semibold text-white">Task-Specific Model Configuration</h2> {editingTask && ( <div className="card space-y-4 border-2 border-blue-500/50"> <h3 className="text-lg font-semibold text-white">Edit {editingTask.task}</h3> <div className="space-y-4"> <div> <label className="block text-sm font-medium text-slate-300 mb-2"> Preferred Model </label> <input type="text" value={editingTask.preferredModel} onChange={(e) => setEditingTask({ ...editingTask, preferredModel: e.target.value })} className="input w-full font-mono text-sm" placeholder="provider/model-name" /> </div> <div> <div className="flex items-center justify-between mb-2"> <label className="block text-sm font-medium text-slate-300"> Fallback Models </label> <button onClick={handleAddFallback} className="text-blue-400 hover:text-blue-300 text-sm"> + Add Fallback </button> </div> <div className="space-y-2"> {editingTask.fallbackModels.map((model, index) => ( <div key={index} className="flex gap-2"> <input type="text" value={model} onChange={(e) => handleFallbackChange(index, e.target.value)} className="input flex-1 font-mono text-sm" placeholder="provider/model-name" /> <button onClick={() => handleRemoveFallback(index)} className="btn-danger" > <Trash2 className="w-4 h-4" /> </button> </div> ))} </div> </div> <div className="flex items-center gap-2"> <input type="checkbox" id="task-enabled" checked={editingTask.enabled} onChange={(e) => setEditingTask({ ...editingTask, enabled: e.target.checked })} className="w-4 h-4 text-blue-600 rounded focus:ring-blue-500" /> <label htmlFor="task-enabled" className="text-sm font-medium text-slate-300"> Enabled </label> </div> </div> <div className="flex gap-3"> <button onClick={handleSave} className="btn-primary"> <Save className="w-4 h-4 mr-2" /> Save Task </button> <button onClick={() => setEditingTask(null)} className="btn-secondary"> Cancel </button> </div> </div> )} <div className="grid grid-cols-1 md:grid-cols-2 gap-4"> {tasks.map(task => ( <div key={task.task} className="card"> <div className="flex items-start justify-between mb-3"> <div className="flex items-center gap-3"> <h3 className="text-lg font-semibold text-white capitalize">{task.task}</h3> <span className={`px-2 py-1 rounded text-xs font-semibold ${ task.enabled ? 'bg-green-500/20 text-green-400' : 'bg-red-500/20 text-red-400' }`}> {task.enabled ? 'Enabled' : 'Disabled'} </span> </div> <button onClick={() => handleEdit(task)} className="btn-secondary text-sm" > Edit </button> </div> <div className="space-y-2"> <div> <span className="text-sm text-slate-400">Preferred:</span> <div className="mt-1 text-sm font-mono text-slate-300 bg-slate-800 px-2 py-1 rounded"> {task.preferredModel} </div> </div> {task.fallbackModels.length > 0 && ( <div> <span className="text-sm text-slate-400">Fallbacks:</span> <div className="mt-1 space-y-1"> {task.fallbackModels.map((model, idx) => ( <div key={idx} className="text-sm font-mono text-slate-300 bg-slate-800 px-2 py-1 rounded"> {model} </div> ))} </div> </div> )} </div> </div> ))} </div> </div> ); } // Features Tab Component function FeaturesTab({ features, onSave }: { features: FeatureFlag[]; onSave: (feature: FeatureFlag) => void; }) { const handleToggle = (feature: FeatureFlag) => { onSave({ ...feature, enabled: !feature.enabled }); }; return ( <div className="space-y-6"> <h2 className="text-xl font-semibold text-white">Feature Flags</h2> <div className="grid grid-cols-1 md:grid-cols-2 gap-4"> {features.map(feature => ( <div key={feature.flag} className="card"> <div className="flex items-start justify-between"> <div className="flex-1"> <h3 className="text-lg font-semibold text-white mb-2">{feature.flag}</h3> {feature.description && ( <p className="text-sm text-slate-400 mb-3">{feature.description}</p> )} {feature.metadata && Object.keys(feature.metadata).length > 0 && ( <div className="text-xs text-slate-500 bg-slate-800 p-2 rounded font-mono"> {JSON.stringify(feature.metadata, null, 2)} </div> )} </div> <button onClick={() => handleToggle(feature)} className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors ${ feature.enabled ? 'bg-blue-600' : 'bg-slate-600' }`} > <span className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${ feature.enabled ? 'translate-x-6' : 'translate-x-1' }`} /> </button> </div> </div> ))} </div> {features.length === 0 && ( <div className="card text-center py-12"> <p className="text-slate-400">No feature flags configured</p> </div> )} </div> ); } <h3 className="text-red-400 font-semibold">Error</h3> <p className="text-red-300 text-sm mt-1">{error}</p> </div> </div> </div> )} {/* General Settings */} <div className="card p-6"> <h2 className="text-xl font-bold text-white mb-6">General Settings</h2> <div className="space-y-4"> <div className="grid grid-cols-1 md:grid-cols-2 gap-6"> <div> <label className="block text-sm font-semibold text-slate-300 mb-2"> Log Level </label> <select value={config.logLevel} onChange={(e) => setConfig({ ...config, logLevel: e.target.value })} className="input w-full" > <option value="debug">Debug</option> <option value="info">Info</option> <option value="warn">Warning</option> <option value="error">Error</option> </select> </div> <div> <label className="block text-sm font-semibold text-slate-300 mb-2"> Default Layer </label> <select value={config.defaultLayer} onChange={(e) => setConfig({ ...config, defaultLayer: e.target.value })} className="input w-full" > <option value="L0">L0 - Free Tier</option> <option value="L1">L1 - Low Cost</option> <option value="L2">L2 - Balanced</option> <option value="L3">L3 - Premium</option> </select> </div> </div> </div> </div> {/* Routing Features */} <div className="card p-6"> <h2 className="text-xl font-bold text-white mb-6">Routing Features</h2> <div className="space-y-4"> <label className="flex items-center justify-between p-4 bg-slate-700/50 rounded-lg cursor-pointer hover:bg-slate-700/70 transition-colors"> <div> <div className="font-semibold text-white">Cross-Check</div> <div className="text-sm text-slate-400 mt-1"> Validate responses across multiple models </div> </div> <input type="checkbox" checked={config.enableCrossCheck} onChange={(e) => setConfig({ ...config, enableCrossCheck: e.target.checked })} className="w-5 h-5 text-blue-500 rounded focus:ring-2 focus:ring-blue-500" /> </label> <label className="flex items-center justify-between p-4 bg-slate-700/50 rounded-lg cursor-pointer hover:bg-slate-700/70 transition-colors"> <div> <div className="font-semibold text-white">Auto Escalate</div> <div className="text-sm text-slate-400 mt-1"> Automatically escalate to higher layers on failure </div> </div> <input type="checkbox" checked={config.enableAutoEscalate} onChange={(e) => setConfig({ ...config, enableAutoEscalate: e.target.checked })} className="w-5 h-5 text-blue-500 rounded focus:ring-2 focus:ring-blue-500" /> </label> {config.enableAutoEscalate && ( <div className="ml-4 pl-4 border-l-2 border-slate-600"> <label className="block text-sm font-semibold text-slate-300 mb-2"> Max Escalation Layer </label> <select value={config.maxEscalationLayer} onChange={(e) => setConfig({ ...config, maxEscalationLayer: e.target.value })} className="input w-full md:w-1/2" > <option value="L1">L1</option> <option value="L2">L2</option> <option value="L3">L3</option> </select> </div> )} </div> </div> {/* Cost Tracking */} <div className="card p-6"> <h2 className="text-xl font-bold text-white mb-6">Cost Management</h2> <div className="space-y-4"> <label className="flex items-center justify-between p-4 bg-slate-700/50 rounded-lg cursor-pointer hover:bg-slate-700/70 transition-colors"> <div> <div className="font-semibold text-white">Enable Cost Tracking</div> <div className="text-sm text-slate-400 mt-1"> Track and monitor API costs </div> </div> <input type="checkbox" checked={config.enableCostTracking} onChange={(e) => setConfig({ ...config, enableCostTracking: e.target.checked })} className="w-5 h-5 text-blue-500 rounded focus:ring-2 focus:ring-blue-500" /> </label> {config.enableCostTracking && ( <div className="ml-4 pl-4 border-l-2 border-slate-600"> <label className="block text-sm font-semibold text-slate-300 mb-2"> Cost Alert Threshold ($) </label> <input type="number" step="0.01" value={config.costAlertThreshold} onChange={(e) => setConfig({ ...config, costAlertThreshold: parseFloat(e.target.value) })} className="input w-full md:w-1/3" /> <p className="text-xs text-slate-400 mt-1"> Alert when costs exceed this threshold </p> </div> )} </div> </div> {/* Layer Control */} <div className="card p-6"> <h2 className="text-xl font-bold text-white mb-6">Layer Control</h2> <div className="grid grid-cols-1 md:grid-cols-2 gap-4"> {Object.entries(config.layerControl).map(([layer, enabled]) => ( <label key={layer} className="flex items-center justify-between p-4 bg-slate-700/50 rounded-lg cursor-pointer hover:bg-slate-700/70 transition-colors" > <div className="font-semibold text-white">{layer}</div> <input type="checkbox" checked={enabled} onChange={(e) => setConfig({ ...config, layerControl: { ...config.layerControl, [layer]: e.target.checked } })} className="w-5 h-5 text-blue-500 rounded focus:ring-2 focus:ring-blue-500" /> </label> ))} </div> </div> {/* Task-Specific Models */} <div className="card p-6"> <h2 className="text-xl font-bold text-white mb-6">Task-Specific Models</h2> <div className="grid grid-cols-1 md:grid-cols-2 gap-6"> {Object.entries(config.taskSpecificModels).map(([task, model]) => ( <div key={task}> <label className="block text-sm font-semibold text-slate-300 mb-2 capitalize"> {task} </label> <input type="text" value={model} onChange={(e) => setConfig({ ...config, taskSpecificModels: { ...config.taskSpecificModels, [task]: e.target.value } })} className="input w-full" placeholder="default" /> </div> ))} </div> </div> </div> ); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/babasida246/ai-mcp-gateway'

If you have feedback or need assistance with the MCP directory API, please join our Discord server