| import { Gauge } from './Gauge' |
| import { MetricCard } from './MetricCard' |
| import { MemoryViz } from './MemoryViz' |
| import type { Metrics, MemorySamples } from '../types' |
|
|
| interface DashboardProps { |
| metrics: Metrics | null |
| memorySamples: MemorySamples | null |
| } |
|
|
| function formatBig(n: number): string { |
| if (n >= 1e9) return `${(n / 1e9).toFixed(1)}B` |
| if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M` |
| if (n >= 1e3) return `${(n / 1e3).toFixed(1)}K` |
| return n.toString() |
| } |
|
|
| function formatLog2Cap(log2: number): string { |
| const exponent = Math.floor(log2 / 3.32) |
| return `10^${exponent}` |
| } |
|
|
| export function Dashboard({ metrics, memorySamples }: DashboardProps) { |
| if (!metrics) { |
| return ( |
| <div className="flex items-center justify-center h-full"> |
| <div className="text-pal-muted text-sm animate-pulse">Loading metrics...</div> |
| </div> |
| ) |
| } |
|
|
| const { model, memory, conversation, config } = metrics |
| const capacityPct = Math.min((model.n_traces / Math.pow(2, model.theoretical_capacity_log2)) * 100, 100) |
|
|
| return ( |
| <div className="flex flex-col h-full overflow-y-auto gap-4 p-1"> |
| {/* Section: Model */} |
| <div> |
| <h3 className="text-xs font-semibold text-pal-muted uppercase tracking-wider mb-2 px-1"> |
| Model |
| </h3> |
| <div className="grid grid-cols-2 gap-2"> |
| <MetricCard |
| label="Dimensionality" |
| value={formatBig(model.D)} |
| sublabel="hypervector dim" |
| icon="📐" |
| accent="#7c3aed" |
| /> |
| <MetricCard |
| label="Memory Traces" |
| value={formatBig(model.n_traces)} |
| sublabel="append-only log" |
| icon="💾" |
| accent="#06b6d4" |
| /> |
| <MetricCard |
| label="Vocab Size" |
| value={model.vocab_size} |
| sublabel="tokens" |
| icon="🔤" |
| accent="#f59e0b" |
| /> |
| <MetricCard |
| label="Context Window" |
| value={model.context_window} |
| sublabel="tokens" |
| icon="🪟" |
| accent="#10b981" |
| /> |
| </div> |
| </div> |
| |
| {/* Section: Capacity gauges */} |
| <div> |
| <h3 className="text-xs font-semibold text-pal-muted uppercase tracking-wider mb-2 px-1"> |
| Capacity |
| </h3> |
| <div className="grid grid-cols-3 gap-2"> |
| <Gauge |
| value={model.n_traces} |
| max={Math.max(model.n_traces * 1.5, 100)} |
| label="Traces" |
| unit="stored" |
| color="#06b6d4" |
| icon="📊" |
| /> |
| <Gauge |
| value={Math.round(capacityPct * 100) / 100} |
| max={100} |
| label="Capacity Used" |
| unit="%" |
| color="#7c3aed" |
| icon="🔋" |
| /> |
| <Gauge |
| value={conversation.known_questions} |
| max={Math.max(conversation.known_questions * 1.5, 10)} |
| label="Known Q" |
| unit="pairs" |
| color="#10b981" |
| icon="❓" |
| /> |
| </div> |
| </div> |
| |
| {/* Section: Theoretical capacity */} |
| <div className="glass-card p-3"> |
| <div className="flex items-center justify-between"> |
| <div> |
| <div className="text-[11px] text-pal-muted uppercase tracking-wide font-medium"> |
| Theoretical Capacity |
| </div> |
| <div className="text-lg font-bold gradient-text"> |
| 2^{formatBig(model.theoretical_capacity_log2)} |
| </div> |
| </div> |
| <div className="text-right"> |
| <div className="text-[11px] text-pal-muted">≈ associations</div> |
| <div className="text-sm font-mono text-pal-accent2"> |
| {formatLog2Cap(model.theoretical_capacity_log2)} |
| </div> |
| </div> |
| </div> |
| <div className="mt-2 h-1 rounded-full bg-pal-border overflow-hidden"> |
| <div |
| className="h-full rounded-full bg-gradient-to-r from-pal-accent to-pal-accent2 transition-all duration-1000" |
| style={{ width: `${Math.min(capacityPct * 1000, 100)}%` }} |
| /> |
| </div> |
| <div className="text-[10px] text-pal-muted mt-1"> |
| {formatBig(model.n_traces)} used of ~{formatLog2Cap(model.theoretical_capacity_log2)} possible |
| </div> |
| </div> |
| |
| {/* Section: Conversation state */} |
| <div> |
| <h3 className="text-xs font-semibold text-pal-muted uppercase tracking-wider mb-2 px-1"> |
| Conversation |
| </h3> |
| <div className="grid grid-cols-2 gap-2"> |
| <MetricCard |
| label="Turns" |
| value={conversation.turns} |
| icon="💬" |
| accent="#06b6d4" |
| /> |
| <MetricCard |
| label="Corrections" |
| value={conversation.n_corrections} |
| icon="✏️" |
| accent="#f59e0b" |
| /> |
| </div> |
| </div> |
| |
| {/* Section: Memory weights */} |
| <div> |
| <h3 className="text-xs font-semibold text-pal-muted uppercase tracking-wider mb-2 px-1"> |
| Memory State |
| </h3> |
| <div className="grid grid-cols-3 gap-2"> |
| <MetricCard |
| label="Mean Wt" |
| value={memory.mean_weight.toFixed(3)} |
| accent="#7c3aed" |
| /> |
| <MetricCard |
| label="Min Wt" |
| value={memory.min_weight.toFixed(3)} |
| accent="#ef4444" |
| /> |
| <MetricCard |
| label="LSH Size" |
| value={formatBig(memory.lsh_size)} |
| accent="#10b981" |
| /> |
| </div> |
| </div> |
| |
| {/* Section: Config */} |
| <div> |
| <h3 className="text-xs font-semibold text-pal-muted uppercase tracking-wider mb-2 px-1"> |
| Configuration |
| </h3> |
| <div className="glass-card p-3 space-y-1.5"> |
| <ConfigRow label="Kernel Radius" value={config.kernel_radius} /> |
| <ConfigRow label="Min Weight" value={config.kernel_min_weight} /> |
| <ConfigRow label="Temperature" value={config.temperature} /> |
| <ConfigRow label="Model Type" value={config.model_type} /> |
| </div> |
| </div> |
| |
| {/* Section: Long Context (hierarchical) */} |
| {metrics.long_context && ( |
| <div> |
| <h3 className="text-xs font-semibold text-pal-muted uppercase tracking-wider mb-2 px-1 flex items-center gap-1.5"> |
| <span className="text-pal-accent2">🚀</span> Long Context |
| <span className="text-[10px] px-1.5 py-0.5 rounded-full bg-pal-accent2/15 text-pal-accent2 font-semibold"> |
| 1M TOKENS |
| </span> |
| </h3> |
| <div className="glass-card p-3 space-y-1.5 border-pal-accent2/20"> |
| <ConfigRow label="Chunks" value={metrics.long_context.n_chunks.toLocaleString()} /> |
| <ConfigRow label="Tokens Ingested" value={metrics.long_context.n_tokens.toLocaleString()} /> |
| <ConfigRow label="Local Window" value={`${metrics.long_context.n_local_tokens} / ${metrics.long_context.local_window}`} /> |
| <ConfigRow label="Chunk Size" value={metrics.long_context.chunk_size} /> |
| <ConfigRow label="Top-K Retrieved" value={metrics.long_context.top_k_chunks} /> |
| <ConfigRow label="Max Context" value={formatBig(metrics.long_context.max_context_tokens)} /> |
| <div className="pt-2 mt-1 border-t border-pal-border/50"> |
| <div className="flex items-center justify-between mb-1"> |
| <span className="text-[10px] text-pal-muted">Context Usage</span> |
| <span className="text-[10px] font-mono text-pal-accent2"> |
| {((metrics.long_context.n_tokens / metrics.long_context.max_context_tokens) * 100).toFixed(1)}% |
| </span> |
| </div> |
| <div className="h-1.5 rounded-full bg-pal-border overflow-hidden"> |
| <div |
| className="h-full rounded-full bg-gradient-to-r from-pal-accent to-pal-accent2 transition-all duration-700" |
| style={{ |
| width: `${Math.min((metrics.long_context.n_tokens / metrics.long_context.max_context_tokens) * 100, 100)}%`, |
| }} |
| /> |
| </div> |
| </div> |
| </div> |
| </div> |
| )} |
| |
| {/* Memory visualization */} |
| {memorySamples && memorySamples.traces.length > 0 && ( |
| <div> |
| <h3 className="text-xs font-semibold text-pal-muted uppercase tracking-wider mb-2 px-1"> |
| Memory Activity (last {memorySamples.traces.length}) |
| </h3> |
| <MemoryViz samples={memorySamples} /> |
| </div> |
| )} |
| </div> |
| ) |
| } |
|
|
| function ConfigRow({ label, value }: { label: string; value: string | number }) { |
| return ( |
| <div className="flex items-center justify-between"> |
| <span className="text-xs text-pal-muted">{label}</span> |
| <span className="text-xs font-mono text-pal-text">{value}</span> |
| </div> |
| ) |
| } |
|
|