import { useCallback, useEffect, useMemo, useState } from "react" import { MarkdownBlock } from "./MarkdownBlock" type Usage = { prompt_tokens?: number completion_tokens?: number total_tokens?: number } type BaseMetric = { id: number sourceType: string timestamp: string detailPath: string } type MemoryMetric = BaseMetric & { type: "memory" queryPreview?: string resultCount?: number } type PromptMetric = BaseMetric & { type: "prompt" messageCount?: number lastUserMessage?: string } type ResponseMetric = BaseMetric & { type: "response" promptMetricId?: number model?: string toolChoice?: string messageCount?: number lastUserMessage?: string responsePreview?: string toolCallCount?: number usage?: Usage } type UsageMetric = BaseMetric & { type: "usage" usage?: Usage } type SummarizationMetric = BaseMetric & { type: "summarization" method?: string before?: number after?: number savedTokens?: number summaryPreview?: string summaryChars?: number } type DiscordMetric = { id: string type: "discord" sourceType: "discord" timestamp: string detailPath: string messageId: string channelId: string guildId?: string authorUsername?: string contentPreview?: string isDm: boolean mentionsBot: boolean isFromBot: boolean } type MetricItem = MemoryMetric | PromptMetric | ResponseMetric | UsageMetric | SummarizationMetric type MetricsPage = { memories: MemoryMetric[] summarization: SummarizationMetric[] response: ResponseMetric[] prompt: PromptMetric[] usage: UsageMetric[] discord: DiscordMetric[] limit: number nextCursor: Record hasMore: Record } type MetricsPageInput = Partial type MemorySearchResult = { chunkId: number kind: string path: string source: string title: string headingPath: string | null preview: string } type MemoryDetail = { id: number type: "memory" timestamp: string query: string results: MemorySearchResult[] } type Message = { role?: string content?: unknown tool_call_id?: string tool_calls?: unknown } type PromptDetail = { id: number type: "prompt" | "prompt_response" timestamp: string messages?: Message[] response?: Message } type TurnDetail = { id: number timestamp: string model?: string usage?: Usage promptText: string responseText?: string toolTraces: ToolTrace[] } type DetailState = | { kind: "idle" } | { kind: "loading"; label: string } | { kind: "error"; text: string } | { kind: "memory"; memory: MemoryDetail; prompt?: PromptDetail } | { kind: "turn"; turn: TurnDetail } | { kind: "metric"; metric: unknown } type MemoryPair = { memory: MemoryMetric prompt?: PromptMetric | ResponseMetric secondsApart?: number overlap: number shared: string[] issue: "ok" | "loose" | "missing" } type ToolTrace = { id: string name: string args: string result?: string } const baseUrl = import.meta.env.VITE_NIRI_BASE_URL ?? "" const METRICS_POLL_INTERVAL_MS = 5_000 const stopWords = new Set([ "a", "an", "and", "are", "as", "at", "be", "but", "by", "for", "from", "have", "i", "in", "is", "it", "me", "my", "of", "on", "or", "that", "the", "this", "to", "was", "with", "you", "your", ]) const formatNumber = (value: number | undefined): string => typeof value === "number" && Number.isFinite(value) ? value.toLocaleString() : "0" const timeLabel = (iso: string): string => { const date = new Date(iso) if (Number.isNaN(date.getTime())) return iso return new Intl.DateTimeFormat(undefined, { month: "short", day: "2-digit", hour: "2-digit", minute: "2-digit", }).format(date) } const shortTime = (iso: string): string => { const date = new Date(iso) if (Number.isNaN(date.getTime())) return iso return new Intl.DateTimeFormat(undefined, { hour: "2-digit", minute: "2-digit" }).format(date) } const textContent = (content: unknown): string => { if (typeof content === "string") return content if (!Array.isArray(content)) return "" return content .flatMap((part) => { if (!part || typeof part !== "object") return [] const record = part as Record return typeof record.text === "string" ? [record.text] : [] }) .join("\n") } const toolResultMarkdown = (result: string): string => { const trimmed = result.trim() if (!trimmed) return "```text\n(no output)\n```" if (/^(```|#{1,6}\s|- |\* |\d+\. |> |\|)/m.test(trimmed)) return trimmed return `\`\`\`text\n${trimmed}\n\`\`\`` } const extractToolTraces = (prompt: PromptDetail | undefined): ToolTrace[] => { const messages = [...(prompt?.messages ?? []), ...(prompt?.response ? [prompt.response] : [])] const traces: ToolTrace[] = [] const byId = new Map() for (const message of messages) { if (Array.isArray(message.tool_calls)) { for (const rawCall of message.tool_calls) { if (!rawCall || typeof rawCall !== "object") continue const call = rawCall as Record const fn = call.function && typeof call.function === "object" ? (call.function as Record) : {} const id = typeof call.id === "string" ? call.id : `tool-${traces.length + 1}` const trace: ToolTrace = { id, name: typeof fn.name === "string" ? fn.name : "tool", args: typeof fn.arguments === "string" ? fn.arguments : "", } traces.push(trace) byId.set(id, trace) } } if (message.role === "tool") { const id = typeof message.tool_call_id === "string" ? message.tool_call_id : "" const result = textContent(message.content) const existing = byId.get(id) if (existing) { existing.result = result } else { traces.push({ id: id || `tool-result-${traces.length + 1}`, name: "tool result", args: "", result, }) } } } return traces } const lastUserMessage = (messages: Message[] | undefined): string => { if (!messages) return "" for (let i = messages.length - 1; i >= 0; i--) { const message = messages[i] if (message?.role === "user") { const text = textContent(message.content).trim() if (text) return text } } return "" } const tokens = (value: string | undefined): string[] => { if (!value) return [] return value .toLowerCase() .replace(/https?:\/\/\S+/g, " ") .replace(/[^a-z0-9\s'-]+/g, " ") .split(/\s+/) .map((token) => token.replace(/^['-]+|['-]+$/g, "")) .filter((token) => token.length > 2 && !stopWords.has(token)) } const overlapFor = (left: string | undefined, right: string | undefined): { score: number; shared: string[] } => { const leftTokens = new Set(tokens(left)) const rightTokens = new Set(tokens(right)) if (leftTokens.size === 0 || rightTokens.size === 0) return { score: 0, shared: [] } const shared = [...leftTokens].filter((token) => rightTokens.has(token)) return { score: shared.length / Math.max(1, Math.min(leftTokens.size, rightTokens.size)), shared: shared.slice(0, 8), } } const fetchJson = async (path: string, signal?: AbortSignal): Promise => { const res = await fetch(`${baseUrl}${path}`, { signal }) if (!res.ok) throw new Error(`${res.status} ${res.statusText}`.trim()) return (await res.json()) as T } const normalizeMetricsPage = (page: MetricsPageInput): MetricsPage => ({ memories: Array.isArray(page.memories) ? page.memories : [], summarization: Array.isArray(page.summarization) ? page.summarization : [], response: Array.isArray(page.response) ? page.response : [], prompt: Array.isArray(page.prompt) ? page.prompt : [], usage: Array.isArray(page.usage) ? page.usage : [], discord: Array.isArray(page.discord) ? page.discord : [], limit: typeof page.limit === "number" ? page.limit : 100, nextCursor: page.nextCursor ?? {}, hasMore: page.hasMore ?? {}, }) function buildMetricsUrl(search: string): string { const params = new URLSearchParams() params.set("limit", "100") if (search.trim()) params.set("q", search.trim()) return `/metrics?${params.toString()}` } function closestResponsePath(timestamp: string, responses: ResponseMetric[]): string | undefined { const usageTime = new Date(timestamp).getTime() if (!Number.isFinite(usageTime)) return undefined let best: ResponseMetric | undefined let bestDelta = Number.POSITIVE_INFINITY for (const r of responses) { const t = new Date(r.timestamp).getTime() if (!Number.isFinite(t) || t > usageTime) continue const delta = usageTime - t if (delta < bestDelta) { best = r bestDelta = delta } } return best?.detailPath } function TokenTrace({ usage, responses, onOpenTurn, latestPromptText, }: { usage: UsageMetric[] responses: ResponseMetric[] onOpenTurn: (path: string) => void latestPromptText?: string }) { const points = useMemo(() => { const usagePoints = usage.map((item) => ({ id: `u-${item.id}`, timestamp: item.timestamp, usage: item.usage, model: undefined as string | undefined, detailPath: closestResponsePath(item.timestamp, responses) ?? item.detailPath, })) const responsePoints = responses .filter((item) => item.usage) .map((item) => ({ id: `r-${item.id}`, timestamp: item.timestamp, usage: item.usage, model: item.model, detailPath: item.detailPath, })) return [...usagePoints, ...responsePoints] .sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()) .slice(-80) }, [responses, usage]) const maxTotal = Math.max(1, ...points.map((point) => point.usage?.total_tokens ?? 0)) const latest = points[points.length - 1] const totals = points.reduce( (sum, point) => ({ prompt: sum.prompt + (point.usage?.prompt_tokens ?? 0), completion: sum.completion + (point.usage?.completion_tokens ?? 0), total: sum.total + (point.usage?.total_tokens ?? 0), }), { prompt: 0, completion: 0, total: 0 }, ) const average = points.length ? Math.round(totals.total / points.length) : 0 return (

Token Usage

{points.length ? `${points.length} recent completions` : "No usage rows yet"}

{formatNumber(latest?.usage?.total_tokens)} latest total
{points.map((point) => { const prompt = point.usage?.prompt_tokens ?? 0 const completion = point.usage?.completion_tokens ?? 0 const total = point.usage?.total_tokens ?? prompt + completion return ( ) })}
{formatNumber(totals.prompt)} prompt
{formatNumber(totals.completion)} completion
{formatNumber(average)} avg total
{latestPromptText && (
latest prompt

{latestPromptText}

)}
) } function MemoryReview({ pairs, selectedId, reviewOnly, onReviewOnlyChange, onSelect, }: { pairs: MemoryPair[] selectedId?: number reviewOnly: boolean onReviewOnlyChange: (value: boolean) => void onSelect: (pair: MemoryPair) => void }) { const visible = reviewOnly ? pairs.filter((pair) => pair.issue !== "ok") : pairs return (

Memory Fit

{visible.length} recalls matched against nearby prompts

time fit memory query near prompt
{visible.map((pair) => ( ))}
) } function DetailPane({ detail }: { detail: DetailState }) { if (detail.kind === "idle") { return ( ) } if (detail.kind === "loading") { return ( ) } if (detail.kind === "error") { return ( ) } if (detail.kind === "turn") { const { turn } = detail return ( ) } if (detail.kind === "metric") { return ( ) } const promptText = lastUserMessage(detail.prompt?.messages) return ( ) } function BucketRail({ metrics, onOpenMetric, }: { metrics: MetricsPage onOpenMetric: (path: string) => void }) { const rows: Array<{ label: string; count: number; items: Array }> = [ { label: "response", count: metrics.response.length, items: metrics.response.slice(0, 6) }, { label: "summarization", count: metrics.summarization.length, items: metrics.summarization.slice(0, 6) }, { label: "discord", count: metrics.discord.length, items: metrics.discord.slice(0, 6) }, ] return (
{rows.map((bucket) => (

{bucket.label}

{bucket.count}
{bucket.items.map((item) => ( ))}
))}
) } export function MetricsWorkbench() { const [metrics, setMetrics] = useState(null) const [error, setError] = useState(null) const [loading, setLoading] = useState(true) const [search, setSearch] = useState("") const [query, setQuery] = useState("") const [reviewOnly, setReviewOnly] = useState(false) const [detail, setDetail] = useState({ kind: "idle" }) const [live, setLive] = useState(true) const [lastUpdated, setLastUpdated] = useState(null) const loadMetrics = useCallback((signal?: AbortSignal, options?: { silent?: boolean }) => { if (!options?.silent) setLoading(true) setError(null) fetchJson(buildMetricsUrl(query), signal) .then((page) => { setMetrics(normalizeMetricsPage(page)) setLastUpdated(new Date().toISOString()) }) .catch((err) => { if (signal?.aborted) return setError(err instanceof Error ? err.message : String(err)) }) .finally(() => { if (!signal?.aborted) setLoading(false) }) }, [query]) useEffect(() => { const controller = new AbortController() loadMetrics(controller.signal) let interval: ReturnType | undefined let pollController: AbortController | null = null if (live) { interval = setInterval(() => { pollController?.abort() pollController = new AbortController() loadMetrics(pollController.signal, { silent: true }) }, METRICS_POLL_INTERVAL_MS) } return () => { controller.abort() pollController?.abort() if (interval) clearInterval(interval) } }, [live, loadMetrics]) const pairs = useMemo(() => { if (!metrics) return [] const prompts = [...metrics.response, ...metrics.prompt] .filter((prompt) => prompt.lastUserMessage) .sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()) return metrics.memories.map((memory) => { const memoryTime = new Date(memory.timestamp).getTime() const prompt = prompts.find((item) => new Date(item.timestamp).getTime() >= memoryTime) ?? prompts[prompts.length - 1] const promptTime = prompt ? new Date(prompt.timestamp).getTime() : Number.NaN const secondsApart = prompt && Number.isFinite(promptTime) ? Math.round((promptTime - memoryTime) / 1000) : undefined const overlap = overlapFor(memory.queryPreview, prompt?.lastUserMessage) const issue = !prompt ? "missing" : overlap.score < 0.16 ? "loose" : "ok" return { memory, prompt, secondsApart, overlap: overlap.score, shared: overlap.shared, issue } }) }, [metrics]) const latestPromptText = useMemo(() => { return metrics?.response[0]?.lastUserMessage ?? undefined }, [metrics]) const selectedMemoryId = detail.kind === "memory" ? detail.memory.id : undefined const selectPair = useCallback(async (pair: MemoryPair) => { setDetail({ kind: "loading", label: `Recall #${pair.memory.id}` }) try { const [memory, prompt] = await Promise.all([ fetchJson(pair.memory.detailPath), pair.prompt ? fetchJson(pair.prompt.detailPath) : Promise.resolve(undefined), ]) setDetail({ kind: "memory", memory, prompt }) } catch (err) { setDetail({ kind: "error", text: err instanceof Error ? err.message : String(err) }) } }, []) const openMetric = useCallback(async (path: string) => { setDetail({ kind: "loading", label: "Turn detail" }) try { const raw = await fetchJson>(path) if (raw?.type === "prompt_response") { const msgs = Array.isArray(raw.messages) ? (raw.messages as Message[]) : [] const response = raw.response as Message | undefined const promptText = lastUserMessage(msgs) const responseText = textContent(response?.content) || undefined const fakeDetail: PromptDetail = { id: raw.id as number, type: "prompt_response", timestamp: raw.timestamp as string, messages: msgs, response, } setDetail({ kind: "turn", turn: { id: raw.id as number, timestamp: raw.timestamp as string, model: typeof raw.model === "string" ? raw.model : undefined, usage: raw.usage as Usage | undefined, promptText: promptText || "(no prompt)", responseText, toolTraces: extractToolTraces(fakeDetail), }, }) } else { setDetail({ kind: "metric", metric: raw }) } } catch (err) { setDetail({ kind: "error", text: err instanceof Error ? err.message : String(err) }) } }, []) return (

Metrics

Token pressure and memory retrieval checks.

{ event.preventDefault() setQuery(search.trim()) }} > setSearch(event.target.value)} placeholder="filter metrics" aria-label="filter metrics" />
{lastUpdated ? `updated ${shortTime(lastUpdated)}` : "not updated yet"}
{error ?

metrics unavailable: {error}

: null} {loading && !metrics ?

loading metrics

: null} {metrics ? (
) : null}
) }