'use client'; import AdminShell from '@/components/AdminShell'; import { api } from '@/lib/api'; import { useEffect, useState, useCallback, useRef } from 'react'; import { Brain, Search, Check, ChevronDown, Play, Loader2, Eye, MessageSquare, Video, Shield, MapPin, Users, AlertTriangle, Server, Cloud, Cpu, Terminal, Upload } from 'lucide-react'; const MODERATION_TYPES = [ { key: 'text', label: 'Text Moderation', icon: MessageSquare }, { key: 'image', label: 'Image Moderation', icon: Eye }, { key: 'video', label: 'Video Moderation', icon: Video }, { key: 'group_text', label: 'Group Chat', icon: Users }, { key: 'group_image', label: 'Group Image', icon: Shield }, { key: 'beacon_text', label: 'Beacon Text', icon: MapPin }, { key: 'beacon_image', label: 'Beacon Image', icon: AlertTriangle }, ]; const ENGINES = [ { id: 'local_ai', label: 'Local AI (Ollama)', icon: Cpu }, { id: 'openrouter', label: 'OpenRouter', icon: Cloud }, { id: 'openai', label: 'OpenAI', icon: Server }, { id: 'google', label: 'Google Vision', icon: Eye }, { id: 'azure', label: 'Azure OpenAI', icon: Cloud }, ]; const LOCAL_MODELS = [ { id: 'llama-guard3:1b', name: 'LLaMA Guard 3 (1B)' }, { id: 'qwen2.5:7b-instruct-q4_K_M', name: 'Qwen 2.5 (7B)' }, ]; interface ModelInfo { id: string; name: string; pricing: { prompt: string; completion: string }; context_length: number; architecture?: Record; } interface ModerationConfig { id: string; moderation_type: string; model_id: string; model_name: string; system_prompt: string; enabled: boolean; engines: string[]; updated_at: string; } interface EngineInfo { id: string; name: string; status: string; } export default function AIModerationPage() { const [configs, setConfigs] = useState([]); const [engines, setEngines] = useState([]); const [loading, setLoading] = useState(true); // Selection states const [selectedType, setSelectedType] = useState('text'); const [selectedEngine, setSelectedEngine] = useState('local_ai'); // Config states const [enabled, setEnabled] = useState(false); const [modelId, setModelId] = useState(''); const [modelName, setModelName] = useState(''); const [systemPrompt, setSystemPrompt] = useState(''); const [saving, setSaving] = useState(false); // OpenRouter model picker const [showPicker, setShowPicker] = useState(false); const [models, setModels] = useState([]); const [modelsLoading, setModelsLoading] = useState(false); const [searchTerm, setSearchTerm] = useState(''); // Test states const [testInput, setTestInput] = useState(''); const [testResponse, setTestResponse] = useState(null); const [testing, setTesting] = useState(false); const [testHistory, setTestHistory] = useState([]); const [uploadedFile, setUploadedFile] = useState(null); const [uploading, setUploading] = useState(false); const loadConfigs = useCallback(() => { setLoading(true); Promise.all([ api.getAIModerationConfigs(), api.getAIEngines() ]) .then(([configData, engineData]) => { setConfigs(configData.configs || []); setEngines(engineData.engines || []); }) .finally(() => setLoading(false)); }, []); useEffect(() => { loadConfigs(); }, [loadConfigs]); // Load config when type changes useEffect(() => { const config = configs.find(c => c.moderation_type === selectedType); if (config) { setEnabled(config.enabled); setModelId(config.model_id || ''); setModelName(config.model_name || ''); setSystemPrompt(config.system_prompt || ''); if (config.engines && config.engines.length > 0) { setSelectedEngine(config.engines[0]); } } else { setEnabled(false); setModelId(''); setModelName(''); setSystemPrompt(''); } }, [selectedType, configs]); const loadModels = useCallback((search?: string) => { setModelsLoading(true); api.listOpenRouterModels({ search }) .then((data) => setModels(data.models || [])) .finally(() => setModelsLoading(false)); }, []); const handleSave = async () => { setSaving(true); try { await api.setAIModerationConfig({ moderation_type: selectedType, model_id: modelId, model_name: modelName, system_prompt: systemPrompt, enabled, engines: [selectedEngine], }); loadConfigs(); } catch (e: any) { alert(e.message); } finally { setSaving(false); } }; const handleFileUpload = async (file: File) => { setUploading(true); try { const result = await api.uploadTestImage(file); setTestInput(result.url); setUploadedFile(file); } catch (e: any) { console.error('Upload error:', e); alert('Upload failed: ' + e.message); } finally { setUploading(false); } }; const handleTest = async () => { if (!testInput.trim() && !uploadedFile) return; setTesting(true); const startTime = Date.now(); try { const isImage = selectedType.includes('image') || selectedType === 'video'; const data: any = { moderation_type: selectedType, engine: selectedEngine, }; if (isImage) { data.image_url = testInput; // Use the uploaded file URL } else { data.content = testInput; } const res = await api.testAIModeration(data); const duration = Date.now() - startTime; const entry = { ...res, timestamp: new Date().toISOString(), duration }; setTestResponse(entry); setTestHistory(prev => [entry, ...prev].slice(0, 10)); } catch (e: any) { const entry = { error: e.message, engine: selectedEngine, moderation_type: selectedType, input: testInput, timestamp: new Date().toISOString(), duration: Date.now() - startTime }; setTestResponse(entry); setTestHistory(prev => [entry, ...prev].slice(0, 10)); } finally { setTesting(false); } }; const typeLabel = MODERATION_TYPES.find(t => t.key === selectedType)?.label || selectedType; const engineLabel = ENGINES.find(e => e.id === selectedEngine)?.label || selectedEngine; const getEngineStatus = (id: string) => { const engine = engines.find(e => e.id === id); if (!engine) return { color: 'text-gray-400', dot: 'bg-gray-300', label: 'Unknown' }; if (engine.status === 'ready') return { color: 'text-green-600', dot: 'bg-green-500', label: 'Online' }; if (engine.status === 'down') return { color: 'text-red-600', dot: 'bg-red-500', label: 'Down' }; return { color: 'text-gray-400', dot: 'bg-gray-300', label: 'Not Configured' }; }; return (

AI Moderation

Configure AI moderation engines

{/* Engine Status - Compact */}
Engine Status: {ENGINES.map(eng => { const status = getEngineStatus(eng.id); return (
{eng.label}
); })}
{/* Left: Configuration */}
{/* Type Selector */}
{/* Engine Selector */}
{/* AI Moderation Instructions */}

Provide specific guidelines for the AI to follow when moderating {typeLabel.toLowerCase()} content.