pi-mega-compact 0.13.7 → 0.14.1
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 +13 -6
- package/dist/extensions/dashboard-server/api-contracts/endpoints/registry.js +6 -0
- package/dist/extensions/dashboard-server/index-reader.js +49 -26
- package/dist/extensions/dashboard-server/routes-cache.js +17 -5
- package/dist/extensions/dashboard-server/routes-rag-settings-helpers.js +7 -0
- package/dist/extensions/dashboard-server/routes-raptor.js +60 -0
- package/dist/extensions/dashboard-server/routes.js +1 -1
- package/dist/extensions/dashboard-server/server.js +3 -1
- package/dist/extensions/mega-pipeline/recall.js +24 -16
- package/dist/extensions/mega-runtime/capture-model.js +15 -2
- package/dist/extensions/mega-runtime/runtime-snapshot.js +26 -0
- package/dist/extensions/mega-runtime/runtime.js +1 -0
- package/dist/src/config/dedup.js +2 -1
- package/dist/src/costApi.js +113 -0
- package/dist/src/dedup/raptor/incremental.js +31 -3
- package/dist/src/dedup/raptor/index.js +10 -6
- package/dist/src/vector-search.js +3 -3
- package/extensions/dashboard-client/dist/assets/{AreaChart-j2VOSRp6.js → AreaChart-BUUketyS.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{AreaChart-j2VOSRp6.js.map → AreaChart-BUUketyS.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{CacheTab-Dr1HeMfc.js → CacheTab-VNFZSdRr.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{CacheTab-Dr1HeMfc.js.map → CacheTab-VNFZSdRr.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{EventsTab-DRG2k7FS.js → EventsTab-Dr7RDBy2.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{EventsTab-DRG2k7FS.js.map → EventsTab-Dr7RDBy2.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{HealthTab-DGE9PpW3.js → HealthTab-B8VZ8gbb.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{HealthTab-DGE9PpW3.js.map → HealthTab-B8VZ8gbb.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-DeYRhF4q.js → MaintenanceTab-DMtrXRcn.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-DeYRhF4q.js.map → MaintenanceTab-DMtrXRcn.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/MemoryMapTab-PFrMdLOm.js +2 -0
- package/extensions/dashboard-client/dist/assets/{MemoryMapTab-DSnSO3nG.js.map → MemoryMapTab-PFrMdLOm.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MetricsTab-RIzNc1Uj.js → MetricsTab-Dd1kXuvg.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MetricsTab-RIzNc1Uj.js.map → MetricsTab-Dd1kXuvg.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{OverviewTab-_1BJ4Bmh.js → OverviewTab-DOpObVA9.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{OverviewTab-_1BJ4Bmh.js.map → OverviewTab-DOpObVA9.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{ReposTab-DkU7ywZB.js → ReposTab-D6pEUtEJ.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{ReposTab-DkU7ywZB.js.map → ReposTab-D6pEUtEJ.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{SessionsTab-Bq_F_aOk.js → SessionsTab-CcFzDJ7W.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{SessionsTab-Bq_F_aOk.js.map → SessionsTab-CcFzDJ7W.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{SetupTab-BkBD2Y97.js → SetupTab-YCt5NGB5.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{SetupTab-BkBD2Y97.js.map → SetupTab-YCt5NGB5.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-BXzFKU7C.js → TimeSavedCard-BsLNhOOj.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-BXzFKU7C.js.map → TimeSavedCard-BsLNhOOj.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TurnsTab-Bjpp3vBH.js → TurnsTab-C1jpVZrH.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TurnsTab-Bjpp3vBH.js.map → TurnsTab-C1jpVZrH.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{WikiTab-dBN2EA5G.js → WikiTab-RHoVt95g.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{WikiTab-dBN2EA5G.js.map → WikiTab-RHoVt95g.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{button-Bt0b5QVp.js → button-DSZI4aZ5.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{button-Bt0b5QVp.js.map → button-DSZI4aZ5.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{card-D5CafXtg.js → card-CsZzIyPW.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{card-D5CafXtg.js.map → card-CsZzIyPW.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{index-GPHRnEN7.js → index-B1_J_Y0b.js} +11 -11
- package/extensions/dashboard-client/dist/assets/index-B1_J_Y0b.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/index-C5TGTOd6.css +1 -0
- package/extensions/dashboard-client/dist/assets/{switch-CY5Gwj1o.js → switch-DOIA1Nxu.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{switch-CY5Gwj1o.js.map → switch-DOIA1Nxu.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{toggle-C9HpkIfH.js → toggle-CyS6PHCO.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{toggle-C9HpkIfH.js.map → toggle-CyS6PHCO.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{useSSE-2JFrLDlE.js → useSSE-CY7UIUzm.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{useSSE-2JFrLDlE.js.map → useSSE-CY7UIUzm.js.map} +1 -1
- package/extensions/dashboard-client/dist/index.html +2 -2
- package/extensions/dashboard-client/src/api/client.ts +10 -0
- package/extensions/dashboard-client/src/components/layout/AppShell.tsx +1 -1
- package/extensions/dashboard-client/src/styles/base.css +2 -4
- package/extensions/dashboard-client/src/tabs/MemoryMapTab/RaptorTreeView.tsx +58 -4
- package/extensions/dashboard-client/tailwind.config.js +1 -1
- package/extensions/dashboard-server/api-contracts/endpoints/registry.ts +9 -1
- package/extensions/dashboard-server/api-contracts/index.ts +2 -2
- package/extensions/dashboard-server/api-contracts/raptor.ts +19 -0
- package/extensions/dashboard-server/index-reader.ts +28 -5
- package/extensions/dashboard-server/routes-cache.ts +17 -5
- package/extensions/dashboard-server/routes-rag-settings-helpers.ts +17 -0
- package/extensions/dashboard-server/routes-raptor.ts +88 -1
- package/extensions/dashboard-server/routes.ts +1 -1
- package/extensions/dashboard-server/server.ts +2 -0
- package/extensions/mega-pipeline/recall.ts +30 -21
- package/extensions/mega-runtime/capture-model.ts +14 -2
- package/extensions/mega-runtime/runtime-snapshot.ts +40 -0
- package/extensions/mega-runtime/runtime.ts +1 -0
- package/package.json +1 -1
- package/src/config/dedup.ts +2 -1
- package/src/costApi.ts +131 -0
- package/src/dedup/raptor/incremental.ts +48 -3
- package/src/dedup/raptor/index.ts +10 -6
- package/src/vector-search.ts +3 -3
- package/extensions/dashboard-client/dist/assets/MemoryMapTab-DSnSO3nG.js +0 -2
- package/extensions/dashboard-client/dist/assets/index-CUA6EDRF.css +0 -1
- package/extensions/dashboard-client/dist/assets/index-GPHRnEN7.js.map +0 -1
|
@@ -30,7 +30,8 @@ body {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
.dashboard-app {
|
|
33
|
-
|
|
33
|
+
width: 100%;
|
|
34
|
+
max-width: 100vw;
|
|
34
35
|
margin: 0 auto;
|
|
35
36
|
padding: clamp(8px, 2vw, 24px);
|
|
36
37
|
}
|
|
@@ -210,9 +211,6 @@ body {
|
|
|
210
211
|
}
|
|
211
212
|
|
|
212
213
|
@media (min-width: 1601px) {
|
|
213
|
-
.dashboard-app {
|
|
214
|
-
max-width: min(1800px, 96vw);
|
|
215
|
-
}
|
|
216
214
|
.card-grid {
|
|
217
215
|
grid-template-columns: repeat(4, 1fr);
|
|
218
216
|
}
|
|
@@ -7,8 +7,12 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import type React from "react";
|
|
9
9
|
import { useEffect, useState } from "react";
|
|
10
|
-
import { fetchRaptorTree } from "../../api/client";
|
|
11
|
-
import type {
|
|
10
|
+
import { fetchRaptorTree, fetchRaptorBuildHistory } from "../../api/client";
|
|
11
|
+
import type {
|
|
12
|
+
RaptorTreeResponse,
|
|
13
|
+
RaptorNodeDTO,
|
|
14
|
+
RaptorBuildHistoryResponse,
|
|
15
|
+
} from "@contracts";
|
|
12
16
|
|
|
13
17
|
/** Per-level border color for node cards. */
|
|
14
18
|
function levelColor(level: number): string {
|
|
@@ -68,6 +72,7 @@ function NodeCard({ node }: { node: RaptorNodeDTO }): React.ReactElement {
|
|
|
68
72
|
|
|
69
73
|
export default function RaptorTreeView(): React.ReactElement {
|
|
70
74
|
const [data, setData] = useState<RaptorTreeResponse | null>(null);
|
|
75
|
+
const [history, setHistory] = useState<RaptorBuildHistoryResponse | null>(null);
|
|
71
76
|
const [loading, setLoading] = useState<boolean>(true);
|
|
72
77
|
const [error, setError] = useState<string | null>(null);
|
|
73
78
|
|
|
@@ -75,10 +80,11 @@ export default function RaptorTreeView(): React.ReactElement {
|
|
|
75
80
|
let cancelled = false;
|
|
76
81
|
setLoading(true);
|
|
77
82
|
setError(null);
|
|
78
|
-
fetchRaptorTree()
|
|
79
|
-
.then((d) => {
|
|
83
|
+
Promise.all([fetchRaptorTree(), fetchRaptorBuildHistory()])
|
|
84
|
+
.then(([d, h]) => {
|
|
80
85
|
if (!cancelled) {
|
|
81
86
|
setData(d);
|
|
87
|
+
setHistory(h);
|
|
82
88
|
setLoading(false);
|
|
83
89
|
}
|
|
84
90
|
})
|
|
@@ -120,6 +126,11 @@ export default function RaptorTreeView(): React.ReactElement {
|
|
|
120
126
|
);
|
|
121
127
|
}
|
|
122
128
|
|
|
129
|
+
const latestBuild = history && !history.empty ? history.builds[0] : null;
|
|
130
|
+
const coherencePct = latestBuild?.coherenceScore != null
|
|
131
|
+
? Math.round(latestBuild.coherenceScore * 100)
|
|
132
|
+
: null;
|
|
133
|
+
|
|
123
134
|
return (
|
|
124
135
|
<div className="w-full p-4">
|
|
125
136
|
<div className="mb-4 flex items-center gap-6 text-sm text-muted-foreground">
|
|
@@ -130,7 +141,50 @@ export default function RaptorTreeView(): React.ReactElement {
|
|
|
130
141
|
<strong>{data.levels + 1}</strong> levels
|
|
131
142
|
</span>
|
|
132
143
|
<span>Built {formatDate(data.builtAt ?? 0)}</span>
|
|
144
|
+
{latestBuild && (
|
|
145
|
+
<>
|
|
146
|
+
<span>
|
|
147
|
+
Coherence:{" "}
|
|
148
|
+
<strong style={{ color: coherencePct != null && coherencePct >= 70 ? "#22c55e" : "#f59e0b" }}>
|
|
149
|
+
{coherencePct != null ? `${coherencePct}%` : "—"}
|
|
150
|
+
</strong>
|
|
151
|
+
</span>
|
|
152
|
+
<span>
|
|
153
|
+
Leaves: <strong>{latestBuild.leafCount}</strong>
|
|
154
|
+
</span>
|
|
155
|
+
<span>
|
|
156
|
+
Depth: <strong>{latestBuild.depth}</strong>
|
|
157
|
+
</span>
|
|
158
|
+
{latestBuild.timedOut && (
|
|
159
|
+
<span style={{ color: "#ef4444" }}>budget timeout</span>
|
|
160
|
+
)}
|
|
161
|
+
</>
|
|
162
|
+
)}
|
|
133
163
|
</div>
|
|
164
|
+
{history && !history.empty && history.builds.length > 1 && (
|
|
165
|
+
<details className="mb-4">
|
|
166
|
+
<summary className="cursor-pointer text-xs text-muted-foreground">
|
|
167
|
+
Build history ({history.builds.length} builds)
|
|
168
|
+
</summary>
|
|
169
|
+
<div className="mt-2 space-y-1">
|
|
170
|
+
{history.builds.slice(0, 10).map((b) => (
|
|
171
|
+
<div key={b.buildId} className="flex items-center gap-4 text-xs text-muted-foreground">
|
|
172
|
+
<span>{formatDate(b.completedAt)}</span>
|
|
173
|
+
<span>{b.nodeCount} nodes</span>
|
|
174
|
+
<span>{b.leafCount} leaves</span>
|
|
175
|
+
<span>depth {b.depth}</span>
|
|
176
|
+
<span>
|
|
177
|
+
coherence{" "}
|
|
178
|
+
{b.coherenceScore != null
|
|
179
|
+
? `${Math.round(b.coherenceScore * 100)}%`
|
|
180
|
+
: "—"}
|
|
181
|
+
</span>
|
|
182
|
+
{b.timedOut && <span style={{ color: "#ef4444" }}>timeout</span>}
|
|
183
|
+
</div>
|
|
184
|
+
))}
|
|
185
|
+
</div>
|
|
186
|
+
</details>
|
|
187
|
+
)}
|
|
134
188
|
{data.nodes.map((node) => (
|
|
135
189
|
<NodeCard key={node.id} node={node} />
|
|
136
190
|
))}
|
|
@@ -15,7 +15,7 @@ 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 { RaptorTreeResponse } from "../raptor.js";
|
|
18
|
+
import type { RaptorTreeResponse, RaptorBuildHistoryResponse } from "../raptor.js";
|
|
19
19
|
import type { MemoryStatusResponse } from "../memory.js";
|
|
20
20
|
import type { SetupStatusResponse, SetupDetectResponse, SetupConfigureRequest, SetupConfigureResponse } from "../setup.js";
|
|
21
21
|
import type { EmbedderHealthResponse } from "../embedder-health.js";
|
|
@@ -424,6 +424,14 @@ export const ENDPOINTS = {
|
|
|
424
424
|
"Hierarchical RAPTOR tree (summary nodes by level) for a session; defaults to the latest session with nodes.",
|
|
425
425
|
} as const satisfies EndpointDef<"GET", undefined, RaptorTreeResponse>,
|
|
426
426
|
|
|
427
|
+
/** GET /api/raptor-build-history — Build history (coherence, depth, timeout) for a session. */
|
|
428
|
+
raptorBuildHistory: {
|
|
429
|
+
method: "GET",
|
|
430
|
+
path: "/api/raptor-build-history",
|
|
431
|
+
description:
|
|
432
|
+
"RAPTOR build history rows (node/leaf count, depth, coherence score, timed_out) for a session; defaults to the latest session with builds.",
|
|
433
|
+
} as const satisfies EndpointDef<"GET", undefined, RaptorBuildHistoryResponse>,
|
|
434
|
+
|
|
427
435
|
/** GET /api/embedder-health — Round-trip a test embed through the active embedder. */
|
|
428
436
|
embedderHealth: {
|
|
429
437
|
method: "GET",
|
|
@@ -241,8 +241,8 @@ export type {
|
|
|
241
241
|
GraphValidationReport,
|
|
242
242
|
} from "./memory-map.js";
|
|
243
243
|
|
|
244
|
-
// Part B — RAPTOR tree
|
|
245
|
-
export type { RaptorNodeDTO, RaptorTreeResponse } from "./raptor.js";
|
|
244
|
+
// Part B — RAPTOR tree + build history
|
|
245
|
+
export type { RaptorNodeDTO, RaptorTreeResponse, RaptorBuildHistoryDTO, RaptorBuildHistoryResponse } from "./raptor.js";
|
|
246
246
|
|
|
247
247
|
// A3 — cache stripe distribution + health
|
|
248
248
|
export type {
|
|
@@ -47,3 +47,22 @@ export interface RaptorTreeResponse {
|
|
|
47
47
|
/** True when there are no RAPTOR nodes for the resolved session. */
|
|
48
48
|
readonly empty: boolean;
|
|
49
49
|
}
|
|
50
|
+
|
|
51
|
+
/** A single build-history row (client-safe). */
|
|
52
|
+
export interface RaptorBuildHistoryDTO {
|
|
53
|
+
readonly buildId: string;
|
|
54
|
+
readonly sessionId: string;
|
|
55
|
+
readonly startedAt: number;
|
|
56
|
+
readonly completedAt: number;
|
|
57
|
+
readonly nodeCount: number;
|
|
58
|
+
readonly leafCount: number;
|
|
59
|
+
readonly depth: number;
|
|
60
|
+
readonly coherenceScore: number | null;
|
|
61
|
+
readonly timedOut: boolean;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Response body for GET /api/raptor-build-history. */
|
|
65
|
+
export interface RaptorBuildHistoryResponse {
|
|
66
|
+
readonly builds: RaptorBuildHistoryDTO[];
|
|
67
|
+
readonly empty: boolean;
|
|
68
|
+
}
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
readProviderCacheForRepo,
|
|
19
19
|
} from "../../src/store/sqlite/perf-samples.js";
|
|
20
20
|
import { lookupModelInputRate } from "../../src/pricing.js";
|
|
21
|
+
import { costApiConfig, lookupCostApiPricing } from "../../src/costApi.js";
|
|
21
22
|
|
|
22
23
|
export function getIndexDir(): string {
|
|
23
24
|
const override = process.env.MEGACOMPACT_INDEX_DIR;
|
|
@@ -41,7 +42,29 @@ export function readIndex(): IndexIndex | null {
|
|
|
41
42
|
const rows = db
|
|
42
43
|
.prepare("SELECT * FROM repo_registry ORDER BY last_seen DESC")
|
|
43
44
|
.all() as Record<string, unknown>[];
|
|
44
|
-
|
|
45
|
+
// Lazily fetch cost-API pricing once per readIndex call (cached for 1 hour
|
|
46
|
+
// inside costApi.ts). Avoids per-row network calls.
|
|
47
|
+
const costApi = costApiConfig();
|
|
48
|
+
const mapped: IndexRepo[] = rows.map((r) => {
|
|
49
|
+
const modelName = String(r.model_name ?? "");
|
|
50
|
+
const localRate = (r.input_rate as number | null) ||
|
|
51
|
+
lookupModelInputRate(modelName) ||
|
|
52
|
+
null;
|
|
53
|
+
let inputRate = localRate;
|
|
54
|
+
let outputRate = (r.output_rate as number | null) ?? null;
|
|
55
|
+
// Fall back to the cost API when the local table has no data.
|
|
56
|
+
if (costApi.enabled && (inputRate == null || outputRate == null) && modelName) {
|
|
57
|
+
const apiPricing = lookupCostApiPricing(modelName);
|
|
58
|
+
if (apiPricing) {
|
|
59
|
+
if (inputRate == null && apiPricing.inputRate != null) {
|
|
60
|
+
inputRate = apiPricing.inputRate;
|
|
61
|
+
}
|
|
62
|
+
if (outputRate == null && apiPricing.outputRate != null) {
|
|
63
|
+
outputRate = apiPricing.outputRate;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
45
68
|
repoRoot: String(r.repo_root ?? ""),
|
|
46
69
|
displayName: String(r.display_name ?? ""),
|
|
47
70
|
stateDir: String(r.state_dir ?? ""),
|
|
@@ -52,9 +75,8 @@ export function readIndex(): IndexIndex | null {
|
|
|
52
75
|
provider: (r.provider as string | null) ?? null,
|
|
53
76
|
providerName: (r.provider_name as string | null) ?? null,
|
|
54
77
|
modelName: (r.model_name as string | null) ?? null,
|
|
55
|
-
inputRate
|
|
56
|
-
|
|
57
|
-
outputRate: (r.output_rate as number | null) ?? null,
|
|
78
|
+
inputRate,
|
|
79
|
+
outputRate,
|
|
58
80
|
lastSeen: Number(r.last_seen ?? 0),
|
|
59
81
|
// Defaults — enriched below from each repo's own store.
|
|
60
82
|
tokensKept: 0,
|
|
@@ -66,7 +88,8 @@ export function readIndex(): IndexIndex | null {
|
|
|
66
88
|
providerCachePct: null,
|
|
67
89
|
providerCacheRead: null,
|
|
68
90
|
providerCacheWrite: null,
|
|
69
|
-
}
|
|
91
|
+
};
|
|
92
|
+
});
|
|
70
93
|
// Enrich each repo with per-store token + model detail read directly via
|
|
71
94
|
// node:sqlite (same zero-dependency invariant as readIndex; no store graph
|
|
72
95
|
// import). Best-effort: a missing/corrupt store degrades to the defaults
|
|
@@ -13,7 +13,8 @@ import { createRequire } from "node:module";
|
|
|
13
13
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
14
14
|
|
|
15
15
|
import type { RouteContext } from "./routes-core.js";
|
|
16
|
-
import { computeCacheSavings } from "../../src/pricing.js";
|
|
16
|
+
import { computeCacheSavings, lookupModelInputRate } from "../../src/pricing.js";
|
|
17
|
+
import { costApiConfig, lookupCostApiPricing } from "../../src/costApi.js";
|
|
17
18
|
|
|
18
19
|
// ---------------------------------------------------------------------------
|
|
19
20
|
// handleProviderCache — "/api/provider-cache"
|
|
@@ -118,18 +119,29 @@ export function handleProviderCache(
|
|
|
118
119
|
} | null = null;
|
|
119
120
|
try {
|
|
120
121
|
const snap = latestModelSnapshot(stateDir); // guardrails-allow PREVENT-PI-004: local SQLite read (loopback dashboard)
|
|
121
|
-
|
|
122
|
+
let rate = snap?.inputRate ?? 0;
|
|
123
|
+
let modelName = snap?.modelName ?? snap?.modelId ?? "unknown";
|
|
124
|
+
// Fallback chain: SQLite snapshot → local pricing table → cost API
|
|
125
|
+
if (rate <= 0 && modelName !== "unknown") {
|
|
126
|
+
rate = lookupModelInputRate(modelName) ?? 0;
|
|
127
|
+
}
|
|
128
|
+
if (rate <= 0 && costApiConfig().enabled && modelName !== "unknown") {
|
|
129
|
+
// guardrails-allow PREVENT-PI-004: cost API pricing lookup (user-opted-in)
|
|
130
|
+
const apiPricing = lookupCostApiPricing(modelName);
|
|
131
|
+
rate = apiPricing?.inputRate ?? 0;
|
|
132
|
+
}
|
|
133
|
+
if (rate > 0) {
|
|
122
134
|
const s = computeCacheSavings(
|
|
123
135
|
lifetime.totalCacheRead,
|
|
124
136
|
lifetime.totalCacheWrite,
|
|
125
|
-
|
|
137
|
+
rate,
|
|
126
138
|
);
|
|
127
139
|
savings = {
|
|
128
140
|
cacheReadSaved: s.cacheReadSaved,
|
|
129
141
|
cacheWriteCost: s.cacheWriteCost,
|
|
130
142
|
netSaved: s.netSaved,
|
|
131
|
-
model:
|
|
132
|
-
inputRate:
|
|
143
|
+
model: modelName,
|
|
144
|
+
inputRate: rate,
|
|
133
145
|
};
|
|
134
146
|
}
|
|
135
147
|
} catch {
|
|
@@ -272,6 +272,23 @@ export const SETTINGS: ReadonlyArray<{
|
|
|
272
272
|
num("MEGACOMPACT_EMBED_CACHE", "Embed Cache Size", "Embedding cache entries (0 = disabled)", 256, 0, 10000),
|
|
273
273
|
],
|
|
274
274
|
},
|
|
275
|
+
{
|
|
276
|
+
name: "Cost API",
|
|
277
|
+
settings: [
|
|
278
|
+
boolDirect(
|
|
279
|
+
"MEGACOMPACT_COST_API_ENABLED",
|
|
280
|
+
"Cost API Lookup",
|
|
281
|
+
"Fetch model pricing from an external API (PREVENT-PI-004 network exception — opt-in). Enriches the dashboard Cache/Repos tabs with real $/token rates for models not in the local pricing table.",
|
|
282
|
+
false,
|
|
283
|
+
),
|
|
284
|
+
str(
|
|
285
|
+
"MEGACOMPACT_COST_API_URL",
|
|
286
|
+
"Cost API URL",
|
|
287
|
+
"OpenRouter-compatible model pricing endpoint (e.g. https://openrouter.ai/api/v1/models). Only contacted when Cost API Lookup is ON.",
|
|
288
|
+
"",
|
|
289
|
+
),
|
|
290
|
+
],
|
|
291
|
+
},
|
|
275
292
|
];
|
|
276
293
|
|
|
277
294
|
/** Flat index of every setting by key for O(1) lookup + validation. */
|
|
@@ -10,7 +10,7 @@ import { createRequire } from "node:module";
|
|
|
10
10
|
import type { ServerResponse, IncomingMessage } from "node:http";
|
|
11
11
|
import { parse as parseUrl } from "node:url";
|
|
12
12
|
import type { RouteContext } from "./routes-core.js";
|
|
13
|
-
import type { RaptorTreeResponse, RaptorNodeDTO } from "./api-contracts/raptor.js";
|
|
13
|
+
import type { RaptorTreeResponse, RaptorNodeDTO, RaptorBuildHistoryResponse, RaptorBuildHistoryDTO } from "./api-contracts/raptor.js";
|
|
14
14
|
import { openStore } from "../../src/store/sqlite.js";
|
|
15
15
|
|
|
16
16
|
const _req = createRequire(import.meta.url);
|
|
@@ -126,3 +126,90 @@ export function handleRaptorTree(
|
|
|
126
126
|
return true;
|
|
127
127
|
}
|
|
128
128
|
}
|
|
129
|
+
|
|
130
|
+
/** Find the most recent session that has build history rows (else null). */
|
|
131
|
+
function latestBuildSession(stateDir: string): string | null {
|
|
132
|
+
try {
|
|
133
|
+
const db = openStore(stateDir);
|
|
134
|
+
const row = db
|
|
135
|
+
.prepare(
|
|
136
|
+
"SELECT DISTINCT session_id FROM raptor_build_history ORDER BY completed_at DESC LIMIT 1",
|
|
137
|
+
)
|
|
138
|
+
.get() as { session_id: string } | undefined;
|
|
139
|
+
return row?.session_id ?? null;
|
|
140
|
+
} catch {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function handleRaptorBuildHistory(
|
|
146
|
+
req: IncomingMessage,
|
|
147
|
+
res: ServerResponse,
|
|
148
|
+
ctx: RouteContext,
|
|
149
|
+
): boolean {
|
|
150
|
+
if (req.method !== "GET") return false;
|
|
151
|
+
const parsed = parseUrl(req.url ?? "", true);
|
|
152
|
+
if (parsed.pathname !== "/api/raptor-build-history") return false;
|
|
153
|
+
|
|
154
|
+
try {
|
|
155
|
+
const { listBuildHistory } = _req(
|
|
156
|
+
"../../src/dedup/raptor/buildHistory.js",
|
|
157
|
+
) as {
|
|
158
|
+
listBuildHistory: (
|
|
159
|
+
sessionId: string,
|
|
160
|
+
stateDir: string,
|
|
161
|
+
) => Array<{
|
|
162
|
+
buildId: string;
|
|
163
|
+
sessionId: string;
|
|
164
|
+
startedAt: number;
|
|
165
|
+
completedAt: number;
|
|
166
|
+
nodeCount: number;
|
|
167
|
+
leafCount: number;
|
|
168
|
+
depth: number;
|
|
169
|
+
coherenceScore: number | null;
|
|
170
|
+
timedOut: boolean;
|
|
171
|
+
}>;
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
let sessionId =
|
|
175
|
+
typeof parsed.query.sessionId === "string" ? parsed.query.sessionId : "";
|
|
176
|
+
|
|
177
|
+
if (!sessionId) {
|
|
178
|
+
sessionId = latestBuildSession(ctx.stateDir) ?? "";
|
|
179
|
+
if (!sessionId) {
|
|
180
|
+
const body: RaptorBuildHistoryResponse = { builds: [], empty: true };
|
|
181
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
182
|
+
res.end(JSON.stringify(body));
|
|
183
|
+
return true;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const rows = listBuildHistory(sessionId, ctx.stateDir);
|
|
188
|
+
if (rows.length === 0) {
|
|
189
|
+
const body: RaptorBuildHistoryResponse = { builds: [], empty: true };
|
|
190
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
191
|
+
res.end(JSON.stringify(body));
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const dtos: RaptorBuildHistoryDTO[] = rows.map((r) => ({
|
|
196
|
+
buildId: r.buildId,
|
|
197
|
+
sessionId: r.sessionId,
|
|
198
|
+
startedAt: r.startedAt,
|
|
199
|
+
completedAt: r.completedAt,
|
|
200
|
+
nodeCount: r.nodeCount,
|
|
201
|
+
leafCount: r.leafCount,
|
|
202
|
+
depth: r.depth,
|
|
203
|
+
coherenceScore: r.coherenceScore,
|
|
204
|
+
timedOut: r.timedOut,
|
|
205
|
+
}));
|
|
206
|
+
const body: RaptorBuildHistoryResponse = { builds: dtos, empty: false };
|
|
207
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
208
|
+
res.end(JSON.stringify(body));
|
|
209
|
+
return true;
|
|
210
|
+
} catch (e) {
|
|
211
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
212
|
+
res.end(JSON.stringify({ error: String(e) }));
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
@@ -23,7 +23,7 @@ export { handleProviderCache } from "./routes-cache.js";
|
|
|
23
23
|
export { handleMemoryStatus } from "./routes-memory.js";
|
|
24
24
|
export { handleSetupStatus, handleSetupDetect, handleSetupConfigure } from "./routes-setup.js";
|
|
25
25
|
export { handleMemoryMap } from "./routes-memory-map.js";
|
|
26
|
-
export { handleRaptorTree } from "./routes-raptor.js";
|
|
26
|
+
export { handleRaptorTree, handleRaptorBuildHistory } from "./routes-raptor.js";
|
|
27
27
|
export { handleCacheStripes } from "./routes-cache.js";
|
|
28
28
|
export { handleContextHealth, handleCachePoison, handleHealthSettings } from "./routes-health.js";
|
|
29
29
|
export { handleEmbedderHealth } from "./routes-embedder-health.js";
|
|
@@ -46,6 +46,7 @@ import {
|
|
|
46
46
|
handleSetupConfigure,
|
|
47
47
|
handleMemoryMap,
|
|
48
48
|
handleRaptorTree,
|
|
49
|
+
handleRaptorBuildHistory,
|
|
49
50
|
handleContextHealth,
|
|
50
51
|
handleCachePoison,
|
|
51
52
|
handleHealthSettings,
|
|
@@ -260,6 +261,7 @@ export async function launchDashboardServer(
|
|
|
260
261
|
if (handleSetupConfigure(req, res, ctx)) return;
|
|
261
262
|
if (handleMemoryMap(req, res, ctx)) return;
|
|
262
263
|
if (handleRaptorTree(req, res, ctx)) return;
|
|
264
|
+
if (handleRaptorBuildHistory(req, res, ctx)) return;
|
|
263
265
|
if (handleContextHealth(req, res, ctx)) return;
|
|
264
266
|
if (handleCachePoison(req, res, ctx)) return;
|
|
265
267
|
if (handleHealthSettings(req, res, ctx)) return;
|
|
@@ -107,16 +107,23 @@ export function doRecall(
|
|
|
107
107
|
);
|
|
108
108
|
runtime.lastWhy = `why: recalled@${scorePct}% (${result.toInject.length} chkpt)`;
|
|
109
109
|
}
|
|
110
|
+
let sumTokens = 0;
|
|
111
|
+
for (const h of result.toInject) sumTokens += h.checkpoint.tokenEstimate;
|
|
110
112
|
if (result.toInject.length > 0) {
|
|
111
|
-
let sumTokens = 0;
|
|
112
|
-
for (const h of result.toInject) sumTokens += h.checkpoint.tokenEstimate;
|
|
113
113
|
runtime.rt.recallInjections += result.toInject.length;
|
|
114
114
|
runtime.rt.cacheHitTokens += sumTokens;
|
|
115
115
|
incRecallInjected(result.toInject.length, runtime.currentStateDir);
|
|
116
116
|
incCacheHitTokens(sumTokens, runtime.currentStateDir);
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
117
|
+
}
|
|
118
|
+
// S43: record recall provenance — which checkpoints/summaries served this
|
|
119
|
+
// turn, their score + source path. Linked to the turn row written at
|
|
120
|
+
// turn_end via the conversation+turnIndex. Best-effort + non-fatal.
|
|
121
|
+
// Persists telemetry (HyDE + recall metrics) even when recall returned
|
|
122
|
+
// no hits, so empty-recall HyDE invocations are still visible in the
|
|
123
|
+
// dashboard Turns/Metrics tabs.
|
|
124
|
+
const hasTelemetry =
|
|
125
|
+
result.hydeInfo != null || result.recallMetrics != null;
|
|
126
|
+
if (result.toInject.length > 0 || hasTelemetry) {
|
|
120
127
|
try {
|
|
121
128
|
const convId = ensureConversationIdFor(
|
|
122
129
|
config,
|
|
@@ -136,22 +143,24 @@ export function doRecall(
|
|
|
136
143
|
},
|
|
137
144
|
runtime.currentStateDir,
|
|
138
145
|
);
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
146
|
+
if (result.toInject.length > 0) {
|
|
147
|
+
recordRecallWrite(
|
|
148
|
+
config,
|
|
149
|
+
turnId,
|
|
150
|
+
result.toInject.map((h) => ({
|
|
151
|
+
checkpointId: h.checkpoint.checkpointId,
|
|
152
|
+
score: h.score,
|
|
153
|
+
source:
|
|
154
|
+
h.raptorLevel !== undefined
|
|
155
|
+
? "raptor"
|
|
156
|
+
: h.repoId
|
|
157
|
+
? "cross-repo"
|
|
158
|
+
: "flat",
|
|
159
|
+
raptorLevel: h.raptorLevel,
|
|
160
|
+
})),
|
|
161
|
+
runtime.currentStateDir,
|
|
162
|
+
);
|
|
163
|
+
}
|
|
155
164
|
} catch {
|
|
156
165
|
/* non-fatal: recall provenance never breaks the recall path */
|
|
157
166
|
}
|
|
@@ -11,6 +11,7 @@ import { resolveRepoRoot } from "../mega-config.js";
|
|
|
11
11
|
import { recordModelSnapshot, recordRepoModel } from "../../src/store/sqlite.js";
|
|
12
12
|
import type { ModelSnapshot } from "../../src/store/sqlite.js";
|
|
13
13
|
import { lookupModelInputRate } from "../../src/pricing.js";
|
|
14
|
+
import { costApiConfig, lookupCostApiPricing } from "../../src/costApi.js";
|
|
14
15
|
|
|
15
16
|
// ---------------------------------------------------------------------- types
|
|
16
17
|
|
|
@@ -45,13 +46,24 @@ export function captureModelImpl(ctx: CaptureModelContext, ectx: ExtensionContex
|
|
|
45
46
|
}
|
|
46
47
|
const modelId = m.id;
|
|
47
48
|
const fallbackInput = lookupModelInputRate(modelId);
|
|
49
|
+
let inputRate = m.cost?.input || fallbackInput || 0;
|
|
50
|
+
let outputRate = m.cost?.output ?? 0;
|
|
51
|
+
// Cost API last-resort fallback for models not in the local pricing table
|
|
52
|
+
if (inputRate <= 0 && costApiConfig().enabled) {
|
|
53
|
+
// guardrails-allow PREVENT-PI-004: cost API pricing lookup (user-opted-in)
|
|
54
|
+
const apiPricing = lookupCostApiPricing(modelId);
|
|
55
|
+
if (apiPricing) {
|
|
56
|
+
inputRate = apiPricing.inputRate ?? 0;
|
|
57
|
+
if (outputRate <= 0) outputRate = apiPricing.outputRate ?? 0;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
48
60
|
const snap: Omit<ModelSnapshot, "capturedAt"> = {
|
|
49
61
|
provider: m.provider,
|
|
50
62
|
providerName,
|
|
51
63
|
modelId,
|
|
52
64
|
modelName: m.name ?? null,
|
|
53
|
-
inputRate
|
|
54
|
-
outputRate
|
|
65
|
+
inputRate,
|
|
66
|
+
outputRate,
|
|
55
67
|
contextWindow: m.contextWindow ?? 0,
|
|
56
68
|
maxTokens: m.maxTokens ?? 0,
|
|
57
69
|
reasoning: !!m.reasoning,
|
|
@@ -72,6 +72,9 @@ export interface RuntimeSnapshotContext extends RuntimeHelpersContext {
|
|
|
72
72
|
lastSnapshotSig: string | null;
|
|
73
73
|
lastWidgetCtx?: ExtensionContext;
|
|
74
74
|
lastActivityAt: number;
|
|
75
|
+
/** Timestamp (ms) of the last session heartbeat write. Used by the
|
|
76
|
+
* material-change gate fast path to refresh heartbeats on a cadence. */
|
|
77
|
+
lastHeartbeatAt: number;
|
|
75
78
|
lastLevel: number;
|
|
76
79
|
diagCtxFastGate: number;
|
|
77
80
|
diagLiveTrimFires: number;
|
|
@@ -117,6 +120,12 @@ export function snapshotImpl(
|
|
|
117
120
|
const sig = materialSigImpl(self);
|
|
118
121
|
if (ctx && self.widgetData && self.lastSnapshotSig === sig) {
|
|
119
122
|
self.renderWidget(ctx);
|
|
123
|
+
// Refresh heartbeat on a time cadence even when material state hasn't
|
|
124
|
+
// changed, so the session doesn't appear stale and get pruned by the
|
|
125
|
+
// dashboard's 30-min cutoff. The full snapshot recompute is still gated
|
|
126
|
+
// (expensive SQLite opens + writeFileSync), but a heartbeat row is
|
|
127
|
+
// cheap (single INSERT OR REPLACE on the index DB).
|
|
128
|
+
refreshHeartbeatIfStale(self, ctx, 10_000);
|
|
120
129
|
return;
|
|
121
130
|
}
|
|
122
131
|
const perfT0 = performance.now();
|
|
@@ -313,4 +322,35 @@ export function snapshotImpl(
|
|
|
313
322
|
/* non-fatal: perf instrumentation never blocks the agent */
|
|
314
323
|
}
|
|
315
324
|
self.lastSnapshotSig = sig;
|
|
325
|
+
self.lastHeartbeatAt = Date.now();
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// ----------------------------------------------------------- heartbeat refresh
|
|
329
|
+
|
|
330
|
+
/** Refresh the session heartbeat if enough time has elapsed since the last
|
|
331
|
+
* write. Called from the material-change gate fast path (where the full
|
|
332
|
+
* snapshot recompute is skipped) so the session stays visible in the
|
|
333
|
+
* dashboard during idle/typing periods. Non-fatal — best-effort like the
|
|
334
|
+
* main heartbeat write. */
|
|
335
|
+
function refreshHeartbeatIfStale(
|
|
336
|
+
self: RuntimeSnapshotContext,
|
|
337
|
+
ctx: ExtensionContext | undefined,
|
|
338
|
+
intervalMs: number,
|
|
339
|
+
): void {
|
|
340
|
+
const now = Date.now();
|
|
341
|
+
if (now - self.lastHeartbeatAt < intervalMs) return;
|
|
342
|
+
try {
|
|
343
|
+
const repo =
|
|
344
|
+
resolveRepoRoot(ctx?.cwd ?? self.currentStateDir) ?? self.currentStateDir;
|
|
345
|
+
recordSessionHeartbeat(
|
|
346
|
+
process.pid,
|
|
347
|
+
self.rt.sessionId,
|
|
348
|
+
repo,
|
|
349
|
+
self.currentStateDir,
|
|
350
|
+
self.lastCtxWindow || 0,
|
|
351
|
+
);
|
|
352
|
+
self.lastHeartbeatAt = now;
|
|
353
|
+
} catch {
|
|
354
|
+
/* non-fatal: heartbeat refresh must never block the snapshot path */
|
|
355
|
+
}
|
|
316
356
|
}
|
|
@@ -209,6 +209,7 @@ export class MegaRuntime {
|
|
|
209
209
|
// (already-registered) widget factory is refreshed. Kills the per-event
|
|
210
210
|
// main-thread block during typing/idle streaming with no material change.
|
|
211
211
|
lastSnapshotSig: string | null = null;
|
|
212
|
+
lastHeartbeatAt: number = 0;
|
|
212
213
|
// v0.8.5: bumped whenever the cached game-state memo is evicted (bumpGameState
|
|
213
214
|
// for in-process /mega-game writes, the fs.watch callback for cross-process
|
|
214
215
|
// dashboard-server writes, and bindRepo on repo switch) so the snapshot gate
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-mega-compact",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.1",
|
|
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",
|
package/src/config/dedup.ts
CHANGED
|
@@ -89,7 +89,8 @@ export function loadDedupConfig(): DedupConfigShape {
|
|
|
89
89
|
L2_ENABLED: envBool("MEGACOMPACT_L2_ENABLED", true),
|
|
90
90
|
// Fix D: RAPTOR promoted to live recall. Default ON; canary.ts sequences it
|
|
91
91
|
// last (L0→L1→L2→RAPTOR) and auto-disables on p95 breach, so promotion is
|
|
92
|
-
// safe.
|
|
92
|
+
// safe. Shadow mode (RAPTOR_SHADOW_MODE=true) opts out of serving while
|
|
93
|
+
// still building + persisting the tree (transition/eval use).
|
|
93
94
|
RAPTOR_ENABLED: envBool("MEGACOMPACT_RAPTOR_ENABLED", true),
|
|
94
95
|
MARK_ONLY_L0: envBool("MEGACOMPACT_MARK_ONLY_L0", false),
|
|
95
96
|
MARK_ONLY_L1: envBool("MEGACOMPACT_MARK_ONLY_L1", false),
|