pi-mega-compact 0.13.4 → 0.13.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (217) hide show
  1. package/README.md +11 -8
  2. package/dist/extensions/dashboard-server/api-contracts/endpoints/registry.js +50 -0
  3. package/dist/extensions/dashboard-server/api-contracts/rag-metrics.js +1 -0
  4. package/dist/extensions/dashboard-server/api-contracts/rag-settings.js +3 -3
  5. package/dist/extensions/dashboard-server/api-contracts/wiki.js +1 -0
  6. package/dist/extensions/dashboard-server/routes-rag-metrics.js +60 -0
  7. package/dist/extensions/dashboard-server/routes-rag-settings-helpers.js +167 -0
  8. package/dist/extensions/dashboard-server/routes-rag-settings.js +136 -77
  9. package/dist/extensions/dashboard-server/routes-wiki-helpers.js +97 -0
  10. package/dist/extensions/dashboard-server/routes-wiki.js +330 -0
  11. package/dist/extensions/dashboard-server/routes.js +2 -0
  12. package/dist/extensions/dashboard-server/server.js +17 -3
  13. package/dist/extensions/mega-events/context-handler/afterCompact.js +35 -2
  14. package/dist/extensions/mega-pipeline/recall.js +31 -0
  15. package/dist/extensions/mega-turn-store.js +2 -1
  16. package/dist/src/config/turns.js +10 -0
  17. package/dist/src/config.js +2 -0
  18. package/dist/src/recall/async.js +10 -1
  19. package/dist/src/recall/format.js +10 -1
  20. package/dist/src/recall/hydeTelemetry.js +22 -0
  21. package/dist/src/recall/sync.js +28 -2
  22. package/dist/src/store/turns/hydeStore.js +32 -0
  23. package/dist/src/store/turns/index.js +1 -0
  24. package/dist/src/store/turns/schema.js +102 -1
  25. package/dist/src/store/turns/sqlite-store/rows.js +23 -0
  26. package/dist/src/store/turns/sqlite-store/write.js +7 -3
  27. package/dist/src/topics/durability.js +187 -0
  28. package/dist/src/topics/index.js +2 -1
  29. package/dist/src/topics/store.js +28 -9
  30. package/dist/src/wiki/curation-helpers.js +71 -0
  31. package/dist/src/wiki/curation.js +147 -0
  32. package/dist/src/wiki/incremental.js +126 -0
  33. package/dist/src/wiki/index.js +5 -0
  34. package/extensions/dashboard-client/dist/assets/AreaChart-KAmp_6bL.js +70 -0
  35. package/extensions/dashboard-client/dist/assets/{SessionsTab-C30AwyoF.js.map → AreaChart-KAmp_6bL.js.map} +1 -1
  36. package/extensions/dashboard-client/dist/assets/CacheTab-Bk-gm-R0.js +2 -0
  37. package/extensions/dashboard-client/dist/assets/CacheTab-Bk-gm-R0.js.map +1 -0
  38. package/extensions/dashboard-client/dist/assets/EventsTab-Cyylo0of.js +2 -0
  39. package/extensions/dashboard-client/dist/assets/EventsTab-Cyylo0of.js.map +1 -0
  40. package/extensions/dashboard-client/dist/assets/HealthTab-D-IzmV29.js +2 -0
  41. package/extensions/dashboard-client/dist/assets/HealthTab-D-IzmV29.js.map +1 -0
  42. package/extensions/dashboard-client/dist/assets/MaintenanceTab-CrlkCFVj.js +2 -0
  43. package/extensions/dashboard-client/dist/assets/MaintenanceTab-CrlkCFVj.js.map +1 -0
  44. package/extensions/dashboard-client/dist/assets/MemoryMapTab-DHUdZ7RR.js +2 -0
  45. package/extensions/dashboard-client/dist/assets/MemoryMapTab-DHUdZ7RR.js.map +1 -0
  46. package/extensions/dashboard-client/dist/assets/MetricsTab-B7I4HWVg.js +2 -0
  47. package/extensions/dashboard-client/dist/assets/MetricsTab-B7I4HWVg.js.map +1 -0
  48. package/extensions/dashboard-client/dist/assets/OverviewTab-K6j4ZI7u.js +2 -0
  49. package/extensions/dashboard-client/dist/assets/OverviewTab-K6j4ZI7u.js.map +1 -0
  50. package/extensions/dashboard-client/dist/assets/ReposTab-BSRWhn56.js +2 -0
  51. package/extensions/dashboard-client/dist/assets/ReposTab-BSRWhn56.js.map +1 -0
  52. package/extensions/dashboard-client/dist/assets/SessionsTab-BXdizHN1.js +2 -0
  53. package/extensions/dashboard-client/dist/assets/SessionsTab-BXdizHN1.js.map +1 -0
  54. package/extensions/dashboard-client/dist/assets/SetupTab-CAgyfqY3.js +2 -0
  55. package/extensions/dashboard-client/dist/assets/SetupTab-CAgyfqY3.js.map +1 -0
  56. package/extensions/dashboard-client/dist/assets/TimeSavedCard-DwgCzPD9.js +2 -0
  57. package/extensions/dashboard-client/dist/assets/TimeSavedCard-DwgCzPD9.js.map +1 -0
  58. package/extensions/dashboard-client/dist/assets/TurnsTab-CVKcStBp.js +5 -0
  59. package/extensions/dashboard-client/dist/assets/TurnsTab-CVKcStBp.js.map +1 -0
  60. package/extensions/dashboard-client/dist/assets/WikiTab-BNohxTbs.js +2 -0
  61. package/extensions/dashboard-client/dist/assets/WikiTab-BNohxTbs.js.map +1 -0
  62. package/extensions/dashboard-client/dist/assets/button-B4A-_dCA.js +2 -0
  63. package/extensions/dashboard-client/dist/assets/button-B4A-_dCA.js.map +1 -0
  64. package/extensions/dashboard-client/dist/assets/card-Cjn7Ifoe.js +2 -0
  65. package/extensions/dashboard-client/dist/assets/card-Cjn7Ifoe.js.map +1 -0
  66. package/extensions/dashboard-client/dist/assets/index-CQjCLvv7.js +137 -0
  67. package/extensions/dashboard-client/dist/assets/index-CQjCLvv7.js.map +1 -0
  68. package/extensions/dashboard-client/dist/assets/index-CUA6EDRF.css +1 -0
  69. package/extensions/dashboard-client/dist/assets/switch-CcdXX0Zm.js +2 -0
  70. package/extensions/dashboard-client/dist/assets/switch-CcdXX0Zm.js.map +1 -0
  71. package/extensions/dashboard-client/dist/assets/toggle-KdBnFLu3.js +2 -0
  72. package/extensions/dashboard-client/dist/assets/toggle-KdBnFLu3.js.map +1 -0
  73. package/extensions/dashboard-client/dist/assets/{useSSE-p1pOI_-P.js → useSSE-B6t44vzc.js} +2 -2
  74. package/extensions/dashboard-client/dist/assets/{useSSE-p1pOI_-P.js.map → useSSE-B6t44vzc.js.map} +1 -1
  75. package/extensions/dashboard-client/dist/index.html +2 -2
  76. package/extensions/dashboard-client/package-lock.json +1995 -191
  77. package/extensions/dashboard-client/package.json +12 -1
  78. package/extensions/dashboard-client/postcss.config.js +6 -0
  79. package/extensions/dashboard-client/src/App.tsx +127 -88
  80. package/extensions/dashboard-client/src/api/client.ts +109 -10
  81. package/extensions/dashboard-client/src/components/ActiveReposTable.tsx +15 -7
  82. package/extensions/dashboard-client/src/components/ActiveSessionsTable.tsx +67 -55
  83. package/extensions/dashboard-client/src/components/CacheHitRateTrendCard.tsx +17 -13
  84. package/extensions/dashboard-client/src/components/CacheHitsCard.tsx +35 -30
  85. package/extensions/dashboard-client/src/components/CacheStatusPerModel.tsx +33 -28
  86. package/extensions/dashboard-client/src/components/EventCategoryFilter.tsx +5 -6
  87. package/extensions/dashboard-client/src/components/EventStream.tsx +17 -13
  88. package/extensions/dashboard-client/src/components/HydeDetailPanel.tsx +173 -0
  89. package/extensions/dashboard-client/src/components/LegendCard.tsx +16 -9
  90. package/extensions/dashboard-client/src/components/PerfCards.tsx +28 -20
  91. package/extensions/dashboard-client/src/components/PerfChart.tsx +9 -6
  92. package/extensions/dashboard-client/src/components/ProviderCacheCard.tsx +15 -7
  93. package/extensions/dashboard-client/src/components/RagDashboard.tsx +253 -0
  94. package/extensions/dashboard-client/src/components/RepoAllSessionsCard.tsx +15 -10
  95. package/extensions/dashboard-client/src/components/RepoContextStack.tsx +24 -16
  96. package/extensions/dashboard-client/src/components/RepoDetailModal.tsx +52 -49
  97. package/extensions/dashboard-client/src/components/RepoTable.tsx +26 -17
  98. package/extensions/dashboard-client/src/components/SessionContextGauges.tsx +11 -14
  99. package/extensions/dashboard-client/src/components/SessionInfo.tsx +20 -15
  100. package/extensions/dashboard-client/src/components/SessionsMemoryChart.tsx +8 -5
  101. package/extensions/dashboard-client/src/components/SummaryTiles.tsx +26 -17
  102. package/extensions/dashboard-client/src/components/TabBar.tsx +1 -3
  103. package/extensions/dashboard-client/src/components/layout/AppShell.tsx +42 -0
  104. package/extensions/dashboard-client/src/components/layout/BottomBar.tsx +88 -0
  105. package/extensions/dashboard-client/src/components/layout/Sidebar.tsx +83 -0
  106. package/extensions/dashboard-client/src/components/ui/badge.tsx +30 -0
  107. package/extensions/dashboard-client/src/components/ui/button.tsx +44 -0
  108. package/extensions/dashboard-client/src/components/ui/card.tsx +32 -0
  109. package/extensions/dashboard-client/src/components/ui/select.tsx +64 -0
  110. package/extensions/dashboard-client/src/components/ui/switch.tsx +26 -0
  111. package/extensions/dashboard-client/src/components/ui/tabs.tsx +36 -0
  112. package/extensions/dashboard-client/src/components/ui/toggle.tsx +28 -0
  113. package/extensions/dashboard-client/src/components/ui/tooltip.tsx +27 -0
  114. package/extensions/dashboard-client/src/config.ts +10 -0
  115. package/extensions/dashboard-client/src/main.tsx +2 -2
  116. package/extensions/dashboard-client/src/memory-map-layout.ts +1 -2
  117. package/extensions/dashboard-client/src/styles/index.css +79 -0
  118. package/extensions/dashboard-client/src/tabs/CacheTab.tsx +33 -26
  119. package/extensions/dashboard-client/src/tabs/EventsTab.tsx +22 -7
  120. package/extensions/dashboard-client/src/tabs/HealthTab.tsx +77 -46
  121. package/extensions/dashboard-client/src/tabs/MaintenanceTab/ActionsCard.tsx +60 -50
  122. package/extensions/dashboard-client/src/tabs/MaintenanceTab/DbStatsCard.tsx +61 -35
  123. package/extensions/dashboard-client/src/tabs/MaintenanceTab/DebugBundleCard.tsx +34 -23
  124. package/extensions/dashboard-client/src/tabs/MaintenanceTab/HealthMitigationCard.tsx +62 -43
  125. package/extensions/dashboard-client/src/tabs/MaintenanceTab/SchemaHealthCard.tsx +53 -32
  126. package/extensions/dashboard-client/src/tabs/MaintenanceTab.tsx +2 -2
  127. package/extensions/dashboard-client/src/tabs/MemoryMapTab/MemoryMapView.tsx +29 -46
  128. package/extensions/dashboard-client/src/tabs/MemoryMapTab/RaptorTreeView.tsx +10 -34
  129. package/extensions/dashboard-client/src/tabs/MemoryMapTab.tsx +14 -34
  130. package/extensions/dashboard-client/src/tabs/MetricsTab.tsx +5 -2
  131. package/extensions/dashboard-client/src/tabs/OverviewTab.tsx +11 -5
  132. package/extensions/dashboard-client/src/tabs/ReposTab.tsx +5 -5
  133. package/extensions/dashboard-client/src/tabs/SessionsTab.tsx +47 -33
  134. package/extensions/dashboard-client/src/tabs/SetupTab/EmbedderSetup.tsx +6 -2
  135. package/extensions/dashboard-client/src/tabs/SetupTab/RagHealthCard.tsx +78 -0
  136. package/extensions/dashboard-client/src/tabs/SetupTab/SettingsPanel.tsx +198 -0
  137. package/extensions/dashboard-client/src/tabs/SetupTab/SettingsSection.tsx +50 -0
  138. package/extensions/dashboard-client/src/tabs/SetupTab.tsx +21 -59
  139. package/extensions/dashboard-client/src/tabs/TopicsTab.tsx +75 -40
  140. package/extensions/dashboard-client/src/tabs/TurnsTab.tsx +150 -112
  141. package/extensions/dashboard-client/src/tabs/WikiTab/TopicEvolutionGraph.tsx +274 -0
  142. package/extensions/dashboard-client/src/tabs/WikiTab/TopicEvolutionView.tsx +71 -0
  143. package/extensions/dashboard-client/src/tabs/WikiTab/TopicTimeline.tsx +103 -0
  144. package/extensions/dashboard-client/src/tabs/WikiTab/WikiPage.tsx +213 -0
  145. package/extensions/dashboard-client/src/tabs/WikiTab/WikiPageControls.tsx +188 -0
  146. package/extensions/dashboard-client/src/tabs/WikiTab.tsx +222 -0
  147. package/extensions/dashboard-client/src/tabs/registry.ts +57 -0
  148. package/extensions/dashboard-client/src/utils/cn.ts +7 -0
  149. package/extensions/dashboard-client/tailwind.config.js +46 -0
  150. package/extensions/dashboard-server/api-contracts/core.ts +59 -0
  151. package/extensions/dashboard-server/api-contracts/endpoints/registry.ts +79 -0
  152. package/extensions/dashboard-server/api-contracts/index.ts +48 -2
  153. package/extensions/dashboard-server/api-contracts/rag-metrics.ts +22 -0
  154. package/extensions/dashboard-server/api-contracts/rag-settings.ts +30 -20
  155. package/extensions/dashboard-server/api-contracts/wiki.ts +138 -0
  156. package/extensions/dashboard-server/routes-rag-metrics.ts +82 -0
  157. package/extensions/dashboard-server/routes-rag-settings-helpers.ts +314 -0
  158. package/extensions/dashboard-server/routes-rag-settings.ts +147 -97
  159. package/extensions/dashboard-server/routes-wiki-helpers.ts +126 -0
  160. package/extensions/dashboard-server/routes-wiki.ts +376 -0
  161. package/extensions/dashboard-server/routes.ts +2 -0
  162. package/extensions/dashboard-server/server.ts +18 -2
  163. package/extensions/mega-events/context-handler/afterCompact.ts +44 -1
  164. package/extensions/mega-pipeline/recall.ts +31 -0
  165. package/extensions/mega-turn-store.ts +13 -0
  166. package/package.json +1 -1
  167. package/src/config/turns.ts +17 -0
  168. package/src/config.ts +3 -0
  169. package/src/recall/async.ts +10 -1
  170. package/src/recall/format.ts +15 -2
  171. package/src/recall/hydeTelemetry.ts +44 -0
  172. package/src/recall/sync.ts +34 -2
  173. package/src/recall/types.ts +36 -0
  174. package/src/store/turns/hydeStore.ts +82 -0
  175. package/src/store/turns/index.ts +4 -0
  176. package/src/store/turns/schema.ts +126 -1
  177. package/src/store/turns/sqlite-store/rows.ts +25 -0
  178. package/src/store/turns/sqlite-store/write.ts +20 -2
  179. package/src/store/turns/types.ts +33 -0
  180. package/src/topics/durability.ts +231 -0
  181. package/src/topics/index.ts +7 -1
  182. package/src/topics/store.ts +35 -9
  183. package/src/wiki/curation-helpers.ts +140 -0
  184. package/src/wiki/curation.ts +217 -0
  185. package/src/wiki/incremental.ts +164 -0
  186. package/src/wiki/index.ts +13 -0
  187. package/extensions/dashboard-client/dist/assets/CacheTab-Cqnc7lMg.js +0 -2
  188. package/extensions/dashboard-client/dist/assets/CacheTab-Cqnc7lMg.js.map +0 -1
  189. package/extensions/dashboard-client/dist/assets/EventsTab-tYk3VFAp.js +0 -2
  190. package/extensions/dashboard-client/dist/assets/EventsTab-tYk3VFAp.js.map +0 -1
  191. package/extensions/dashboard-client/dist/assets/HealthTab-D2_kjgxw.js +0 -2
  192. package/extensions/dashboard-client/dist/assets/HealthTab-D2_kjgxw.js.map +0 -1
  193. package/extensions/dashboard-client/dist/assets/MaintenanceTab-Bc-uPYrz.js +0 -2
  194. package/extensions/dashboard-client/dist/assets/MaintenanceTab-Bc-uPYrz.js.map +0 -1
  195. package/extensions/dashboard-client/dist/assets/MemoryMapTab-D1V9g_NC.js +0 -2
  196. package/extensions/dashboard-client/dist/assets/MemoryMapTab-D1V9g_NC.js.map +0 -1
  197. package/extensions/dashboard-client/dist/assets/MetricsTab-DT_XfL96.js +0 -2
  198. package/extensions/dashboard-client/dist/assets/MetricsTab-DT_XfL96.js.map +0 -1
  199. package/extensions/dashboard-client/dist/assets/OverviewTab-BY6KZCme.js +0 -2
  200. package/extensions/dashboard-client/dist/assets/OverviewTab-BY6KZCme.js.map +0 -1
  201. package/extensions/dashboard-client/dist/assets/ReposTab-CeS-tpDM.js +0 -2
  202. package/extensions/dashboard-client/dist/assets/ReposTab-CeS-tpDM.js.map +0 -1
  203. package/extensions/dashboard-client/dist/assets/SessionsTab-C30AwyoF.js +0 -70
  204. package/extensions/dashboard-client/dist/assets/SetupTab-BJUHOVK2.js +0 -2
  205. package/extensions/dashboard-client/dist/assets/SetupTab-BJUHOVK2.js.map +0 -1
  206. package/extensions/dashboard-client/dist/assets/TimeSavedCard-p55K1tFP.js +0 -2
  207. package/extensions/dashboard-client/dist/assets/TimeSavedCard-p55K1tFP.js.map +0 -1
  208. package/extensions/dashboard-client/dist/assets/TopicsTab-D8FqyAbK.js +0 -2
  209. package/extensions/dashboard-client/dist/assets/TopicsTab-D8FqyAbK.js.map +0 -1
  210. package/extensions/dashboard-client/dist/assets/TurnsTab-ju-nXDys.js +0 -2
  211. package/extensions/dashboard-client/dist/assets/TurnsTab-ju-nXDys.js.map +0 -1
  212. package/extensions/dashboard-client/dist/assets/index-BEj1Zahy.js +0 -42
  213. package/extensions/dashboard-client/dist/assets/index-BEj1Zahy.js.map +0 -1
  214. package/extensions/dashboard-client/dist/assets/index-CUNn22iV.css +0 -1
  215. package/extensions/dashboard-client/src/styles/metrics-extra.css +0 -69
  216. package/extensions/dashboard-client/src/styles/turns.css +0 -142
  217. package/extensions/dashboard-client/src/tabs/SetupTab/RagSettingsCard.tsx +0 -160
