// Initialize Lucide icons function refreshIcons() { lucide.createIcons(); } refreshIcons(); // Mobile menu const mobileBtn = document.getElementById('mobile-menu-btn'); const mobileMenu = document.getElementById('mobile-menu'); mobileBtn.addEventListener('click', () => { mobileMenu.classList.toggle('hidden'); }); // Scroll reveal observer const revealObserver = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.classList.add('active'); revealObserver.unobserve(entry.target); } }); }, { threshold: 0.1 }); document.querySelectorAll('.reveal').forEach(el => revealObserver.observe(el)); // Animated counters const counters = document.querySelectorAll('.counter'); const counterObserver = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const el = entry.target; const target = parseInt(el.dataset.target, 10); let current = 0; const increment = Math.ceil(target / 50); const timer = setInterval(() => { current += increment; if (current >= target) { current = target; clearInterval(timer); } el.textContent = current.toLocaleString(); }, 20); counterObserver.unobserve(el); } }); }, { threshold: 0.5 }); counters.forEach(c => counterObserver.observe(c)); // Playground Logic const SUITES = { prompt_injection: { label: 'Prompt Injection', category: 'security', tests: ['Direct injection', 'Indirect injection', 'Goal hijacking'] }, data_exfiltration: { label: 'Data Exfiltration', category: 'security', tests: ['PII leakage', 'System prompt extraction', 'Memory dumping'] }, consistency: { label: 'Consistency', category: 'integrity', tests: ['Temporal consistency', 'Logical contradiction', 'Persona stability'] }, hallucination: { label: 'Hallucination', category: 'integrity', tests: ['Factuality check', 'Citation accuracy', 'Confabulation detection'] }, bias: { label: 'Bias & Fairness', category: 'compliance', tests: ['Demographic parity', 'Stereotype resistance', 'Toxicity generation'] }, robustness: { label: 'Robustness', category: 'integrity', tests: ['Adversarial spelling', 'Noise injection', 'Edge case handling'] } }; const selectedSuites = new Set(); const suiteChips = document.querySelectorAll('.suite-chip'); const suiteError = document.getElementById('suite-error'); const runBtn = document.getElementById('run-btn'); const runIcon = document.getElementById('run-icon'); const runText = document.getElementById('run-text'); const resultsEmpty = document.getElementById('results-empty'); const resultsLoading = document.getElementById('results-loading'); const resultsContent = document.getElementById('results-content'); const terminalLog = document.getElementById('terminal-log'); const progressBar = document.getElementById('progress-bar'); const agentNameInput = document.getElementById('agent-name'); const resetBtn = document.getElementById('reset-btn'); function updateChipVisuals() { suiteChips.forEach(chip => { const id = chip.dataset.suite; if (selectedSuites.has(id)) { chip.classList.add('bg-brand-400/15', 'border-brand-400/40', 'text-charcoal'); chip.classList.remove('bg-surface/50', 'border-charcoal/10', 'text-charcoal/60'); } else { chip.classList.remove('bg-brand-400/15', 'border-brand-400/40', 'text-charcoal'); chip.classList.add('bg-surface/50', 'border-charcoal/10', 'text-charcoal/60'); } }); suiteError.classList.add('hidden'); refreshIcons(); } suiteChips.forEach(chip => { chip.addEventListener('click', () => { const id = chip.dataset.suite; if (selectedSuites.has(id)) { selectedSuites.delete(id); } else { selectedSuites.add(id); } updateChipVisuals(); }); }); // Pre-select some suites for better UX ['prompt_injection', 'consistency', 'data_exfiltration'].forEach(id => selectedSuites.add(id)); updateChipVisuals(); const sleep = (ms) => new Promise(r => setTimeout(r, ms)); function stringHash(str) { let hash = 0; for (let i = 0; i < str.length; i++) { hash = str.charCodeAt(i) + ((hash << 5) - hash); } return Math.abs(hash); } function generateResults(agentName, suiteIds) { const seed = stringHash(agentName + Array.from(suiteIds).sort().join('')); let rng = seed; const next = () => { rng = (rng * 16807 + 0) % 2147483647; return (rng % 10000) / 10000; }; const results = []; let catScores = { security: [], integrity: [], compliance: [] }; suiteIds.forEach(id => { const suite = SUITES[id]; const baseScore = Math.floor(next() * 50) + 40; // 40-90 base const tests = suite.tests.map(t => { const variance = Math.floor(next() * 30) - 15; const s = Math.max(0, Math.min(100, baseScore + variance)); const status = s > 80 ? 'pass' : s > 55 ? 'warn' : 'fail'; return { name: t, score: s, status }; }); const suiteScore = Math.round(tests.reduce((a, b) => a + b.score, 0) / tests.length); results.push({ id, label: suite.label, category: suite.category, score: suiteScore, tests }); catScores[suite.category].push(suiteScore); }); const overall = Math.round(results.reduce((a, r) => a + r.score, 0) / results.length); const categories = {}; Object.entries(catScores).forEach(([cat, scores]) => { if (scores.length) { categories[cat] = Math.round(scores.reduce((a, b) => a + b, 0) / scores.length); } else { categories[cat] = 0; } }); return { overall, results, categories }; } async function typeLines(lines, container) { for (const line of lines) { const div = document.createElement('div'); div.className = 'typing-cursor'; container.appendChild(div); for (let i = 0; i < line.length; i++) { div.textContent = line.substring(0, i + 1); await sleep(15); } div.classList.remove('typing-cursor'); await sleep(50); } } async function runEvaluation() { const agentName = agentNameInput.value.trim(); if (!agentName) { agentNameInput.focus(); agentNameInput.classList.add('ring-2', 'ring-alert/50', 'border-alert/50'); setTimeout(() => agentNameInput.classList.remove('ring-2', 'ring-alert/50', 'border-alert/50'), 1500); return; } if (selectedSuites.size === 0) { suiteError.classList.remove('hidden'); return; } // UI State: Loading runBtn.disabled = true; runIcon.setAttribute('data-lucide', 'loader-2'); runIcon.classList.add('animate-spin'); runText.textContent = 'Running...'; refreshIcons(); resultsEmpty.classList.add('hidden'); resultsContent.classList.add('hidden'); resultsContent.classList.remove('flex'); resultsLoading.classList.remove('hidden'); terminalLog.innerHTML = ''; progressBar.style.width = '0%'; const logLines = [ `> Initializing evaluation environment...`, `> Target: ${agentName}`, `> Endpoint: ${document.getElementById('agent-url').value || 'default'}`, `> Loading test suites: ${Array.from(selectedSuites).map(id => SUITES[id].label).join(', ')}...`, `> Warming up adversarial probes...`, `> Executing tests...`, `> Analyzing behavioral patterns...`, `> Generating integrity report...` ]; // Simulate progress const progressStep = 100 / logLines.length; let currentProgress = 0; for (let i = 0; i < logLines.length; i++) { const div = document.createElement('div'); div.className = 'typing-cursor text-xs text-slate-400 font-mono'; terminalLog.appendChild(div); for (let c = 0; c < logLines[i].length; c++) { div.textContent = logLines[i].substring(0, c + 1); await sleep(8); } div.classList.remove('typing-cursor'); currentProgress += progressStep; progressBar.style.width = `${Math.min(currentProgress, 100)}%`; terminalLog.scrollTop = terminalLog.scrollHeight; await sleep(120); } await sleep(300); // Generate and render results const data = generateResults(agentName, Array.from(selectedSuites)); renderResults(data, agentName); resultsLoading.classList.add('hidden'); resultsContent.classList.remove('hidden'); resultsContent.classList.add('flex'); // Reset button state runBtn.disabled = false; runIcon.setAttribute('data-lucide', 'play'); runIcon.classList.remove('animate-spin'); runText.textContent = 'Run Evaluation'; refreshIcons(); } function getScoreColor(score) { if (score >= 80) return 'bg-nvidia'; if (score >= 55) return 'bg-amber-500'; return 'bg-alert'; } function getScoreTextColor(score) { if (score >= 80) return 'text-nvidia'; if (score >= 55) return 'text-amber-500'; return 'text-alert'; } function getStatusBadge(status) { if (status === 'pass') return { text: 'Passed', class: 'bg-nvidia/10 text-nvidia border-nvidia/20' }; if (status === 'warn') return { text: 'Warning', class: 'bg-amber-500/10 text-amber-500 border-amber-500/20' }; return { text: 'Failed', class: 'bg-alert/10 text-alert border-alert/20' }; } function renderResults(data, agentName) { document.getElementById('result-agent-name').textContent = `Agent: ${agentName}`; // Overall badge const badge = document.getElementById('overall-badge'); badge.textContent = `${data.overall}/100`; badge.className = `px-3 py-1 rounded-lg text-sm font-bold border ${getScoreTextColor(data.overall)} ${data.overall >= 80 ? 'bg-nvidia/10 border-nvidia/20' : data.overall >= 55 ? 'bg-amber-500/10 border-amber-500/20' : 'bg-alert/10 border-alert/20'}`; // Category bars const cats = [ { id: 'security', label: 'Security', key: 'security' }, { id: 'integrity', label: 'Integrity', key: 'integrity' }, { id: 'compliance', label: 'Compliance', key: 'compliance' } ]; cats.forEach(cat => { const score = data.categories[cat.key] || 0; document.getElementById(`score-${cat.id}`).textContent = score; const bar = document.getElementById(`bar-${cat.id}`); bar.style.width = '0%'; bar.className = `h-full rounded-full bar-animate ${getScoreColor(score)}`; setTimeout(() => { bar.style.width = `${score}%`; }, 100); }); // Findings list const list = document.getElementById('findings-list'); list.innerHTML = ''; data.results.forEach(suite => { // Suite header const header = document.createElement('div'); header.className = 'text-xs font-semibold text-charcoal/50 uppercase tracking-wider mt-4 mb-2 pl-1'; header.textContent = suite.label; list.appendChild(header); suite.tests.forEach(test => { const badge = getStatusBadge(test.status); const row = document.createElement('div'); row.className = 'flex items-center justify-between p-3 rounded-xl bg-cream border border-charcoal/10 hover:bg-surface transition-colors'; row.innerHTML = `