"use client"; import { useState, useEffect, useCallback } from "react"; import { Button, EmptyState } from "@/shared/components"; import { useNotificationStore } from "@/store/notificationStore"; import { useTranslations } from "next-intl"; // ──────────────── Types ──────────────── interface ReasoningCacheEntry { toolCallId: string; provider: string; model: string; reasoning: string; charCount: number; createdAt: string; expiresAt: string; } interface ReasoningCacheStats { memoryEntries: number; dbEntries: number; totalEntries: number; totalChars: number; hits: number; misses: number; replays: number; replayRate: string; byProvider: Record; byModel: Record; oldestEntry: string | null; newestEntry: string | null; } interface ReasoningCacheData { stats: ReasoningCacheStats; entries: ReasoningCacheEntry[]; } // ──────────────── Helpers ──────────────── function formatChars(chars: number): string { if (chars >= 1_000_000) return `${(chars / 1_000_000).toFixed(1)}M`; if (chars >= 1_000) return `${(chars / 1_000).toFixed(1)}K`; return String(chars); } // ──────────────── Sub-Components ──────────────── function StatCard({ icon, label, value, sub, accent = "text-text-main", }: { icon: string; label: string; value: string | number; sub?: string; accent?: string; }) { return (
{label}
{value}
{sub &&
{sub}
}
); } function SectionBadge({ icon, children, tone = "neutral", }: { icon: string; children: React.ReactNode; tone?: "neutral" | "green" | "amber" | "blue"; }) { const toneClass = tone === "green" ? "border-green-500/20 bg-green-500/10 text-green-300" : tone === "amber" ? "border-amber-400/20 bg-amber-400/10 text-amber-300" : tone === "blue" ? "border-blue-400/20 bg-blue-400/10 text-blue-300" : "border-border/40 bg-surface/50 text-text-muted"; return ( {children} ); } function InfoRow({ icon, children }: { icon: string; children: React.ReactNode }) { return (
{children}
); } // ──────────────── Main Component ──────────────── const REFRESH_INTERVAL_MS = 10_000; export default function ReasoningCacheTab() { const t = useTranslations("cache"); const notify = useNotificationStore(); const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [clearing, setClearing] = useState(false); const [expandedId, setExpandedId] = useState(null); const timeAgo = (dateStr: string): string => { const diff = Date.now() - new Date(dateStr).getTime(); const minutes = Math.floor(diff / 60000); if (minutes < 1) return t("justNow"); if (minutes < 60) return t("minutesAgo", { minutes }); const hours = Math.floor(minutes / 60); if (hours < 24) return t("hoursAgo", { hours }); const days = Math.floor(hours / 24); return t("daysAgo", { days }); }; const fetchData = useCallback(async () => { try { const res = await fetch("/api/cache/reasoning"); if (res.ok) { const json: ReasoningCacheData = await res.json(); setData(json); } } catch (error) { console.error("[ReasoningCacheTab] Failed to fetch:", error); } finally { setLoading(false); } }, []); useEffect(() => { void fetchData(); const id = setInterval(() => void fetchData(), REFRESH_INTERVAL_MS); return () => clearInterval(id); }, [fetchData]); const handleClear = async () => { setClearing(true); try { const res = await fetch("/api/cache/reasoning", { method: "DELETE" }); if (res.ok) { const result = await res.json(); notify.success(t("reasoningClearSuccess", { count: result.cleared ?? 0 })); await fetchData(); } else { notify.error(t("reasoningClearError")); } } catch { notify.error(t("reasoningClearError")); } finally { setClearing(false); } }; if (loading) { return (
); } if (!data) { return ( void fetchData()} /> ); } const { stats, entries } = data; const providerEntries = Object.entries(stats.byProvider).sort( ([, a], [, b]) => b.entries - a.entries ); const modelEntries = Object.entries(stats.byModel).sort(([, a], [, b]) => b.entries - a.entries); const totalLookups = stats.hits + stats.misses; return (
{/* Header */}
{t("reasoningCache")}

{t("reasoningCache")}

{t("reasoningCacheDesc")}

{/* Stat Cards */}
{/* By Provider */} {providerEntries.length > 0 && (

{t("reasoningByProvider")}

{providerEntries.map(([prov, d]) => { const share = stats.totalEntries > 0 ? ((d.entries / stats.totalEntries) * 100).toFixed(1) : "0.0"; return ( ); })}
{t("tableProvider")} {t("reasoningEntries")} {t("reasoningChars")} {t("tableShare")}
{prov} {d.entries.toLocaleString()} {formatChars(d.chars)}
{share}%
)} {/* By Model */} {modelEntries.length > 0 && (

{t("reasoningByModel")}

{modelEntries.map(([mdl, d]) => { const avgChars = d.entries > 0 ? Math.round(d.chars / d.entries) : 0; return ( ); })}
{t("tableModel")} {t("reasoningEntries")} {t("reasoningAvgChars")} {t("reasoningChars")}
{mdl} {d.entries.toLocaleString()} {avgChars.toLocaleString()} {formatChars(d.chars)}
)} {/* Recent Entries */}

{t("reasoningRecentEntries")}

{t("reasoningCacheDesc")}

{entries.length === 0 ? (
{t("reasoningNoData")}
) : (
{t("reasoningToolCallId")} {t("tableProvider")} {t("tableModel")} {t("reasoningChars")} {t("reasoningAge")}
{entries.map((entry) => (
{entry.toolCallId}
{entry.provider}
{entry.model}
{entry.charCount.toLocaleString()}
{timeAgo(entry.createdAt)}
{/* Expanded Detail */} {expandedId === entry.toolCallId && (
psychology {t("reasoningDetail")} ({entry.toolCallId})
                        {entry.reasoning}
                      
{t("tableProvider")}:{" "} {entry.provider} {t("tableModel")}: {entry.model} {t("created")}:{" "} {new Date(entry.createdAt).toLocaleString()} {t("expires")}:{" "} {new Date(entry.expiresAt).toLocaleString()} {t("reasoningChars")}:{" "} {entry.charCount.toLocaleString()}
)}
))}
)}
{/* Behavior Info */}

{t("reasoningBehavior")}

{t("reasoningBehaviorCapture")} {t("reasoningBehaviorReplay")} {t("reasoningBehaviorFallback")} {t("reasoningBehaviorTtl")} {t("reasoningBehaviorModels")}
); }