pi-mega-compact 0.16.0 → 0.16.2
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/extensions/dashboard-server/api-contracts/endpoints/registry.js +7 -0
- package/dist/extensions/dashboard-server/api-contracts/model-thresholds.js +1 -0
- package/dist/extensions/dashboard-server/routes-model-thresholds.js +167 -0
- package/dist/extensions/dashboard-server/routes.js +3 -2
- package/dist/extensions/dashboard-server/server.js +6 -2
- package/dist/extensions/mega-events/context-handler.js +37 -11
- package/dist/src/store/sqlite/model-snapshots.js +21 -0
- package/dist/src/store/sqlite/model-thresholds.js +93 -0
- package/dist/src/store/sqlite/schema/core.js +10 -0
- package/dist/src/store/sqlite.js +1 -0
- package/dist/src/vector-search.js +1 -1
- package/extensions/dashboard-client/dist/assets/{AreaChart-CrmTcE7w.js → AreaChart-Y_X0Bbjd.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{AreaChart-CrmTcE7w.js.map → AreaChart-Y_X0Bbjd.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{BarChart-Cf8DTaXS.js → BarChart-B9XDb-Ob.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{BarChart-Cf8DTaXS.js.map → BarChart-B9XDb-Ob.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{CacheTab-Ce0MES6p.js → CacheTab-BUGVb1G1.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{CacheTab-Ce0MES6p.js.map → CacheTab-BUGVb1G1.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{EventsTab-DbaUuA58.js → EventsTab-dOqLYTsy.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{EventsTab-DbaUuA58.js.map → EventsTab-dOqLYTsy.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{HealthTab-BmEXkcfL.js → HealthTab-c2vezKg1.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{HealthTab-BmEXkcfL.js.map → HealthTab-c2vezKg1.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-n2JQrMy7.js → MaintenanceTab-bQYHIcdx.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-n2JQrMy7.js.map → MaintenanceTab-bQYHIcdx.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/MemoryMapTab-2L8pH6y2.js +2 -0
- package/extensions/dashboard-client/dist/assets/{MemoryMapTab-Dvu7vkOm.js.map → MemoryMapTab-2L8pH6y2.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MetricsTab-Q0g32AmP.js → MetricsTab-CzUx1jpo.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MetricsTab-Q0g32AmP.js.map → MetricsTab-CzUx1jpo.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{OverviewTab-iKiEcOLL.js → OverviewTab-eCNoaclv.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{OverviewTab-iKiEcOLL.js.map → OverviewTab-eCNoaclv.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{ReposTab-DWpnv77y.js → ReposTab-tb0IW3NF.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{ReposTab-DWpnv77y.js.map → ReposTab-tb0IW3NF.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{SessionsTab-G0YxR1vG.js → SessionsTab-C-IoLw5h.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{SessionsTab-G0YxR1vG.js.map → SessionsTab-C-IoLw5h.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/SetupTab-BvmFi8gf.js +2 -0
- package/extensions/dashboard-client/dist/assets/SetupTab-BvmFi8gf.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-1_LCFiJp.js → TimeSavedCard-DBH-cOlA.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-1_LCFiJp.js.map → TimeSavedCard-DBH-cOlA.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TurnsTab-pqOkjE4L.js → TurnsTab-DBMgmvnX.js} +3 -3
- package/extensions/dashboard-client/dist/assets/{TurnsTab-pqOkjE4L.js.map → TurnsTab-DBMgmvnX.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/WikiTab-1LcQrXk3.js +2 -0
- package/extensions/dashboard-client/dist/assets/{WikiTab-99sKE4nB.js.map → WikiTab-1LcQrXk3.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/button-DtEsZTUE.js +2 -0
- package/extensions/dashboard-client/dist/assets/{button-YcV2VQEP.js.map → button-DtEsZTUE.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{card-Bwxzo50p.js → card-DcfTmw-4.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{card-Bwxzo50p.js.map → card-DcfTmw-4.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-C6hlfMYG.js → generateCategoricalChart-BsIxUDyw.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-C6hlfMYG.js.map → generateCategoricalChart-BsIxUDyw.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{index-DRa2d4TF.js → index-B93cewiX.js} +14 -14
- package/extensions/dashboard-client/dist/assets/index-B93cewiX.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/index-C_tCbOI0.css +1 -0
- package/extensions/dashboard-client/dist/assets/{switch-rU-lor0v.js → switch-Bowd7jCD.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{switch-rU-lor0v.js.map → switch-Bowd7jCD.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{toggle-h-9B4w9X.js → toggle-Bo7kJZ4u.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{toggle-h-9B4w9X.js.map → toggle-Bo7kJZ4u.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{useSSE-BxJORWx8.js → useSSE-zNhb0c3u.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{useSSE-BxJORWx8.js.map → useSSE-zNhb0c3u.js.map} +1 -1
- package/extensions/dashboard-client/dist/index.html +2 -2
- package/extensions/dashboard-client/src/api/client.ts +38 -13
- package/extensions/dashboard-client/src/tabs/SetupTab/ThresholdsPanel.tsx +334 -0
- package/extensions/dashboard-client/src/tabs/SetupTab.tsx +9 -1
- package/extensions/dashboard-server/api-contracts/endpoints/registry.ts +50 -17
- package/extensions/dashboard-server/api-contracts/index.ts +28 -3
- package/extensions/dashboard-server/api-contracts/model-thresholds.ts +76 -0
- package/extensions/dashboard-server/routes-model-thresholds.ts +222 -0
- package/extensions/dashboard-server/routes.ts +11 -2
- package/extensions/dashboard-server/server.ts +5 -1
- package/extensions/mega-events/context-handler.ts +106 -69
- package/package.json +1 -1
- package/src/store/sqlite/model-snapshots.ts +99 -62
- package/src/store/sqlite/model-thresholds.ts +168 -0
- package/src/store/sqlite/schema/core.ts +10 -0
- package/src/store/sqlite.ts +1 -0
- package/src/vector-search.ts +1 -1
- package/extensions/dashboard-client/dist/assets/MemoryMapTab-Dvu7vkOm.js +0 -2
- package/extensions/dashboard-client/dist/assets/SetupTab-C-y6H-F-.js +0 -2
- package/extensions/dashboard-client/dist/assets/SetupTab-C-y6H-F-.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/WikiTab-99sKE4nB.js +0 -2
- package/extensions/dashboard-client/dist/assets/button-YcV2VQEP.js +0 -2
- package/extensions/dashboard-client/dist/assets/index-DRa2d4TF.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/index-za_gmEmQ.css +0 -1
|
@@ -54,6 +54,9 @@ import type {
|
|
|
54
54
|
SettingsUpdateRequest,
|
|
55
55
|
SettingsResponsePost,
|
|
56
56
|
RagMetricsResponse,
|
|
57
|
+
ModelThresholdsResponse,
|
|
58
|
+
ModelThresholdPutRequest,
|
|
59
|
+
ModelThresholdPutResponse,
|
|
57
60
|
WikiIndexResponse,
|
|
58
61
|
WikiPageResponse,
|
|
59
62
|
CurationResult,
|
|
@@ -337,7 +340,9 @@ export function configureEmbedder(
|
|
|
337
340
|
// ─── RAPTOR tree (Part B) ─────────────────────────────────────────────
|
|
338
341
|
|
|
339
342
|
/** GET /api/raptor-tree — tree for a session (defaults to latest with nodes). */
|
|
340
|
-
export function fetchRaptorTree(
|
|
343
|
+
export function fetchRaptorTree(
|
|
344
|
+
sessionId?: string,
|
|
345
|
+
): Promise<RaptorTreeResponse> {
|
|
341
346
|
return getJson<RaptorTreeResponse>(
|
|
342
347
|
`${ENDPOINTS.raptorTree.path}${query({ sessionId })}`,
|
|
343
348
|
);
|
|
@@ -398,10 +403,7 @@ export function fetchWikiIndex(): Promise<WikiIndexResponse> {
|
|
|
398
403
|
/** GET /api/wiki/topic/:topicId — single topic page + provenance. */
|
|
399
404
|
export function fetchWikiTopic(topicId: string): Promise<WikiPageResponse> {
|
|
400
405
|
return getJson<WikiPageResponse>(
|
|
401
|
-
ENDPOINTS.wikiTopic.path.replace(
|
|
402
|
-
":topicId",
|
|
403
|
-
encodeURIComponent(topicId),
|
|
404
|
-
),
|
|
406
|
+
ENDPOINTS.wikiTopic.path.replace(":topicId", encodeURIComponent(topicId)),
|
|
405
407
|
);
|
|
406
408
|
}
|
|
407
409
|
|
|
@@ -411,10 +413,7 @@ export function renameTopic(
|
|
|
411
413
|
label: string,
|
|
412
414
|
): Promise<CurationResult> {
|
|
413
415
|
return putJson<CurationResult>(
|
|
414
|
-
ENDPOINTS.renameTopic.path.replace(
|
|
415
|
-
":topicId",
|
|
416
|
-
encodeURIComponent(topicId),
|
|
417
|
-
),
|
|
416
|
+
ENDPOINTS.renameTopic.path.replace(":topicId", encodeURIComponent(topicId)),
|
|
418
417
|
{ label } satisfies RenameTopicRequest,
|
|
419
418
|
);
|
|
420
419
|
}
|
|
@@ -436,10 +435,7 @@ export function splitTopic(
|
|
|
436
435
|
memoryIds: string[],
|
|
437
436
|
): Promise<CurationResult> {
|
|
438
437
|
return postJson<CurationResult>(
|
|
439
|
-
ENDPOINTS.splitTopic.path.replace(
|
|
440
|
-
":topicId",
|
|
441
|
-
encodeURIComponent(topicId),
|
|
442
|
-
),
|
|
438
|
+
ENDPOINTS.splitTopic.path.replace(":topicId", encodeURIComponent(topicId)),
|
|
443
439
|
{ memoryIds } satisfies SplitTopicRequest,
|
|
444
440
|
);
|
|
445
441
|
}
|
|
@@ -460,3 +456,32 @@ export function fetchTopicTimeline(
|
|
|
460
456
|
export function fetchTopicEvolution(): Promise<TopicEvolutionResponse> {
|
|
461
457
|
return getJson<TopicEvolutionResponse>(ENDPOINTS.topicEvolution.path);
|
|
462
458
|
}
|
|
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,334 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SetupTab/ThresholdsPanel.tsx — per-model compaction thresholds (S52 / v0.16.1).
|
|
3
|
+
*
|
|
4
|
+
* Lists every known model (from model_snapshots) with two sliders each:
|
|
5
|
+
* - Safety Margin (0-20%): reserved after summary + maxOutput. Lower =
|
|
6
|
+
* more usable tail but risks the "too long even after compaction" error.
|
|
7
|
+
* - Fire Point (10-90%): % of window where compaction fires. Lower = compact
|
|
8
|
+
* earlier (more headroom); higher = more context before compaction.
|
|
9
|
+
*
|
|
10
|
+
* Warnings fire on <5% safety (overflow risk) and >85% fire point (aggressive
|
|
11
|
+
* — may hit the cap before compaction triggers). Changes save immediately via
|
|
12
|
+
* PUT /api/model-thresholds; a "Reset to default" button DELETEs the override.
|
|
13
|
+
*
|
|
14
|
+
* PREVENT-PI-004: all data comes from localhost dashboard API endpoints.
|
|
15
|
+
*/
|
|
16
|
+
import type React from "react";
|
|
17
|
+
import { useState, useEffect, useCallback } from "react";
|
|
18
|
+
import {
|
|
19
|
+
fetchModelThresholds,
|
|
20
|
+
putModelThreshold,
|
|
21
|
+
deleteModelThreshold,
|
|
22
|
+
} from "../../api/client";
|
|
23
|
+
import type { ModelThresholdsResponse } from "@contracts";
|
|
24
|
+
|
|
25
|
+
interface ModelRow {
|
|
26
|
+
readonly modelId: string;
|
|
27
|
+
readonly provider: string;
|
|
28
|
+
readonly modelName: string | null;
|
|
29
|
+
readonly contextWindow: number;
|
|
30
|
+
readonly maxTokens: number;
|
|
31
|
+
readonly hasOverride: boolean;
|
|
32
|
+
readonly threshold: {
|
|
33
|
+
readonly safetyMarginPct: number;
|
|
34
|
+
readonly firePointPct: number;
|
|
35
|
+
readonly isOverride: boolean;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function fmtWindow(n: number): string {
|
|
40
|
+
if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(2)}M`;
|
|
41
|
+
if (n >= 1000) return `${Math.round(n / 1000)}K`;
|
|
42
|
+
return String(n);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Live preview of the usable tail budget for a model + slider values. */
|
|
46
|
+
function usableTail(
|
|
47
|
+
ctxWindow: number,
|
|
48
|
+
maxTokens: number,
|
|
49
|
+
safetyPct: number,
|
|
50
|
+
): string {
|
|
51
|
+
if (ctxWindow <= 0) return "—";
|
|
52
|
+
const maxOut = maxTokens > 0 ? maxTokens : Math.ceil(ctxWindow * 0.1);
|
|
53
|
+
const reserve = maxOut + Math.ceil(ctxWindow * (safetyPct / 100));
|
|
54
|
+
const usable = ctxWindow - reserve;
|
|
55
|
+
if (usable <= 0) return "0 (at risk)";
|
|
56
|
+
return `${fmtWindow(usable)} tokens`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function ThresholdSlider({
|
|
60
|
+
label,
|
|
61
|
+
value,
|
|
62
|
+
min,
|
|
63
|
+
max,
|
|
64
|
+
step = 1,
|
|
65
|
+
onChange,
|
|
66
|
+
warning,
|
|
67
|
+
}: {
|
|
68
|
+
readonly label: string;
|
|
69
|
+
readonly value: number;
|
|
70
|
+
readonly min: number;
|
|
71
|
+
readonly max: number;
|
|
72
|
+
readonly step?: number;
|
|
73
|
+
readonly onChange: (v: number) => void;
|
|
74
|
+
readonly warning?: string;
|
|
75
|
+
}): React.ReactElement {
|
|
76
|
+
return (
|
|
77
|
+
<div className="flex flex-col gap-1">
|
|
78
|
+
<div className="flex items-center justify-between text-xs">
|
|
79
|
+
<span className="font-medium text-fg-muted">{label}</span>
|
|
80
|
+
<span
|
|
81
|
+
className={`font-mono ${warning ? "text-warning" : "text-fg"}`}
|
|
82
|
+
title={warning}
|
|
83
|
+
>
|
|
84
|
+
{value}%
|
|
85
|
+
</span>
|
|
86
|
+
</div>
|
|
87
|
+
<input
|
|
88
|
+
type="range"
|
|
89
|
+
min={min}
|
|
90
|
+
max={max}
|
|
91
|
+
step={step}
|
|
92
|
+
value={value}
|
|
93
|
+
onChange={(e) => onChange(Number(e.target.value))}
|
|
94
|
+
className="threshold-slider w-full"
|
|
95
|
+
/>
|
|
96
|
+
{warning && <p className="text-[10px] text-warning">⚠ {warning}</p>}
|
|
97
|
+
</div>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function ModelThresholdRow({
|
|
102
|
+
model,
|
|
103
|
+
defaults,
|
|
104
|
+
safetyRange,
|
|
105
|
+
fireRange,
|
|
106
|
+
onSave,
|
|
107
|
+
onReset,
|
|
108
|
+
saving,
|
|
109
|
+
}: {
|
|
110
|
+
readonly model: ModelRow;
|
|
111
|
+
readonly defaults: { safetyMarginPct: number; firePointPct: number };
|
|
112
|
+
readonly safetyRange: readonly [number, number];
|
|
113
|
+
readonly fireRange: readonly [number, number];
|
|
114
|
+
readonly onSave: (
|
|
115
|
+
modelId: string,
|
|
116
|
+
safetyMarginPct: number,
|
|
117
|
+
firePointPct: number,
|
|
118
|
+
) => Promise<void>;
|
|
119
|
+
readonly onReset: (modelId: string) => Promise<void>;
|
|
120
|
+
readonly saving: boolean;
|
|
121
|
+
}): React.ReactElement {
|
|
122
|
+
const [safety, setSafety] = useState(model.threshold.safetyMarginPct);
|
|
123
|
+
const [fire, setFire] = useState(model.threshold.firePointPct);
|
|
124
|
+
const [dirty, setDirty] = useState(false);
|
|
125
|
+
const [err, setErr] = useState<string | null>(null);
|
|
126
|
+
|
|
127
|
+
// Re-sync when the server data changes (e.g. after reset).
|
|
128
|
+
useEffect(() => {
|
|
129
|
+
setSafety(model.threshold.safetyMarginPct);
|
|
130
|
+
setFire(model.threshold.firePointPct);
|
|
131
|
+
setDirty(false);
|
|
132
|
+
}, [model.threshold.safetyMarginPct, model.threshold.firePointPct]);
|
|
133
|
+
|
|
134
|
+
const safetyWarning =
|
|
135
|
+
safety < 5
|
|
136
|
+
? `Low safety margin — risks "conversation too long" errors when a single turn injects a large tool output between gate fires.`
|
|
137
|
+
: undefined;
|
|
138
|
+
const fireWarning =
|
|
139
|
+
fire > 85
|
|
140
|
+
? `High fire point — compaction may trigger close to the window cap; a single large turn can overshoot before the gate fires.`
|
|
141
|
+
: undefined;
|
|
142
|
+
|
|
143
|
+
const save = useCallback(async () => {
|
|
144
|
+
setErr(null);
|
|
145
|
+
try {
|
|
146
|
+
await onSave(model.modelId, safety, fire);
|
|
147
|
+
setDirty(false);
|
|
148
|
+
} catch (e) {
|
|
149
|
+
setErr(e instanceof Error ? e.message : String(e));
|
|
150
|
+
}
|
|
151
|
+
}, [model.modelId, safety, fire, onSave]);
|
|
152
|
+
|
|
153
|
+
return (
|
|
154
|
+
<div className="rounded-md border border-border bg-bg-elevated/30 p-3">
|
|
155
|
+
<div className="mb-2 flex items-start justify-between">
|
|
156
|
+
<div>
|
|
157
|
+
<div className="flex items-center gap-2">
|
|
158
|
+
<span className="text-sm font-semibold text-fg">
|
|
159
|
+
{model.modelName ?? model.modelId}
|
|
160
|
+
</span>
|
|
161
|
+
{model.threshold.isOverride && (
|
|
162
|
+
<span
|
|
163
|
+
className="rounded border border-primary/40 bg-primary/15 px-1.5 py-0.5 text-[10px] font-semibold uppercase text-primary"
|
|
164
|
+
title="This model has a custom override (not the default)"
|
|
165
|
+
>
|
|
166
|
+
Override
|
|
167
|
+
</span>
|
|
168
|
+
)}
|
|
169
|
+
</div>
|
|
170
|
+
<div className="text-[11px] text-fg-muted">
|
|
171
|
+
{model.provider} · {fmtWindow(model.contextWindow)} window ·{" "}
|
|
172
|
+
{fmtWindow(model.maxTokens)} output
|
|
173
|
+
</div>
|
|
174
|
+
</div>
|
|
175
|
+
<div className="text-right text-[11px] text-fg-muted">
|
|
176
|
+
<div>
|
|
177
|
+
Usable tail:{" "}
|
|
178
|
+
<span className="font-mono text-fg">
|
|
179
|
+
{usableTail(model.contextWindow, model.maxTokens, safety)}
|
|
180
|
+
</span>
|
|
181
|
+
</div>
|
|
182
|
+
<div>
|
|
183
|
+
Compaction fires at:{" "}
|
|
184
|
+
<span className="font-mono text-fg">
|
|
185
|
+
{fmtWindow(Math.ceil(model.contextWindow * (fire / 100)))}
|
|
186
|
+
</span>
|
|
187
|
+
</div>
|
|
188
|
+
</div>
|
|
189
|
+
</div>
|
|
190
|
+
<div className="grid grid-cols-1 gap-3 sm:grid-cols-2">
|
|
191
|
+
<ThresholdSlider
|
|
192
|
+
label="Safety margin"
|
|
193
|
+
value={safety}
|
|
194
|
+
min={safetyRange[0]}
|
|
195
|
+
max={safetyRange[1]}
|
|
196
|
+
onChange={(v) => {
|
|
197
|
+
setSafety(v);
|
|
198
|
+
setDirty(true);
|
|
199
|
+
}}
|
|
200
|
+
warning={safetyWarning}
|
|
201
|
+
/>
|
|
202
|
+
<ThresholdSlider
|
|
203
|
+
label="Fire point"
|
|
204
|
+
value={fire}
|
|
205
|
+
min={fireRange[0]}
|
|
206
|
+
max={fireRange[1]}
|
|
207
|
+
onChange={(v) => {
|
|
208
|
+
setFire(v);
|
|
209
|
+
setDirty(true);
|
|
210
|
+
}}
|
|
211
|
+
warning={fireWarning}
|
|
212
|
+
/>
|
|
213
|
+
</div>
|
|
214
|
+
<div className="mt-2 flex items-center gap-2">
|
|
215
|
+
<button
|
|
216
|
+
type="button"
|
|
217
|
+
onClick={save}
|
|
218
|
+
disabled={!dirty || saving}
|
|
219
|
+
className="rounded-md bg-primary px-2.5 py-1 text-xs font-semibold text-primary-fg disabled:cursor-not-allowed disabled:opacity-50"
|
|
220
|
+
>
|
|
221
|
+
{dirty ? "Save override" : "Saved"}
|
|
222
|
+
</button>
|
|
223
|
+
{model.threshold.isOverride && (
|
|
224
|
+
<button
|
|
225
|
+
type="button"
|
|
226
|
+
onClick={() => onReset(model.modelId)}
|
|
227
|
+
disabled={saving}
|
|
228
|
+
className="rounded-md border border-border px-2.5 py-1 text-xs text-fg-muted disabled:cursor-not-allowed disabled:opacity-50"
|
|
229
|
+
>
|
|
230
|
+
Reset to default ({defaults.safetyMarginPct}% /{" "}
|
|
231
|
+
{defaults.firePointPct}%)
|
|
232
|
+
</button>
|
|
233
|
+
)}
|
|
234
|
+
{err && <span className="text-[11px] text-error">{err}</span>}
|
|
235
|
+
</div>
|
|
236
|
+
</div>
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export default function ThresholdsPanel(): React.ReactElement {
|
|
241
|
+
const [data, setData] = useState<ModelThresholdsResponse | null>(null);
|
|
242
|
+
const [loading, setLoading] = useState(true);
|
|
243
|
+
const [error, setError] = useState<string | null>(null);
|
|
244
|
+
const [saving, setSaving] = useState(false);
|
|
245
|
+
|
|
246
|
+
const load = useCallback(async () => {
|
|
247
|
+
try {
|
|
248
|
+
setError(null);
|
|
249
|
+
setData(await fetchModelThresholds());
|
|
250
|
+
} catch (e) {
|
|
251
|
+
setError(e instanceof Error ? e.message : String(e));
|
|
252
|
+
} finally {
|
|
253
|
+
setLoading(false);
|
|
254
|
+
}
|
|
255
|
+
}, []);
|
|
256
|
+
|
|
257
|
+
useEffect(() => {
|
|
258
|
+
load();
|
|
259
|
+
}, [load]);
|
|
260
|
+
|
|
261
|
+
const onSave = useCallback(
|
|
262
|
+
async (modelId: string, safetyMarginPct: number, firePointPct: number) => {
|
|
263
|
+
setSaving(true);
|
|
264
|
+
try {
|
|
265
|
+
await putModelThreshold({ modelId, safetyMarginPct, firePointPct });
|
|
266
|
+
await load();
|
|
267
|
+
} finally {
|
|
268
|
+
setSaving(false);
|
|
269
|
+
}
|
|
270
|
+
},
|
|
271
|
+
[load],
|
|
272
|
+
);
|
|
273
|
+
|
|
274
|
+
const onReset = useCallback(
|
|
275
|
+
async (modelId: string) => {
|
|
276
|
+
setSaving(true);
|
|
277
|
+
try {
|
|
278
|
+
await deleteModelThreshold(modelId);
|
|
279
|
+
await load();
|
|
280
|
+
} finally {
|
|
281
|
+
setSaving(false);
|
|
282
|
+
}
|
|
283
|
+
},
|
|
284
|
+
[load],
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
if (loading)
|
|
288
|
+
return <div className="p-4 text-sm text-fg-muted">Loading thresholds…</div>;
|
|
289
|
+
if (error)
|
|
290
|
+
return <div className="p-4 text-sm text-error">Error: {error}</div>;
|
|
291
|
+
if (!data) return <div className="p-4 text-sm text-fg-muted">No data.</div>;
|
|
292
|
+
|
|
293
|
+
return (
|
|
294
|
+
<div className="flex flex-col gap-4">
|
|
295
|
+
<div className="rounded-md border border-border bg-bg-elevated/20 p-3">
|
|
296
|
+
<h3 className="text-sm font-semibold">
|
|
297
|
+
Per-Model Compaction Thresholds
|
|
298
|
+
</h3>
|
|
299
|
+
<p className="mt-1 text-[11px] text-fg-muted">
|
|
300
|
+
Tune when compaction fires + how much safety margin to reserve,
|
|
301
|
+
<strong> per model</strong>. Different providers' models range from 8K
|
|
302
|
+
to 1M+ context — one global % is wrong. Defaults:{" "}
|
|
303
|
+
<span className="font-mono">
|
|
304
|
+
{data.defaults.safetyMarginPct}% safety
|
|
305
|
+
</span>{" "}
|
|
306
|
+
/{" "}
|
|
307
|
+
<span className="font-mono">{data.defaults.firePointPct}% fire</span>.
|
|
308
|
+
Values outside the safe band show warnings.
|
|
309
|
+
</p>
|
|
310
|
+
</div>
|
|
311
|
+
{data.models.length === 0 ? (
|
|
312
|
+
<div className="rounded-md border border-border p-4 text-sm text-fg-muted">
|
|
313
|
+
No models captured yet. Start a pi session in any repo to populate
|
|
314
|
+
model snapshots, then return here to tune per-model thresholds.
|
|
315
|
+
</div>
|
|
316
|
+
) : (
|
|
317
|
+
<div className="flex flex-col gap-3">
|
|
318
|
+
{data.models.map((m) => (
|
|
319
|
+
<ModelThresholdRow
|
|
320
|
+
key={m.modelId}
|
|
321
|
+
model={m}
|
|
322
|
+
defaults={data.defaults}
|
|
323
|
+
safetyRange={data.defaults.safetyMarginRange}
|
|
324
|
+
fireRange={data.defaults.firePointRange}
|
|
325
|
+
onSave={onSave}
|
|
326
|
+
onReset={onReset}
|
|
327
|
+
saving={saving}
|
|
328
|
+
/>
|
|
329
|
+
))}
|
|
330
|
+
</div>
|
|
331
|
+
)}
|
|
332
|
+
</div>
|
|
333
|
+
);
|
|
334
|
+
}
|
|
@@ -13,12 +13,19 @@ import GameTab from "./GameTab";
|
|
|
13
13
|
import AchievementsTab from "./AchievementsTab";
|
|
14
14
|
import ConfigTab from "./ConfigTab";
|
|
15
15
|
import EmbedderSetup from "./SetupTab/EmbedderSetup";
|
|
16
|
+
import ThresholdsPanel from "./SetupTab/ThresholdsPanel";
|
|
16
17
|
import { Toggle } from "../components/ui/toggle";
|
|
17
18
|
|
|
18
|
-
type SetupSubTab =
|
|
19
|
+
type SetupSubTab =
|
|
20
|
+
| "embedder"
|
|
21
|
+
| "thresholds"
|
|
22
|
+
| "config"
|
|
23
|
+
| "game"
|
|
24
|
+
| "achievements";
|
|
19
25
|
|
|
20
26
|
const SUB_TABS: ReadonlyArray<{ id: SetupSubTab; label: string }> = [
|
|
21
27
|
{ id: "embedder", label: "Embedder" },
|
|
28
|
+
{ id: "thresholds", label: "Thresholds" },
|
|
22
29
|
{ id: "config", label: "Config" },
|
|
23
30
|
{ id: "game", label: "Game Mode" },
|
|
24
31
|
{ id: "achievements", label: "Achievements" },
|
|
@@ -45,6 +52,7 @@ export default function SetupTab(): React.ReactElement {
|
|
|
45
52
|
</nav>
|
|
46
53
|
|
|
47
54
|
{subTab === "embedder" && <EmbedderSetup />}
|
|
55
|
+
{subTab === "thresholds" && <ThresholdsPanel />}
|
|
48
56
|
{subTab === "config" && <ConfigTab />}
|
|
49
57
|
{subTab === "game" && <GameTab />}
|
|
50
58
|
{subTab === "achievements" && <AchievementsTab />}
|
|
@@ -15,9 +15,17 @@ import type { IndexesSummaryResponse } from "../multi-repo.js";
|
|
|
15
15
|
import type { GameStateResponse } from "../game.js";
|
|
16
16
|
import type { SseEvent } from "../index.js";
|
|
17
17
|
import type { ProviderCacheResponse } from "../provider-cache.js";
|
|
18
|
-
import type {
|
|
18
|
+
import type {
|
|
19
|
+
RaptorTreeResponse,
|
|
20
|
+
RaptorBuildHistoryResponse,
|
|
21
|
+
} from "../raptor.js";
|
|
19
22
|
import type { MemoryStatusResponse } from "../memory.js";
|
|
20
|
-
import type {
|
|
23
|
+
import type {
|
|
24
|
+
SetupStatusResponse,
|
|
25
|
+
SetupDetectResponse,
|
|
26
|
+
SetupConfigureRequest,
|
|
27
|
+
SetupConfigureResponse,
|
|
28
|
+
} from "../setup.js";
|
|
21
29
|
import type { EmbedderHealthResponse } from "../embedder-health.js";
|
|
22
30
|
import type {
|
|
23
31
|
RagSettingsResponse,
|
|
@@ -25,6 +33,7 @@ import type {
|
|
|
25
33
|
RagSettingsResponsePost,
|
|
26
34
|
} from "../rag-settings.js";
|
|
27
35
|
import type { RagMetricsResponse } from "../rag-metrics.js";
|
|
36
|
+
import type { ModelThresholdsResponse } from "../model-thresholds.js";
|
|
28
37
|
import type {
|
|
29
38
|
TurnsResponse,
|
|
30
39
|
ConversationTurnsResponse,
|
|
@@ -192,7 +201,11 @@ export const ENDPOINTS = {
|
|
|
192
201
|
path: "/api/perf/samples",
|
|
193
202
|
description:
|
|
194
203
|
"Raw perf_samples for one kind (time-series drill-down across any perf metric).",
|
|
195
|
-
} as const satisfies EndpointDef<
|
|
204
|
+
} as const satisfies EndpointDef<
|
|
205
|
+
"GET",
|
|
206
|
+
PerfSamplesQuery,
|
|
207
|
+
PerfSamplesResponse
|
|
208
|
+
>,
|
|
196
209
|
|
|
197
210
|
/** GET /api/achievements — Achievement tiles with unlock state. */
|
|
198
211
|
achievements: {
|
|
@@ -329,8 +342,7 @@ export const ENDPOINTS = {
|
|
|
329
342
|
topicTimeline: {
|
|
330
343
|
method: "GET",
|
|
331
344
|
path: "/api/wiki/topic/:topicId/timeline",
|
|
332
|
-
description:
|
|
333
|
-
"Per-topic memory-assignment timeline buckets for charts.",
|
|
345
|
+
description: "Per-topic memory-assignment timeline buckets for charts.",
|
|
334
346
|
} as const satisfies EndpointDef<"GET", undefined, TopicTimelineResponse>,
|
|
335
347
|
|
|
336
348
|
/** GET /api/wiki/evolution — node/edge D3 evolution graph. */
|
|
@@ -380,16 +392,15 @@ export const ENDPOINTS = {
|
|
|
380
392
|
"Lifetime provider prompt cache hit-rate aggregates + dollar savings estimate.",
|
|
381
393
|
} as const satisfies EndpointDef<"GET", undefined, ProviderCacheResponse>,
|
|
382
394
|
|
|
395
|
+
// ─── A3 Cache Stripes (PLAN_V2 Phase 4) ───────────────────────────
|
|
383
396
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
"Per-stripe distribution counts, average stability scores, and composite cache health.",
|
|
392
|
-
} as const satisfies EndpointDef<"GET", undefined, CacheStripesResponse>,
|
|
397
|
+
/** GET /api/cache-stripes — Per-stripe distribution + health score. */
|
|
398
|
+
cacheStripes: {
|
|
399
|
+
method: "GET",
|
|
400
|
+
path: "/api/cache-stripes",
|
|
401
|
+
description:
|
|
402
|
+
"Per-stripe distribution counts, average stability scores, and composite cache health.",
|
|
403
|
+
} as const satisfies EndpointDef<"GET", undefined, CacheStripesResponse>,
|
|
393
404
|
// ─── Memory Effectiveness (S53B) ───────────────────────────────────────
|
|
394
405
|
|
|
395
406
|
/** GET /api/memory-status — Memory store aggregate statistics. */
|
|
@@ -422,7 +433,11 @@ export const ENDPOINTS = {
|
|
|
422
433
|
path: "/api/setup-configure",
|
|
423
434
|
description:
|
|
424
435
|
"Write the chosen embedder configuration to .mega-compact.env (loaded at next startup). Returns whether a restart is required.",
|
|
425
|
-
} as const satisfies EndpointDef<
|
|
436
|
+
} as const satisfies EndpointDef<
|
|
437
|
+
"POST",
|
|
438
|
+
SetupConfigureRequest,
|
|
439
|
+
SetupConfigureResponse
|
|
440
|
+
>,
|
|
426
441
|
|
|
427
442
|
// ─── RAPTOR tree (Part B) ───────────────────────────────────────
|
|
428
443
|
|
|
@@ -440,7 +455,11 @@ export const ENDPOINTS = {
|
|
|
440
455
|
path: "/api/raptor-build-history",
|
|
441
456
|
description:
|
|
442
457
|
"RAPTOR build history rows (node/leaf count, depth, coherence score, timed_out) for a session; defaults to the latest session with builds.",
|
|
443
|
-
} as const satisfies EndpointDef<
|
|
458
|
+
} as const satisfies EndpointDef<
|
|
459
|
+
"GET",
|
|
460
|
+
undefined,
|
|
461
|
+
RaptorBuildHistoryResponse
|
|
462
|
+
>,
|
|
444
463
|
|
|
445
464
|
/** GET /api/embedder-health — Round-trip a test embed through the active embedder. */
|
|
446
465
|
embedderHealth: {
|
|
@@ -466,7 +485,11 @@ export const ENDPOINTS = {
|
|
|
466
485
|
path: "/api/rag-settings",
|
|
467
486
|
description:
|
|
468
487
|
"Update RAG feature flag states by writing _DISABLED env vars to .mega-compact.env.",
|
|
469
|
-
} as const satisfies EndpointDef<
|
|
488
|
+
} as const satisfies EndpointDef<
|
|
489
|
+
"POST",
|
|
490
|
+
RagSettingsRequest,
|
|
491
|
+
RagSettingsResponsePost
|
|
492
|
+
>,
|
|
470
493
|
|
|
471
494
|
// ─── RAG Metrics (Sprint H2) ──────────────────────────────────────
|
|
472
495
|
|
|
@@ -477,4 +500,14 @@ export const ENDPOINTS = {
|
|
|
477
500
|
description:
|
|
478
501
|
"HyDE invocation + recall-quality telemetry: flags, totals, recent turns, and daily series.",
|
|
479
502
|
} as const satisfies EndpointDef<"GET", undefined, RagMetricsResponse>,
|
|
503
|
+
|
|
504
|
+
// ─── Per-model compaction thresholds (S52 / v0.16.1) ───────────────
|
|
505
|
+
|
|
506
|
+
/** GET /api/model-thresholds — List known models + their thresholds. */
|
|
507
|
+
modelThresholds: {
|
|
508
|
+
method: "GET",
|
|
509
|
+
path: "/api/model-thresholds",
|
|
510
|
+
description:
|
|
511
|
+
"Every known model (from model_snapshots) with its per-model threshold override (or defaults).",
|
|
512
|
+
} as const satisfies EndpointDef<"GET", undefined, ModelThresholdsResponse>,
|
|
480
513
|
} as const;
|
|
@@ -230,7 +230,14 @@ export type {
|
|
|
230
230
|
export type { MemoryStatusResponse, MemoryStatsTopMemory } from "./memory.js";
|
|
231
231
|
|
|
232
232
|
// Setup wizard — embedder configuration + backend detection
|
|
233
|
-
export type {
|
|
233
|
+
export type {
|
|
234
|
+
SetupStatusResponse,
|
|
235
|
+
SetupDetectResponse,
|
|
236
|
+
SetupConfigureRequest,
|
|
237
|
+
SetupConfigureResponse,
|
|
238
|
+
DetectResult,
|
|
239
|
+
OllamaDetectResult,
|
|
240
|
+
} from "./setup.js";
|
|
234
241
|
|
|
235
242
|
// Embedder health probe
|
|
236
243
|
export type { EmbedderHealthResponse } from "./embedder-health.js";
|
|
@@ -245,7 +252,12 @@ export type {
|
|
|
245
252
|
} from "./memory-map.js";
|
|
246
253
|
|
|
247
254
|
// Part B — RAPTOR tree + build history
|
|
248
|
-
export type {
|
|
255
|
+
export type {
|
|
256
|
+
RaptorNodeDTO,
|
|
257
|
+
RaptorTreeResponse,
|
|
258
|
+
RaptorBuildHistoryDTO,
|
|
259
|
+
RaptorBuildHistoryResponse,
|
|
260
|
+
} from "./raptor.js";
|
|
249
261
|
|
|
250
262
|
// A3 — cache stripe distribution + health
|
|
251
263
|
export type {
|
|
@@ -288,4 +300,17 @@ export type {
|
|
|
288
300
|
SseWikiTopicSplit,
|
|
289
301
|
WikiSseEvent,
|
|
290
302
|
} from "./wiki.js";
|
|
291
|
-
export type {
|
|
303
|
+
export type {
|
|
304
|
+
CurationResult,
|
|
305
|
+
OverrideKind,
|
|
306
|
+
WikiCurationStore,
|
|
307
|
+
} from "../../../src/wiki/curation.js";
|
|
308
|
+
export type {
|
|
309
|
+
ModelThresholdDTO,
|
|
310
|
+
KnownModel,
|
|
311
|
+
ModelThresholdsResponse,
|
|
312
|
+
ModelThresholdPutRequest,
|
|
313
|
+
ModelThresholdPutResponse,
|
|
314
|
+
ModelThresholdDeleteResponse,
|
|
315
|
+
ModelThresholdsError,
|
|
316
|
+
} from "./model-thresholds.js";
|