Spaces:
Sleeping
Sleeping
| import React, { useState, useRef, useEffect } from 'react'; | |
| import Plot from '../utils/PlotlyWrapper'; | |
| import saeData from '../data/sae_features.json'; | |
| const CATEGORIES = ['all', 'science', 'code', 'language', 'domain']; | |
| const CATEGORY_COLORS = { | |
| code: '#00D9C0', // Neon Teal | |
| 'science/medical': '#FF5063', // Red | |
| 'names/pronouns': '#4A9EFF', // Blue | |
| syntax: '#9B59F5', // Purple | |
| semantic: '#FFB347', // Orange | |
| science: '#FF5063', | |
| language: '#9B59F5', | |
| domain: '#FFB347' | |
| }; | |
| // HTML5 Canvas Sparkline for per-token activation rendering | |
| const Sparkline = ({ values, color }) => { | |
| const canvasRef = useRef(null); | |
| useEffect(() => { | |
| const canvas = canvasRef.current; | |
| if (!canvas) return; | |
| const ctx = canvas.getContext('2d'); | |
| if (!ctx) return; | |
| const dpr = window.devicePixelRatio || 1; | |
| const width = 110; | |
| const height = 22; | |
| canvas.width = width * dpr; | |
| canvas.height = height * dpr; | |
| canvas.style.width = `${width}px`; | |
| canvas.style.height = `${height}px`; | |
| ctx.scale(dpr, dpr); | |
| ctx.clearRect(0, 0, width, height); | |
| if (!values || values.length === 0) return; | |
| const maxVal = Math.max(...values, 0.001); | |
| // Draw background guide line | |
| ctx.strokeStyle = 'rgba(30, 43, 69, 0.25)'; | |
| ctx.lineWidth = 1; | |
| ctx.beginPath(); | |
| ctx.moveTo(0, height - 1); | |
| ctx.lineTo(width, height - 1); | |
| ctx.stroke(); | |
| // Draw sparkline curve | |
| ctx.strokeStyle = color; | |
| ctx.lineWidth = 1.8; | |
| ctx.lineCap = 'round'; | |
| ctx.lineJoin = 'round'; | |
| ctx.beginPath(); | |
| const step = width / Math.max(1, values.length - 1); | |
| values.forEach((v, idx) => { | |
| const x = idx * step; | |
| const y = height - (v / maxVal) * (height - 4) - 2; | |
| if (idx === 0) { | |
| ctx.moveTo(x, y); | |
| } else { | |
| ctx.lineTo(x, y); | |
| } | |
| }); | |
| ctx.stroke(); | |
| // Fill area below sparkline | |
| ctx.lineTo(width, height); | |
| ctx.lineTo(0, height); | |
| ctx.closePath(); | |
| ctx.fillStyle = `${color}10`; | |
| ctx.fill(); | |
| }, [values, color]); | |
| return <canvas ref={canvasRef} style={{ display: 'block', borderRadius: 2 }} />; | |
| }; | |
| export const SparseAutoencoder = () => { | |
| const [selectedCategory, setSelectedCategory] = useState('all'); | |
| const [selectedFeature, setSelectedFeature] = useState(null); | |
| const [barsInView, setBarsInView] = useState(false); | |
| const barsRef = useRef(null); | |
| // Live Activation Lab State hooks | |
| const [activeTab, setActiveTab] = useState('gallery'); // 'gallery' or 'live' | |
| const [customPrompt, setCustomPrompt] = useState('When the doctor analyzed the DNA sequence, he found a gene mutation.'); | |
| const [scanning, setScanning] = useState(false); | |
| const [scanResult, setScanResult] = useState(null); | |
| const [scanError, setScanError] = useState(null); | |
| const [modelStatus, setModelStatus] = useState(null); | |
| const [sparsityThreshold, setSparsityThreshold] = useState(0.0001); | |
| const [copied, setCopied] = useState(false); | |
| const [selectedCategoryFilter, setSelectedCategoryFilter] = useState(null); | |
| useEffect(() => { | |
| const observer = new IntersectionObserver( | |
| ([entry]) => { if (entry.isIntersecting) setBarsInView(true); }, | |
| { threshold: 0.2 } | |
| ); | |
| if (barsRef.current) observer.observe(barsRef.current); | |
| return () => observer.disconnect(); | |
| }, []); | |
| // Fetch model status on mount | |
| useEffect(() => { | |
| fetch('http://localhost:8000/api/health') | |
| .then(res => res.json()) | |
| .then(data => { | |
| setModelStatus(data.real_inference_active ? 'live' : 'demo'); | |
| }) | |
| .catch(() => { | |
| setModelStatus('demo'); | |
| }); | |
| }, []); | |
| const handleScan = async () => { | |
| if (!customPrompt.trim()) return; | |
| setScanning(true); | |
| setScanError(null); | |
| setSelectedCategoryFilter(null); | |
| try { | |
| const response = await fetch('http://localhost:8000/api/sae/activate', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: JSON.stringify({ | |
| prompt: customPrompt, | |
| threshold: parseFloat(sparsityThreshold) | |
| }), | |
| }); | |
| if (!response.ok) { | |
| throw new Error('Inference server returned an error'); | |
| } | |
| const data = await response.json(); | |
| setScanResult(data); | |
| if (data.real_inference) { | |
| setModelStatus('live'); | |
| } else { | |
| setModelStatus('demo'); | |
| } | |
| } catch (err) { | |
| setScanError(err.message || 'Failed to communicate with the inference server.'); | |
| } finally { | |
| setScanning(false); | |
| } | |
| }; | |
| const features = selectedCategory === 'all' | |
| ? saeData.features | |
| : saeData.features.filter(f => f.category === selectedCategory); | |
| const trainingTrace1 = { | |
| x: saeData.training.steps, | |
| y: saeData.training.total_loss, | |
| type: 'scatter', | |
| mode: 'lines', | |
| name: 'Total loss', | |
| line: { color: '#9B59F5', width: 2 }, | |
| }; | |
| const trainingTrace2 = { | |
| x: saeData.training.steps, | |
| y: saeData.training.recon_loss, | |
| type: 'scatter', | |
| mode: 'lines', | |
| name: 'Reconstruction only', | |
| line: { color: '#00D9C0', width: 2 }, | |
| }; | |
| return ( | |
| <section id="monosemanticity" className="scroll-mt-section" data-testid="section-sae" style={{ padding: '80px 0' }}> | |
| <div className="section-container"> | |
| <div className="mb-2"><span className="badge-violet">Sparse Autoencoder</span></div> | |
| <h2 style={{ fontFamily: "'Space Grotesk', sans-serif", fontWeight: 600, fontSize: 30, color: '#E8EEF8', marginBottom: 8 }}> | |
| Reproducing Anthropic's Monosemanticity Findings | |
| </h2> | |
| <p style={{ fontSize: 15, color: '#8A9BC4', maxWidth: 680, marginBottom: 32, lineHeight: 1.75 }}> | |
| Anthropic's 2023 paper trained a sparse autoencoder on GPT-2 Small MLP activations and found 15,000 features where 70% are interpretable by human raters. We reproduce the core experiment. | |
| </p> | |
| {/* Paper Summary */} | |
| <div className="research-card-accent mb-8" style={{ borderLeftColor: '#9B59F5' }}> | |
| <h3 style={{ fontFamily: "'Space Grotesk', sans-serif", fontWeight: 600, fontSize: 16, color: '#E8EEF8', marginBottom: 4 }}> | |
| Towards Monosemanticity (Anthropic, 2023) | |
| </h3> | |
| <div style={{ fontSize: 12, color: '#4A5A7A', marginBottom: 8 }}>Bricken, Templeton, Batson, Chen, Jermyn et al.</div> | |
| <p style={{ fontSize: 13, color: '#8A9BC4', lineHeight: 1.75 }}> | |
| A 512-neuron MLP layer contains features in superposition — the model represents MORE features than it has neurons by packing them into overcomplete directions. A sparse autoencoder with 8× expansion extracts ~4,096 features. 70% of features are monosemantic by human evaluation vs ~20% for individual neurons. | |
| </p> | |
| </div> | |
| {/* Polysemantic vs Monosemantic */} | |
| <div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-8"> | |
| <div className="research-card" style={{ borderLeft: '3px solid #FF5063' }}> | |
| <div style={{ fontSize: 14, fontWeight: 600, color: '#FF5063', marginBottom: 8 }}>Polysemantic: Individual Neuron #47</div> | |
| <p style={{ fontSize: 12, color: '#4A5A7A', marginBottom: 8 }}>One neuron, many unrelated concepts</p> | |
| {[{ token: 'Arabic script tokens', val: 0.87 }, { token: 'Hebrew text tokens', val: 0.82 }, { token: 'Mathematical symbols', val: 0.71 }, { token: 'Programming syntax', val: 0.68 }].map((item, i) => ( | |
| <div key={i} className="flex items-center gap-2 mb-1"> | |
| <div style={{ width: `${item.val * 100}%`, maxWidth: '60%', height: 6, background: '#FF5063', borderRadius: 3, transition: 'width 700ms' }} /> | |
| <span style={{ fontSize: 11, color: '#8A9BC4' }}>{item.token} ({item.val})</span> | |
| </div> | |
| ))} | |
| </div> | |
| <div className="research-card" style={{ borderLeft: '3px solid #00E676' }}> | |
| <div style={{ fontSize: 14, fontWeight: 600, color: '#00E676', marginBottom: 8 }}>Monosemantic: SAE Feature #1,847</div> | |
| <p style={{ fontSize: 12, color: '#4A5A7A', marginBottom: 8 }}>One direction, one concept</p> | |
| {[{ token: '"DNA"', val: 0.95 }, { token: '"sequence"', val: 0.91 }, { token: '"genome"', val: 0.89 }, { token: '"ATCG"', val: 0.88 }].map((item, i) => ( | |
| <div key={i} className="flex items-center gap-2 mb-1"> | |
| <div style={{ width: `${item.val * 100}%`, maxWidth: '60%', height: 6, background: '#00E676', borderRadius: 3, transition: 'width 700ms' }} /> | |
| <span style={{ fontSize: 11, color: '#8A9BC4' }}>{item.token} ({item.val})</span> | |
| </div> | |
| ))} | |
| </div> | |
| </div> | |
| {/* Why Superposition */} | |
| <div className="research-card mb-8" style={{ borderLeft: '3px solid #4A9EFF' }}> | |
| <h3 style={{ fontFamily: "'Space Grotesk', sans-serif", fontWeight: 600, fontSize: 18, color: '#E8EEF8', marginBottom: 8 }}>Why Does This Happen?</h3> | |
| <p style={{ fontSize: 13, color: '#8A9BC4', lineHeight: 1.75 }}> | |
| A neural network with N neurons can represent N orthogonal features. But real models need to represent far MORE than N concepts. <strong style={{ color: '#4A9EFF' }}>Solution:</strong> store features as directions in high-dimensional space, not in individual neurons. Directions can be nearly orthogonal even when their count exceeds the neuron count. | |
| </p> | |
| <p style={{ fontSize: 13, color: '#8A9BC4', lineHeight: 1.75, marginTop: 8 }}> | |
| <strong style={{ color: '#FF5063' }}>Cost:</strong> neurons appear polysemantic because multiple features share each neuron. <strong style={{ color: '#00E676' }}>Benefit:</strong> exponentially more representational capacity. The sparse autoencoder recovers these hidden directions. | |
| </p> | |
| </div> | |
| {/* SAE Architecture */} | |
| <div className="research-card mb-8"> | |
| <h3 style={{ fontFamily: "'Space Grotesk', sans-serif", fontWeight: 600, fontSize: 18, color: '#E8EEF8', marginBottom: 12 }}>The Architecture</h3> | |
| <div className="code-block" style={{ marginBottom: 12 }}> | |
| <span style={{ color: '#9B59F5' }}>class</span> <span style={{ color: '#00D9C0' }}>SparseAutoencoder</span>(nn.Module):<br /> | |
| <span style={{ color: '#9B59F5' }}>def</span> <span style={{ color: '#4A9EFF' }}>__init__</span>(self, d_model=<span style={{ color: '#FFB347' }}>512</span>, expansion=<span style={{ color: '#FFB347' }}>8</span>, l1_coeff=<span style={{ color: '#FFB347' }}>1e-3</span>):<br /> | |
| d_hidden = d_model * expansion <span style={{ color: '#4A5A7A' }}># 512 × 8 = 4096 features</span><br /> | |
| self.W_enc = nn.Parameter(torch.randn(d_model, d_hidden) * <span style={{ color: '#FFB347' }}>0.01</span>)<br /> | |
| self.W_dec = nn.Parameter(torch.randn(d_hidden, d_model) * <span style={{ color: '#FFB347' }}>0.01</span>)<br /> | |
| <br /> | |
| <span style={{ color: '#9B59F5' }}>def</span> <span style={{ color: '#4A9EFF' }}>forward</span>(self, x):<br /> | |
| h = F.relu(x_centered @ self.W_enc + self.b_enc)<br /> | |
| x_hat = h @ self.W_dec + self.b_dec<br /> | |
| loss = ((x - x_hat)**<span style={{ color: '#FFB347' }}>2</span>).mean() + self.l1_coeff * h.abs().mean()<br /> | |
| <span style={{ color: '#9B59F5' }}>return</span> x_hat, h, loss | |
| </div> | |
| <div className="flex flex-wrap gap-4" style={{ fontSize: 12, color: '#8A9BC4' }}> | |
| <span>Input: <code style={{ color: '#00D9C0' }}>x ∈ ℝ^512</code></span> | |
| <span>Hidden: <code style={{ color: '#9B59F5' }}>h ∈ ℝ^4096</code></span> | |
| <span>Loss: <code style={{ color: '#FFB347' }}>||x - x̂||² + λ||h||₁</code></span> | |
| </div> | |
| </div> | |
| {/* Training Details */} | |
| <div className="research-card mb-8"> | |
| <h3 style={{ fontFamily: "'Space Grotesk', sans-serif", fontWeight: 600, fontSize: 18, color: '#E8EEF8', marginBottom: 12 }}>Training Details</h3> | |
| <div className="grid grid-cols-2 md:grid-cols-4 gap-3"> | |
| {[ | |
| { label: 'Dataset', value: '4B MLP activations', sub: 'GPT-2 Small layer 6, OpenWebText' }, | |
| { label: 'Expansion', value: '8× (4,096)', sub: '4,096 features from 512 neurons' }, | |
| { label: 'L1 Coefficient', value: '1e-3', sub: 'Tuned via grid search with W&B' }, | |
| { label: 'Batch Size', value: '8,192', sub: '200K training steps' }, | |
| { label: 'Optimizer', value: 'Adam', sub: 'lr=3e-4, β₁=0.9, β₂=0.999' }, | |
| { label: 'Resampling', value: 'Every 50K', sub: 'Prevents dead features' }, | |
| { label: 'Result', value: '93% alive', sub: '5% ultra-low-density' }, | |
| { label: 'Cost', value: '~$8 on A10G', sub: '~6 hours training via Modal' }, | |
| ].map((item, i) => ( | |
| <div key={i} style={{ padding: '8px 12px', background: '#121729', borderRadius: 8, border: '1px solid #1E2B45' }}> | |
| <div style={{ fontSize: 10, color: '#4A5A7A', textTransform: 'uppercase', letterSpacing: '0.05em', marginBottom: 2 }}>{item.label}</div> | |
| <div style={{ fontSize: 14, fontWeight: 600, color: '#E8EEF8', fontFamily: "'JetBrains Mono', monospace" }}>{item.value}</div> | |
| <div style={{ fontSize: 10, color: '#4A5A7A', marginTop: 2 }}>{item.sub}</div> | |
| </div> | |
| ))} | |
| </div> | |
| </div> | |
| {/* Training Loss Chart */} | |
| <div className="research-card mb-8 plotly-container"> | |
| <h3 style={{ fontFamily: "'Space Grotesk', sans-serif", fontWeight: 600, fontSize: 16, color: '#E8EEF8', marginBottom: 4 }}>Training Loss Curve</h3> | |
| <Plot | |
| data={[trainingTrace1, trainingTrace2]} | |
| layout={{ | |
| paper_bgcolor: 'rgba(0,0,0,0)', | |
| plot_bgcolor: 'rgba(0,0,0,0)', | |
| font: { family: "'Inter', sans-serif", color: '#E8EEF8', size: 12 }, | |
| margin: { l: 50, r: 20, t: 20, b: 50 }, | |
| xaxis: { title: { text: 'Training Step', font: { color: '#8A9BC4', size: 11 } }, gridcolor: 'rgba(30,43,69,0.55)', tickfont: { color: '#8A9BC4', size: 10 } }, | |
| yaxis: { title: { text: 'Loss', font: { color: '#8A9BC4', size: 11 } }, gridcolor: 'rgba(30,43,69,0.55)', tickfont: { color: '#8A9BC4', size: 10 } }, | |
| legend: { bgcolor: 'rgba(12,15,26,0.65)', bordercolor: 'rgba(30,43,69,0.9)', borderwidth: 1, font: { color: '#8A9BC4', size: 11 } }, | |
| height: 300, | |
| annotations: saeData.training.annotations.map(a => ({ | |
| x: a.step, y: saeData.training.total_loss[saeData.training.steps.indexOf(a.step)] || 0.3, | |
| text: a.label, showarrow: true, arrowhead: 2, arrowcolor: '#4A5A7A', | |
| font: { color: '#FFB347', size: 10 }, ax: 0, ay: -30, | |
| })), | |
| }} | |
| config={{ displayModeBar: false, responsive: true }} | |
| style={{ width: '100%' }} | |
| /> | |
| </div> | |
| {/* Tab Switcher */} | |
| <div className="flex border-b border-[#1E2B45] mb-8"> | |
| <button | |
| onClick={() => setActiveTab('gallery')} | |
| style={{ | |
| padding: '12px 24px', | |
| fontWeight: 600, | |
| fontSize: 15, | |
| color: activeTab === 'gallery' ? '#B57BFF' : '#8A9BC4', | |
| borderBottom: activeTab === 'gallery' ? '3px solid #9B59F5' : '3px solid transparent', | |
| background: 'transparent', | |
| borderTop: 'none', | |
| borderLeft: 'none', | |
| borderRight: 'none', | |
| cursor: 'pointer', | |
| transition: 'all 200ms', | |
| fontFamily: "'Space Grotesk', sans-serif" | |
| }} | |
| > | |
| Static Feature Gallery | |
| </button> | |
| <button | |
| onClick={() => { | |
| setActiveTab('live'); | |
| if (!scanResult) { | |
| handleScan(); | |
| } | |
| }} | |
| style={{ | |
| padding: '12px 24px', | |
| fontWeight: 600, | |
| fontSize: 15, | |
| color: activeTab === 'live' ? '#B57BFF' : '#8A9BC4', | |
| borderBottom: activeTab === 'live' ? '3px solid #9B59F5' : '3px solid transparent', | |
| background: 'transparent', | |
| borderTop: 'none', | |
| borderLeft: 'none', | |
| borderRight: 'none', | |
| cursor: 'pointer', | |
| transition: 'all 200ms', | |
| fontFamily: "'Space Grotesk', sans-serif" | |
| }} | |
| > | |
| Live SAE Activation Lab | |
| </button> | |
| </div> | |
| {activeTab === 'gallery' ? ( | |
| <> | |
| <h3 style={{ fontFamily: "'Space Grotesk', sans-serif", fontWeight: 600, fontSize: 20, color: '#E8EEF8', marginBottom: 12 }}>Features Discovered by the SAE</h3> | |
| <div className="flex flex-wrap gap-2 mb-4"> | |
| {CATEGORIES.map(cat => ( | |
| <button | |
| key={cat} | |
| onClick={() => setSelectedCategory(cat)} | |
| className="text-xs px-3 py-1.5 rounded-full transition-colors duration-200 cursor-pointer capitalize" | |
| style={{ | |
| background: selectedCategory === cat ? 'rgba(155,89,245,0.15)' : 'transparent', | |
| border: `1px solid ${selectedCategory === cat ? 'rgba(155,89,245,0.4)' : '#2A3A58'}`, | |
| color: selectedCategory === cat ? '#B57BFF' : '#8A9BC4', | |
| }} | |
| > | |
| {cat} | |
| </button> | |
| ))} | |
| </div> | |
| <div ref={barsRef} className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3"> | |
| {features.map((f, i) => ( | |
| <div | |
| key={f.id} | |
| data-testid="sae-feature-card" | |
| className="research-card cursor-pointer" | |
| style={{ | |
| padding: '14px 16px', | |
| borderLeft: `3px solid ${CATEGORY_COLORS[f.category] || '#00D9C0'}`, | |
| transition: 'border-color 200ms, box-shadow 200ms', | |
| }} | |
| onClick={() => setSelectedFeature(selectedFeature?.id === f.id ? null : f)} | |
| onMouseEnter={e => { e.currentTarget.style.boxShadow = `0 0 0 1px ${CATEGORY_COLORS[f.category]}33`; }} | |
| onMouseLeave={e => { e.currentTarget.style.boxShadow = 'none'; }} | |
| > | |
| <div className="flex items-center justify-between mb-2"> | |
| <span style={{ fontSize: 12, fontWeight: 600, color: CATEGORY_COLORS[f.category], fontFamily: "'JetBrains Mono', monospace" }}> | |
| Feature #{f.id.toString().padStart(4, '0')} | |
| </span> | |
| <span className="badge-teal" style={{ fontSize: 10, padding: '1px 8px', background: `${CATEGORY_COLORS[f.category]}12`, color: CATEGORY_COLORS[f.category], borderColor: `${CATEGORY_COLORS[f.category]}30` }}> | |
| {f.category} | |
| </span> | |
| </div> | |
| <div style={{ fontSize: 14, fontWeight: 600, color: '#E8EEF8', marginBottom: 8 }}>{f.label}</div> | |
| <div className="flex flex-wrap gap-1 mb-2"> | |
| {f.tokens.slice(0, 5).map((t, j) => ( | |
| <span key={j} style={{ fontSize: 11, background: '#121729', color: '#8A9BC4', padding: '1px 6px', borderRadius: 4, border: '1px solid #1E2B45' }}> | |
| {t} | |
| </span> | |
| ))} | |
| </div> | |
| {/* Mini activation bars */} | |
| <div className="space-y-1"> | |
| {f.tokens.slice(0, 4).map((t, j) => ( | |
| <div key={j} className="flex items-center gap-2"> | |
| <div style={{ | |
| width: barsInView ? `${f.activations[j] * 80}%` : '0%', | |
| height: 4, background: CATEGORY_COLORS[f.category], borderRadius: 2, | |
| transition: `width ${600 + j * 100}ms ease-out`, | |
| }} /> | |
| <span style={{ fontSize: 9, color: '#4A5A7A', minWidth: 24 }}>{f.activations[j]}</span> | |
| </div> | |
| ))} | |
| </div> | |
| {/* Expanded detail */} | |
| {selectedFeature?.id === f.id && ( | |
| <div className="mt-3 pt-3" style={{ borderTop: '1px solid #1E2B45' }}> | |
| <div style={{ fontSize: 11, color: '#4A5A7A', marginBottom: 4 }}>All activating tokens:</div> | |
| <div className="flex flex-wrap gap-1"> | |
| {f.tokens.map((t, j) => ( | |
| <span key={j} style={{ fontSize: 11, background: '#0A0D18', color: CATEGORY_COLORS[f.category], padding: '2px 8px', borderRadius: 4, border: `1px solid ${CATEGORY_COLORS[f.category]}30` }}> | |
| {t} <span style={{ color: '#4A5A7A' }}>({f.activations[j]})</span> | |
| </span> | |
| ))} | |
| </div> | |
| </div> | |
| )} | |
| </div> | |
| ))} | |
| </div> | |
| </> | |
| ) : ( | |
| <div className="research-card" style={{ padding: '24px', borderLeft: '3px solid #9B59F5' }}> | |
| <h3 style={{ fontFamily: "'Space Grotesk', sans-serif", fontWeight: 600, fontSize: 20, color: '#E8EEF8', marginBottom: 8 }}> | |
| Dynamic Layer 6 SAE Projection Lab | |
| </h3> | |
| <p style={{ fontSize: 13, color: '#8A9BC4', marginBottom: 20, lineHeight: 1.6 }}> | |
| Type a custom prompt to extract raw Layer 6 activations of GPT-2 Small, project them onto a 4,096-dimensional overcomplete space, and measure the active features and sparsity in real-time. | |
| </p> | |
| {/* Model Status Pill */} | |
| <div className="flex items-center gap-2 mb-4"> | |
| <span style={{ fontSize: 11, color: '#4A5A7A' }}>Engine Status:</span> | |
| {modelStatus === 'live' ? ( | |
| <span style={{ background: 'rgba(0,230,118,0.1)', color: '#00E676', border: '1px solid rgba(0,230,118,0.3)', fontSize: 11, padding: '2px 10px', borderRadius: 4 }}> | |
| 🟢 Live Model Inference (GPT-2 Small on CPU) | |
| </span> | |
| ) : ( | |
| <span style={{ background: 'rgba(255,179,71,0.1)', color: '#FFB347', border: '1px solid rgba(255,179,71,0.3)', fontSize: 11, padding: '2px 10px', borderRadius: 4 }}> | |
| 🟡 Fallback Simulation (Model Offline) | |
| </span> | |
| )} | |
| </div> | |
| {/* Input Form */} | |
| <div className="flex flex-col sm:flex-row gap-3 mb-6"> | |
| <textarea | |
| value={customPrompt} | |
| onChange={e => setCustomPrompt(e.target.value)} | |
| placeholder="Type a custom prompt..." | |
| rows={2} | |
| style={{ | |
| flex: 1, | |
| background: '#0C0F1A', | |
| border: '1px solid #1E2B45', | |
| borderRadius: '8px', | |
| padding: '12px', | |
| color: '#E8EEF8', | |
| fontFamily: 'inherit', | |
| fontSize: '14px', | |
| resize: 'none', | |
| outline: 'none', | |
| boxShadow: 'none', | |
| }} | |
| /> | |
| <button | |
| onClick={handleScan} | |
| disabled={scanning} | |
| style={{ | |
| background: 'linear-gradient(135deg, #9B59F5 0%, #00D9C0 100%)', | |
| color: '#060810', | |
| fontWeight: 600, | |
| border: 'none', | |
| borderRadius: '8px', | |
| padding: '12px 24px', | |
| alignSelf: 'stretch', | |
| display: 'flex', | |
| alignItems: 'center', | |
| justifyContent: 'center', | |
| minWidth: '150px', | |
| transition: 'opacity 200ms', | |
| cursor: scanning ? 'not-allowed' : 'pointer' | |
| }} | |
| > | |
| {scanning ? 'Scanning...' : 'Scan Activations'} | |
| </button> | |
| </div> | |
| {/* Premium Dynamic Sparsity Threshold Slider */} | |
| <div style={{ marginBottom: '24px', background: '#0C0F1A', border: '1px solid #1E2B45', borderRadius: '8px', padding: '16px' }}> | |
| <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2 mb-3"> | |
| <span style={{ fontSize: 13, fontWeight: 600, color: '#E8EEF8', display: 'flex', alignItems: 'center', gap: '8px' }}> | |
| Sparsity Threshold: | |
| <span style={{ color: '#00D9C0', fontFamily: "'JetBrains Mono', monospace", background: '#121729', padding: '2px 8px', borderRadius: '4px', border: '1px solid #1E2B45', fontSize: '12px' }}> | |
| {parseFloat(sparsityThreshold).toExponential(2)} | |
| </span> | |
| </span> | |
| <span style={{ fontSize: 11, color: '#4A5A7A' }}> | |
| Hunches feature sensitivity (standard default is 1e-4) | |
| </span> | |
| </div> | |
| <div className="flex items-center gap-4"> | |
| <span style={{ fontSize: 10, color: '#4A5A7A', fontFamily: 'monospace' }}>1e-5</span> | |
| <input | |
| type="range" | |
| min="0.00001" | |
| max="0.01" | |
| step="0.00001" | |
| value={sparsityThreshold} | |
| onChange={e => setSparsityThreshold(parseFloat(e.target.value))} | |
| style={{ | |
| flex: 1, | |
| accentColor: '#9B59F5', | |
| cursor: 'pointer', | |
| background: '#1E2B45', | |
| height: '6px', | |
| borderRadius: '3px' | |
| }} | |
| /> | |
| <span style={{ fontSize: 10, color: '#4A5A7A', fontFamily: 'monospace' }}>1e-2</span> | |
| </div> | |
| </div> | |
| {scanError && ( | |
| <div style={{ color: '#FF5063', fontSize: 13, marginBottom: 16 }}>{scanError}</div> | |
| )} | |
| {scanResult && ( | |
| <div> | |
| {/* Dynamic Telemetry Header & Copy JSON Tool */} | |
| <div className="flex items-center justify-between mb-4 border-b border-[#1E2B45] pb-3" style={{ marginTop: '8px' }}> | |
| <h4 style={{ fontFamily: "'Space Grotesk', sans-serif", fontWeight: 600, fontSize: 15, color: '#E8EEF8', margin: 0 }}> | |
| SAE Latent Activation Telemetry | |
| </h4> | |
| <button | |
| onClick={() => { | |
| navigator.clipboard.writeText(JSON.stringify(scanResult, null, 2)); | |
| setCopied(true); | |
| setTimeout(() => setCopied(false), 2000); | |
| }} | |
| style={{ | |
| background: '#121729', | |
| border: '1px solid #1E2B45', | |
| borderRadius: '6px', | |
| padding: '4px 12px', | |
| color: copied ? '#00D9C0' : '#E8EEF8', | |
| fontSize: '11px', | |
| fontWeight: 500, | |
| cursor: 'pointer', | |
| display: 'flex', | |
| alignItems: 'center', | |
| gap: '6px', | |
| transition: 'all 200ms' | |
| }} | |
| > | |
| {copied ? '✓ JSON Copied' : 'Export JSON Payload'} | |
| </button> | |
| </div> | |
| {/* Pre-trained SAE Specifications card */} | |
| {scanResult.sae_info && ( | |
| <div className="research-card mb-6" style={{ background: '#0a0d18', border: '1px solid #1E2B45' }}> | |
| <h4 style={{ fontSize: 13, fontWeight: 600, color: '#E8EEF8', marginBottom: 12 }}>SAE Dictionary Specifications</h4> | |
| <div className="grid grid-cols-2 md:grid-cols-4 gap-4" style={{ fontSize: 12 }}> | |
| <div><span style={{ color: '#4A5A7A' }}>Dictionary Release:</span> <span style={{ color: '#00D9C0', fontFamily: "'JetBrains Mono', monospace" }}>{scanResult.sae_info.release}</span></div> | |
| <div><span style={{ color: '#4A5A7A' }}>Hook Point:</span> <span style={{ color: '#9B59F5', fontFamily: "'JetBrains Mono', monospace" }}>{scanResult.sae_info.hook_point}</span></div> | |
| <div><span style={{ color: '#4A5A7A' }}>Latents Count (d_sae):</span> <span style={{ color: '#FFB347', fontFamily: "'JetBrains Mono', monospace" }}>{scanResult.sae_info.d_sae}</span></div> | |
| <div><span style={{ color: '#4A5A7A' }}>Training Resource:</span> <span style={{ color: '#8A9BC4' }}>{scanResult.sae_info.trained_tokens}</span></div> | |
| </div> | |
| </div> | |
| )} | |
| {/* Stats Dashboard */} | |
| <div className="grid grid-cols-1 sm:grid-cols-3 gap-4 mb-6"> | |
| <div style={{ padding: '16px', background: '#121729', borderRadius: 8, border: '1px solid #1E2B45', textAlign: 'center' }}> | |
| <div style={{ fontSize: 11, color: '#4A5A7A', textTransform: 'uppercase', letterSpacing: '0.05em', marginBottom: 4 }}>L0 Sparsity</div> | |
| <div style={{ fontFamily: "'Space Grotesk', sans-serif", fontWeight: 700, fontSize: 32, color: '#9B59F5' }}> | |
| {scanResult.l0_sparsity} | |
| </div> | |
| <div style={{ fontSize: 10, color: '#4A5A7A', marginTop: 4 }}>avg active latents/token</div> | |
| </div> | |
| <div style={{ padding: '16px', background: '#121729', borderRadius: 8, border: '1px solid #1E2B45', textAlign: 'center' }}> | |
| <div style={{ fontSize: 11, color: '#4A5A7A', textTransform: 'uppercase', letterSpacing: '0.05em', marginBottom: 4 }}>Explained Variance</div> | |
| <div style={{ fontFamily: "'Space Grotesk', sans-serif", fontWeight: 700, fontSize: 32, color: '#00D9C0' }}> | |
| {(scanResult.explained_variance * 100).toFixed(1)}% | |
| </div> | |
| <div style={{ fontSize: 10, color: '#4A5A7A', marginTop: 4 }}>reconstructed info ratio</div> | |
| </div> | |
| <div style={{ padding: '16px', background: '#121729', borderRadius: 8, border: '1px solid #1E2B45', textAlign: 'center' }}> | |
| <div style={{ fontSize: 11, color: '#4A5A7A', textTransform: 'uppercase', letterSpacing: '0.05em', marginBottom: 4 }}>Active Latents</div> | |
| <div style={{ fontFamily: "'Space Grotesk', sans-serif", fontWeight: 700, fontSize: 32, color: '#FFB347' }}> | |
| {scanResult.active_features.length} | |
| </div> | |
| <div style={{ fontSize: 10, color: '#4A5A7A', marginTop: 4 }}>features active above threshold</div> | |
| </div> | |
| </div> | |
| {/* Dynamic Feature Clustering Composition Bar & Legend */} | |
| {scanResult.feature_clusters && scanResult.feature_clusters.length > 0 && ( | |
| <div style={{ marginBottom: '24px', background: '#0C0F1A', border: '1px solid #1E2B45', borderRadius: '8px', padding: '16px' }}> | |
| <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12 }}> | |
| <h4 style={{ fontFamily: "'Space Grotesk', sans-serif", fontWeight: 600, fontSize: 13, color: '#E8EEF8', margin: 0 }}> | |
| Semantic Prompt Composition Profile | |
| </h4> | |
| {selectedCategoryFilter && ( | |
| <span | |
| onClick={() => setSelectedCategoryFilter(null)} | |
| style={{ fontSize: 10, color: '#FF5063', cursor: 'pointer', fontWeight: 600 }} | |
| > | |
| Clear Filter ✕ | |
| </span> | |
| )} | |
| </div> | |
| {/* Visual Stacked Progress Bar */} | |
| <div style={{ display: 'flex', width: '100%', height: '12px', borderRadius: '6px', overflow: 'hidden', background: '#121729', marginBottom: '16px', border: '1px solid #1E2B4555' }}> | |
| {scanResult.feature_clusters.map((cluster, idx) => { | |
| const isFiltered = selectedCategoryFilter === cluster.key; | |
| const isAnyFiltered = selectedCategoryFilter !== null; | |
| return ( | |
| <div | |
| key={idx} | |
| onClick={() => setSelectedCategoryFilter(isFiltered ? null : cluster.key)} | |
| style={{ | |
| width: `${cluster.percentage}%`, | |
| height: '100%', | |
| background: cluster.color, | |
| cursor: 'pointer', | |
| opacity: isAnyFiltered && !isFiltered ? 0.35 : 1.0, | |
| transform: isFiltered ? 'scaleY(1.2)' : 'none', | |
| transition: 'all 200ms ease-in-out', | |
| }} | |
| title={`${cluster.category}: ${cluster.percentage}% (Click to Filter)`} | |
| /> | |
| ); | |
| })} | |
| </div> | |
| {/* Dynamic Prompt Analysis Interpretation abstraction layer */} | |
| <div style={{ fontSize: '12px', color: '#8A9BC4', background: '#12172955', border: '1px solid #1E2B4533', padding: '10px 12px', borderRadius: '6px', marginBottom: '16px', lineHeight: 1.5 }}> | |
| <span style={{ fontWeight: 600, color: '#00D9C0' }}>Prompt Analysis: </span> | |
| {(() => { | |
| const top = scanResult.feature_clusters[0]; | |
| const second = scanResult.feature_clusters[1]; | |
| let desc = `Activations are dominated by ${top.category} features (${top.percentage}%)`; | |
| if (second && second.percentage > 15) { | |
| desc += ` with a strong secondary trace of ${second.category} (${second.percentage}%)`; | |
| } else { | |
| desc += ` indicating highly uniform activation profiling`; | |
| } | |
| desc += `. Firing weights map dynamically on Layer 6 residual dictionary coordinates. Click a category to isolate individual latent neurons below.`; | |
| return desc; | |
| })()} | |
| </div> | |
| {/* Interactive Grid Details */} | |
| <div style={{ display: 'flex', flexWrap: 'wrap', gap: '8px', rowGap: '8px' }}> | |
| {scanResult.feature_clusters.map((cluster, idx) => { | |
| const isFiltered = selectedCategoryFilter === cluster.key; | |
| return ( | |
| <div | |
| key={idx} | |
| onClick={() => setSelectedCategoryFilter(isFiltered ? null : cluster.key)} | |
| style={{ | |
| display: 'flex', | |
| alignItems: 'center', | |
| gap: '8px', | |
| minWidth: '120px', | |
| cursor: 'pointer', | |
| padding: '4px 8px', | |
| borderRadius: '4px', | |
| background: isFiltered ? 'rgba(155, 89, 245, 0.1)' : 'transparent', | |
| border: `1px solid ${isFiltered ? 'rgba(155, 89, 245, 0.3)' : 'transparent'}`, | |
| transition: 'all 150ms ease' | |
| }} | |
| onMouseEnter={e => { | |
| if (!isFiltered) e.currentTarget.style.background = '#121729'; | |
| }} | |
| onMouseLeave={e => { | |
| if (!isFiltered) e.currentTarget.style.background = 'transparent'; | |
| }} | |
| > | |
| <div style={{ width: '8px', height: '8px', borderRadius: '50%', background: cluster.color }} /> | |
| <div> | |
| <div style={{ fontSize: '11px', color: isFiltered ? '#E8EEF8' : '#8A9BC4', fontWeight: isFiltered ? 600 : 500, lineHeight: 1.2 }}> | |
| {cluster.category} | |
| </div> | |
| <div style={{ fontSize: '12px', fontWeight: 700, color: '#E8EEF8', fontFamily: "'JetBrains Mono', monospace" }}>{cluster.percentage}%</div> | |
| </div> | |
| </div> | |
| ); | |
| })} | |
| </div> | |
| </div> | |
| )} | |
| {/* Firing Tokens Highlight */} | |
| <h4 style={{ fontFamily: "'Space Grotesk', sans-serif", fontWeight: 600, fontSize: 16, color: '#E8EEF8', marginBottom: 8 }}>Token Activation Profile</h4> | |
| <p style={{ fontSize: 12, color: '#4A5A7A', marginBottom: 12 }}> | |
| Numbers indicate active features firing at each position. | |
| </p> | |
| <div className="flex flex-wrap gap-2 p-4 bg-[#0C0F1A] rounded-lg border border-[#1E2B45] mb-8"> | |
| {scanResult.tokens.map((token, idx) => { | |
| const featuresFired = scanResult.active_features.filter(f => f.tokens_fired.includes(token)); | |
| return ( | |
| <span | |
| key={idx} | |
| className="px-2 py-1 bg-[#121729] rounded text-sm text-[#E8EEF8] border border-[#1E2B45] cursor-pointer hover:bg-[#9B59F515] hover:border-[#9B59F5] transition duration-150" | |
| title={featuresFired.length ? `Fired features: ${featuresFired.map(f => `#${f.index}`).join(', ')}` : 'No features fired'} | |
| > | |
| {token} | |
| {featuresFired.length > 0 && ( | |
| <span className="ml-1 text-[10px] text-[#00D9C0] font-mono">({featuresFired.length})</span> | |
| )} | |
| </span> | |
| ); | |
| })} | |
| </div> | |
| {/* Active Features Grid */} | |
| <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '12px' }}> | |
| <h4 style={{ fontFamily: "'Space Grotesk', sans-serif", fontWeight: 600, fontSize: 16, color: '#E8EEF8', margin: 0 }}>Active Latent Features Details</h4> | |
| {selectedCategoryFilter && ( | |
| <button | |
| onClick={() => setSelectedCategoryFilter(null)} | |
| style={{ | |
| background: 'rgba(255, 80, 99, 0.1)', | |
| border: '1px solid rgba(255, 80, 99, 0.3)', | |
| borderRadius: '4px', | |
| padding: '2px 8px', | |
| fontSize: '11px', | |
| color: '#FF5063', | |
| fontWeight: 600, | |
| cursor: 'pointer', | |
| transition: 'all 150ms ease' | |
| }} | |
| > | |
| Filtered to: {selectedCategoryFilter.toUpperCase()} ✕ | |
| </button> | |
| )} | |
| </div> | |
| {scanResult.active_features.filter(f => !selectedCategoryFilter || f.category === selectedCategoryFilter).length === 0 ? ( | |
| <p style={{ fontSize: 13, color: '#4A5A7A' }}>No active features detected matching this category filter.</p> | |
| ) : ( | |
| <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3"> | |
| {scanResult.active_features | |
| .filter(f => !selectedCategoryFilter || f.category === selectedCategoryFilter) | |
| .map(f => ( | |
| <div | |
| key={f.index} | |
| className="research-card flex flex-col justify-between" | |
| style={{ | |
| padding: '14px 16px', | |
| borderLeft: `3px solid ${CATEGORY_COLORS[f.category] || '#00D9C0'}`, | |
| boxShadow: selectedCategoryFilter ? `0 0 12px ${CATEGORY_COLORS[f.category]}15` : 'none', | |
| }} | |
| > | |
| <div> | |
| <div className="flex items-center justify-between mb-2"> | |
| <span style={{ fontSize: 12, fontWeight: 600, color: CATEGORY_COLORS[f.category], fontFamily: "'JetBrains Mono', monospace" }}> | |
| Feature #{f.index.toString().padStart(4, '0')} | |
| </span> | |
| <span className="badge-teal" style={{ fontSize: 10, padding: '1px 8px', background: `${CATEGORY_COLORS[f.category]}12`, color: CATEGORY_COLORS[f.category], borderColor: `${CATEGORY_COLORS[f.category]}30` }}> | |
| {f.category} | |
| </span> | |
| </div> | |
| <div style={{ fontSize: 14, fontWeight: 600, color: '#E8EEF8', marginBottom: 8 }}>{f.label}</div> | |
| <div className="flex flex-wrap gap-1 mb-3"> | |
| {f.tokens_fired.map((t, j) => ( | |
| <span key={j} style={{ fontSize: 11, background: '#121729', color: '#00D9C0', padding: '1px 6px', borderRadius: 4, border: '1px solid #1E2B45' }}> | |
| "{t.trim()}" | |
| </span> | |
| ))} | |
| </div> | |
| </div> | |
| {/* Visual Sparkline Canvas */} | |
| <div style={{ marginTop: 'auto', paddingTop: '10px', borderTop: '1px solid #1E2B4533', display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '8px' }}> | |
| <div style={{ fontSize: 11, color: '#4A5A7A' }}> | |
| Max Act: <span style={{ color: '#E8EEF8', fontWeight: 600 }}>{f.activation_value}</span> | |
| </div> | |
| {f.feature_activations && f.feature_activations.length > 0 && ( | |
| <div title="Per-token activation sparkline"> | |
| <Sparkline values={f.feature_activations} color={CATEGORY_COLORS[f.category] || '#00D9C0'} /> | |
| </div> | |
| )} | |
| </div> | |
| </div> | |
| ))} | |
| </div> | |
| )} | |
| </div> | |
| )} | |
| </div> | |
| )} | |
| {/* Interpretability Metrics */} | |
| <div className="grid grid-cols-1 md:grid-cols-3 gap-4 mt-8"> | |
| <div className="stat-card" style={{ borderTop: '2px solid #00E676' }}> | |
| <div style={{ fontFamily: "'Space Grotesk', sans-serif", fontWeight: 700, fontSize: 40, color: '#00E676' }}>70%</div> | |
| <div style={{ fontSize: 14, color: '#E8EEF8', fontWeight: 500, marginTop: 4 }}>features monosemantic</div> | |
| <p style={{ fontSize: 12, color: '#4A5A7A', marginTop: 4 }}>human raters, same as Anthropic's original result</p> | |
| </div> | |
| <div className="stat-card" style={{ borderTop: '2px solid #00D9C0' }}> | |
| <div style={{ fontFamily: "'Space Grotesk', sans-serif", fontWeight: 700, fontSize: 40, color: '#00D9C0' }}>93%</div> | |
| <div style={{ fontSize: 14, color: '#E8EEF8', fontWeight: 500, marginTop: 4 }}>features alive</div> | |
| <p style={{ fontSize: 12, color: '#4A5A7A', marginTop: 4 }}>5% ultra-low-density (Anthropic: similar rate)</p> | |
| </div> | |
| <div className="stat-card" style={{ borderTop: '2px solid #9B59F5' }}> | |
| <div style={{ fontFamily: "'Space Grotesk', sans-serif", fontWeight: 700, fontSize: 40, color: '#9B59F5' }}>0.12</div> | |
| <div style={{ fontSize: 14, color: '#E8EEF8', fontWeight: 500, marginTop: 4 }}>mean reconstruction loss</div> | |
| <p style={{ fontSize: 12, color: '#4A5A7A', marginTop: 4 }}>vs 0.18 baseline without sparsity</p> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| ); | |
| }; | |