pi-mega-compact 0.20.32 → 0.20.34
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/dist/config/vector-cortex-pcc.js +30 -0
- package/dist/config/vector-cortex.js +2 -2
- package/dist/config.js +1 -1
- package/dist/extensions/dashboard-server/api-contracts/endpoints/registry-ext.js +7 -0
- package/dist/extensions/dashboard-server/api-contracts/prefix-stability.js +8 -0
- package/dist/extensions/dashboard-server/route-dispatch.js +3 -1
- package/dist/extensions/dashboard-server/routes-prefix-stability.js +132 -0
- package/dist/extensions/dashboard-server/routes-rag-settings-vector-cortex.js +1 -0
- package/dist/extensions/dashboard-server/routes.js +1 -0
- package/dist/extensions/mega-events/context-handler/tailResult.js +5 -1
- package/dist/src/config/vector-cortex-pcc.js +30 -0
- package/dist/src/config/vector-cortex.js +2 -2
- package/dist/src/config.js +1 -1
- package/extensions/dashboard-client/dist/assets/{AreaChart-EL8l2Fcj.js → AreaChart-DjXp8u2Y.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{AreaChart-EL8l2Fcj.js.map → AreaChart-DjXp8u2Y.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{BarChart-TrwfSh0w.js → BarChart-BZbus-dR.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{BarChart-TrwfSh0w.js.map → BarChart-BZbus-dR.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/CacheTab-DQ2CjxVW.js +2 -0
- package/extensions/dashboard-client/dist/assets/CacheTab-DQ2CjxVW.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{EventsTab-CNrT-b7t.js → EventsTab-DUpnOV1p.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{EventsTab-CNrT-b7t.js.map → EventsTab-DUpnOV1p.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{HealthTab-CkBCFPk6.js → HealthTab-Bul_xjcn.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{HealthTab-CkBCFPk6.js.map → HealthTab-Bul_xjcn.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-CcbKszwx.js → MaintenanceTab-DLUjdKd3.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-CcbKszwx.js.map → MaintenanceTab-DLUjdKd3.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MemoryMapTab-B2LvwScj.js → MemoryMapTab-BFy0hb6F.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MemoryMapTab-B2LvwScj.js.map → MemoryMapTab-BFy0hb6F.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MetricsTab-Dv_xZtOG.js → MetricsTab-BpYuUkq3.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MetricsTab-Dv_xZtOG.js.map → MetricsTab-BpYuUkq3.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{OverviewTab-D7xwhpOl.js → OverviewTab-z7yuT-uq.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{OverviewTab-D7xwhpOl.js.map → OverviewTab-z7yuT-uq.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{ReposTab-BfhiPXdA.js → ReposTab-ClORoDy_.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{ReposTab-BfhiPXdA.js.map → ReposTab-ClORoDy_.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{SessionsTab-tQM0TQLp.js → SessionsTab-CUpehIt9.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{SessionsTab-tQM0TQLp.js.map → SessionsTab-CUpehIt9.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/SetupTab-Cii80Fdz.js +3 -0
- package/extensions/dashboard-client/dist/assets/{SetupTab-G-MkdQiR.js.map → SetupTab-Cii80Fdz.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-CYltysD3.js → TimeSavedCard-BV2iCSdV.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-CYltysD3.js.map → TimeSavedCard-BV2iCSdV.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TurnsTab-1Y_Lpxx-.js → TurnsTab-1T86rHnD.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TurnsTab-1Y_Lpxx-.js.map → TurnsTab-1T86rHnD.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{VcStatusBadge-BNHVeTG7.js → VcStatusBadge-DKPleja8.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{VcStatusBadge-BNHVeTG7.js.map → VcStatusBadge-DKPleja8.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{VectorCortexTab-goacPxL1.js → VectorCortexTab-BRzvd_pU.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{VectorCortexTab-goacPxL1.js.map → VectorCortexTab-BRzvd_pU.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{WikiTab-DTOemYtE.js → WikiTab-DKTILa1M.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{WikiTab-DTOemYtE.js.map → WikiTab-DKTILa1M.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{button-B_4j_PVR.js → button-5fSU8BZj.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{button-B_4j_PVR.js.map → button-5fSU8BZj.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{card-Ce58rSXG.js → card-BFUnpXE_.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{card-Ce58rSXG.js.map → card-BFUnpXE_.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/client-extra-BZbw21wu.js +2 -0
- package/extensions/dashboard-client/dist/assets/client-extra-BZbw21wu.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-DtNoJ6fZ.js → generateCategoricalChart-xCzi6pWn.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-DtNoJ6fZ.js.map → generateCategoricalChart-xCzi6pWn.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{index-eoCm0uxj.js → index-DqjmZ88m.js} +21 -21
- package/extensions/dashboard-client/dist/assets/index-DqjmZ88m.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{switch-B9kM0N7R.js → switch-CeZUPf_e.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{switch-B9kM0N7R.js.map → switch-CeZUPf_e.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{toggle-BwoHYpMa.js → toggle-MQQwdkwD.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{toggle-BwoHYpMa.js.map → toggle-MQQwdkwD.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{useSSE-DohG5AGv.js → useSSE-BetS7QHN.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{useSSE-DohG5AGv.js.map → useSSE-BetS7QHN.js.map} +1 -1
- package/extensions/dashboard-client/dist/index.html +1 -1
- package/extensions/dashboard-client/src/api/client-extra.ts +53 -0
- package/extensions/dashboard-client/src/api/client-http.ts +81 -0
- package/extensions/dashboard-client/src/api/client.ts +11 -100
- package/extensions/dashboard-client/src/components/PrefixStabilityCard.tsx +98 -0
- package/extensions/dashboard-client/src/tabs/CacheTab.tsx +27 -0
- package/extensions/dashboard-server/api-contracts/endpoints/registry-ext.ts +15 -0
- package/extensions/dashboard-server/api-contracts/index.ts +6 -0
- package/extensions/dashboard-server/api-contracts/prefix-stability.ts +40 -0
- package/extensions/dashboard-server/route-dispatch.ts +2 -0
- package/extensions/dashboard-server/routes-prefix-stability.ts +142 -0
- package/extensions/dashboard-server/routes-rag-settings-vector-cortex.ts +6 -0
- package/extensions/dashboard-server/routes.ts +1 -0
- package/extensions/mega-events/context-handler/tailResult.ts +5 -1
- package/package.json +1 -1
- package/src/config/vector-cortex-pcc.ts +32 -0
- package/src/config/vector-cortex.ts +2 -2
- package/src/config.ts +1 -0
- package/extensions/dashboard-client/dist/assets/CacheTab-C3KWDFDm.js +0 -2
- package/extensions/dashboard-client/dist/assets/CacheTab-C3KWDFDm.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/SetupTab-G-MkdQiR.js +0 -3
- package/extensions/dashboard-client/dist/assets/index-eoCm0uxj.js.map +0 -1
|
@@ -54,9 +54,6 @@ import type {
|
|
|
54
54
|
SettingsUpdateRequest,
|
|
55
55
|
SettingsResponsePost,
|
|
56
56
|
RagMetricsResponse,
|
|
57
|
-
ModelThresholdsResponse,
|
|
58
|
-
ModelThresholdPutRequest,
|
|
59
|
-
ModelThresholdPutResponse,
|
|
60
57
|
WikiIndexResponse,
|
|
61
58
|
WikiPageResponse,
|
|
62
59
|
CurationResult,
|
|
@@ -67,74 +64,17 @@ import type {
|
|
|
67
64
|
TopicEvolutionResponse,
|
|
68
65
|
} from "@contracts";
|
|
69
66
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
async function getJson<T>(path: string): Promise<T> {
|
|
82
|
-
// guardrails-allow PREVENT-PI-004: relative-path fetch to same-origin dashboard server (loopback-only, static bundle served by the same Node HTTP server).
|
|
83
|
-
const res = await fetch(path);
|
|
84
|
-
if (!res.ok) {
|
|
85
|
-
throw new ApiError(
|
|
86
|
-
res.status,
|
|
87
|
-
await res.text().catch(() => res.statusText),
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
return res.json() as Promise<T>;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/** Internal: typed PUT that throws ApiError on non-2xx. */
|
|
94
|
-
async function putJson<T>(path: string, body: unknown): Promise<T> {
|
|
95
|
-
// guardrails-allow PREVENT-PI-004: relative-path fetch to same-origin dashboard server (loopback-only).
|
|
96
|
-
const res = await fetch(path, {
|
|
97
|
-
method: "PUT",
|
|
98
|
-
headers: { "Content-Type": "application/json" },
|
|
99
|
-
body: JSON.stringify(body),
|
|
100
|
-
});
|
|
101
|
-
if (!res.ok) {
|
|
102
|
-
throw new ApiError(
|
|
103
|
-
res.status,
|
|
104
|
-
await res.text().catch(() => res.statusText),
|
|
105
|
-
);
|
|
106
|
-
}
|
|
107
|
-
return res.json() as Promise<T>;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/** Internal: typed POST that throws ApiError on non-2xx. */
|
|
111
|
-
async function postJson<T>(path: string, body: unknown): Promise<T> {
|
|
112
|
-
// guardrails-allow PREVENT-PI-004: relative-path fetch to same-origin dashboard server (loopback-only, static bundle served by the same Node HTTP server).
|
|
113
|
-
const res = await fetch(path, {
|
|
114
|
-
method: "POST",
|
|
115
|
-
headers: { "Content-Type": "application/json" },
|
|
116
|
-
body: JSON.stringify(body),
|
|
117
|
-
});
|
|
118
|
-
if (!res.ok) {
|
|
119
|
-
throw new ApiError(
|
|
120
|
-
res.status,
|
|
121
|
-
await res.text().catch(() => res.statusText),
|
|
122
|
-
);
|
|
123
|
-
}
|
|
124
|
-
return res.json() as Promise<T>;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/** Build a query string from a record, skipping undefined/null values. */
|
|
128
|
-
function query(
|
|
129
|
-
params: Record<string, string | number | undefined | null>,
|
|
130
|
-
): string {
|
|
131
|
-
const sp = new URLSearchParams();
|
|
132
|
-
for (const [k, v] of Object.entries(params)) {
|
|
133
|
-
if (v !== undefined && v !== null) sp.set(k, String(v));
|
|
134
|
-
}
|
|
135
|
-
const qs = sp.toString();
|
|
136
|
-
return qs ? `?${qs}` : "";
|
|
137
|
-
}
|
|
67
|
+
// Shared HTTP helpers (ApiError/getJson/putJson/postJson/query) live in
|
|
68
|
+
// client-http.ts (delegate-shell split keeps client.ts under 400 lines).
|
|
69
|
+
import { getJson, putJson, postJson, query } from "./client-http.js";
|
|
70
|
+
// Newer endpoint groups (model thresholds, PC-C prefix-stability) live in
|
|
71
|
+
// client-extra.ts; re-exported here so downstream imports are unchanged.
|
|
72
|
+
export {
|
|
73
|
+
fetchModelThresholds,
|
|
74
|
+
putModelThreshold,
|
|
75
|
+
deleteModelThreshold,
|
|
76
|
+
fetchPrefixStability,
|
|
77
|
+
} from "./client-extra.js";
|
|
138
78
|
|
|
139
79
|
// ─── Endpoint wrappers ──────────────────────────────────────────────────────
|
|
140
80
|
|
|
@@ -456,32 +396,3 @@ export function fetchTopicTimeline(
|
|
|
456
396
|
export function fetchTopicEvolution(): Promise<TopicEvolutionResponse> {
|
|
457
397
|
return getJson<TopicEvolutionResponse>(ENDPOINTS.topicEvolution.path);
|
|
458
398
|
}
|
|
459
|
-
|
|
460
|
-
// ─── Per-model compaction thresholds (S52 / v0.16.1) ────────────────────
|
|
461
|
-
|
|
462
|
-
/** GET /api/model-thresholds — list known models with their thresholds. */
|
|
463
|
-
export function fetchModelThresholds(): Promise<ModelThresholdsResponse> {
|
|
464
|
-
return getJson<ModelThresholdsResponse>(ENDPOINTS.modelThresholds.path);
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
/** PUT /api/model-thresholds — upsert a per-model override. */
|
|
468
|
-
export function putModelThreshold(
|
|
469
|
-
body: ModelThresholdPutRequest,
|
|
470
|
-
): Promise<ModelThresholdPutResponse> {
|
|
471
|
-
return putJson<ModelThresholdPutResponse>(
|
|
472
|
-
ENDPOINTS.modelThresholds.path,
|
|
473
|
-
body,
|
|
474
|
-
);
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
/** DELETE /api/model-thresholds/:modelId — delete an override (revert). */
|
|
478
|
-
export async function deleteModelThreshold(
|
|
479
|
-
modelId: string,
|
|
480
|
-
): Promise<{ deleted: boolean }> {
|
|
481
|
-
const res = await fetch(
|
|
482
|
-
`${ENDPOINTS.modelThresholds.path}/${encodeURIComponent(modelId)}`,
|
|
483
|
-
{ method: "DELETE" },
|
|
484
|
-
);
|
|
485
|
-
if (!res.ok) throw new Error(`deleteModelThreshold ${res.status}`);
|
|
486
|
-
return res.json() as Promise<{ deleted: boolean }>;
|
|
487
|
-
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dashboard-client/src/components/PrefixStabilityCard.tsx -- Prompt-cache per-turn
|
|
3
|
+
* stable-prefix ratio trend (PC-C).
|
|
4
|
+
*
|
|
5
|
+
* Renders a sparkline of the per-turn stable-prefix ratio (stablePrefix /
|
|
6
|
+
* totalMessages) read from /api/prefix-stability, with the average ratio and a
|
|
7
|
+
* three-point trend badge ("improving" | "stable" | "degrading"). Each bar is one
|
|
8
|
+
* turn. Uses the same .ov-sparkline / .ov-bar-* CSS classes as CacheHitRateTrendCard.
|
|
9
|
+
* No external charting dependencies.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type React from "react";
|
|
13
|
+
import type { PrefixStabilityResponse } from "@contracts";
|
|
14
|
+
import { Card, CardContent, CardHeader, CardTitle } from "../components/ui/card";
|
|
15
|
+
|
|
16
|
+
export interface PrefixStabilityCardProps {
|
|
17
|
+
data: PrefixStabilityResponse;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function fmtRatio(n: number): string {
|
|
21
|
+
return `${(n * 100).toFixed(1)}%`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Map a ratio (0–1) to a CSS color class (mirrors pctColorClass thresholds). */
|
|
25
|
+
function ratioColorClass(ratio: number): string {
|
|
26
|
+
const pct = ratio * 100;
|
|
27
|
+
if (pct >= 80) return "ov-bar-green";
|
|
28
|
+
if (pct >= 60) return "ov-bar-teal";
|
|
29
|
+
if (pct >= 40) return "ov-bar-yellow";
|
|
30
|
+
if (pct >= 20) return "ov-bar-orange";
|
|
31
|
+
return "ov-bar-red";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const TREND_LABEL: Record<PrefixStabilityResponse["trend"], string> = {
|
|
35
|
+
improving: "Improving",
|
|
36
|
+
stable: "Stable",
|
|
37
|
+
degrading: "Degrading",
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const PrefixStabilityCard: React.FC<PrefixStabilityCardProps> = ({
|
|
41
|
+
data,
|
|
42
|
+
}) => {
|
|
43
|
+
const { turns, avgRatio, trend } = data;
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<Card className="electric-hover">
|
|
47
|
+
<CardHeader>
|
|
48
|
+
<CardTitle>Per-Turn Stable Prefix</CardTitle>
|
|
49
|
+
</CardHeader>
|
|
50
|
+
<CardContent>
|
|
51
|
+
<div className="mb-3 flex gap-4 text-sm text-muted-foreground">
|
|
52
|
+
<div>
|
|
53
|
+
Avg:{" "}
|
|
54
|
+
<strong className="text-foreground">{fmtRatio(avgRatio)}</strong>
|
|
55
|
+
</div>
|
|
56
|
+
<div>
|
|
57
|
+
Trend:{" "}
|
|
58
|
+
<strong className="text-foreground">{TREND_LABEL[trend]}</strong>
|
|
59
|
+
</div>
|
|
60
|
+
<div>
|
|
61
|
+
Turns: <strong className="text-foreground">{turns.length}</strong>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
{turns.length > 0 ? (
|
|
66
|
+
<>
|
|
67
|
+
<div
|
|
68
|
+
className="ov-sparkline"
|
|
69
|
+
role="img"
|
|
70
|
+
aria-label={`Per-turn stable-prefix ratio trend: ${turns.length} turns, average ${fmtRatio(avgRatio)}, trend ${TREND_LABEL[trend]}`}
|
|
71
|
+
>
|
|
72
|
+
{turns.map((t, i) => {
|
|
73
|
+
const barH = Math.max(2, Math.round(t.ratio * 60));
|
|
74
|
+
const colorClass = ratioColorClass(t.ratio);
|
|
75
|
+
const isLatest = i === turns.length - 1;
|
|
76
|
+
return (
|
|
77
|
+
<div
|
|
78
|
+
key={i}
|
|
79
|
+
className={`ov-sparkline-bar ${colorClass}${isLatest ? " ov-bar-latest" : ""}`}
|
|
80
|
+
style={{ height: `${barH}px` }}
|
|
81
|
+
title={`Turn ${t.turnIndex}: ${fmtRatio(t.ratio)} (${t.stablePrefix}/${t.totalMessages} msg) @ ${new Date(t.timestamp).toLocaleTimeString()}`}
|
|
82
|
+
/>
|
|
83
|
+
);
|
|
84
|
+
})}
|
|
85
|
+
</div>
|
|
86
|
+
<div className="mt-1 text-xs text-muted-foreground">
|
|
87
|
+
Shared prompt-cache prefix ratio per turn — higher is better.
|
|
88
|
+
</div>
|
|
89
|
+
</>
|
|
90
|
+
) : (
|
|
91
|
+
<div className="text-sm text-muted-foreground">
|
|
92
|
+
No stable-prefix samples yet
|
|
93
|
+
</div>
|
|
94
|
+
)}
|
|
95
|
+
</CardContent>
|
|
96
|
+
</Card>
|
|
97
|
+
);
|
|
98
|
+
};
|
|
@@ -17,12 +17,14 @@ import type {
|
|
|
17
17
|
ProviderCacheResponse,
|
|
18
18
|
CacheStripesResponse,
|
|
19
19
|
PerfResponse,
|
|
20
|
+
PrefixStabilityResponse,
|
|
20
21
|
} from "@contracts";
|
|
21
22
|
import { useApi } from "../hooks/useApi";
|
|
22
23
|
import {
|
|
23
24
|
fetchSnapshot,
|
|
24
25
|
fetchProviderCache,
|
|
25
26
|
fetchCacheStripes,
|
|
27
|
+
fetchPrefixStability,
|
|
26
28
|
fetchPerf,
|
|
27
29
|
} from "../api/client";
|
|
28
30
|
import { CacheHitsCard } from "../components/CacheHitsCard";
|
|
@@ -30,6 +32,7 @@ import { TimeSavedCard } from "../components/TimeSavedCard";
|
|
|
30
32
|
import { ProviderCacheCard } from "../components/ProviderCacheCard";
|
|
31
33
|
import { StripeDistributionCard } from "../components/StripeDistributionCard";
|
|
32
34
|
import { CacheHitRateTrendCard } from "../components/CacheHitRateTrendCard";
|
|
35
|
+
import { PrefixStabilityCard } from "../components/PrefixStabilityCard";
|
|
33
36
|
import { Button } from "../components/ui/button";
|
|
34
37
|
import { Card, CardContent, CardHeader, CardTitle } from "../components/ui/card";
|
|
35
38
|
|
|
@@ -65,6 +68,15 @@ export default function CacheTab(): React.ReactElement {
|
|
|
65
68
|
{ pollInterval: 15000 },
|
|
66
69
|
);
|
|
67
70
|
|
|
71
|
+
/* --- Per-turn stable-prefix ratio trend (PC-C, 15s poll) --- */
|
|
72
|
+
const {
|
|
73
|
+
data: prefixStability,
|
|
74
|
+
loading: psLoading,
|
|
75
|
+
} = useApi<PrefixStabilityResponse>(
|
|
76
|
+
useCallback(() => fetchPrefixStability(50), []),
|
|
77
|
+
{ pollInterval: 15000 },
|
|
78
|
+
);
|
|
79
|
+
|
|
68
80
|
/* --- Dedup cache snapshot (5s poll) --- */
|
|
69
81
|
const {
|
|
70
82
|
data: snapshot,
|
|
@@ -164,6 +176,21 @@ export default function CacheTab(): React.ReactElement {
|
|
|
164
176
|
<CacheHitRateTrendCard perf={perf} />
|
|
165
177
|
) : null}
|
|
166
178
|
|
|
179
|
+
{/* ==============================================================
|
|
180
|
+
Section 3b -- Per-Turn Stable Prefix (PC-C)
|
|
181
|
+
Omitted when flag-off: the endpoint 404s, so psError is set and
|
|
182
|
+
this section stays empty. No sample/time-series data, only
|
|
183
|
+
per-turn prefix ratios read from the local events log.
|
|
184
|
+
============================================================== */}
|
|
185
|
+
{prefixStability && !psLoading ? (
|
|
186
|
+
<div>
|
|
187
|
+
<h2 className="font-heading text-lg font-semibold">
|
|
188
|
+
Per-Turn Stable Prefix
|
|
189
|
+
</h2>
|
|
190
|
+
<PrefixStabilityCard data={prefixStability} />
|
|
191
|
+
</div>
|
|
192
|
+
) : null}
|
|
193
|
+
|
|
167
194
|
{/* ==============================================================
|
|
168
195
|
Section 4 -- Mega-Compact Dedup Cache
|
|
169
196
|
============================================================== */}
|
|
@@ -24,6 +24,7 @@ import type {
|
|
|
24
24
|
SetupCortexActionLogQuery,
|
|
25
25
|
SetupCortexActionLogResponse,
|
|
26
26
|
} from "../setup-cortex.js";
|
|
27
|
+
import type { PrefixStabilityResponse } from "../prefix-stability.js";
|
|
27
28
|
|
|
28
29
|
/** Additive endpoint entries spread into the ENDPOINTS registry. */
|
|
29
30
|
export const EXTRA_ENDPOINTS = {
|
|
@@ -108,4 +109,18 @@ export const EXTRA_ENDPOINTS = {
|
|
|
108
109
|
SetupCortexActionLogQuery,
|
|
109
110
|
SetupCortexActionLogResponse
|
|
110
111
|
>,
|
|
112
|
+
|
|
113
|
+
// ─── PC-C Prompt-cache prefix stability (dashboard Cache tab) ───────
|
|
114
|
+
|
|
115
|
+
/** GET /api/prefix-stability — per-turn stable-prefix ratio trend (PC-C). */
|
|
116
|
+
prefixStability: {
|
|
117
|
+
method: "GET",
|
|
118
|
+
path: "/api/prefix-stability",
|
|
119
|
+
description:
|
|
120
|
+
"Per-turn prompt-cache stable-prefix ratio trend (GET /api/prefix-stability?limit=N) read from prefix_stability rows in the local events log. Flag-off (MEGACOMPACT_PC_C=0) returns 404.",
|
|
121
|
+
} as const satisfies EndpointDef<
|
|
122
|
+
"GET",
|
|
123
|
+
undefined,
|
|
124
|
+
PrefixStabilityResponse
|
|
125
|
+
>,
|
|
111
126
|
} as const;
|
|
@@ -269,6 +269,12 @@ export type {
|
|
|
269
269
|
CacheStripesResponse,
|
|
270
270
|
} from "./cache-stripes.js";
|
|
271
271
|
|
|
272
|
+
// PC-C — prompt-cache prefix-stability per-turn trend
|
|
273
|
+
export type {
|
|
274
|
+
PrefixStabilitySample,
|
|
275
|
+
PrefixStabilityResponse,
|
|
276
|
+
} from "./prefix-stability.js";
|
|
277
|
+
|
|
272
278
|
// RAG Settings — comprehensive adjustable settings (B6)
|
|
273
279
|
export type {
|
|
274
280
|
SettingState,
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* api-contracts/prefix-stability.ts — Prompt-cache prefix-stability API contract (PC-C).
|
|
3
|
+
*
|
|
4
|
+
* Response shape for GET /api/prefix-stability?limit=50: per-turn stable-prefix
|
|
5
|
+
* ratio trend surfaced from the local monitoring events log (prefix_stability rows
|
|
6
|
+
* appended by tailResult.ts). Loopback-only data; zero network (PREVENT-PI-004).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/** A single per-turn prefix-stability sample. */
|
|
10
|
+
export interface PrefixStabilitySample {
|
|
11
|
+
/** Monotonic turn index (order of emission, 0-based). */
|
|
12
|
+
readonly turnIndex: number;
|
|
13
|
+
/** Number of messages sharing the stable prefix (cache-able head). */
|
|
14
|
+
readonly stablePrefix: number;
|
|
15
|
+
/** Total messages in the assembled prompt for that turn. */
|
|
16
|
+
readonly totalMessages: number;
|
|
17
|
+
/** stablePrefix / totalMessages (0.0–1.0); 0 when totalMessages is 0. */
|
|
18
|
+
readonly ratio: number;
|
|
19
|
+
/** Ollama-style striping mode at emit time: "v3" (cache-striping) | "v2" | "off". */
|
|
20
|
+
readonly striping: string;
|
|
21
|
+
/** ISO-8601 timestamp of the event (from the events log row). */
|
|
22
|
+
readonly timestamp: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Three-point trend classification across the returned window. */
|
|
26
|
+
export type PrefixStabilityTrend = "improving" | "stable" | "degrading";
|
|
27
|
+
|
|
28
|
+
/** Response body for GET /api/prefix-stability. */
|
|
29
|
+
export interface PrefixStabilityResponse {
|
|
30
|
+
/** Per-turn samples in chronological order (oldest first), at most `limit`. */
|
|
31
|
+
readonly turns: PrefixStabilitySample[];
|
|
32
|
+
/** Mean ratio across returned samples (0.0–1.0); 0 when empty. */
|
|
33
|
+
readonly avgRatio: number;
|
|
34
|
+
/** Trend classification of the tail of the window vs its head. */
|
|
35
|
+
readonly trend: PrefixStabilityTrend;
|
|
36
|
+
/** Number of samples returned. */
|
|
37
|
+
readonly count: number;
|
|
38
|
+
/** Epoch-millis timestamp the endpoint last scanned the events log. */
|
|
39
|
+
readonly lastScanAt: number;
|
|
40
|
+
}
|
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
handleProviderCache,
|
|
29
29
|
handleMemoryStatus,
|
|
30
30
|
handleCacheStripes,
|
|
31
|
+
handlePrefixStability,
|
|
31
32
|
handleSetupStatus,
|
|
32
33
|
handleSetupDetect,
|
|
33
34
|
handleSetupConfigure,
|
|
@@ -114,6 +115,7 @@ export function dispatchRoutes(
|
|
|
114
115
|
if (handleProviderCache(req, res, ctx)) return true;
|
|
115
116
|
if (handleMemoryStatus(req, res, ctx)) return true;
|
|
116
117
|
if (handleCacheStripes(req, res, ctx)) return true;
|
|
118
|
+
if (handlePrefixStability(req, res, ctx)) return true;
|
|
117
119
|
if (handleSetupStatus(req, res, ctx)) return true;
|
|
118
120
|
if (handleSetupDetect(req, res, ctx)) return true;
|
|
119
121
|
if (handleSetupConfigure(req, res, ctx)) return true;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dashboard-server/routes-prefix-stability.ts — Prompt-cache prefix-stability route (PC-C).
|
|
3
|
+
*
|
|
4
|
+
* GET /api/prefix-stability?limit=50 — reads recent prefix_stability rows from the
|
|
5
|
+
* local monitoring events log (ctx.eventsPath) and returns the per-turn stable-prefix
|
|
6
|
+
* ratio trend. Flag-off (MEGACOMPACT_PC_C=0) → 404, byte-identical to PC-B predecessor.
|
|
7
|
+
*
|
|
8
|
+
* Guardrails: PREVENT-PI-004 (loopback-only local dashboard), PREVENT-001 (null-safe
|
|
9
|
+
* JSON.parse), PREVENT-011 (no `any`). Zero network reads.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { readFileSync } from "node:fs";
|
|
13
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
14
|
+
|
|
15
|
+
import type { PrefixStabilityResponse } from "./api-contracts/prefix-stability.js";
|
|
16
|
+
import type { RouteContext } from "./routes-core.js";
|
|
17
|
+
|
|
18
|
+
/** Parse one events.log line into a generic JSON object, null-safe (PREVENT-001). */
|
|
19
|
+
function parseEventLine(line: string): Record<string, unknown> | null {
|
|
20
|
+
try {
|
|
21
|
+
const parsed: unknown = JSON.parse(line);
|
|
22
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
23
|
+
return parsed as Record<string, unknown>;
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
} catch {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Read the tail of events.log and return only prefix_stability rows, newest last. */
|
|
32
|
+
function readPrefixStabilityEvents(
|
|
33
|
+
eventsPath: string,
|
|
34
|
+
limit: number,
|
|
35
|
+
): Array<{ ts: number; stablePrefix: number; totalMessages: number; separation: string; striping: string }> {
|
|
36
|
+
const samples: Array<{
|
|
37
|
+
ts: number;
|
|
38
|
+
stablePrefix: number;
|
|
39
|
+
totalMessages: number;
|
|
40
|
+
separation: string;
|
|
41
|
+
striping: string;
|
|
42
|
+
}> = [];
|
|
43
|
+
let raw = "";
|
|
44
|
+
try {
|
|
45
|
+
raw = readFileSync(eventsPath, "utf-8"); // guardrails-allow PREVENT-PI-004: local events.log read (loopback dashboard)
|
|
46
|
+
} catch {
|
|
47
|
+
return samples;
|
|
48
|
+
}
|
|
49
|
+
for (const line of raw.split("\n")) {
|
|
50
|
+
if (!line.includes('"prefix_stability"')) continue;
|
|
51
|
+
const obj = parseEventLine(line);
|
|
52
|
+
if (!obj) continue;
|
|
53
|
+
// Only rows we appended (event === "prefix_stability").
|
|
54
|
+
if (obj.event !== "prefix_stability") continue;
|
|
55
|
+
const ts = typeof obj.ts === "number" ? obj.ts : 0;
|
|
56
|
+
const stablePrefix = typeof obj.stablePrefix === "number" ? obj.stablePrefix : 0;
|
|
57
|
+
const totalMessages = typeof obj.totalMessages === "number" ? obj.totalMessages : 0;
|
|
58
|
+
const striping = typeof obj.striping === "string" ? obj.striping : "off";
|
|
59
|
+
const separation = typeof obj.separation === "string" ? obj.separation : "off";
|
|
60
|
+
samples.push({ ts, stablePrefix, totalMessages, separation, striping });
|
|
61
|
+
}
|
|
62
|
+
return samples.slice(-limit);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Classify the three-point trend across the returned window (head-mean vs tail-mean). */
|
|
66
|
+
function classifyTrend(ratios: number[]): PrefixStabilityResponse["trend"] {
|
|
67
|
+
if (ratios.length < 3) return "stable";
|
|
68
|
+
const third = Math.max(1, Math.floor(ratios.length / 3));
|
|
69
|
+
const head = ratios.slice(0, third).reduce((a, b) => a + b, 0) / third;
|
|
70
|
+
const tail = ratios.slice(-third).reduce((a, b) => a + b, 0) / third;
|
|
71
|
+
const delta = tail - head;
|
|
72
|
+
if (delta > 0.05) return "improving";
|
|
73
|
+
if (delta < -0.05) return "degrading";
|
|
74
|
+
return "stable";
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Positive sprint-flag check (matches sprintFlag: `=0`/`=false`/`_DISABLED=true` → OFF). */
|
|
78
|
+
function prefixStabilityEnabled(): boolean {
|
|
79
|
+
const v = process.env.MEGACOMPACT_PC_C;
|
|
80
|
+
if (v === "0" || v === "false") return false;
|
|
81
|
+
const disabled = process.env.MEGACOMPACT_PC_C_DISABLED;
|
|
82
|
+
if (disabled === "true" || disabled === "1") return false;
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function handlePrefixStability(
|
|
87
|
+
req: IncomingMessage,
|
|
88
|
+
res: ServerResponse,
|
|
89
|
+
ctx: RouteContext,
|
|
90
|
+
): boolean {
|
|
91
|
+
if (!req.url?.startsWith("/api/prefix-stability")) return false;
|
|
92
|
+
if (!prefixStabilityEnabled()) {
|
|
93
|
+
// guardrails-allow PREVENT-PI-004: loopback dashboard response (local)
|
|
94
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
95
|
+
res.end(JSON.stringify({ error: "not_found" }));
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
if (req.method !== "GET") {
|
|
99
|
+
// guardrails-allow PREVENT-PI-004: loopback dashboard response (local)
|
|
100
|
+
res.writeHead(405, { "Content-Type": "application/json" });
|
|
101
|
+
res.end(JSON.stringify({ error: "method_not_allowed" }));
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
let limit = 50;
|
|
105
|
+
if (req.url.includes("?")) {
|
|
106
|
+
const params = new URLSearchParams(req.url.slice(req.url.indexOf("?")));
|
|
107
|
+
const l = params.get("limit");
|
|
108
|
+
if (l != null) {
|
|
109
|
+
const n = parseInt(l, 10);
|
|
110
|
+
if (Number.isFinite(n) && n > 0) limit = Math.min(n, 500);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
const events = readPrefixStabilityEvents(ctx.eventsPath, limit);
|
|
115
|
+
const turns = events.map((e, i) => ({
|
|
116
|
+
turnIndex: i,
|
|
117
|
+
stablePrefix: e.stablePrefix,
|
|
118
|
+
totalMessages: e.totalMessages,
|
|
119
|
+
ratio: e.totalMessages > 0 ? e.stablePrefix / e.totalMessages : 0,
|
|
120
|
+
striping: e.striping,
|
|
121
|
+
timestamp: new Date(e.ts).toISOString(),
|
|
122
|
+
}));
|
|
123
|
+
const avgRatio = turns.length
|
|
124
|
+
? turns.reduce((a, t) => a + t.ratio, 0) / turns.length
|
|
125
|
+
: 0;
|
|
126
|
+
const body: PrefixStabilityResponse = {
|
|
127
|
+
turns,
|
|
128
|
+
avgRatio: Math.round(avgRatio * 1000) / 1000,
|
|
129
|
+
trend: classifyTrend(turns.map((t) => t.ratio)),
|
|
130
|
+
count: turns.length,
|
|
131
|
+
lastScanAt: Date.now(),
|
|
132
|
+
};
|
|
133
|
+
// guardrails-allow PREVENT-PI-004: loopback dashboard response (local)
|
|
134
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
135
|
+
res.end(JSON.stringify(body));
|
|
136
|
+
} catch (e) {
|
|
137
|
+
// guardrails-allow PREVENT-PI-004: loopback dashboard response (local)
|
|
138
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
139
|
+
res.end(JSON.stringify({ error: "prefix_stability_unavailable", detail: String(e) }));
|
|
140
|
+
}
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
@@ -216,5 +216,11 @@ export const VECTOR_CORTEX_SETTINGS: SettingGroup = {
|
|
|
216
216
|
"Embedder-detect consolidation + VC9 workstream roll-up: memoizes /api/setup-detect against the mutable input (resolved binary path + mtime) so consecutive requests reuse the result without re-spawning, and unifies the embedder + cortex sub-tabs' 5s poll contract. OFF = byte-identical predecessor (VC9C-era): detect spawns fresh per request and the embedder poll keeps its previous cadence.",
|
|
217
217
|
true,
|
|
218
218
|
),
|
|
219
|
+
boolDirect(
|
|
220
|
+
"MEGACOMPACT_PC_C",
|
|
221
|
+
"PC-C Dashboard Cache Visibility",
|
|
222
|
+
"Dashboard per-turn prompt-cache visibility: surfaces the per-turn stable-prefix ratio trend (GET /api/prefix-stability) in the CacheTab PrefixStabilityCard. Reads aggregate ratios/counts from the local monitoring events log only — no payload bytes. OFF = byte-identical predecessor (PC-B-era): /api/prefix-stability returns 404 and the CacheTab omits the PrefixStabilityCard.",
|
|
223
|
+
true,
|
|
224
|
+
),
|
|
219
225
|
],
|
|
220
226
|
};
|
|
@@ -30,6 +30,7 @@ export {
|
|
|
30
30
|
export { handleMemoryMap } from "./routes-memory-map.js";
|
|
31
31
|
export { handleRaptorTree, handleRaptorBuildHistory } from "./routes-raptor.js";
|
|
32
32
|
export { handleCacheStripes } from "./routes-cache.js";
|
|
33
|
+
export { handlePrefixStability } from "./routes-prefix-stability.js";
|
|
33
34
|
export {
|
|
34
35
|
handleContextHealth,
|
|
35
36
|
handleCachePoison,
|
|
@@ -87,7 +87,11 @@ export function buildTailResult(
|
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
89
|
lastTurnPrefix.set(runtime, { sessionId, turn, fingerprints });
|
|
90
|
-
|
|
90
|
+
// PC-C: write to the always-on monitoring events log (appendEvent),
|
|
91
|
+
// not the debug-gated runtime logger (mega-compact.log), so the
|
|
92
|
+
// dashboard /api/prefix-stability endpoint can read this per-turn
|
|
93
|
+
// stable-prefix ratio from ctx.eventsPath. Same field shape.
|
|
94
|
+
runtime.appendEvent("prefix_stability", {
|
|
91
95
|
stablePrefix,
|
|
92
96
|
totalMessages: result.length,
|
|
93
97
|
separation: config.messageSeparation ? "v2" : "off",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-mega-compact",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.34",
|
|
4
4
|
"description": "Layered, local, vector-backed context compressor for pi — supersede/collapse/cluster compaction with deduped inline recall.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "BSD-3-Clause",
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* config/vector-cortex-pcc.ts — PC-C dashboard prompt-cache visibility flag.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from vector-cortex.ts so that file stays under the 300-line soft
|
|
5
|
+
* limit (soft-as-hard gate), exactly as the vector-cortex-vc9{a,b,c,d}.ts
|
|
6
|
+
* siblings were. This is the PC-C dashboard per-turn prefix-stability flag.
|
|
7
|
+
* vector-cortex.ts re-exports it below and root src/config.ts re-exports it,
|
|
8
|
+
* so no consumer import path changes.
|
|
9
|
+
*
|
|
10
|
+
* The split is purely mechanical: PCC_ENABLED is byte-identical in name,
|
|
11
|
+
* semantics, and default to the sprint-flag convention it follows, and
|
|
12
|
+
* vector-cortex.ts re-exports it so every existing `from
|
|
13
|
+
* "./config/vector-cortex.js"` import keeps resolving unchanged.
|
|
14
|
+
*
|
|
15
|
+
* Pi-agnostic, dependency-free (PREVENT-PI-004 / PREVENT-011).
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { sprintFlag } from "./vector-cortex-flag.js";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* PC-C — dashboard per-turn prefix-stability visibility. Default ON.
|
|
22
|
+
* `MEGACOMPACT_PC_C=0` disables and is byte-identical to the predecessor
|
|
23
|
+
* (PC-B-era): /api/prefix-stability returns 404/disabled and the CacheTab
|
|
24
|
+
* renders exactly as before (stripe distribution + hit-rate trend only). When
|
|
25
|
+
* ON, the endpoint surfaces the per-turn stable-prefix ratio trend from the
|
|
26
|
+
* `prefix_stability` events in the monitoring log. The response contains
|
|
27
|
+
* aggregate ratios/counts only, never message content (EVAL-REDACT-002), and
|
|
28
|
+
* reads only the local events log (PREVENT-PI-004). This flag MUST also be a
|
|
29
|
+
* dashboard SETTINGS toggle (visible in config UI, never in EXCLUDED_SETTINGS),
|
|
30
|
+
* mirroring the other PC/VC flags.
|
|
31
|
+
*/
|
|
32
|
+
export const PCC_ENABLED = (): boolean => sprintFlag("MEGACOMPACT_PC_C");
|
|
@@ -280,9 +280,9 @@ export { VC9A_ENABLED } from "./vector-cortex-vc9a.js";
|
|
|
280
280
|
export { VC9B_ENABLED } from "./vector-cortex-vc9b.js";
|
|
281
281
|
export { VC9C_ENABLED } from "./vector-cortex-vc9c.js";
|
|
282
282
|
export { VC9D_ENABLED } from "./vector-cortex-vc9d.js";
|
|
283
|
+
export { PCC_ENABLED } from "./vector-cortex-pcc.js";
|
|
283
284
|
|
|
284
|
-
// Breaker
|
|
285
|
-
// vector-cortex-breakers.ts to keep this file under the 300-line soft limit.
|
|
285
|
+
// Breaker constants (TRIAD_RESILIENCE.md §breaker) extracted to vector-cortex-breakers.ts.
|
|
286
286
|
export {
|
|
287
287
|
BREAKER_WINDOW_MS,
|
|
288
288
|
BREAKER_MIN_ATTEMPTS,
|
package/src/config.ts
CHANGED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{j as e,r as h,u as j,A as L,D as $,z as H,l as M}from"./index-eoCm0uxj.js";import{C as I,T as k}from"./TimeSavedCard-CYltysD3.js";import{b as m,c as u,d as w,e as W,f as d}from"./format-COjDIVGj.js";import{l as D}from"./pricing-DkYp8-vi.js";import{C as g,a as f,b as C,c as S}from"./card-Ce58rSXG.js";import{B as _}from"./button-B_4j_PVR.js";function R(a){return a>=80?"ov-stat-value-green":a>=50?"ov-stat-value-yellow":"ov-stat-value-red"}function F(a){return a==null?"":a>=0?"ov-stat-value-green":"ov-stat-value-red"}function r({label:a,value:s,className:t,title:l}){return e.jsxs("div",{className:"flex items-baseline justify-between border-b border-border py-1 text-sm",children:[e.jsx("span",{className:"text-xs text-muted-foreground",title:l,children:a}),e.jsx("span",{className:`font-semibold${t?` ${t}`:""}`,children:s})]})}function O(a){return e.jsxs(g,{className:"electric-hover",children:[e.jsx(f,{children:e.jsx(C,{children:"Provider Prompt Cache"})}),e.jsxs(S,{children:[e.jsx(r,{label:"Cache Hit Rate",value:`${a.hitPct.toFixed(1)}%`,className:R(a.hitPct)}),e.jsx(r,{label:"Turns Recorded",value:String(a.turnCount)}),e.jsx(r,{label:"Cache Read Tokens",value:m(a.totalCacheRead)}),e.jsx(r,{label:"Cache Write Tokens",value:m(a.totalCacheWrite)}),e.jsx(r,{label:"Input Tokens",value:m(a.totalInput)}),e.jsx(r,{label:"$ Saved (reads)",value:u(a.cacheReadSaved)}),e.jsx(r,{label:"Write Investment",value:u(a.cacheWriteCost)}),e.jsx(r,{label:"Net Saved",value:u(a.netSaved),className:F(a.netSaved)}),e.jsx(r,{label:"Model",value:a.modelLabel??"—"}),e.jsx(r,{label:"Tracked Since",value:w(a.firstTurnAt)}),e.jsx(r,{label:"Last Updated",value:W(a.latestTurnAt)}),a.byModel.length>0&&e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"mt-2 text-xs font-semibold text-muted-foreground",children:"Per Model"}),a.byModel.map(s=>{const t=D(s.model),l=t!=null?s.totalCacheRead/1e6*t:null,n=t!=null?s.totalCacheWrite/1e6*t:null;return e.jsxs("div",{className:"mt-2",children:[e.jsx(r,{label:"Model",value:s.model}),e.jsx(r,{label:"Hit Rate",value:`${s.hitPct.toFixed(1)}%`,className:R(s.hitPct)}),e.jsx(r,{label:"Cache Read",value:m(s.totalCacheRead)}),e.jsx(r,{label:"Cache Write",value:m(s.totalCacheWrite)}),e.jsx(r,{label:"Samples",value:String(s.sampleCount)}),e.jsx(r,{label:"$ Saved (reads)",value:l!=null?u(l):"—"}),e.jsx(r,{label:"Write Investment",value:n!=null?u(n):"—"})]},s.model)})]})]})]})}const G=["ov-bar-green","ov-bar-blue","ov-bar-teal","ov-bar-yellow","ov-bar-gray"],B=["Permanent","Epoch","Topic","Thread","Volatile"],z={good:"ov-text-green",fair:"ov-text-yellow",degraded:"ov-text-orange",poor:"ov-text-red"};function K({data:a}){const s=Math.max(...a.buckets.map(l=>l.count),1),t=z[a.health.label]??"ov-text-gray";return e.jsxs("div",{className:"ov-card",children:[e.jsx("h3",{className:"ov-card-title",children:"Cache Stripe Distribution"}),e.jsxs("div",{className:"ov-stripe-health-summary",children:[e.jsx("span",{className:`ov-stripe-health-badge ${t}`,children:a.health.label}),e.jsx("span",{className:"ov-stripe-health-score",children:d(a.health.score)}),e.jsxs("span",{className:"ov-stripe-churn",children:["Churn: ",d(a.health.churnRate)]}),e.jsxs("span",{className:"ov-stripe-total",children:[a.totalChunks," total chunks"]})]}),e.jsx("div",{className:"ov-stripe-bars",children:a.buckets.map(l=>{const n=s>0?Math.max(0,Math.min(100,l.count/s*100)):0,o=G[l.stripe]??"ov-bar-gray",c=l.label||(B[l.stripe]??`Stripe ${l.stripe}`);return e.jsxs("div",{className:"ov-stripe-row",children:[e.jsx("span",{className:"ov-stripe-label",title:c,children:c}),e.jsx("div",{className:"ov-bar-track",children:e.jsx("div",{className:`ov-bar-fill ${o}`,style:{width:`${n}%`}})}),e.jsx("span",{className:"ov-stripe-count",children:l.count}),e.jsx("span",{className:"ov-stripe-stability",title:`min ${d(l.minStability)} / max ${d(l.maxStability)}`,children:d(l.avgStability)})]},l.stripe)})}),e.jsxs("div",{className:"ov-stripe-footer",children:["Epoch: ",a.epochId??"—","· Dominant tier: ",d(a.health.dominantTier)]})]})}function x(a){return`${a.toFixed(1)}%`}function U(a){return a>=80?"ov-bar-green":a>=60?"ov-bar-teal":a>=40?"ov-bar-yellow":a>=20?"ov-bar-orange":"ov-bar-red"}const V=({perf:a})=>{const{cache_hit_pct:s}=a,t=s.samples&&s.samples.length>0?s.samples:[];return e.jsxs(g,{className:"electric-hover",children:[e.jsx(f,{children:e.jsx(C,{children:"Cache Hit-Rate Trend"})}),e.jsxs(S,{children:[e.jsxs("div",{className:"mb-3 flex gap-4 text-sm text-muted-foreground",children:[e.jsxs("div",{children:["Avg: ",e.jsx("strong",{className:"text-foreground",children:x(s.avg)})]}),e.jsxs("div",{children:["Latest: ",e.jsx("strong",{className:"text-foreground",children:x(s.latest)})]}),e.jsxs("div",{children:["Samples: ",e.jsx("strong",{className:"text-foreground",children:s.n})]})]}),t.length>0?e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"ov-sparkline",role:"img","aria-label":`Cache hit rate trend: ${s.n} samples, average ${x(s.avg)}, latest ${x(s.latest)}`,children:t.map((l,n)=>{const o=Math.max(2,Math.round(l.pct*.6)),c=U(l.pct),v=n===t.length-1;return e.jsx("div",{className:`ov-sparkline-bar ${c}${v?" ov-bar-latest":""}`,style:{height:`${o}px`},title:`${x(l.pct)} at ${new Date(l.ts).toLocaleTimeString()}`},n)})}),e.jsxs("div",{className:"mt-1 text-xs text-muted-foreground",children:[t.length," sample",t.length!==1?"s":""," over"," ",t.length>1?`${Math.round((t[t.length-1].ts-t[0].ts)/1e3)}s`:"latest turn"]})]}):e.jsx("div",{className:"text-sm text-muted-foreground",children:"No cache-hit samples yet"})]})]})};function ee(){const[a,s]=h.useState(!1),{data:t,loading:l,error:n}=j(h.useCallback(()=>L(),[]),{pollInterval:5e3}),{data:o,loading:c,error:v}=j(h.useCallback(()=>$(),[]),{pollInterval:15e3}),{data:N,loading:A,error:T}=j(h.useCallback(()=>H({minutes:30}),[]),{pollInterval:15e3}),{data:i,loading:E,error:y}=j(h.useCallback(()=>M(),[]),{pollInterval:5e3});return e.jsxs("div",{className:"flex flex-col gap-4",children:[e.jsxs("div",{children:[e.jsx(_,{type:"button",variant:"outline",onClick:()=>s(!a),children:a?"Hide Guidance":"Show Cache-Friendly Prompt Ordering Guidance"}),a&&e.jsxs(g,{className:"mt-3",children:[e.jsx(f,{children:e.jsx(C,{children:"Cache-Friendly Prompt Ordering"})}),e.jsxs(S,{children:[e.jsxs("ul",{className:"list-disc space-y-1 pl-5",children:[e.jsx("li",{children:"Keep system prompts and stable context at the top of your conversation — the provider caches the leading prefix."}),e.jsxs("li",{children:["Tool results and volatile content are automatically moved to the tail by message separation (",e.jsx("code",{children:"MEGACOMPACT_MESSAGE_SEPARATION"}),") so they don't invalidate the cache prefix."]}),e.jsxs("li",{children:["Cache striping (",e.jsx("code",{children:"MEGACOMPACT_CACHE_STRIPING"}),") further orders stable context by a stability score so the most durable chunks lead."]}),e.jsx("li",{children:"Avoid inserting new instructions mid-conversation — prepend them instead."})]}),i&&e.jsxs("div",{className:"mt-2 text-sm",children:[e.jsx("strong",{children:"Status:"})," ",i.config.auto?"Auto-compaction Enabled":"Auto-compaction Disabled"," |"," ","fast-gate ",i.config.fastGatePct,"% / threshold"," ",i.config.thresholdTokens.toLocaleString()," tokens"]})]})]})]}),e.jsx("h2",{className:"font-heading text-lg font-semibold",children:"Provider Prompt Cache"}),l?e.jsx("p",{className:"tab-stub",children:"Loading..."}):n?e.jsxs("p",{className:"tab-stub",children:["Provider cache unavailable: ",n.message]}):t?e.jsx(O,{hitPct:t.cache.avgHitPct,turnCount:t.cache.turnCount,totalCacheRead:t.cache.totalCacheRead,totalCacheWrite:t.cache.totalCacheWrite,totalInput:t.cache.totalInput,cacheReadSaved:t.savings?.cacheReadSaved??null,cacheWriteCost:t.savings?.cacheWriteCost??null,netSaved:t.savings?.netSaved??null,modelLabel:t.savings?.model??null,firstTurnAt:t.cache.firstTurnAt,latestTurnAt:t.cache.latestTurnAt,byModel:t.cache.byModel??[]}):null,e.jsx("h2",{className:"font-heading text-lg font-semibold",children:"Cache Stripe Distribution"}),c?e.jsx("p",{className:"tab-stub",children:"Loading..."}):v?e.jsxs("p",{className:"tab-stub",children:["Cache stripe data unavailable: ",v.message]}):o?e.jsx(K,{data:o}):null,e.jsx("h2",{className:"font-heading text-lg font-semibold",children:"Cache Hit-Rate Trend"}),A?e.jsx("p",{className:"tab-stub",children:"Loading..."}):T?e.jsxs("p",{className:"tab-stub",children:["Hit-rate trend unavailable: ",T.message]}):N?e.jsx(V,{perf:N}):null,e.jsx("h2",{className:"font-heading text-lg font-semibold",children:"Mega-Compact Dedup Cache"}),E?e.jsx("p",{className:"tab-stub",children:"Loading..."}):y?e.jsxs("p",{className:"tab-stub",children:["Snapshot unavailable: ",y.message]}):i&&(()=>{const{cacheHits:p,compacts:P,timeSaved:b}=i;return e.jsxs("div",{className:"grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3",children:[e.jsx(I,{cacheHitsSession:p.session,cacheHitsTotal:p.total,tokensSavedSession:p.sessionTokensSaved,tokensSavedTotal:p.totalTokensSaved,compactionsSession:P.session,compactionsTotal:P.total}),e.jsx(k,{compactSessionSec:b.compact.sessionSec,compactTotalSec:b.compact.totalSec,cacheHitSessionSec:b.cacheHit.sessionSec,cacheHitTotalSec:b.cacheHit.totalSec})]})})()]})}export{ee as default};
|
|
2
|
-
//# sourceMappingURL=CacheTab-C3KWDFDm.js.map
|