@@ -3,17 +3,40 @@ import { useState } from "react";
33import { Card } from "../../components/layout/Card" ;
44import { formatCostSummary } from "../../format" ;
55import { cn } from "../../styles" ;
6- import type { MemoryExtractionDay } from "./memoryDashboard" ;
6+ import type { MemoryCostDay } from "./memoryDashboard" ;
77
88type MemoryRange = 7 | 30 | 90 ;
99
10- /** Render passive memory extraction cost from durable plugin events. */
11- export function MemoryExtractionCost ( props : { days : MemoryExtractionDay [ ] } ) {
10+ /** Render stacked memory extraction and recall cost from durable plugin events. */
11+ export function MemoryCostChart ( props : {
12+ extractionDays : MemoryCostDay [ ] ;
13+ recallDays : MemoryCostDay [ ] ;
14+ } ) {
1215 const [ range , setRange ] = useState < MemoryRange > ( 30 ) ;
13- const days = props . days . slice ( - range ) ;
14- const total = days . reduce ( ( sum , day ) => sum + day . costUsd , 0 ) ;
15- const runs = days . reduce ( ( sum , day ) => sum + day . events , 0 ) ;
16- const maximum = Math . max ( 0.01 , ...days . map ( ( day ) => day . costUsd ) ) ;
16+ const recallByDate = new Map ( props . recallDays . map ( ( day ) => [ day . date , day ] ) ) ;
17+ const days = props . extractionDays . slice ( - range ) . map ( ( extraction ) => ( {
18+ date : extraction . date ,
19+ extraction,
20+ recall : recallByDate . get ( extraction . date ) ?? {
21+ costUsd : 0 ,
22+ date : extraction . date ,
23+ events : 0 ,
24+ } ,
25+ } ) ) ;
26+ const extractionTotal = days . reduce (
27+ ( sum , day ) => sum + day . extraction . costUsd ,
28+ 0 ,
29+ ) ;
30+ const recallTotal = days . reduce ( ( sum , day ) => sum + day . recall . costUsd , 0 ) ;
31+ const total = extractionTotal + recallTotal ;
32+ const runs = days . reduce (
33+ ( sum , day ) => sum + day . extraction . events + day . recall . events ,
34+ 0 ,
35+ ) ;
36+ const maximum = Math . max (
37+ 0.01 ,
38+ ...days . map ( ( day ) => day . extraction . costUsd + day . recall . costUsd ) ,
39+ ) ;
1740 const width = 720 ;
1841 const height = 200 ;
1942 const left = 48 ;
@@ -29,17 +52,34 @@ export function MemoryExtractionCost(props: { days: MemoryExtractionDay[] }) {
2952 < div className = "flex flex-wrap items-start justify-between gap-4" >
3053 < div >
3154 < div className = "font-mono text-[0.6rem] uppercase tracking-[0.16em] text-cyan-200/65" >
32- Extraction cost
55+ Memory cost
3356 </ div >
3457 < h2 className = "mt-1 mb-0 font-display text-xl font-medium text-dashboard-text" >
3558 { formatCostSummary ( { total } ) }
3659 </ h2 >
3760 < p className = "mt-1 mb-0 font-mono text-[0.64rem] leading-relaxed text-dashboard-text-muted" >
38- System-wide estimate across { runs . toLocaleString ( ) } passive runs.
61+ System-wide estimate across { formatRunCount ( runs ) } spanning
62+ extraction and recall.
3963 </ p >
64+ < div className = "mt-2 flex flex-wrap gap-x-4 gap-y-1 font-mono text-[0.6rem] text-dashboard-text-muted" >
65+ < span className = "inline-flex items-center gap-1.5" >
66+ < span
67+ aria-hidden = "true"
68+ className = "h-2 w-2 rounded-[1px] bg-cyan-300"
69+ />
70+ Extraction { formatCostSummary ( { total : extractionTotal } ) }
71+ </ span >
72+ < span className = "inline-flex items-center gap-1.5" >
73+ < span
74+ aria-hidden = "true"
75+ className = "h-2 w-2 rounded-[1px] bg-fuchsia-400"
76+ />
77+ Recall { formatCostSummary ( { total : recallTotal } ) }
78+ </ span >
79+ </ div >
4080 </ div >
4181 < div
42- aria-label = "Memory extraction cost range"
82+ aria-label = "Memory cost range"
4383 className = "inline-flex rounded border border-white/[0.08] bg-black/20 p-0.5"
4484 >
4585 { ( [ 7 , 30 , 90 ] as const ) . map ( ( days ) => (
@@ -63,7 +103,7 @@ export function MemoryExtractionCost(props: { days: MemoryExtractionDay[] }) {
63103
64104 < div className = "relative mt-4 overflow-hidden" >
65105 < svg
66- aria-label = { `Memory extraction cost during the last ${ range } days` }
106+ aria-label = { `Memory extraction and recall cost during the last ${ range } days` }
67107 className = "block h-auto min-h-40 w-full"
68108 role = "img"
69109 viewBox = { `0 0 ${ width } ${ height } ` }
@@ -94,22 +134,39 @@ export function MemoryExtractionCost(props: { days: MemoryExtractionDay[] }) {
94134 ) ;
95135 } ) }
96136 { days . map ( ( day , index ) => {
97- const height = ( day . costUsd / maximum ) * plotHeight ;
137+ const extractionHeight =
138+ ( day . extraction . costUsd / maximum ) * plotHeight ;
139+ const recallHeight = ( day . recall . costUsd / maximum ) * plotHeight ;
98140 const x = left + index * step + ( step - barWidth ) / 2 ;
141+ const extractionLabel = `${ formatDate ( day . date ) } extraction: ${ formatCostSummary ( { total : day . extraction . costUsd } ) } , ${ formatRunCount ( day . extraction . events ) } ` ;
142+ const recallLabel = `${ formatDate ( day . date ) } recall: ${ formatCostSummary ( { total : day . recall . costUsd } ) } , ${ formatRunCount ( day . recall . events ) } ` ;
99143 return (
100- < rect
101- aria-label = { `${ formatDate ( day . date ) } : ${ formatCostSummary ( { total : day . costUsd } ) } , ${ day . events } runs` }
102- fill = "#67e8f9"
103- height = { height }
104- key = { day . date }
105- opacity = { day . costUsd > 0 ? 0.82 : 0.1 }
106- rx = "1"
107- width = { barWidth }
108- x = { x }
109- y = { top + plotHeight - height }
110- >
111- < title > { `${ formatDate ( day . date ) } : ${ formatCostSummary ( { total : day . costUsd } ) } , ${ day . events } runs` } </ title >
112- </ rect >
144+ < g key = { day . date } >
145+ < rect
146+ aria-label = { extractionLabel }
147+ fill = "#67e8f9"
148+ height = { extractionHeight }
149+ opacity = { day . extraction . costUsd > 0 ? 0.82 : 0.1 }
150+ rx = "1"
151+ width = { barWidth }
152+ x = { x }
153+ y = { top + plotHeight - extractionHeight }
154+ >
155+ < title > { extractionLabel } </ title >
156+ </ rect >
157+ < rect
158+ aria-label = { recallLabel }
159+ fill = "#e879f9"
160+ height = { recallHeight }
161+ opacity = { day . recall . costUsd > 0 ? 0.82 : 0.1 }
162+ rx = "1"
163+ width = { barWidth }
164+ x = { x }
165+ y = { top + plotHeight - extractionHeight - recallHeight }
166+ >
167+ < title > { recallLabel } </ title >
168+ </ rect >
169+ </ g >
113170 ) ;
114171 } ) }
115172 { [ 0 , Math . floor ( ( days . length - 1 ) / 2 ) , days . length - 1 ] . map (
@@ -146,14 +203,18 @@ export function MemoryExtractionCost(props: { days: MemoryExtractionDay[] }) {
146203 </ svg >
147204 { runs === 0 ? (
148205 < div className = "pointer-events-none absolute inset-0 grid place-items-center pt-12 font-mono text-[0.68rem] text-dashboard-text-muted" >
149- No passive extractions ran in this period.
206+ No memory extraction or recall ran in this period.
150207 </ div >
151208 ) : null }
152209 </ div >
153210 </ Card >
154211 ) ;
155212}
156213
214+ function formatRunCount ( count : number ) : string {
215+ return `${ count . toLocaleString ( ) } ${ count === 1 ? "run" : "runs" } ` ;
216+ }
217+
157218function formatDate ( date : string ) : string {
158219 return new Intl . DateTimeFormat ( "en-US" , {
159220 day : "numeric" ,
0 commit comments