@@ -7,6 +7,7 @@
7
7
  */
8
8
 
9
9
  import type React from "react";
10
+ import { Card, CardContent, CardHeader, CardTitle } from "../components/ui/card";
10
11
 
11
12
  export interface CacheHitsCardProps {
12
13
  /** Cache hits in the current session. */
@@ -31,9 +32,9 @@ function StatRow({
31
32
  value: string | number;
32
33
  }): React.ReactElement {
33
34
  return (
34
- <div className="ov-stat-row">
35
- <span className="ov-stat-label">{label}</span>
36
- <span className="ov-stat-value">{value}</span>
35
+ <div className="flex items-baseline justify-between border-b border-border py-1 text-sm">
36
+ <span className="text-xs text-muted-foreground">{label}</span>
37
+ <span className="font-semibold">{value}</span>
37
38
  </div>
38
39
  );
39
40
  }
@@ -42,32 +43,36 @@ export function CacheHitsCard(
42
43
  props: CacheHitsCardProps,
43
44
  ): React.ReactElement {
44
45
  return (
45
- <div className="card cache-hits-card">
46
- <h3>💾 Cache Hits &amp; Compactions</h3>
47
- <StatRow
48
- label="Cache Hits (session)"
49
- value={props.cacheHitsSession.toLocaleString()}
50
- />
51
- <StatRow
52
- label="Cache Hits (total)"
53
- value={props.cacheHitsTotal.toLocaleString()}
54
- />
55
- <StatRow
56
- label="Tokens Saved (session)"
57
- value={props.tokensSavedSession.toLocaleString()}
58
- />
59
- <StatRow
60
- label="Tokens Saved (total)"
61
- value={props.tokensSavedTotal.toLocaleString()}
62
- />
63
- <StatRow
64
- label="Compactions (session)"
65
- value={props.compactionsSession.toLocaleString()}
66
- />
67
- <StatRow
68
- label="Compactions (total)"
69
- value={props.compactionsTotal.toLocaleString()}
70
- />
71
- </div>
46
+ <Card className="electric-hover">
47
+ <CardHeader>
48
+ <CardTitle>💾 Cache Hits &amp; Compactions</CardTitle>
49
+ </CardHeader>
50
+ <CardContent>
51
+ <StatRow
52
+ label="Cache Hits (session)"
53
+ value={props.cacheHitsSession.toLocaleString()}
54
+ />
55
+ <StatRow
56
+ label="Cache Hits (total)"
57
+ value={props.cacheHitsTotal.toLocaleString()}
58
+ />
59
+ <StatRow
60
+ label="Tokens Saved (session)"
61
+ value={props.tokensSavedSession.toLocaleString()}
62
+ />
63
+ <StatRow
64
+ label="Tokens Saved (total)"
65
+ value={props.tokensSavedTotal.toLocaleString()}
66
+ />
67
+ <StatRow
68
+ label="Compactions (session)"
69
+ value={props.compactionsSession.toLocaleString()}
70
+ />
71
+ <StatRow
72
+ label="Compactions (total)"
73
+ value={props.compactionsTotal.toLocaleString()}
74
+ />
75
+ </CardContent>
76
+ </Card>
72
77
  );
73
78
  }
@@ -6,6 +6,7 @@
6
6
  */
7
7
 
8
8
  import type React from "react";
9
+ import { Card, CardContent, CardHeader, CardTitle } from "../components/ui/card";
9
10
 
10
11
  export interface ModelCostCardProps {
11
12
  /** Model name. */
@@ -32,11 +33,11 @@ function StatRow({
32
33
  title?: string;
33
34
  }): React.ReactElement {
34
35
  return (
35
- <div className="ov-stat-row">
36
- <span className="ov-stat-label" title={title}>
36
+ <div className="flex items-baseline justify-between border-b border-border py-1 text-sm">
37
+ <span className="text-xs text-muted-foreground" title={title}>
37
38
  {label}
38
39
  </span>
39
- <span className="ov-stat-value">{value}</span>
40
+ <span className="font-semibold">{value}</span>
40
41
  </div>
41
42
  );
42
43
  }
@@ -58,30 +59,34 @@ export function CacheStatusPerModel(
58
59
  const windowsStr = `${windows} context-windows extended`;
59
60
 
60
61
  return (
61
- <div className="card model-cost-card">
62
- <h3>💰 Model &amp; Cost Savings</h3>
63
- <div className="ov-cost-usd">{usdStr}</div>
64
- <div className="ov-cost-sub">{windowsStr}</div>
65
- <StatRow
66
- label="Model"
67
- value={props.name}
68
- title="The model pi is currently using — its pricing drives the cost figure."
69
- />
70
- <StatRow
71
- label="Provider"
72
- value={props.provider}
73
- title="The provider serving the model."
74
- />
75
- <StatRow
76
- label="Input Rate"
77
- value={`$${props.inputRate.toFixed(6)}`}
78
- title="USD per input token, from the model's pricing."
79
- />
80
- <StatRow
81
- label="Output Rate"
82
- value={`$${props.outputRate.toFixed(6)}`}
83
- title="USD per output token, from the model's pricing."
84
- />
85
- </div>
62
+ <Card className="electric-hover">
63
+ <CardHeader>
64
+ <CardTitle>💰 Model &amp; Cost Savings</CardTitle>
65
+ </CardHeader>
66
+ <CardContent>
67
+ <div className="text-neon text-xl font-semibold">{usdStr}</div>
68
+ <div className="mb-2 text-xs text-muted-foreground">{windowsStr}</div>
69
+ <StatRow
70
+ label="Model"
71
+ value={props.name}
72
+ title="The model pi is currently using — its pricing drives the cost figure."
73
+ />
74
+ <StatRow
75
+ label="Provider"
76
+ value={props.provider}
77
+ title="The provider serving the model."
78
+ />
79
+ <StatRow
80
+ label="Input Rate"
81
+ value={`$${props.inputRate.toFixed(6)}`}
82
+ title="USD per input token, from the model's pricing."
83
+ />
84
+ <StatRow
85
+ label="Output Rate"
86
+ value={`$${props.outputRate.toFixed(6)}`}
87
+ title="USD per output token, from the model's pricing."
88
+ />
89
+ </CardContent>
90
+ </Card>
86
91
  );
87
92
  }
@@ -6,6 +6,7 @@
6
6
  */
7
7
 
8
8
  import type React from "react";
9
+ import { Toggle } from "../components/ui/toggle";
9
10
 
10
11
  export type EventCategory =
11
12
  | "all"
@@ -36,17 +37,15 @@ export function EventCategoryFilter({
36
37
  onFilter,
37
38
  }: EventCategoryFilterProps): React.ReactElement {
38
39
  return (
39
- <div className="event-category-filter" role="toolbar" aria-label="Event category filter">
40
+ <div className="flex flex-wrap gap-2" role="toolbar" aria-label="Event category filter">
40
41
  {CATEGORIES.map((cat) => (
41
- <button
42
+ <Toggle
42
43
  key={cat}
43
- type="button"
44
- className={`category-chip${active === cat ? " active" : ""}`}
44
+ pressed={active === cat}
45
45
  onClick={() => onFilter(cat)}
46
- aria-pressed={active === cat}
47
46
  >
48
47
  {cat}
49
- </button>
48
+ </Toggle>
50
49
  ))}
51
50
  </div>
52
51
  );
@@ -9,6 +9,7 @@
9
9
  import type React from "react";
10
10
  import { useState, useMemo } from "react";
11
11
  import type { SseEvent } from "@contracts";
12
+ import { Toggle } from "../components/ui/toggle";
12
13
 
13
14
  export interface EventStreamProps {
14
15
  events: SseEvent[];
@@ -90,6 +91,8 @@ function summarize(ev: SseEvent): string {
90
91
  case "game_mode_changed":
91
92
  case "game_render":
92
93
  return `stage=${ev.type}`;
94
+ default:
95
+ return `event=${ev.type}`;
93
96
  }
94
97
  }
95
98
 
@@ -114,37 +117,38 @@ export function EventStream({ events }: EventStreamProps): React.ReactElement {
114
117
  }, [events, filter]);
115
118
 
116
119
  return (
117
- <div className="event-stream">
118
- <div className="event-filters" role="toolbar">
120
+ <div className="flex flex-col gap-3">
121
+ <div className="flex flex-wrap items-center gap-2" role="toolbar">
119
122
  {FILTER_TYPES.map((t) => (
120
- <button
123
+ <Toggle
121
124
  key={t}
122
- type="button"
123
- className={`filter-chip ${filter === t ? "active" : ""}`}
125
+ pressed={filter === t}
124
126
  onClick={() => setFilter(t)}
125
127
  >
126
128
  {t === "all" ? "all" : t.replace(/_/g, " ")}
127
- </button>
129
+ </Toggle>
128
130
  ))}
129
- <span className="event-count">{events.length} buffered</span>
131
+ <span className="text-xs text-muted-foreground">{events.length} buffered</span>
130
132
  </div>
131
- <ul className="event-list">
133
+ <ul className="flex flex-col gap-1 font-mono text-xs">
132
134
  {filtered.length === 0 && (
133
- <li className="event-empty">No events yet.</li>
135
+ <li className="text-muted-foreground">No events yet.</li>
134
136
  )}
135
137
  {filtered.map((ev, idx) => {
136
138
  const cls = TYPE_COLORS[ev.type] ?? "ev-default";
137
139
  return (
138
140
  <li
139
141
  key={`${ev.ts}-${idx}`}
140
- className={`event-row ${cls}`}
142
+ className={`rounded-md px-2 py-1.5 transition-colors hover:bg-bg-elevated/40 ${cls}`}
141
143
  onClick={() => setExpanded(expanded === idx ? null : idx)}
142
144
  >
143
- <span className="ev-time">{formatTs(ev.ts)}</span>
144
- <span className="ev-type">{ev.type}</span>
145
+ <span className="ev-time">{formatTs(ev.ts)}</span>{" "}
146
+ <span className="ev-type">{ev.type}</span>{" "}
145
147
  <span className="ev-summary">{summarize(ev)}</span>
146
148
  {expanded === idx && (
147
- <pre className="ev-detail">{JSON.stringify(ev, null, 2)}</pre>
149
+ <pre className="mt-2 overflow-x-auto rounded-md bg-bg-elevated/60 p-2 text-xs">
150
+ {JSON.stringify(ev, null, 2)}
151
+ </pre>
148
152
  )}
149
153
  </li>
150
154
  );
@@ -0,0 +1,173 @@
1
+ /**
2
+ * dashboard-client/src/components/HydeDetailPanel.tsx — per-turn HyDE + recall
3
+ * quality detail (H3.1).
4
+ *
5
+ * Fetches /api/rag-metrics once and indexes telemetry rows by the composite
6
+ * `conversationId:turnIndex` key (the dashboard TurnRow has no DB id, so the
7
+ * pair is the stable identifier). Renders one of three HyDE states:
8
+ * - HyDE ran: hypothetical doc (collapsible), raw/hyde/fused hit counts,
9
+ * lift multiplier, generation latency.
10
+ * - HyDE skipped: reason (disabled / no-llm / generation-failed).
11
+ * - No telemetry: nothing (the parent only renders us when flagged).
12
+ * Plus a CRAG-style mini score line when recall metrics exist.
13
+ *
14
+ * PREVENT-PI-004: relative-path fetch to the same-origin dashboard server.
15
+ */
16
+
17
+ import type React from "react";
18
+ import { useMemo, useState } from "react";
19
+ import { useApi } from "../hooks/useApi";
20
+ import { fetchRagMetrics } from "../api/client";
21
+ import type { RagMetricsResponse } from "@contracts";
22
+ import { Card } from "./ui/card";
23
+ import { Badge } from "./ui/badge";
24
+
25
+ /** Per-turn telemetry row shape derived from the rag-metrics contract. */
26
+ type TelemetryRow = RagMetricsResponse["recent"][number];
27
+
28
+ export interface HydeDetailPanelProps {
29
+ conversationId: string;
30
+ turnIndex: number;
31
+ }
32
+
33
+ /** Composite key matching a telemetry row to a rendered turn. */
34
+ function rowKey(conversationId: string, turnIndex: number): string {
35
+ return `${conversationId}:${turnIndex}`;
36
+ }
37
+
38
+ /** First N lines of the hypothetical doc (or full when expanded). */
39
+ function truncateDoc(doc: string, maxLines: number): string {
40
+ const lines = doc.split("\n");
41
+ return lines.length > maxLines ? lines.slice(0, maxLines).join("\n") : doc;
42
+ }
43
+
44
+ /** CRAG-style mini score line when recall metrics were captured. */
45
+ function ScoreLine({ row }: { row: TelemetryRow }): React.ReactElement {
46
+ const hasScore = row.recallScore > 0 || row.recallPass === 1;
47
+ if (!hasScore) {
48
+ return (
49
+ <div className="mt-2 text-xs text-muted-foreground">
50
+ No recall-quality metrics for this turn.
51
+ </div>
52
+ );
53
+ }
54
+ return (
55
+ <div className="mt-2 rounded-md border border-border/60 bg-bg-elevated/40 p-2">
56
+ <div className="flex items-center gap-2 text-xs">
57
+ <span className="font-medium text-muted-foreground">Recall quality</span>
58
+ <Badge variant={row.recallPass === 1 ? "success" : "warning"}>
59
+ {row.recallPass === 1 ? "pass" : "fail"}
60
+ </Badge>
61
+ <span>
62
+ score <strong className="text-foreground">{row.recallScore.toFixed(2)}</strong>
63
+ </span>
64
+ </div>
65
+ <div className="mt-1 flex flex-wrap gap-x-4 gap-y-0.5 text-xs text-muted-foreground">
66
+ <span>relevance {row.recallRelevance.toFixed(2)}</span>
67
+ <span>coverage {row.recallCoverage.toFixed(2)}</span>
68
+ <span>diversity {row.recallDiversity.toFixed(2)}</span>
69
+ <span>specificity {row.recallSpecificity.toFixed(2)}</span>
70
+ </div>
71
+ </div>
72
+ );
73
+ }
74
+
75
+ export const HydeDetailPanel: React.FC<HydeDetailPanelProps> = ({
76
+ conversationId,
77
+ turnIndex,
78
+ }) => {
79
+ const { data: metrics } = useApi<RagMetricsResponse>(
80
+ useMemo(() => () => fetchRagMetrics(), []),
81
+ { pollInterval: 30_000 },
82
+ );
83
+ const [showFullDoc, setShowFullDoc] = useState(false);
84
+
85
+ // Rebuild the index whenever metrics arrive; rare enough that a full
86
+ // rebuild on each render is cheaper than memoizing with a mutable dep.
87
+ const row = useMemo(
88
+ () =>
89
+ metrics?.recent.find(
90
+ (t) => rowKey(t.conversationId, t.turnIndex) === rowKey(conversationId, turnIndex),
91
+ ) ?? null,
92
+ [metrics, conversationId, turnIndex],
93
+ );
94
+
95
+ if (!metrics) {
96
+ return <div className="text-xs text-muted-foreground">Loading HyDE stats…</div>;
97
+ }
98
+ if (!row) {
99
+ return (
100
+ <div className="text-xs text-muted-foreground">
101
+ No HyDE/recall telemetry recorded for this turn.
102
+ </div>
103
+ );
104
+ }
105
+
106
+ const doc = row.hydeDoc ?? "";
107
+ const docLines = doc.split("\n").length;
108
+ const showDoc = docLines > 3;
109
+ const visibleDoc = showFullDoc || !showDoc ? doc : truncateDoc(doc, 3);
110
+
111
+ return (
112
+ <Card className="border border-border/60 bg-bg-elevated/20 p-3">
113
+ {row.hydeRan === 1 ? (
114
+ <>
115
+ <div className="mb-2 flex flex-wrap items-center gap-2 text-xs">
116
+ <Badge variant="accent">HyDE ran</Badge>
117
+ <span className="text-muted-foreground">
118
+ gen <strong className="text-foreground">{row.hydeGenerationMs}ms</strong>
119
+ </span>
120
+ <span className="text-muted-foreground">
121
+ raw <strong className="text-foreground">{row.hydeRawCount}</strong>
122
+ <Arrow />
123
+ hyde <strong className="text-foreground">{row.hydeHydeCount}</strong>
124
+ <Arrow />
125
+ fused <strong className="text-foreground">{row.hydeFusedCount}</strong>
126
+ </span>
127
+ <span className="text-muted-foreground">
128
+ lift{" "}
129
+ <strong className="text-neon">{row.hydeLift.toFixed(2)}×</strong>
130
+ </span>
131
+ </div>
132
+ {doc && (
133
+ <pre className="max-h-40 overflow-auto whitespace-pre-wrap rounded-md border border-border/50 bg-bg-elevated/40 p-2 font-mono text-xs text-muted-foreground">
134
+ {visibleDoc}
135
+ </pre>
136
+ )}
137
+ {showDoc && (
138
+ <button
139
+ type="button"
140
+ className="mt-1 text-xs text-accent underline"
141
+ onClick={() => setShowFullDoc((v) => !v)}
142
+ >
143
+ {showFullDoc ? "Show less" : "Show full hypothetical doc"}
144
+ </button>
145
+ )}
146
+ </>
147
+ ) : (
148
+ <div className="flex items-center gap-2 text-xs">
149
+ <Badge variant="warning">HyDE skipped</Badge>
150
+ <span className="text-muted-foreground">{skipReason(row)}</span>
151
+ </div>
152
+ )}
153
+ <ScoreLine row={row} />
154
+ </Card>
155
+ );
156
+ };
157
+
158
+ function Arrow(): React.ReactElement {
159
+ return <span className="text-muted-foreground/60"> → </span>;
160
+ }
161
+
162
+ function skipReason(row: TelemetryRow): string {
163
+ switch (row.hydeReason) {
164
+ case "no-llm":
165
+ return "No LLM embedder available (requires Ollama/HTTP embedder).";
166
+ case "generation-failed":
167
+ return "Hypothetical doc generation failed this turn.";
168
+ case "disabled":
169
+ return "HyDE is disabled (MEGACOMPACT_HYDE_DISABLED).";
170
+ default:
171
+ return "HyDE was not invoked for this turn.";
172
+ }
173
+ }
@@ -6,6 +6,7 @@
6
6
  */
7
7
 
8
8
  import type React from "react";
9
+ import { Card, CardContent } from "../components/ui/card";
9
10
 
10
11
  const LEGEND_ITEMS: ReadonlyArray<React.ReactNode> = [
11
12
  <li key="1">
@@ -36,14 +37,20 @@ const LEGEND_ITEMS: ReadonlyArray<React.ReactNode> = [
36
37
 
37
38
  export function LegendCard(): React.ReactElement {
38
39
  return (
39
- <div className="card legend-card">
40
- <details>
41
- <summary>What these numbers mean</summary>
42
- <ol className="legend-list">{LEGEND_ITEMS}</ol>
43
- <p className="legend-note">
44
- Hover any label above for a quick explanation.
45
- </p>
46
- </details>
47
- </div>
40
+ <Card>
41
+ <CardContent>
42
+ <details>
43
+ <summary className="cursor-pointer text-sm font-semibold">
44
+ What these numbers mean
45
+ </summary>
46
+ <ol className="mt-2 list-decimal space-y-1 pl-5 text-sm">
47
+ {LEGEND_ITEMS}
48
+ </ol>
49
+ <p className="mt-2 text-xs text-muted-foreground">
50
+ Hover any label above for a quick explanation.
51
+ </p>
52
+ </details>
53
+ </CardContent>
54
+ </Card>
48
55
  );
49
56
  }
@@ -8,6 +8,12 @@
8
8
 
9
9
  import type React from "react";
10
10
  import type { PerfResponse } from "@contracts";
11
+ import {
12
+ Card,
13
+ CardContent,
14
+ CardHeader,
15
+ CardTitle,
16
+ } from "../components/ui/card";
11
17
 
12
18
  /** Format milliseconds (em-dash for null/undefined). */
13
19
  function fmtMs(v: number | null | undefined): string {
@@ -31,20 +37,22 @@ interface PerfCardsProps {
31
37
  /** A single stat row inside a perf card. */
32
38
  function Stat({ label, value }: { label: string; value: string }): React.ReactElement {
33
39
  return (
34
- <div className="perf-metric">
35
- <span className="perf-label">{label}</span>
36
- <span className="perf-value">{value}</span>
40
+ <div className="flex items-baseline justify-between border-b border-border py-1 text-sm">
41
+ <span className="text-xs text-muted-foreground">{label}</span>
42
+ <span className="font-semibold">{value}</span>
37
43
  </div>
38
44
  );
39
45
  }
40
46
 
41
47
  /** Card wrapper with a title. */
42
- function Card({ title, children }: { title: string; children: React.ReactNode }): React.ReactElement {
48
+ function PerfCard({ title, children }: { title: string; children: React.ReactNode }): React.ReactElement {
43
49
  return (
44
- <div className="perf-card">
45
- <h3 className="perf-card-title">{title}</h3>
46
- <div className="perf-card-body">{children}</div>
47
- </div>
50
+ <Card className="electric-hover">
51
+ <CardHeader>
52
+ <CardTitle>{title}</CardTitle>
53
+ </CardHeader>
54
+ <CardContent>{children}</CardContent>
55
+ </Card>
48
56
  );
49
57
  }
50
58
 
@@ -52,40 +60,40 @@ export function PerfCards({ perf }: PerfCardsProps): React.ReactElement {
52
60
  const cpuTxt = `${fmtNum(perf.cpu_user_ms.latest, 1)} / ${fmtNum(perf.cpu_sys_ms.latest, 1)} ms`;
53
61
 
54
62
  return (
55
- <div className="perf-cards-grid">
56
- <Card title="Model latency">
63
+ <div className="grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3">
64
+ <PerfCard title="Model latency">
57
65
  <Stat label="Turn p50" value={fmtMs(perf.turn_latency_ms.p50)} />
58
66
  <Stat label="Turn p95" value={fmtMs(perf.turn_latency_ms.p95)} />
59
67
  <Stat label="Provider p50" value={fmtMs(perf.provider_latency_ms.p50)} />
60
68
  <Stat label="Provider p95" value={fmtMs(perf.provider_latency_ms.p95)} />
61
- </Card>
69
+ </PerfCard>
62
70
 
63
- <Card title="Throughput">
71
+ <PerfCard title="Throughput">
64
72
  <Stat label="TPS (avg)" value={fmtNum(perf.tps.avg, 1)} />
65
73
  <Stat
66
74
  label="Cache hit %"
67
75
  value={`${fmtNum(perf.cache_hit_pct.avg, 1)}%`}
68
76
  />
69
- </Card>
77
+ </PerfCard>
70
78
 
71
- <Card title="Process">
79
+ <PerfCard title="Process">
72
80
  <Stat label="RSS" value={`${fmtNum(perf.rss_mb.latest, 1)} MB`} />
73
81
  <Stat label="Heap" value={`${fmtNum(perf.heap_mb.latest, 1)} MB`} />
74
82
  <Stat label="CPU user / sys" value={cpuTxt} />
75
- </Card>
83
+ </PerfCard>
76
84
 
77
- <Card title="Snapshot cost">
85
+ <PerfCard title="Snapshot cost">
78
86
  <Stat label="DB recompute p50" value={fmtMs(perf.db_recompute_ms.p50)} />
79
87
  <Stat label="DB recompute p95" value={fmtMs(perf.db_recompute_ms.p95)} />
80
88
  <Stat label="Disk write p50" value={fmtMs(perf.disk_write_ms.p50)} />
81
- </Card>
89
+ </PerfCard>
82
90
 
83
- <Card title="TUI lag proxy">
91
+ <PerfCard title="TUI lag proxy">
84
92
  <Stat label="Live-trim fires" value={fmtDiag(perf.diag?.liveTrimFires)} />
85
93
  <Stat label="Cache replays" value={fmtDiag(perf.diag?.liveTrimReplays)} />
86
94
  <Stat label="Fast-gate skips" value={fmtDiag(perf.diag?.ctxFastGate)} />
87
- <span className="perf-card-note">skip vs recompute vs replay cadence</span>
88
- </Card>
95
+ <span className="text-xs text-muted-foreground">skip vs recompute vs replay cadence</span>
96
+ </PerfCard>
89
97
  </div>
90
98
  );
91
99
  }
@@ -8,6 +8,7 @@
8
8
 
9
9
  import type React from "react";
10
10
  import type { PerfResponse } from "@contracts";
11
+ import { Card, CardContent, CardHeader, CardTitle } from "../components/ui/card";
11
12
 
12
13
  export interface PerfChartProps {
13
14
  perf: PerfResponse;
@@ -50,13 +51,14 @@ export function PerfChart({ perf }: PerfChartProps): React.ReactElement {
50
51
  const tpsRef = Math.max(perf.tps.avg, 1);
51
52
 
52
53
  return (
53
- <div className="perf-chart">
54
- <div className="perf-header">
55
- <h3>Performance</h3>
56
- <span className="perf-window">
54
+ <Card>
55
+ <CardHeader className="flex-row items-center justify-between space-y-0">
56
+ <CardTitle>Performance</CardTitle>
57
+ <span className="text-xs text-muted-foreground">
57
58
  {perf.windowMinutes}min window · {perf.sampleCount} samples
58
59
  </span>
59
- </div>
60
+ </CardHeader>
61
+ <CardContent>
60
62
  <div className="perf-grid">
61
63
  <div className="perf-metric">
62
64
  <span className="perf-label">Turn latency p50 / p95</span>
@@ -155,6 +157,7 @@ export function PerfChart({ perf }: PerfChartProps): React.ReactElement {
155
157
  </span>
156
158
  </div>
157
159
  )}
158
- </div>
160
+ </CardContent>
161
+ </Card>
159
162
  );
160
163
  }