Select Neon.ai models and comparison models, then add your question and click 'Compare'
Select Neon.ai models and comparison models using the menu on the upper left, then add your question and click 'Compare'
import { useState, useEffect, useCallback, useRef } from 'react'; import { Sun, Moon, Download, RefreshCw, Menu, X, Settings, Send, FileText, Users, Cpu, Ban, Eye, EyeOff, Sparkles } from 'lucide-react'; import { useTheme } from './contexts/ThemeContext'; import { fetchModels, runComparisonStream, uploadCsvComparison, downloadHistory } from './utils/api'; import NeonModelSelector from './components/NeonModelSelector'; import ComparisonSelector from './components/ComparisonSelector'; import QueryInput from './components/QueryInput'; import ResultsArea from './components/ResultsArea'; import './App.css'; function App() { const { theme, toggleTheme } = useTheme(); const [neonModels, setNeonModels] = useState([]); const [comparisonProviders, setComparisonProviders] = useState([]); const [selectedNeon, setSelectedNeon] = useState([]); const [selectedComparison, setSelectedComparison] = useState([]); const [results, setResults] = useState([]); const [loading, setLoading] = useState(false); const [modelsLoading, setModelsLoading] = useState(true); const [error, setError] = useState(null); const sessionIdRef = useRef(`session-${Date.now()}-${Math.random().toString(36).slice(2)}`); const [hasHistory, setHasHistory] = useState(false); const [sidebarOpen, setSidebarOpen] = useState(false); const [csvMode, setCsvMode] = useState(false); const [settingsOpen, setSettingsOpen] = useState(false); const [personaTarget, setPersonaTarget] = useState('neon-only'); const [showPersonaPrompt, setShowPersonaPrompt] = useState(false); const [showPrePromptIndicator, setShowPrePromptIndicator] = useState(false); const settingsRef = useRef(null); useEffect(() => { const handleClickOutside = (e) => { if (settingsRef.current && !settingsRef.current.contains(e.target)) { setSettingsOpen(false); } }; document.addEventListener('mousedown', handleClickOutside); return () => document.removeEventListener('mousedown', handleClickOutside); }, []); const loadModels = useCallback(async () => { setModelsLoading(true); setError(null); try { const data = await fetchModels(); setNeonModels(data.neon_models || []); setComparisonProviders(data.comparison_providers || []); } catch (e) { setError(`Failed to load models: ${e.message}`); } finally { setModelsLoading(false); } }, []); useEffect(() => { loadModels(); }, [loadModels]); const handleQuery = async (query) => { if (!selectedNeon.length) { setError('Please select at least one Neon model'); return; } setLoading(true); setError(null); const resultEntry = { query, groups: [], timestamp: Date.now() }; setResults(prev => [resultEntry, ...prev]); const resultIndex = 0; try { await runComparisonStream( query, selectedNeon, selectedComparison, sessionIdRef.current, (groupMeta) => { setResults(prev => { const updated = [...prev]; const entry = { ...updated[resultIndex] }; entry.groups = [...entry.groups, { neon_model_id: groupMeta.neon_model_id, neon_persona: groupMeta.neon_persona, system_prompt: groupMeta.system_prompt || '', query: groupMeta.query, responses: [], }]; updated[resultIndex] = entry; return updated; }); }, (response) => { setResults(prev => { const updated = [...prev]; const entry = { ...updated[resultIndex] }; entry.groups = entry.groups.map((g, gi) => { if (gi !== response.group_index) return g; return { ...g, responses: [...g.responses, response] }; }); updated[resultIndex] = entry; return updated; }); }, () => { setHasHistory(true); }, personaTarget, ); } catch (e) { setError(e.message); } finally { setLoading(false); } }; const handleCsvUpload = async (file) => { if (!selectedNeon.length) { setError('Please select at least one Neon model'); return; } setLoading(true); setError(null); try { const blob = await uploadCsvComparison(file, selectedNeon, selectedComparison, personaTarget); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `comparison_${new Date().toISOString().slice(0, 19).replace(/[T:]/g, '-')}.csv`; a.click(); URL.revokeObjectURL(url); } catch (e) { setError(e.message); } finally { setLoading(false); } }; const handleDownloadHistory = async () => { try { const blob = await downloadHistory(sessionIdRef.current); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `comparison_${new Date().toISOString().slice(0, 19).replace(/[T:]/g, '-')}.csv`; a.click(); URL.revokeObjectURL(url); } catch (e) { setError('No history to download yet'); } }; return (
Select Neon.ai models and comparison models, then add your question and click 'Compare'
Select Neon.ai models and comparison models using the menu on the upper left, then add your question and click 'Compare'