"use client"; import { useMemo } from "react"; import { motion, AnimatePresence } from "framer-motion"; import { useSteppedVisualization } from "@/hooks/useSteppedVisualization"; import { StepControls } from "@/components/visualizations/shared/step-controls"; type BlockType = "user" | "assistant" | "tool_result"; interface ContextBlock { id: string; type: BlockType; label: string; tokens: number; } const BLOCK_COLORS: Record = { user: "bg-blue-500", assistant: "bg-zinc-500 dark:bg-zinc-600", tool_result: "bg-emerald-500", }; const BLOCK_LABELS: Record = { user: "USR", assistant: "AST", tool_result: "TRL", }; function generateBlocks(count: number, seed: number): ContextBlock[] { const types: BlockType[] = ["user", "assistant", "tool_result"]; const blocks: ContextBlock[] = []; for (let i = 0; i < count; i++) { const typeIndex = (i + seed) % 3; const type = types[typeIndex]; const tokens = type === "tool_result" ? 4000 + (i % 3) * 1000 : 1500 + (i % 4) * 500; blocks.push({ id: `b-${seed}-${i}`, type, label: `${BLOCK_LABELS[type]} ${i + 1}`, tokens, }); } return blocks; } const MAX_TOKENS = 100000; const WINDOW_HEIGHT = 350; interface StepState { blocks: { id: string; type: BlockType; label: string; heightPx: number; compressed?: boolean }[]; tokenCount: number; fillPercent: number; compressionLabel: string | null; } function computeStepState(step: number): StepState { switch (step) { case 0: { const raw = generateBlocks(8, 0); const tokenCount = 30000; const totalRawTokens = raw.reduce((a, b) => a + b.tokens, 0); const blocks = raw.map((b) => ({ ...b, heightPx: Math.max(16, (b.tokens / totalRawTokens) * WINDOW_HEIGHT * 0.3), })); return { blocks, tokenCount, fillPercent: 30, compressionLabel: null }; } case 1: { const raw = generateBlocks(16, 0); const tokenCount = 60000; const totalRawTokens = raw.reduce((a, b) => a + b.tokens, 0); const blocks = raw.map((b) => ({ ...b, heightPx: Math.max(12, (b.tokens / totalRawTokens) * WINDOW_HEIGHT * 0.6), })); return { blocks, tokenCount, fillPercent: 60, compressionLabel: null }; } case 2: { const raw = generateBlocks(20, 0); const tokenCount = 80000; const totalRawTokens = raw.reduce((a, b) => a + b.tokens, 0); const blocks = raw.map((b) => ({ ...b, heightPx: Math.max(10, (b.tokens / totalRawTokens) * WINDOW_HEIGHT * 0.8), })); return { blocks, tokenCount, fillPercent: 80, compressionLabel: null }; } case 3: { const raw = generateBlocks(20, 0); const tokenCount = 60000; const totalRawTokens = raw.reduce((a, b) => a + b.tokens, 0); const blocks = raw.map((b) => ({ ...b, heightPx: b.type === "tool_result" ? 6 : Math.max(12, (b.tokens / totalRawTokens) * WINDOW_HEIGHT * 0.6), compressed: b.type === "tool_result", })); return { blocks, tokenCount, fillPercent: 60, compressionLabel: "MICRO-COMPACT", }; } case 4: { const raw = generateBlocks(24, 1); const tokenCount = 85000; const totalRawTokens = raw.reduce((a, b) => a + b.tokens, 0); const blocks = raw.map((b) => ({ ...b, heightPx: Math.max(10, (b.tokens / totalRawTokens) * WINDOW_HEIGHT * 0.85), })); return { blocks, tokenCount, fillPercent: 85, compressionLabel: null }; } case 5: { const tokenCount = 25000; const summaryBlock = { id: "auto-summary", type: "assistant" as BlockType, label: "SUMMARY", heightPx: 40, compressed: false, }; const recentBlocks = generateBlocks(4, 2).map((b) => ({ ...b, heightPx: 20, })); return { blocks: [summaryBlock, ...recentBlocks], tokenCount, fillPercent: 25, compressionLabel: "AUTO-COMPACT", }; } case 6: { const tokenCount = 8000; const compactBlock = { id: "compact-summary", type: "assistant" as BlockType, label: "COMPACT SUMMARY", heightPx: 24, compressed: false, }; return { blocks: [compactBlock], tokenCount, fillPercent: 8, compressionLabel: "/compact", }; } default: return { blocks: [], tokenCount: 0, fillPercent: 0, compressionLabel: null }; } } const STEPS = [ { title: "Growing Context", description: "The context window holds the conversation. Each API call adds more messages.", }, { title: "Context Growing", description: "As the agent works, messages accumulate. The context window fills up.", }, { title: "Approaching Limit", description: "Old tool_results are the biggest consumers. Micro-compact targets these first.", }, { title: "Stage 1: Micro-Compact", description: "Replace old tool_results with short summaries. Automatic, transparent to the model.", }, { title: "Still Growing", description: "Work continues. Context grows again toward the threshold...", }, { title: "Stage 2: Auto-Compact", description: "Entire conversation summarized into a compact block. Triggered at token threshold.", }, { title: "Stage 3: /compact", description: "User-triggered, most aggressive. Three layers of strategic forgetting enable infinite sessions.", }, ]; const COMPRESSION_LAYERS = [ { label: "Micro", full: "MICRO-COMPACT", trigger: "old tool_result", action: "shrink bulky outputs", step: 3, classes: "border-amber-200 bg-amber-50 text-amber-800 dark:border-amber-900 dark:bg-amber-950/30 dark:text-amber-200", }, { label: "Auto", full: "AUTO-COMPACT", trigger: "token threshold", action: "summarize the conversation", step: 5, classes: "border-blue-200 bg-blue-50 text-blue-800 dark:border-blue-900 dark:bg-blue-950/30 dark:text-blue-200", }, { label: "Manual", full: "/compact", trigger: "user command", action: "keep one compact summary", step: 6, classes: "border-emerald-200 bg-emerald-50 text-emerald-800 dark:border-emerald-900 dark:bg-emerald-950/30 dark:text-emerald-200", }, ]; export default function ContextCompact({ title }: { title?: string }) { const { currentStep, totalSteps, next, prev, reset, isPlaying, toggleAutoPlay, } = useSteppedVisualization({ totalSteps: STEPS.length, autoPlayInterval: 2500 }); const state = useMemo(() => computeStepState(currentStep), [currentStep]); const fillColor = state.fillPercent > 75 ? "bg-red-500" : state.fillPercent > 45 ? "bg-amber-500" : "bg-emerald-500"; const tokenDisplay = `${(state.tokenCount / 1000).toFixed(0)}K`; return (

{title || "Three-Layer Context Compression"}

{/* Token Window (tall vertical bar on the left) */}
Context Window
{/* Blocks stacked from bottom up */}
{state.blocks.map((block) => ( {block.heightPx >= 14 && ( {block.label} )} ))}
{/* Fill level line */} {state.fillPercent}%
{/* Token count */} {tokenDisplay}
/ 100K
{/* Right side: state display and compression stage */}
{/* Top: horizontal token bar */}
Token usage {state.tokenCount.toLocaleString()} / {MAX_TOKENS.toLocaleString()}
{/* Message type legend */}
user
assistant
tool_result
{COMPRESSION_LAYERS.map((layer) => { const reached = currentStep >= layer.step; const active = state.compressionLabel === layer.full; return (
{layer.label} {reached ? "used" : "waiting"}
{layer.trigger}
{layer.action}
); })}
{/* Highlight old tool_results at step 2 */} {currentStep === 2 && (
tool_results are the largest blocks
File contents, command outputs, search results -- each one is thousands of tokens.
)}
{/* Compression stage label */} {state.compressionLabel && (
{state.compressionLabel}
{currentStep === 3 && "Old tool_results shrunk to tiny summaries"} {currentStep === 5 && "Full conversation compressed to summary block"} {currentStep === 6 && "Most aggressive compression -- near-empty context"}
)}
{/* Three stages overview on final step */} {currentStep === 6 && ( {COMPRESSION_LAYERS.map((layer, index) => (
Stage {index + 1}: {layer.label} -- {layer.action} {layer.trigger}
))}
)}
{/* Step Controls */}
); }