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.
- package/README.md +11 -8
- package/dist/extensions/dashboard-server/api-contracts/endpoints/registry.js +50 -0
- package/dist/extensions/dashboard-server/api-contracts/rag-metrics.js +1 -0
- package/dist/extensions/dashboard-server/api-contracts/rag-settings.js +3 -3
- package/dist/extensions/dashboard-server/api-contracts/wiki.js +1 -0
- package/dist/extensions/dashboard-server/routes-rag-metrics.js +60 -0
- package/dist/extensions/dashboard-server/routes-rag-settings-helpers.js +167 -0
- package/dist/extensions/dashboard-server/routes-rag-settings.js +136 -77
- package/dist/extensions/dashboard-server/routes-wiki-helpers.js +97 -0
- package/dist/extensions/dashboard-server/routes-wiki.js +330 -0
- package/dist/extensions/dashboard-server/routes.js +2 -0
- package/dist/extensions/dashboard-server/server.js +17 -3
- package/dist/extensions/mega-events/context-handler/afterCompact.js +35 -2
- package/dist/extensions/mega-pipeline/recall.js +31 -0
- package/dist/extensions/mega-turn-store.js +2 -1
- package/dist/src/config/turns.js +10 -0
- package/dist/src/config.js +2 -0
- package/dist/src/recall/async.js +10 -1
- package/dist/src/recall/format.js +10 -1
- package/dist/src/recall/hydeTelemetry.js +22 -0
- package/dist/src/recall/sync.js +28 -2
- package/dist/src/store/turns/hydeStore.js +32 -0
- package/dist/src/store/turns/index.js +1 -0
- package/dist/src/store/turns/schema.js +102 -1
- package/dist/src/store/turns/sqlite-store/rows.js +23 -0
- package/dist/src/store/turns/sqlite-store/write.js +7 -3
- package/dist/src/topics/durability.js +187 -0
- package/dist/src/topics/index.js +2 -1
- package/dist/src/topics/store.js +28 -9
- package/dist/src/wiki/curation-helpers.js +71 -0
- package/dist/src/wiki/curation.js +147 -0
- package/dist/src/wiki/incremental.js +126 -0
- package/dist/src/wiki/index.js +5 -0
- package/extensions/dashboard-client/dist/assets/AreaChart-KAmp_6bL.js +70 -0
- package/extensions/dashboard-client/dist/assets/{SessionsTab-C30AwyoF.js.map → AreaChart-KAmp_6bL.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/CacheTab-Bk-gm-R0.js +2 -0
- package/extensions/dashboard-client/dist/assets/CacheTab-Bk-gm-R0.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/EventsTab-Cyylo0of.js +2 -0
- package/extensions/dashboard-client/dist/assets/EventsTab-Cyylo0of.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/HealthTab-D-IzmV29.js +2 -0
- package/extensions/dashboard-client/dist/assets/HealthTab-D-IzmV29.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/MaintenanceTab-CrlkCFVj.js +2 -0
- package/extensions/dashboard-client/dist/assets/MaintenanceTab-CrlkCFVj.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/MemoryMapTab-DHUdZ7RR.js +2 -0
- package/extensions/dashboard-client/dist/assets/MemoryMapTab-DHUdZ7RR.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/MetricsTab-B7I4HWVg.js +2 -0
- package/extensions/dashboard-client/dist/assets/MetricsTab-B7I4HWVg.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/OverviewTab-K6j4ZI7u.js +2 -0
- package/extensions/dashboard-client/dist/assets/OverviewTab-K6j4ZI7u.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/ReposTab-BSRWhn56.js +2 -0
- package/extensions/dashboard-client/dist/assets/ReposTab-BSRWhn56.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/SessionsTab-BXdizHN1.js +2 -0
- package/extensions/dashboard-client/dist/assets/SessionsTab-BXdizHN1.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/SetupTab-CAgyfqY3.js +2 -0
- package/extensions/dashboard-client/dist/assets/SetupTab-CAgyfqY3.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/TimeSavedCard-DwgCzPD9.js +2 -0
- package/extensions/dashboard-client/dist/assets/TimeSavedCard-DwgCzPD9.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/TurnsTab-CVKcStBp.js +5 -0
- package/extensions/dashboard-client/dist/assets/TurnsTab-CVKcStBp.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/WikiTab-BNohxTbs.js +2 -0
- package/extensions/dashboard-client/dist/assets/WikiTab-BNohxTbs.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/button-B4A-_dCA.js +2 -0
- package/extensions/dashboard-client/dist/assets/button-B4A-_dCA.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/card-Cjn7Ifoe.js +2 -0
- package/extensions/dashboard-client/dist/assets/card-Cjn7Ifoe.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/index-CQjCLvv7.js +137 -0
- package/extensions/dashboard-client/dist/assets/index-CQjCLvv7.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/index-CUA6EDRF.css +1 -0
- package/extensions/dashboard-client/dist/assets/switch-CcdXX0Zm.js +2 -0
- package/extensions/dashboard-client/dist/assets/switch-CcdXX0Zm.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/toggle-KdBnFLu3.js +2 -0
- package/extensions/dashboard-client/dist/assets/toggle-KdBnFLu3.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{useSSE-p1pOI_-P.js → useSSE-B6t44vzc.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{useSSE-p1pOI_-P.js.map → useSSE-B6t44vzc.js.map} +1 -1
- package/extensions/dashboard-client/dist/index.html +2 -2
- package/extensions/dashboard-client/package-lock.json +1995 -191
- package/extensions/dashboard-client/package.json +12 -1
- package/extensions/dashboard-client/postcss.config.js +6 -0
- package/extensions/dashboard-client/src/App.tsx +127 -88
- package/extensions/dashboard-client/src/api/client.ts +109 -10
- package/extensions/dashboard-client/src/components/ActiveReposTable.tsx +15 -7
- package/extensions/dashboard-client/src/components/ActiveSessionsTable.tsx +67 -55
- package/extensions/dashboard-client/src/components/CacheHitRateTrendCard.tsx +17 -13
- package/extensions/dashboard-client/src/components/CacheHitsCard.tsx +35 -30
- package/extensions/dashboard-client/src/components/CacheStatusPerModel.tsx +33 -28
- package/extensions/dashboard-client/src/components/EventCategoryFilter.tsx +5 -6
- package/extensions/dashboard-client/src/components/EventStream.tsx +17 -13
- package/extensions/dashboard-client/src/components/HydeDetailPanel.tsx +173 -0
- package/extensions/dashboard-client/src/components/LegendCard.tsx +16 -9
- package/extensions/dashboard-client/src/components/PerfCards.tsx +28 -20
- package/extensions/dashboard-client/src/components/PerfChart.tsx +9 -6
- package/extensions/dashboard-client/src/components/ProviderCacheCard.tsx +15 -7
- package/extensions/dashboard-client/src/components/RagDashboard.tsx +253 -0
- package/extensions/dashboard-client/src/components/RepoAllSessionsCard.tsx +15 -10
- package/extensions/dashboard-client/src/components/RepoContextStack.tsx +24 -16
- package/extensions/dashboard-client/src/components/RepoDetailModal.tsx +52 -49
- package/extensions/dashboard-client/src/components/RepoTable.tsx +26 -17
- package/extensions/dashboard-client/src/components/SessionContextGauges.tsx +11 -14
- package/extensions/dashboard-client/src/components/SessionInfo.tsx +20 -15
- package/extensions/dashboard-client/src/components/SessionsMemoryChart.tsx +8 -5
- package/extensions/dashboard-client/src/components/SummaryTiles.tsx +26 -17
- package/extensions/dashboard-client/src/components/TabBar.tsx +1 -3
- package/extensions/dashboard-client/src/components/layout/AppShell.tsx +42 -0
- package/extensions/dashboard-client/src/components/layout/BottomBar.tsx +88 -0
- package/extensions/dashboard-client/src/components/layout/Sidebar.tsx +83 -0
- package/extensions/dashboard-client/src/components/ui/badge.tsx +30 -0
- package/extensions/dashboard-client/src/components/ui/button.tsx +44 -0
- package/extensions/dashboard-client/src/components/ui/card.tsx +32 -0
- package/extensions/dashboard-client/src/components/ui/select.tsx +64 -0
- package/extensions/dashboard-client/src/components/ui/switch.tsx +26 -0
- package/extensions/dashboard-client/src/components/ui/tabs.tsx +36 -0
- package/extensions/dashboard-client/src/components/ui/toggle.tsx +28 -0
- package/extensions/dashboard-client/src/components/ui/tooltip.tsx +27 -0
- package/extensions/dashboard-client/src/config.ts +10 -0
- package/extensions/dashboard-client/src/main.tsx +2 -2
- package/extensions/dashboard-client/src/memory-map-layout.ts +1 -2
- package/extensions/dashboard-client/src/styles/index.css +79 -0
- package/extensions/dashboard-client/src/tabs/CacheTab.tsx +33 -26
- package/extensions/dashboard-client/src/tabs/EventsTab.tsx +22 -7
- package/extensions/dashboard-client/src/tabs/HealthTab.tsx +77 -46
- package/extensions/dashboard-client/src/tabs/MaintenanceTab/ActionsCard.tsx +60 -50
- package/extensions/dashboard-client/src/tabs/MaintenanceTab/DbStatsCard.tsx +61 -35
- package/extensions/dashboard-client/src/tabs/MaintenanceTab/DebugBundleCard.tsx +34 -23
- package/extensions/dashboard-client/src/tabs/MaintenanceTab/HealthMitigationCard.tsx +62 -43
- package/extensions/dashboard-client/src/tabs/MaintenanceTab/SchemaHealthCard.tsx +53 -32
- package/extensions/dashboard-client/src/tabs/MaintenanceTab.tsx +2 -2
- package/extensions/dashboard-client/src/tabs/MemoryMapTab/MemoryMapView.tsx +29 -46
- package/extensions/dashboard-client/src/tabs/MemoryMapTab/RaptorTreeView.tsx +10 -34
- package/extensions/dashboard-client/src/tabs/MemoryMapTab.tsx +14 -34
- package/extensions/dashboard-client/src/tabs/MetricsTab.tsx +5 -2
- package/extensions/dashboard-client/src/tabs/OverviewTab.tsx +11 -5
- package/extensions/dashboard-client/src/tabs/ReposTab.tsx +5 -5
- package/extensions/dashboard-client/src/tabs/SessionsTab.tsx +47 -33
- package/extensions/dashboard-client/src/tabs/SetupTab/EmbedderSetup.tsx +6 -2
- package/extensions/dashboard-client/src/tabs/SetupTab/RagHealthCard.tsx +78 -0
- package/extensions/dashboard-client/src/tabs/SetupTab/SettingsPanel.tsx +198 -0
- package/extensions/dashboard-client/src/tabs/SetupTab/SettingsSection.tsx +50 -0
- package/extensions/dashboard-client/src/tabs/SetupTab.tsx +21 -59
- package/extensions/dashboard-client/src/tabs/TopicsTab.tsx +75 -40
- package/extensions/dashboard-client/src/tabs/TurnsTab.tsx +150 -112
- package/extensions/dashboard-client/src/tabs/WikiTab/TopicEvolutionGraph.tsx +274 -0
- package/extensions/dashboard-client/src/tabs/WikiTab/TopicEvolutionView.tsx +71 -0
- package/extensions/dashboard-client/src/tabs/WikiTab/TopicTimeline.tsx +103 -0
- package/extensions/dashboard-client/src/tabs/WikiTab/WikiPage.tsx +213 -0
- package/extensions/dashboard-client/src/tabs/WikiTab/WikiPageControls.tsx +188 -0
- package/extensions/dashboard-client/src/tabs/WikiTab.tsx +222 -0
- package/extensions/dashboard-client/src/tabs/registry.ts +57 -0
- package/extensions/dashboard-client/src/utils/cn.ts +7 -0
- package/extensions/dashboard-client/tailwind.config.js +46 -0
- package/extensions/dashboard-server/api-contracts/core.ts +59 -0
- package/extensions/dashboard-server/api-contracts/endpoints/registry.ts +79 -0
- package/extensions/dashboard-server/api-contracts/index.ts +48 -2
- package/extensions/dashboard-server/api-contracts/rag-metrics.ts +22 -0
- package/extensions/dashboard-server/api-contracts/rag-settings.ts +30 -20
- package/extensions/dashboard-server/api-contracts/wiki.ts +138 -0
- package/extensions/dashboard-server/routes-rag-metrics.ts +82 -0
- package/extensions/dashboard-server/routes-rag-settings-helpers.ts +314 -0
- package/extensions/dashboard-server/routes-rag-settings.ts +147 -97
- package/extensions/dashboard-server/routes-wiki-helpers.ts +126 -0
- package/extensions/dashboard-server/routes-wiki.ts +376 -0
- package/extensions/dashboard-server/routes.ts +2 -0
- package/extensions/dashboard-server/server.ts +18 -2
- package/extensions/mega-events/context-handler/afterCompact.ts +44 -1
- package/extensions/mega-pipeline/recall.ts +31 -0
- package/extensions/mega-turn-store.ts +13 -0
- package/package.json +1 -1
- package/src/config/turns.ts +17 -0
- package/src/config.ts +3 -0
- package/src/recall/async.ts +10 -1
- package/src/recall/format.ts +15 -2
- package/src/recall/hydeTelemetry.ts +44 -0
- package/src/recall/sync.ts +34 -2
- package/src/recall/types.ts +36 -0
- package/src/store/turns/hydeStore.ts +82 -0
- package/src/store/turns/index.ts +4 -0
- package/src/store/turns/schema.ts +126 -1
- package/src/store/turns/sqlite-store/rows.ts +25 -0
- package/src/store/turns/sqlite-store/write.ts +20 -2
- package/src/store/turns/types.ts +33 -0
- package/src/topics/durability.ts +231 -0
- package/src/topics/index.ts +7 -1
- package/src/topics/store.ts +35 -9
- package/src/wiki/curation-helpers.ts +140 -0
- package/src/wiki/curation.ts +217 -0
- package/src/wiki/incremental.ts +164 -0
- package/src/wiki/index.ts +13 -0
- package/extensions/dashboard-client/dist/assets/CacheTab-Cqnc7lMg.js +0 -2
- package/extensions/dashboard-client/dist/assets/CacheTab-Cqnc7lMg.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/EventsTab-tYk3VFAp.js +0 -2
- package/extensions/dashboard-client/dist/assets/EventsTab-tYk3VFAp.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/HealthTab-D2_kjgxw.js +0 -2
- package/extensions/dashboard-client/dist/assets/HealthTab-D2_kjgxw.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/MaintenanceTab-Bc-uPYrz.js +0 -2
- package/extensions/dashboard-client/dist/assets/MaintenanceTab-Bc-uPYrz.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/MemoryMapTab-D1V9g_NC.js +0 -2
- package/extensions/dashboard-client/dist/assets/MemoryMapTab-D1V9g_NC.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/MetricsTab-DT_XfL96.js +0 -2
- package/extensions/dashboard-client/dist/assets/MetricsTab-DT_XfL96.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/OverviewTab-BY6KZCme.js +0 -2
- package/extensions/dashboard-client/dist/assets/OverviewTab-BY6KZCme.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/ReposTab-CeS-tpDM.js +0 -2
- package/extensions/dashboard-client/dist/assets/ReposTab-CeS-tpDM.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/SessionsTab-C30AwyoF.js +0 -70
- package/extensions/dashboard-client/dist/assets/SetupTab-BJUHOVK2.js +0 -2
- package/extensions/dashboard-client/dist/assets/SetupTab-BJUHOVK2.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/TimeSavedCard-p55K1tFP.js +0 -2
- package/extensions/dashboard-client/dist/assets/TimeSavedCard-p55K1tFP.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/TopicsTab-D8FqyAbK.js +0 -2
- package/extensions/dashboard-client/dist/assets/TopicsTab-D8FqyAbK.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/TurnsTab-ju-nXDys.js +0 -2
- package/extensions/dashboard-client/dist/assets/TurnsTab-ju-nXDys.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/index-BEj1Zahy.js +0 -42
- package/extensions/dashboard-client/dist/assets/index-BEj1Zahy.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/index-CUNn22iV.css +0 -1
- package/extensions/dashboard-client/src/styles/metrics-extra.css +0 -69
- package/extensions/dashboard-client/src/styles/turns.css +0 -142
- 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="
|
|
35
|
-
<span className="
|
|
36
|
-
<span className="
|
|
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
|
-
<
|
|
46
|
-
<
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
46
|
+
<Card className="electric-hover">
|
|
47
|
+
<CardHeader>
|
|
48
|
+
<CardTitle>💾 Cache Hits & 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="
|
|
36
|
-
<span className="
|
|
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="
|
|
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
|
-
<
|
|
62
|
-
<
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
<
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
62
|
+
<Card className="electric-hover">
|
|
63
|
+
<CardHeader>
|
|
64
|
+
<CardTitle>💰 Model & 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="
|
|
40
|
+
<div className="flex flex-wrap gap-2" role="toolbar" aria-label="Event category filter">
|
|
40
41
|
{CATEGORIES.map((cat) => (
|
|
41
|
-
<
|
|
42
|
+
<Toggle
|
|
42
43
|
key={cat}
|
|
43
|
-
|
|
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
|
-
</
|
|
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="
|
|
118
|
-
<div className="
|
|
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
|
-
<
|
|
123
|
+
<Toggle
|
|
121
124
|
key={t}
|
|
122
|
-
|
|
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
|
-
</
|
|
129
|
+
</Toggle>
|
|
128
130
|
))}
|
|
129
|
-
<span className="
|
|
131
|
+
<span className="text-xs text-muted-foreground">{events.length} buffered</span>
|
|
130
132
|
</div>
|
|
131
|
-
<ul className="
|
|
133
|
+
<ul className="flex flex-col gap-1 font-mono text-xs">
|
|
132
134
|
{filtered.length === 0 && (
|
|
133
|
-
<li className="
|
|
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={`
|
|
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="
|
|
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
|
-
<
|
|
40
|
-
<
|
|
41
|
-
<
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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="
|
|
35
|
-
<span className="
|
|
36
|
-
<span className="
|
|
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
|
|
48
|
+
function PerfCard({ title, children }: { title: string; children: React.ReactNode }): React.ReactElement {
|
|
43
49
|
return (
|
|
44
|
-
<
|
|
45
|
-
<
|
|
46
|
-
|
|
47
|
-
|
|
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="
|
|
56
|
-
<
|
|
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
|
-
</
|
|
69
|
+
</PerfCard>
|
|
62
70
|
|
|
63
|
-
<
|
|
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
|
-
</
|
|
77
|
+
</PerfCard>
|
|
70
78
|
|
|
71
|
-
<
|
|
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
|
-
</
|
|
83
|
+
</PerfCard>
|
|
76
84
|
|
|
77
|
-
<
|
|
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
|
-
</
|
|
89
|
+
</PerfCard>
|
|
82
90
|
|
|
83
|
-
<
|
|
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="
|
|
88
|
-
</
|
|
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
|
-
<
|
|
54
|
-
<
|
|
55
|
-
<
|
|
56
|
-
<span className="
|
|
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
|
-
</
|
|
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
|
-
|
|
160
|
+
</CardContent>
|
|
161
|
+
</Card>
|
|
159
162
|
);
|
|
160
163
|
}
|