pi-mega-compact 0.13.0 → 0.13.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/embedder-health.js +11 -0
- package/dist/extensions/dashboard-server/api-contracts/endpoints/registry.js +225 -0
- package/dist/extensions/dashboard-server/api-contracts/endpoints/types.js +1 -0
- package/dist/extensions/dashboard-server/api-contracts/endpoints.js +5 -224
- package/dist/extensions/dashboard-server/api-contracts/raptor.js +8 -0
- package/dist/extensions/dashboard-server/api-contracts.test/_helpers.js +32 -0
- package/dist/extensions/dashboard-server/routes-cache.test/_helpers.js +48 -0
- package/dist/extensions/dashboard-server/routes-embedder-health.js +67 -0
- package/dist/extensions/dashboard-server/routes-raptor.js +88 -0
- package/dist/extensions/dashboard-server/routes-setup.js +1 -1
- package/dist/extensions/dashboard-server/routes.js +2 -0
- package/dist/extensions/dashboard-server/server.js +5 -1
- package/dist/extensions/mega-cache-replay.test/_helpers.js +175 -0
- package/dist/extensions/mega-commands/dataCommands.js +151 -0
- package/dist/extensions/mega-commands/helpers.js +61 -0
- package/dist/extensions/mega-commands/historyCommands.js +97 -0
- package/dist/extensions/mega-commands/setupCommand.js +200 -0
- package/dist/extensions/mega-commands.js +14 -499
- package/dist/extensions/mega-compact-s38.test/_helpers.js +194 -0
- package/dist/extensions/mega-compact.js +12 -2
- package/dist/extensions/mega-compact.test/_helpers.js +225 -0
- package/dist/extensions/mega-events/agent-handlers/agentEndHandler.js +211 -0
- package/dist/extensions/mega-events/agent-handlers/turnEndHandler/cacheStripe.js +49 -0
- package/dist/extensions/mega-events/agent-handlers/turnEndHandler/contextHealth.js +33 -0
- package/dist/extensions/mega-events/agent-handlers/turnEndHandler/errorRetry.js +387 -0
- package/dist/extensions/mega-events/agent-handlers/turnEndHandler/event.js +7 -0
- package/dist/extensions/mega-events/agent-handlers/turnEndHandler/gameScoring.js +55 -0
- package/dist/extensions/mega-events/agent-handlers/turnEndHandler/lengthStop.js +12 -0
- package/dist/extensions/mega-events/agent-handlers/turnEndHandler/memoryReview.js +22 -0
- package/dist/extensions/mega-events/agent-handlers/turnEndHandler/recordTurnRow.js +38 -0
- package/dist/extensions/mega-events/agent-handlers/turnEndHandler.js +36 -0
- package/dist/extensions/mega-events/agent-handlers.js +4 -799
- package/dist/extensions/mega-events/context-handler/afterCompact.js +163 -0
- package/dist/extensions/mega-events/context-handler/messageText.js +12 -0
- package/dist/extensions/mega-events/context-handler/tailResult.js +46 -0
- package/dist/extensions/mega-events/context-handler.js +11 -198
- package/dist/extensions/mega-events/separated-prompt.test/_helpers.js +25 -0
- package/dist/src/config.js +2 -0
- package/dist/src/dedup-engine.test/_helpers.js +55 -0
- package/dist/src/e2e.test/_helpers.js +52 -0
- package/dist/src/embedder.js +1 -0
- package/dist/src/httpEmbedder.js +6 -0
- package/dist/src/hyde.js +146 -0
- package/dist/src/importance.test/_helpers.js +5 -0
- package/dist/src/memoryGraph/sources/checkpoints.js +78 -0
- package/dist/src/memoryGraph/sources/edges.js +17 -0
- package/dist/src/memoryGraph/sources/flags.js +22 -0
- package/dist/src/memoryGraph/sources/helpers.js +14 -0
- package/dist/src/memoryGraph/sources/memories.js +88 -0
- package/dist/src/memoryGraph/sources/raptor.js +26 -0
- package/dist/src/memoryGraph/sources/turns.js +176 -0
- package/dist/src/memoryGraph/sources.js +15 -441
- package/dist/src/memoryGraph-gates.test/_helpers.js +120 -0
- package/dist/src/ratio.bench.test/_helpers.js +198 -0
- package/dist/src/recall/async.js +112 -0
- package/dist/src/recall/format.js +149 -0
- package/dist/src/recall/memory.js +62 -0
- package/dist/src/recall/reformulate.js +82 -0
- package/dist/src/recall/sync.js +169 -0
- package/dist/src/recall/types.js +1 -0
- package/dist/src/recall.js +13 -530
- package/dist/src/store/sqlite/schema/core.js +232 -0
- package/dist/src/store/sqlite/schema/game.js +65 -0
- package/dist/src/store/sqlite/schema/plan-v2.js +107 -0
- package/dist/src/store/sqlite/schema/turns.js +59 -0
- package/dist/src/store/sqlite/schema.js +8 -441
- package/dist/src/store/turns/contract-compliance.test/_helpers.js +21 -0
- package/dist/src/store/turns/sqlite-store/admin.js +171 -0
- package/dist/src/store/turns/sqlite-store/ctx.js +1 -0
- package/dist/src/store/turns/sqlite-store/read.js +111 -0
- package/dist/src/store/turns/sqlite-store/rows.js +42 -0
- package/dist/src/store/turns/sqlite-store/write.js +102 -0
- package/dist/src/store/turns/sqlite-store.js +31 -367
- package/dist/src/vectorStore/class.js +356 -0
- package/dist/src/vectorStore/hash.js +10 -0
- package/dist/src/vectorStore/types.js +2 -0
- package/dist/src/vectorStore.js +8 -359
- package/dist/src/vectorStore.test/_helpers.js +17 -0
- package/extensions/dashboard-client/dist/assets/{CacheTab-BZNnKhh5.js → CacheTab-DPRHjYZ0.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{CacheTab-BZNnKhh5.js.map → CacheTab-DPRHjYZ0.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{EventsTab-CzF5L42U.js → EventsTab-CgwYq-Qf.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{EventsTab-CzF5L42U.js.map → EventsTab-CgwYq-Qf.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{HealthTab-BLpidMrX.js → HealthTab-BgOCjVzR.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{HealthTab-BLpidMrX.js.map → HealthTab-BgOCjVzR.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/MaintenanceTab-Yw1yFpsy.js +2 -0
- package/extensions/dashboard-client/dist/assets/MaintenanceTab-Yw1yFpsy.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/MemoryMapTab-yHUX7Yzi.js +2 -0
- package/extensions/dashboard-client/dist/assets/MemoryMapTab-yHUX7Yzi.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{MetricsTab-B0PCysqs.js → MetricsTab-CcH9fP8P.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MetricsTab-B0PCysqs.js.map → MetricsTab-CcH9fP8P.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{OverviewTab-DsOhqFu4.js → OverviewTab-BOs8ZNev.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{OverviewTab-DsOhqFu4.js.map → OverviewTab-BOs8ZNev.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{ReposTab-B3YKYuOf.js → ReposTab-B-MfSPvg.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{ReposTab-B3YKYuOf.js.map → ReposTab-B-MfSPvg.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{SessionsTab-B96FWcrC.js → SessionsTab-BmeN649Q.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{SessionsTab-B96FWcrC.js.map → SessionsTab-BmeN649Q.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/SetupTab-nOknoph5.js +2 -0
- package/extensions/dashboard-client/dist/assets/SetupTab-nOknoph5.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-BfkUVTvA.js → TimeSavedCard-DnPg0L0z.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-BfkUVTvA.js.map → TimeSavedCard-DnPg0L0z.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TopicsTab-DLRsZmXJ.js → TopicsTab-BODHOg_b.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TopicsTab-DLRsZmXJ.js.map → TopicsTab-BODHOg_b.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TurnsTab-DKGU2Bf6.js → TurnsTab-BK0I7hKr.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TurnsTab-DKGU2Bf6.js.map → TurnsTab-BK0I7hKr.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{index-D-lPLRWB.js → index-mOlljTFI.js} +10 -10
- package/extensions/dashboard-client/dist/assets/index-mOlljTFI.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{useSSE-ClbbcgIp.js → useSSE-BYOtv1-3.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{useSSE-ClbbcgIp.js.map → useSSE-BYOtv1-3.js.map} +1 -1
- package/extensions/dashboard-client/dist/index.html +1 -1
- package/extensions/dashboard-client/src/App.tsx +0 -4
- package/extensions/dashboard-client/src/api/client.ts +16 -0
- package/extensions/dashboard-client/src/tabs/MaintenanceTab/ActionsCard.tsx +178 -0
- package/extensions/dashboard-client/src/tabs/MaintenanceTab/DbStatsCard.tsx +140 -0
- package/extensions/dashboard-client/src/tabs/MaintenanceTab/DebugBundleCard.tsx +109 -0
- package/extensions/dashboard-client/src/tabs/MaintenanceTab/HealthMitigationCard.tsx +98 -0
- package/extensions/dashboard-client/src/tabs/MaintenanceTab/SchemaHealthCard.tsx +108 -0
- package/extensions/dashboard-client/src/tabs/MaintenanceTab.tsx +12 -598
- package/extensions/dashboard-client/src/tabs/MemoryMapTab/MemoryMapView.tsx +476 -0
- package/extensions/dashboard-client/src/tabs/MemoryMapTab/RaptorTreeView.tsx +163 -0
- package/extensions/dashboard-client/src/tabs/MemoryMapTab.tsx +56 -466
- package/extensions/dashboard-client/src/tabs/SetupTab/EmbedderHealthCard.tsx +109 -0
- package/extensions/dashboard-client/src/tabs/SetupTab/EmbedderSetup.tsx +468 -0
- package/extensions/dashboard-client/src/tabs/SetupTab.tsx +23 -484
- package/extensions/dashboard-server/api-contracts/embedder-health.ts +32 -0
- package/extensions/dashboard-server/api-contracts/endpoints/registry.ts +360 -0
- package/extensions/dashboard-server/api-contracts/endpoints/types.ts +350 -0
- package/extensions/dashboard-server/api-contracts/endpoints.ts +27 -648
- package/extensions/dashboard-server/api-contracts/index.ts +6 -0
- package/extensions/dashboard-server/api-contracts/raptor.ts +49 -0
- package/extensions/dashboard-server/api-contracts.test/_helpers.ts +47 -0
- package/extensions/dashboard-server/routes-cache.test/_helpers.ts +94 -0
- package/extensions/dashboard-server/routes-embedder-health.ts +77 -0
- package/extensions/dashboard-server/routes-raptor.ts +128 -0
- package/extensions/dashboard-server/routes-setup.ts +1 -1
- package/extensions/dashboard-server/routes.ts +2 -0
- package/extensions/dashboard-server/server.ts +4 -0
- package/extensions/mega-cache-replay.test/_helpers.ts +188 -0
- package/extensions/mega-commands/dataCommands.ts +169 -0
- package/extensions/mega-commands/helpers.ts +62 -0
- package/extensions/mega-commands/historyCommands.ts +117 -0
- package/extensions/mega-commands/setupCommand.ts +235 -0
- package/extensions/mega-commands.ts +25 -538
- package/extensions/mega-compact-s38.test/_helpers.ts +201 -0
- package/extensions/mega-compact.test/_helpers.ts +237 -0
- package/extensions/mega-compact.ts +12 -2
- package/extensions/mega-events/agent-handlers/agentEndHandler.ts +238 -0
- package/extensions/mega-events/agent-handlers/turnEndHandler/cacheStripe.ts +74 -0
- package/extensions/mega-events/agent-handlers/turnEndHandler/contextHealth.ts +50 -0
- package/extensions/mega-events/agent-handlers/turnEndHandler/errorRetry.ts +421 -0
- package/extensions/mega-events/agent-handlers/turnEndHandler/event.ts +16 -0
- package/extensions/mega-events/agent-handlers/turnEndHandler/gameScoring.ts +73 -0
- package/extensions/mega-events/agent-handlers/turnEndHandler/lengthStop.ts +29 -0
- package/extensions/mega-events/agent-handlers/turnEndHandler/memoryReview.ts +42 -0
- package/extensions/mega-events/agent-handlers/turnEndHandler/recordTurnRow.ts +61 -0
- package/extensions/mega-events/agent-handlers/turnEndHandler.ts +64 -0
- package/extensions/mega-events/agent-handlers.ts +15 -861
- package/extensions/mega-events/context-handler/afterCompact.ts +213 -0
- package/extensions/mega-events/context-handler/messageText.ts +20 -0
- package/extensions/mega-events/context-handler/tailResult.ts +63 -0
- package/extensions/mega-events/context-handler.ts +17 -235
- package/extensions/mega-events/separated-prompt.test/_helpers.ts +35 -0
- package/package.json +1 -1
- package/src/config.ts +4 -0
- package/src/dedup-engine.test/_helpers.ts +79 -0
- package/src/e2e.test/_helpers.ts +59 -0
- package/src/embedder.ts +4 -0
- package/src/httpEmbedder.ts +7 -0
- package/src/hyde.ts +162 -0
- package/src/importance.test/_helpers.ts +5 -0
- package/src/memoryGraph/sources/checkpoints.ts +99 -0
- package/src/memoryGraph/sources/edges.ts +23 -0
- package/src/memoryGraph/sources/flags.ts +26 -0
- package/src/memoryGraph/sources/helpers.ts +13 -0
- package/src/memoryGraph/sources/memories.ts +108 -0
- package/src/memoryGraph/sources/raptor.ts +37 -0
- package/src/memoryGraph/sources/turns.ts +226 -0
- package/src/memoryGraph/sources.ts +19 -544
- package/src/memoryGraph-gates.test/_helpers.ts +164 -0
- package/src/ratio.bench.test/_helpers.ts +268 -0
- package/src/recall/async.ts +142 -0
- package/src/recall/format.ts +173 -0
- package/src/recall/memory.ts +90 -0
- package/src/recall/reformulate.ts +111 -0
- package/src/recall/sync.ts +196 -0
- package/src/recall/types.ts +63 -0
- package/src/recall.ts +25 -712
- package/src/store/sqlite/schema/core.ts +233 -0
- package/src/store/sqlite/schema/game.ts +66 -0
- package/src/store/sqlite/schema/plan-v2.ts +108 -0
- package/src/store/sqlite/schema/turns.ts +60 -0
- package/src/store/sqlite/schema.ts +13 -441
- package/src/store/turns/contract-compliance.test/_helpers.ts +38 -0
- package/src/store/turns/sqlite-store/admin.ts +249 -0
- package/src/store/turns/sqlite-store/ctx.ts +14 -0
- package/src/store/turns/sqlite-store/read.ts +174 -0
- package/src/store/turns/sqlite-store/rows.ts +55 -0
- package/src/store/turns/sqlite-store/write.ts +193 -0
- package/src/store/turns/sqlite-store.ts +33 -514
- package/src/vectorStore/class.ts +433 -0
- package/src/vectorStore/hash.ts +11 -0
- package/src/vectorStore/types.ts +58 -0
- package/src/vectorStore.test/_helpers.ts +20 -0
- package/src/vectorStore.ts +13 -489
- package/extensions/dashboard-client/dist/assets/ConfigTab-DG0WkuuG.js +0 -2
- package/extensions/dashboard-client/dist/assets/ConfigTab-DG0WkuuG.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/MaintenanceTab-BQF7W7xX.js +0 -2
- package/extensions/dashboard-client/dist/assets/MaintenanceTab-BQF7W7xX.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/MemoryMapTab-CPtHLv0A.js +0 -2
- package/extensions/dashboard-client/dist/assets/MemoryMapTab-CPtHLv0A.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/SetupTab-hbzQ_NZv.js +0 -2
- package/extensions/dashboard-client/dist/assets/SetupTab-hbzQ_NZv.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/index-D-lPLRWB.js.map +0 -1
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MemoryMapTab/MemoryMapView.tsx — D3 memory map with force-directed graph.
|
|
3
|
+
* Node shape encodes nodeType: checkpoint=filled circle, turn=hollow circle,
|
|
4
|
+
* turn-content=hollow+ring, memory=diamond.
|
|
5
|
+
* Fetches from /api/memory-map, renders SVG force layout.
|
|
6
|
+
*/
|
|
7
|
+
import type React from "react";
|
|
8
|
+
import { useEffect, useRef, useState, useCallback, useMemo } from "react";
|
|
9
|
+
import { renderNodeShape, NODE_COLORS, NODE_TYPE_LABELS } from "../../memory-map-shapes.js";
|
|
10
|
+
import type { NodeType } from "../../memory-map-shapes.js";
|
|
11
|
+
import { buildLayout, applyForces } from "../../memory-map-layout.js";
|
|
12
|
+
import type { LayoutNode, LayoutEdge } from "../../memory-map-layout.js";
|
|
13
|
+
|
|
14
|
+
// Types (mirror of api-contracts/memory-map.ts — client-side only)
|
|
15
|
+
|
|
16
|
+
interface GraphNode {
|
|
17
|
+
id: string;
|
|
18
|
+
sessionId: string;
|
|
19
|
+
label: string;
|
|
20
|
+
summaryTruncated: string;
|
|
21
|
+
tokenEstimate: number;
|
|
22
|
+
timestamp: number;
|
|
23
|
+
dedupStatus: string | undefined;
|
|
24
|
+
raptorLevel: number;
|
|
25
|
+
topicSummary: string | undefined;
|
|
26
|
+
decisionCount: number;
|
|
27
|
+
textSnippet: string;
|
|
28
|
+
/** Source type discriminator for UI node-shape encoding. */
|
|
29
|
+
nodeType: "checkpoint" | "turn" | "turn-content" | "memory";
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface GraphEdge {
|
|
33
|
+
source: string;
|
|
34
|
+
target: string;
|
|
35
|
+
weight: number;
|
|
36
|
+
type: "temporal" | "semantic" | "raptor_parent";
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
interface GraphValidationReport {
|
|
40
|
+
readonly gatesRun: number;
|
|
41
|
+
readonly gatesPassed: number;
|
|
42
|
+
readonly dropped: { nodes: number; edges: number };
|
|
43
|
+
readonly warnings: Array<{ gate: string; code: string; count: number }>;
|
|
44
|
+
readonly sources: {
|
|
45
|
+
checkpoint: number;
|
|
46
|
+
turn: number;
|
|
47
|
+
turnContent: number;
|
|
48
|
+
memory: number;
|
|
49
|
+
};
|
|
50
|
+
readonly builtAt: number;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
interface GraphMetadata {
|
|
54
|
+
totalNodes: number;
|
|
55
|
+
totalEdges: number;
|
|
56
|
+
avgWeight: number;
|
|
57
|
+
nodeTypeBreakdown: Record<string, number>;
|
|
58
|
+
edgeTypeBreakdown: Record<string, number>;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
interface MemoryMapResponse {
|
|
62
|
+
nodes: GraphNode[];
|
|
63
|
+
edges: GraphEdge[];
|
|
64
|
+
metadata: GraphMetadata;
|
|
65
|
+
/** Validation report from the 9-gate pipeline. Optional for backward compat. */
|
|
66
|
+
validation?: GraphValidationReport;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Simulation rendering constants (layout math is in memory-map-layout.ts)
|
|
70
|
+
|
|
71
|
+
const MAX_ITERATIONS = 300;
|
|
72
|
+
const EDGE_ALPHA = 0.3;
|
|
73
|
+
const SVG_WIDTH = 900;
|
|
74
|
+
const SVG_HEIGHT = 600;
|
|
75
|
+
const PADDING = 40;
|
|
76
|
+
|
|
77
|
+
// Helpers
|
|
78
|
+
|
|
79
|
+
function edgeColor(type: GraphEdge["type"]): string {
|
|
80
|
+
switch (type) {
|
|
81
|
+
case "temporal":
|
|
82
|
+
return "#6366f1"; // indigo
|
|
83
|
+
case "semantic":
|
|
84
|
+
return "#22c55e"; // green
|
|
85
|
+
case "raptor_parent":
|
|
86
|
+
return "#f59e0b"; // amber
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Minimal legend swatch component
|
|
91
|
+
interface SwatchProps {
|
|
92
|
+
type: "node" | "edge";
|
|
93
|
+
color: string;
|
|
94
|
+
shape: "filled-circle" | "hollow-circle" | "hollow-ring" | "diamond" | "line";
|
|
95
|
+
label: string;
|
|
96
|
+
}
|
|
97
|
+
const Swatch: React.FC<SwatchProps> = ({ color, shape, label }) => {
|
|
98
|
+
const base: React.CSSProperties = {
|
|
99
|
+
display: "inline-block",
|
|
100
|
+
width: "10px",
|
|
101
|
+
height: "10px",
|
|
102
|
+
verticalAlign: "middle",
|
|
103
|
+
marginRight: "3px",
|
|
104
|
+
borderRadius: shape.startsWith("hollow") || shape === "filled-circle" ? "50%" : undefined,
|
|
105
|
+
};
|
|
106
|
+
let swatch: JSX.Element;
|
|
107
|
+
if (shape === "line") {
|
|
108
|
+
return <span style={{ color, marginRight: "6px" }}>{label}</span>;
|
|
109
|
+
} else if (shape === "filled-circle") {
|
|
110
|
+
swatch = <span style={{ ...base, backgroundColor: color }} />;
|
|
111
|
+
} else if (shape === "hollow-circle") {
|
|
112
|
+
swatch = <span style={{ ...base, border: `2px solid ${color}`, backgroundColor: "transparent" }} />;
|
|
113
|
+
} else if (shape === "hollow-ring") {
|
|
114
|
+
swatch = (
|
|
115
|
+
<span style={{ ...base, border: `2px solid ${color}`, backgroundColor: "transparent", position: "relative" }}>
|
|
116
|
+
<span style={{ display: "block", width: "4px", height: "4px", borderRadius: "50%", backgroundColor: color, margin: "2px auto" }} />
|
|
117
|
+
</span>
|
|
118
|
+
);
|
|
119
|
+
} else {
|
|
120
|
+
swatch = <span style={{ ...base, clipPath: "polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%)", backgroundColor: color }} />;
|
|
121
|
+
}
|
|
122
|
+
return <><span className="memory-map-legend-item">{swatch}{label}</span> </>;
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
// Force simulation engine (delegated to memory-map-layout.ts)
|
|
126
|
+
const MemoryMapView: React.FC = () => {
|
|
127
|
+
const [data, setData] = useState<MemoryMapResponse | null>(null);
|
|
128
|
+
const [loading, setLoading] = useState<boolean>(true);
|
|
129
|
+
const [error, setError] = useState<string | null>(null);
|
|
130
|
+
const [selectedNode, setSelectedNode] = useState<GraphNode | null>(null);
|
|
131
|
+
const [searchQuery, setSearchQuery] = useState<string>("");
|
|
132
|
+
const [filterSession] = useState<string>("");
|
|
133
|
+
const [frame, setFrame] = useState<number>(0);
|
|
134
|
+
|
|
135
|
+
const layoutRef = useRef<{ nodes: LayoutNode[]; edges: LayoutEdge[] } | null>(null);
|
|
136
|
+
const animRef = useRef<number>(0);
|
|
137
|
+
|
|
138
|
+
// Fetch graph data when filterSession changes
|
|
139
|
+
useEffect(() => {
|
|
140
|
+
let cancelled = false;
|
|
141
|
+
const params = new URLSearchParams();
|
|
142
|
+
if (filterSession) params.set("sessionId", filterSession);
|
|
143
|
+
params.set("threshold", "0.7");
|
|
144
|
+
params.set("maxEdgesPerNode", "4");
|
|
145
|
+
|
|
146
|
+
setLoading(true);
|
|
147
|
+
setError(null);
|
|
148
|
+
|
|
149
|
+
fetch(`/api/memory-map?${params.toString()}`)
|
|
150
|
+
.then((r) => {
|
|
151
|
+
if (!r.ok) throw new Error(`HTTP ${r.status}`);
|
|
152
|
+
return r.json() as Promise<MemoryMapResponse>;
|
|
153
|
+
})
|
|
154
|
+
.then((d) => {
|
|
155
|
+
if (!cancelled) {
|
|
156
|
+
setData(d);
|
|
157
|
+
layoutRef.current = buildLayout(d);
|
|
158
|
+
setFrame(1);
|
|
159
|
+
setLoading(false);
|
|
160
|
+
}
|
|
161
|
+
})
|
|
162
|
+
.catch((e: unknown) => {
|
|
163
|
+
if (!cancelled) {
|
|
164
|
+
setError(e instanceof Error ? e.message : "Unknown error");
|
|
165
|
+
setLoading(false);
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
return () => {
|
|
170
|
+
cancelled = true;
|
|
171
|
+
cancelAnimationFrame(animRef.current);
|
|
172
|
+
};
|
|
173
|
+
}, [filterSession]);
|
|
174
|
+
|
|
175
|
+
// Run force simulation via requestAnimationFrame
|
|
176
|
+
useEffect(() => {
|
|
177
|
+
if (!layoutRef.current || frame === 0) return;
|
|
178
|
+
|
|
179
|
+
const layout = layoutRef.current;
|
|
180
|
+
let iter = 0;
|
|
181
|
+
|
|
182
|
+
const tick = () => {
|
|
183
|
+
if (iter >= MAX_ITERATIONS) return;
|
|
184
|
+
applyForces(layout);
|
|
185
|
+
iter++;
|
|
186
|
+
setFrame(iter);
|
|
187
|
+
animRef.current = requestAnimationFrame(tick);
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
animRef.current = requestAnimationFrame(tick);
|
|
191
|
+
return () => cancelAnimationFrame(animRef.current);
|
|
192
|
+
}, [frame]);
|
|
193
|
+
|
|
194
|
+
// Toggle pin / select a node
|
|
195
|
+
const handleNodeClick = useCallback((node: LayoutNode, index: number) => {
|
|
196
|
+
const layout = layoutRef.current;
|
|
197
|
+
if (!layout) return;
|
|
198
|
+
layout.nodes[index].pinned = !layout.nodes[index].pinned;
|
|
199
|
+
if (!layout.nodes[index].pinned) {
|
|
200
|
+
layout.nodes[index].vx = 0;
|
|
201
|
+
layout.nodes[index].vy = 0;
|
|
202
|
+
}
|
|
203
|
+
setSelectedNode((prev) => (prev?.id === node.id ? null : (node as unknown as GraphNode)));
|
|
204
|
+
setFrame((s) => s + 1);
|
|
205
|
+
}, []);
|
|
206
|
+
|
|
207
|
+
// Filtered node indices for search
|
|
208
|
+
const filteredSet = useMemo<Set<number> | null>(() => {
|
|
209
|
+
const layout = layoutRef.current;
|
|
210
|
+
if (!layout || !searchQuery) return null;
|
|
211
|
+
const q = searchQuery.toLowerCase();
|
|
212
|
+
return new Set(
|
|
213
|
+
layout.nodes
|
|
214
|
+
.map((n, i) =>
|
|
215
|
+
n.summaryTruncated.toLowerCase().includes(q) || n.label.toLowerCase().includes(q) ? i : -1,
|
|
216
|
+
)
|
|
217
|
+
.filter((i) => i >= 0),
|
|
218
|
+
);
|
|
219
|
+
}, [searchQuery]);
|
|
220
|
+
|
|
221
|
+
// Health indicator helpers (D3)
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Derive graph health from validation report:
|
|
225
|
+
* green = all gates passed + no warnings
|
|
226
|
+
* yellow = warnings present but no critical gate failed
|
|
227
|
+
* red = a critical gate (gatesRun > gatesPassed) failed
|
|
228
|
+
*/
|
|
229
|
+
const graphHealth = useMemo<{ level: "green" | "yellow" | "red"; label: string }>(() => {
|
|
230
|
+
const v = data?.validation;
|
|
231
|
+
if (!v) return { level: "yellow", label: "No validation" };
|
|
232
|
+
if (v.gatesPassed < v.gatesRun) return { level: "red", label: "Gate failure" };
|
|
233
|
+
if (v.warnings.length > 0) return { level: "yellow", label: `${v.warnings.length} warning(s)` };
|
|
234
|
+
return { level: "green", label: "Healthy" };
|
|
235
|
+
}, [data?.validation]);
|
|
236
|
+
|
|
237
|
+
const healthColor: Record<string, string> = {
|
|
238
|
+
green: "#22c55e",
|
|
239
|
+
yellow: "#eab308",
|
|
240
|
+
red: "#ef4444",
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
// Source availability indicators (D3)
|
|
244
|
+
|
|
245
|
+
/** Count badge fragments from validation.sources. */
|
|
246
|
+
const countBadge = useMemo<string>(() => {
|
|
247
|
+
const v = data?.validation;
|
|
248
|
+
if (!v) {
|
|
249
|
+
const totalNodes = data?.metadata.totalNodes ?? 0;
|
|
250
|
+
return `${totalNodes} nodes`;
|
|
251
|
+
}
|
|
252
|
+
const parts: string[] = [];
|
|
253
|
+
if (v.sources.checkpoint > 0) parts.push(`${v.sources.checkpoint} checkpoints`);
|
|
254
|
+
if (v.sources.turn > 0) parts.push(`${v.sources.turn} turns`);
|
|
255
|
+
if (v.sources.turnContent > 0) parts.push(`${v.sources.turnContent} turn-content`);
|
|
256
|
+
if (v.sources.memory > 0) parts.push(`${v.sources.memory} memories`);
|
|
257
|
+
if (parts.length === 0) {
|
|
258
|
+
parts.push(`${data?.metadata.totalNodes ?? 0} nodes`);
|
|
259
|
+
}
|
|
260
|
+
return parts.join(" · ");
|
|
261
|
+
}, [data?.validation, data?.metadata.totalNodes]);
|
|
262
|
+
|
|
263
|
+
/** Per-source availability: ✓ for available sources, ✗ for empty ones. */
|
|
264
|
+
const sourceAvail = useMemo<string>(() => {
|
|
265
|
+
const v = data?.validation;
|
|
266
|
+
if (!v) return "";
|
|
267
|
+
const parts: string[] = [];
|
|
268
|
+
parts.push(v.sources.checkpoint > 0 ? "✓ checkpoints" : "✗ checkpoints");
|
|
269
|
+
parts.push(v.sources.turn > 0 ? "✓ turns" : "✗ turns");
|
|
270
|
+
parts.push(v.sources.turnContent > 0 ? "✓ turn-content" : "✗ turn-content");
|
|
271
|
+
parts.push(v.sources.memory > 0 ? "✓ memories" : "✗ memories");
|
|
272
|
+
return parts.join(" | ");
|
|
273
|
+
}, [data?.validation]);
|
|
274
|
+
|
|
275
|
+
/** Whether all four sources are empty (true empty state). */
|
|
276
|
+
const allSourcesEmpty = useMemo<boolean>(() => {
|
|
277
|
+
const v = data?.validation;
|
|
278
|
+
if (!v) return data?.metadata.totalNodes === 0;
|
|
279
|
+
return (
|
|
280
|
+
v.sources.checkpoint === 0 &&
|
|
281
|
+
v.sources.turn === 0 &&
|
|
282
|
+
v.sources.turnContent === 0 &&
|
|
283
|
+
v.sources.memory === 0
|
|
284
|
+
);
|
|
285
|
+
}, [data?.validation, data?.metadata.totalNodes]);
|
|
286
|
+
|
|
287
|
+
// Loading state
|
|
288
|
+
|
|
289
|
+
if (loading) {
|
|
290
|
+
return <div className="memory-map-loading">Loading memory graph...</div>;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Error state
|
|
294
|
+
|
|
295
|
+
if (error) {
|
|
296
|
+
return (
|
|
297
|
+
<div className="memory-map-error">
|
|
298
|
+
<p>Failed to load memory map: {error}</p>
|
|
299
|
+
<button
|
|
300
|
+
className="memory-map-retry-btn"
|
|
301
|
+
onClick={() => setFrame((s) => s + 1)}
|
|
302
|
+
>
|
|
303
|
+
Retry
|
|
304
|
+
</button>
|
|
305
|
+
</div>
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Empty state (D2)
|
|
310
|
+
|
|
311
|
+
if (!data || !layoutRef.current || allSourcesEmpty) {
|
|
312
|
+
return (
|
|
313
|
+
<div className="memory-map-empty">
|
|
314
|
+
<p>Memories appear after your first compaction. The graph shows checkpoints (compaction summaries) linked by semantic similarity and time. Run a longer session or lower the compaction tier to see it sooner.</p>
|
|
315
|
+
</div>
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const layout = layoutRef.current;
|
|
320
|
+
|
|
321
|
+
// Render
|
|
322
|
+
|
|
323
|
+
return (
|
|
324
|
+
<div className="memory-map-container">
|
|
325
|
+
{/* Toolbar */}
|
|
326
|
+
<div className="memory-map-toolbar">
|
|
327
|
+
<input
|
|
328
|
+
type="text"
|
|
329
|
+
placeholder="Search memories..."
|
|
330
|
+
value={searchQuery}
|
|
331
|
+
onInput={(e: React.FormEvent<HTMLInputElement>) =>
|
|
332
|
+
setSearchQuery((e.target as HTMLInputElement).value)
|
|
333
|
+
}
|
|
334
|
+
className="memory-map-search"
|
|
335
|
+
/>
|
|
336
|
+
<span className="memory-map-stats">
|
|
337
|
+
{/* Count badge */}
|
|
338
|
+
<span className="memory-map-count-badge">{countBadge}</span>
|
|
339
|
+
<br />
|
|
340
|
+
<span className="memory-map-edges">{data.edges.length} edges</span>
|
|
341
|
+
</span>
|
|
342
|
+
{/* Graph-health indicator */}
|
|
343
|
+
<span
|
|
344
|
+
className="memory-map-health-badge"
|
|
345
|
+
style={{
|
|
346
|
+
backgroundColor: healthColor[graphHealth.level] ?? "#6b7280",
|
|
347
|
+
color: "#fff",
|
|
348
|
+
padding: "2px 8px",
|
|
349
|
+
borderRadius: "10px",
|
|
350
|
+
fontSize: "11px",
|
|
351
|
+
fontWeight: 600,
|
|
352
|
+
marginLeft: "8px",
|
|
353
|
+
}}
|
|
354
|
+
title={`Validation: ${graphHealth.label}`}
|
|
355
|
+
>
|
|
356
|
+
{graphHealth.level === "green" ? "Healthy" : graphHealth.level === "yellow" ? "Warnings" : "Critical"}
|
|
357
|
+
</span>
|
|
358
|
+
</div>
|
|
359
|
+
|
|
360
|
+
{/* Source availability indicators */}
|
|
361
|
+
{sourceAvail ? (
|
|
362
|
+
<div className="memory-map-source-avail" style={{ fontSize: "11px", padding: "2px 12px", color: "#6b7280" }}>
|
|
363
|
+
{sourceAvail}
|
|
364
|
+
</div>
|
|
365
|
+
) : null}
|
|
366
|
+
|
|
367
|
+
{/* Legend — node types + edge types */}
|
|
368
|
+
<div className="memory-map-legend">
|
|
369
|
+
<span className="memory-map-legend-section">Nodes:</span>
|
|
370
|
+
<Swatch type="node" color={NODE_COLORS.checkpoint} shape="filled-circle" label="Checkpoint" />
|
|
371
|
+
<Swatch type="node" color={NODE_COLORS.turn} shape="hollow-circle" label="Turn" />
|
|
372
|
+
<Swatch type="node" color={NODE_COLORS["turn-content"]} shape="hollow-ring" label="Turn Content" />
|
|
373
|
+
<Swatch type="node" color={NODE_COLORS.memory} shape="diamond" label="Memory" />
|
|
374
|
+
<span className="memory-map-legend-sep">|</span>
|
|
375
|
+
<span className="memory-map-legend-section">Edges:</span>
|
|
376
|
+
<Swatch type="edge" color={edgeColor("semantic")} shape="line" label="Semantic" />
|
|
377
|
+
<Swatch type="edge" color={edgeColor("temporal")} shape="line" label="Temporal" />
|
|
378
|
+
<Swatch type="edge" color={edgeColor("raptor_parent")} shape="line" label="Raptor" />
|
|
379
|
+
</div>
|
|
380
|
+
|
|
381
|
+
{/* SVG graph */}
|
|
382
|
+
<svg
|
|
383
|
+
width={SVG_WIDTH}
|
|
384
|
+
height={SVG_HEIGHT}
|
|
385
|
+
viewBox={`0 0 ${SVG_WIDTH} ${SVG_HEIGHT}`}
|
|
386
|
+
className="memory-map-svg"
|
|
387
|
+
>
|
|
388
|
+
{/* Edges */}
|
|
389
|
+
{layout.edges.map((e, i) => {
|
|
390
|
+
const src = layout.nodes[e.source];
|
|
391
|
+
const tgt = layout.nodes[e.target];
|
|
392
|
+
const color = edgeColor(e.type);
|
|
393
|
+
const strokeW = Math.max(1, e.weight * 2.5);
|
|
394
|
+
return (
|
|
395
|
+
<line
|
|
396
|
+
key={`edge-${i}`}
|
|
397
|
+
x1={PADDING + (src.pos.x / (layout.nodes.length || 1)) * (SVG_WIDTH - 2 * PADDING)}
|
|
398
|
+
y1={PADDING + (src.pos.y / (layout.nodes.length || 1)) * (SVG_HEIGHT - 2 * PADDING)}
|
|
399
|
+
x2={PADDING + (tgt.pos.x / (layout.nodes.length || 1)) * (SVG_WIDTH - 2 * PADDING)}
|
|
400
|
+
y2={PADDING + (tgt.pos.y / (layout.nodes.length || 1)) * (SVG_HEIGHT - 2 * PADDING)}
|
|
401
|
+
stroke={color}
|
|
402
|
+
strokeWidth={strokeW}
|
|
403
|
+
opacity={EDGE_ALPHA}
|
|
404
|
+
/>
|
|
405
|
+
);
|
|
406
|
+
})}
|
|
407
|
+
|
|
408
|
+
{/* Nodes — shape encodes nodeType */}
|
|
409
|
+
{layout.nodes.map((n, i) => {
|
|
410
|
+
const cx =
|
|
411
|
+
PADDING + (n.pos.x / (layout.nodes.length || 1)) * (SVG_WIDTH - 2 * PADDING);
|
|
412
|
+
const cy =
|
|
413
|
+
PADDING + (n.pos.y / (layout.nodes.length || 1)) * (SVG_HEIGHT - 2 * PADDING);
|
|
414
|
+
const radius = Math.min(20, 8 + n.tokenEstimate / 500);
|
|
415
|
+
const isFiltered = filteredSet ? filteredSet.has(i) : true;
|
|
416
|
+
const isSelected = selectedNode?.id === n.id;
|
|
417
|
+
const nodeLabel = n.label.length > 20 ? n.label.slice(0, 18) + "..." : n.label;
|
|
418
|
+
|
|
419
|
+
return (
|
|
420
|
+
<g
|
|
421
|
+
key={`node-${i}`}
|
|
422
|
+
className="memory-map-node-group"
|
|
423
|
+
onClick={() => handleNodeClick(n, i)}
|
|
424
|
+
style={{ cursor: "pointer" }}
|
|
425
|
+
>
|
|
426
|
+
{renderNodeShape(cx, cy, radius, n.nodeType as NodeType, isSelected, isFiltered, n.dedupStatus)}
|
|
427
|
+
<text
|
|
428
|
+
x={cx}
|
|
429
|
+
y={cy + radius + 12}
|
|
430
|
+
textAnchor="middle"
|
|
431
|
+
fontSize="9px"
|
|
432
|
+
fill="#374151"
|
|
433
|
+
className="memory-map-label"
|
|
434
|
+
>
|
|
435
|
+
{nodeLabel}
|
|
436
|
+
</text>
|
|
437
|
+
</g>
|
|
438
|
+
);
|
|
439
|
+
})}
|
|
440
|
+
</svg>
|
|
441
|
+
|
|
442
|
+
{/* Node detail panel */}
|
|
443
|
+
{selectedNode ? (
|
|
444
|
+
<div className="memory-map-detail">
|
|
445
|
+
<h3>
|
|
446
|
+
<span
|
|
447
|
+
className="memory-map-detail-type"
|
|
448
|
+
style={{
|
|
449
|
+
display: "inline-block",
|
|
450
|
+
fontSize: "12px",
|
|
451
|
+
fontWeight: 400,
|
|
452
|
+
color: "#6b7280",
|
|
453
|
+
marginRight: "6px",
|
|
454
|
+
}}
|
|
455
|
+
>
|
|
456
|
+
{NODE_TYPE_LABELS[selectedNode.nodeType]}
|
|
457
|
+
</span>
|
|
458
|
+
{selectedNode.label}
|
|
459
|
+
</h3>
|
|
460
|
+
<p>{selectedNode.summaryTruncated}</p>
|
|
461
|
+
{selectedNode.topicSummary ? (
|
|
462
|
+
<p className="memory-map-topic">Topic: {selectedNode.topicSummary}</p>
|
|
463
|
+
) : null}
|
|
464
|
+
<p>
|
|
465
|
+
Tokens: {selectedNode.tokenEstimate} | Decisions: {selectedNode.decisionCount}
|
|
466
|
+
</p>
|
|
467
|
+
<p>Session: {selectedNode.sessionId}</p>
|
|
468
|
+
<p>Raptor Level: {selectedNode.raptorLevel}</p>
|
|
469
|
+
<p className="memory-map-snippet">{selectedNode.textSnippet}</p>
|
|
470
|
+
</div>
|
|
471
|
+
) : null}
|
|
472
|
+
</div>
|
|
473
|
+
);
|
|
474
|
+
};
|
|
475
|
+
|
|
476
|
+
export default MemoryMapView;
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MemoryMapTab/RaptorTreeView.tsx — RAPTOR tree sub-tab (Part B).
|
|
3
|
+
*
|
|
4
|
+
* Fetches the hierarchical RAPTOR tree from /api/raptor-tree (defaults to the
|
|
5
|
+
* most recent session with nodes) and renders it as level-indented summary
|
|
6
|
+
* cards. No D3 needed — CSS indentation encodes hierarchy.
|
|
7
|
+
*/
|
|
8
|
+
import type React from "react";
|
|
9
|
+
import { useEffect, useState } from "react";
|
|
10
|
+
import { fetchRaptorTree } from "../../api/client";
|
|
11
|
+
import type { RaptorTreeResponse, RaptorNodeDTO } from "@contracts";
|
|
12
|
+
|
|
13
|
+
/** Per-level border color for node cards. */
|
|
14
|
+
function levelColor(level: number): string {
|
|
15
|
+
if (level === 0) return "#d4a017"; // gold — root
|
|
16
|
+
if (level === 1) return "#3b82f6"; // blue
|
|
17
|
+
return "#14b8a6"; // teal — level 2+
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function formatDate(ts: number): string {
|
|
21
|
+
if (!ts) return "—";
|
|
22
|
+
return new Date(ts).toLocaleString();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const containerStyle: React.CSSProperties = {
|
|
26
|
+
padding: "1rem",
|
|
27
|
+
width: "100%",
|
|
28
|
+
boxSizing: "border-box",
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const headerStyle: React.CSSProperties = {
|
|
32
|
+
display: "flex",
|
|
33
|
+
gap: "1.5rem",
|
|
34
|
+
alignItems: "center",
|
|
35
|
+
marginBottom: "1rem",
|
|
36
|
+
color: "#a0a0c0",
|
|
37
|
+
fontSize: "0.9rem",
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const emptyStyle: React.CSSProperties = {
|
|
41
|
+
padding: "2rem",
|
|
42
|
+
border: "1px dashed #2a2a4e",
|
|
43
|
+
borderRadius: "8px",
|
|
44
|
+
color: "#a0a0c0",
|
|
45
|
+
textAlign: "center",
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const loadingStyle: React.CSSProperties = {
|
|
49
|
+
padding: "1rem",
|
|
50
|
+
color: "#a0a0c0",
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
function NodeCard({ node }: { node: RaptorNodeDTO }): React.ReactElement {
|
|
54
|
+
const border = levelColor(node.level);
|
|
55
|
+
const childrenLabel =
|
|
56
|
+
node.children.length > 0
|
|
57
|
+
? `${node.children.length} child${node.children.length === 1 ? "" : "ren"}`
|
|
58
|
+
: "leaf";
|
|
59
|
+
return (
|
|
60
|
+
<div
|
|
61
|
+
style={{
|
|
62
|
+
background: "#14142b",
|
|
63
|
+
border: `1px solid ${border}`,
|
|
64
|
+
borderRadius: "8px",
|
|
65
|
+
padding: "0.75rem 1rem",
|
|
66
|
+
marginBottom: "0.6rem",
|
|
67
|
+
marginLeft: node.level * 24,
|
|
68
|
+
maxWidth: "90%",
|
|
69
|
+
}}
|
|
70
|
+
>
|
|
71
|
+
<div style={{ display: "flex", gap: "0.75rem", alignItems: "center", marginBottom: "0.35rem" }}>
|
|
72
|
+
<span
|
|
73
|
+
style={{
|
|
74
|
+
display: "inline-block",
|
|
75
|
+
background: border,
|
|
76
|
+
color: "#fff",
|
|
77
|
+
borderRadius: "10px",
|
|
78
|
+
padding: "1px 8px",
|
|
79
|
+
fontSize: "11px",
|
|
80
|
+
fontWeight: 600,
|
|
81
|
+
}}
|
|
82
|
+
>
|
|
83
|
+
L{node.level}
|
|
84
|
+
</span>
|
|
85
|
+
<span style={{ fontSize: "12px", color: "#6b7280" }}>
|
|
86
|
+
{childrenLabel} · {node.tokenEstimate} tok · {node.qualityMarker}
|
|
87
|
+
</span>
|
|
88
|
+
<span style={{ fontSize: "12px", color: "#6b7280", marginLeft: "auto" }}>
|
|
89
|
+
{formatDate(node.builtAt)}
|
|
90
|
+
</span>
|
|
91
|
+
</div>
|
|
92
|
+
<div style={{ fontSize: "0.92rem", color: "#e5e7eb" }}>{node.summary}</div>
|
|
93
|
+
</div>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export default function RaptorTreeView(): React.ReactElement {
|
|
98
|
+
const [data, setData] = useState<RaptorTreeResponse | null>(null);
|
|
99
|
+
const [loading, setLoading] = useState<boolean>(true);
|
|
100
|
+
const [error, setError] = useState<string | null>(null);
|
|
101
|
+
|
|
102
|
+
useEffect(() => {
|
|
103
|
+
let cancelled = false;
|
|
104
|
+
setLoading(true);
|
|
105
|
+
setError(null);
|
|
106
|
+
fetchRaptorTree()
|
|
107
|
+
.then((d) => {
|
|
108
|
+
if (!cancelled) {
|
|
109
|
+
setData(d);
|
|
110
|
+
setLoading(false);
|
|
111
|
+
}
|
|
112
|
+
})
|
|
113
|
+
.catch((e: unknown) => {
|
|
114
|
+
if (!cancelled) {
|
|
115
|
+
setError(e instanceof Error ? e.message : "Unknown error");
|
|
116
|
+
setLoading(false);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
return () => {
|
|
120
|
+
cancelled = true;
|
|
121
|
+
};
|
|
122
|
+
}, []);
|
|
123
|
+
|
|
124
|
+
if (loading) {
|
|
125
|
+
return <div style={loadingStyle}>Loading RAPTOR tree...</div>;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (error) {
|
|
129
|
+
return (
|
|
130
|
+
<div style={emptyStyle}>
|
|
131
|
+
<p>Failed to load RAPTOR tree: {error}</p>
|
|
132
|
+
</div>
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (!data || data.empty || data.nodes.length === 0) {
|
|
137
|
+
return (
|
|
138
|
+
<div style={emptyStyle}>
|
|
139
|
+
<p>
|
|
140
|
+
No RAPTOR tree built yet — run a compaction (the tree builds during
|
|
141
|
+
compaction).
|
|
142
|
+
</p>
|
|
143
|
+
</div>
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return (
|
|
148
|
+
<div style={containerStyle}>
|
|
149
|
+
<div style={headerStyle}>
|
|
150
|
+
<span>
|
|
151
|
+
<strong>{data.nodes.length}</strong> nodes
|
|
152
|
+
</span>
|
|
153
|
+
<span>
|
|
154
|
+
<strong>{data.levels + 1}</strong> levels
|
|
155
|
+
</span>
|
|
156
|
+
<span>Built {formatDate(data.builtAt ?? 0)}</span>
|
|
157
|
+
</div>
|
|
158
|
+
{data.nodes.map((node) => (
|
|
159
|
+
<NodeCard key={node.id} node={node} />
|
|
160
|
+
))}
|
|
161
|
+
</div>
|
|
162
|
+
);
|
|
163
|
+
}
|