/** * ContextMemoryDisplay Component * Visualize conversation context and summarization * OQI-007: LLM Strategy Agent */ import React, { useState, useMemo } from 'react'; import { CircleStackIcon, DocumentTextIcon, ClockIcon, ChevronDownIcon, ChevronUpIcon, TrashIcon, BookmarkIcon, ArrowDownTrayIcon, InformationCircleIcon, ExclamationTriangleIcon, } from '@heroicons/react/24/outline'; // ============================================================================ // Types // ============================================================================ export interface ContextMessage { id: string; role: 'user' | 'assistant'; preview: string; timestamp: string; tokenCount: number; isSummarized?: boolean; } export interface ContextSummary { id: string; createdAt: string; messageCount: number; content: string; tokenCount: number; } export interface ContextMemoryState { messages: ContextMessage[]; summaries: ContextSummary[]; maxContextTokens: number; currentTokens: number; lastCheckpoint?: string; } interface ContextMemoryDisplayProps { state: ContextMemoryState; onClearOldContext?: () => void; onSaveCheckpoint?: () => void; onExportConversation?: () => void; onRestoreCheckpoint?: (checkpointId: string) => void; compact?: boolean; } // ============================================================================ // Helper Functions // ============================================================================ const formatTimestamp = (timestamp: string) => { const date = new Date(timestamp); const now = new Date(); const diff = now.getTime() - date.getTime(); if (diff < 60000) return 'Just now'; if (diff < 3600000) return `${Math.floor(diff / 60000)}m ago`; if (diff < 86400000) return `${Math.floor(diff / 3600000)}h ago`; return date.toLocaleDateString(); }; const formatTokens = (tokens: number) => { if (tokens >= 1000000) return `${(tokens / 1000000).toFixed(1)}M`; if (tokens >= 1000) return `${(tokens / 1000).toFixed(1)}K`; return tokens.toString(); }; // ============================================================================ // Component // ============================================================================ export const ContextMemoryDisplay: React.FC = ({ state, onClearOldContext, onSaveCheckpoint, onExportConversation, onRestoreCheckpoint, compact = false, }) => { const [isExpanded, setIsExpanded] = useState(false); const [showSummaryContent, setShowSummaryContent] = useState(null); const [activeTab, setActiveTab] = useState<'messages' | 'summaries'>('messages'); const usagePercent = useMemo(() => { return Math.min((state.currentTokens / state.maxContextTokens) * 100, 100); }, [state.currentTokens, state.maxContextTokens]); const usageColor = useMemo(() => { if (usagePercent >= 90) return 'bg-red-500'; if (usagePercent >= 70) return 'bg-yellow-500'; return 'bg-emerald-500'; }, [usagePercent]); const recentMessages = state.messages.filter((m) => !m.isSummarized); const summarizedMessages = state.messages.filter((m) => m.isSummarized); if (compact) { return (
Context
{/* Progress Bar */}
{formatTokens(state.currentTokens)} tokens {usagePercent.toFixed(0)}%
{isExpanded && (
{recentMessages.length} messages in context {state.summaries.length > 0 && ` + ${state.summaries.length} summaries`}
{onClearOldContext && ( )} {onSaveCheckpoint && ( )}
)}
); } return (
{/* Header */}

Context Memory

{onExportConversation && ( )} {onSaveCheckpoint && ( )}
{/* Usage Gauge */}
Context Usage {formatTokens(state.currentTokens)} / {formatTokens(state.maxContextTokens)}
{usagePercent >= 80 && (
Context filling up. Old messages may be summarized.
)}
{/* Stats */}
{recentMessages.length}
Active Messages
{state.summaries.length}
Summaries
{summarizedMessages.length}
Summarized
{/* Tabs */}
{/* Content */}
{activeTab === 'messages' && (
{recentMessages.length === 0 ? (
No messages in context yet
) : ( recentMessages.map((message) => (
{message.role === 'user' ? 'U' : 'A'}

{message.preview}

{formatTimestamp(message.timestamp)} {message.tokenCount} tokens
)) )}
)} {activeTab === 'summaries' && (
{state.summaries.length === 0 ? (
No summaries created yet
) : ( state.summaries.map((summary) => (
{summary.messageCount} messages summarized
{formatTimestamp(summary.createdAt)} {summary.tokenCount} tokens
{showSummaryContent === summary.id && (
{summary.content}
)}
)) )}
)}
{/* Footer Actions */}
{onClearOldContext && ( )} {onSaveCheckpoint && ( )}
{/* Info */}

Recent messages are sent in full. Older messages are summarized to save tokens. The AI uses summaries to maintain conversation context.

); }; export default ContextMemoryDisplay;