Spaces:
Running
Running
Delete console/src/pages/OverviewPage.tsx
Browse files
console/src/pages/OverviewPage.tsx
DELETED
|
@@ -1,302 +0,0 @@
|
|
| 1 |
-
import { useEffect, useState } from "react";
|
| 2 |
-
import { Link as RouterLink } from "react-router-dom";
|
| 3 |
-
import {
|
| 4 |
-
Inline,
|
| 5 |
-
SectionBand,
|
| 6 |
-
SectionBandContent,
|
| 7 |
-
SectionBandDescription,
|
| 8 |
-
SectionBandEyebrow,
|
| 9 |
-
SectionBandHeader,
|
| 10 |
-
SectionBandTitle,
|
| 11 |
-
SectionStack,
|
| 12 |
-
Stat,
|
| 13 |
-
StatLabel,
|
| 14 |
-
StatValue,
|
| 15 |
-
StatusIndicator,
|
| 16 |
-
} from "lumen-ui-kit";
|
| 17 |
-
import { useWorkspace } from "../workspaces/WorkspaceProvider";
|
| 18 |
-
import { workspacePath } from "../workspaces/workspaceRoutes";
|
| 19 |
-
import { PageFrame } from "../components/Workbench";
|
| 20 |
-
|
| 21 |
-
interface StatCard {
|
| 22 |
-
label: string;
|
| 23 |
-
value: string | number;
|
| 24 |
-
loading: boolean;
|
| 25 |
-
failed: boolean;
|
| 26 |
-
to: string;
|
| 27 |
-
}
|
| 28 |
-
|
| 29 |
-
const shortcuts = [
|
| 30 |
-
{
|
| 31 |
-
title: "Inspect graph",
|
| 32 |
-
description: "Explore entities and relationships for the active brain.",
|
| 33 |
-
to: "/graph",
|
| 34 |
-
},
|
| 35 |
-
{
|
| 36 |
-
title: "Browse memory",
|
| 37 |
-
description: "Search text chunks, structured records, and observations.",
|
| 38 |
-
to: "/data",
|
| 39 |
-
},
|
| 40 |
-
{
|
| 41 |
-
title: "Ingest text",
|
| 42 |
-
description: "Submit new content and track the resulting task pipeline.",
|
| 43 |
-
to: "/ingest",
|
| 44 |
-
},
|
| 45 |
-
];
|
| 46 |
-
|
| 47 |
-
export default function OverviewPage() {
|
| 48 |
-
const { apiFetch, activeWorkspace } = useWorkspace();
|
| 49 |
-
const [stats, setStats] = useState<StatCard[]>([
|
| 50 |
-
{ label: "Entities", value: "—", loading: true, failed: false, to: "/graph" },
|
| 51 |
-
{
|
| 52 |
-
label: "Relationships",
|
| 53 |
-
value: "—",
|
| 54 |
-
loading: true,
|
| 55 |
-
failed: false,
|
| 56 |
-
to: "/graph",
|
| 57 |
-
},
|
| 58 |
-
{
|
| 59 |
-
label: "Text chunks",
|
| 60 |
-
value: "—",
|
| 61 |
-
loading: true,
|
| 62 |
-
failed: false,
|
| 63 |
-
to: "/data",
|
| 64 |
-
},
|
| 65 |
-
{
|
| 66 |
-
label: "Observations",
|
| 67 |
-
value: "—",
|
| 68 |
-
loading: true,
|
| 69 |
-
failed: false,
|
| 70 |
-
to: "/observations",
|
| 71 |
-
},
|
| 72 |
-
{ label: "Tasks", value: "—", loading: true, failed: false, to: "/tasks" },
|
| 73 |
-
{
|
| 74 |
-
label: "Vectors",
|
| 75 |
-
value: "—",
|
| 76 |
-
loading: true,
|
| 77 |
-
failed: false,
|
| 78 |
-
to: "/vectors",
|
| 79 |
-
},
|
| 80 |
-
]);
|
| 81 |
-
|
| 82 |
-
useEffect(() => {
|
| 83 |
-
let cancelled = false;
|
| 84 |
-
|
| 85 |
-
async function load() {
|
| 86 |
-
const results = [...stats];
|
| 87 |
-
|
| 88 |
-
try {
|
| 89 |
-
const entities = await apiFetch<{ total?: number }>(
|
| 90 |
-
"/retrieve/entities?limit=1",
|
| 91 |
-
);
|
| 92 |
-
results[0] = {
|
| 93 |
-
...results[0],
|
| 94 |
-
value: entities.total ?? 0,
|
| 95 |
-
loading: false,
|
| 96 |
-
failed: false,
|
| 97 |
-
};
|
| 98 |
-
} catch {
|
| 99 |
-
results[0] = { ...results[0], value: "?", loading: false, failed: true };
|
| 100 |
-
}
|
| 101 |
-
|
| 102 |
-
try {
|
| 103 |
-
const rels = await apiFetch<{ total?: number }>(
|
| 104 |
-
"/retrieve/relationships?limit=1",
|
| 105 |
-
);
|
| 106 |
-
results[1] = {
|
| 107 |
-
...results[1],
|
| 108 |
-
value: rels.total ?? 0,
|
| 109 |
-
loading: false,
|
| 110 |
-
failed: false,
|
| 111 |
-
};
|
| 112 |
-
} catch {
|
| 113 |
-
results[1] = { ...results[1], value: "?", loading: false, failed: true };
|
| 114 |
-
}
|
| 115 |
-
|
| 116 |
-
try {
|
| 117 |
-
const chunks = await apiFetch<{ total?: number }>(
|
| 118 |
-
"/retrieve/text-chunks?limit=1",
|
| 119 |
-
);
|
| 120 |
-
results[2] = {
|
| 121 |
-
...results[2],
|
| 122 |
-
value: chunks.total ?? 0,
|
| 123 |
-
loading: false,
|
| 124 |
-
failed: false,
|
| 125 |
-
};
|
| 126 |
-
} catch {
|
| 127 |
-
results[2] = { ...results[2], value: "?", loading: false, failed: true };
|
| 128 |
-
}
|
| 129 |
-
|
| 130 |
-
try {
|
| 131 |
-
const obs = await apiFetch<{ count?: number; observations?: unknown[] }>(
|
| 132 |
-
"/retrieve/observations?limit=1",
|
| 133 |
-
);
|
| 134 |
-
results[3] = {
|
| 135 |
-
...results[3],
|
| 136 |
-
value: obs.count ?? obs.observations?.length ?? 0,
|
| 137 |
-
loading: false,
|
| 138 |
-
failed: false,
|
| 139 |
-
};
|
| 140 |
-
} catch {
|
| 141 |
-
results[3] = { ...results[3], value: "?", loading: false, failed: true };
|
| 142 |
-
}
|
| 143 |
-
|
| 144 |
-
try {
|
| 145 |
-
const tasks = await apiFetch<{ tasks?: unknown[] }>("/tasks/");
|
| 146 |
-
results[4] = {
|
| 147 |
-
...results[4],
|
| 148 |
-
value: tasks.tasks?.length ?? 0,
|
| 149 |
-
loading: false,
|
| 150 |
-
failed: false,
|
| 151 |
-
};
|
| 152 |
-
} catch {
|
| 153 |
-
results[4] = { ...results[4], value: "?", loading: false, failed: true };
|
| 154 |
-
}
|
| 155 |
-
|
| 156 |
-
try {
|
| 157 |
-
const stores = await apiFetch<{ stores: { name: string }[] }>(
|
| 158 |
-
"/retrieve/vectors/stores",
|
| 159 |
-
);
|
| 160 |
-
let vectorTotal = 0;
|
| 161 |
-
for (const store of stores.stores) {
|
| 162 |
-
const res = await apiFetch<{ total?: number }>(
|
| 163 |
-
`/retrieve/vectors/${store.name}?limit=1`,
|
| 164 |
-
);
|
| 165 |
-
vectorTotal += res.total ?? 0;
|
| 166 |
-
}
|
| 167 |
-
results[5] = {
|
| 168 |
-
...results[5],
|
| 169 |
-
value: vectorTotal,
|
| 170 |
-
loading: false,
|
| 171 |
-
failed: false,
|
| 172 |
-
};
|
| 173 |
-
} catch {
|
| 174 |
-
results[5] = { ...results[5], value: "?", loading: false, failed: true };
|
| 175 |
-
}
|
| 176 |
-
|
| 177 |
-
if (!cancelled) setStats(results);
|
| 178 |
-
}
|
| 179 |
-
|
| 180 |
-
load();
|
| 181 |
-
return () => {
|
| 182 |
-
cancelled = true;
|
| 183 |
-
};
|
| 184 |
-
}, [apiFetch, activeWorkspace?.brain_id]);
|
| 185 |
-
|
| 186 |
-
const healthy = stats.filter((s) => !s.loading && !s.failed).length;
|
| 187 |
-
const failed = stats.filter((s) => s.failed).length;
|
| 188 |
-
|
| 189 |
-
return (
|
| 190 |
-
<PageFrame className="overflow-auto">
|
| 191 |
-
<SectionStack className="border border-lumen-border">
|
| 192 |
-
<SectionBand tone="accent">
|
| 193 |
-
<SectionBandHeader>
|
| 194 |
-
<SectionBandEyebrow>Operations</SectionBandEyebrow>
|
| 195 |
-
<SectionBandTitle>Brain health</SectionBandTitle>
|
| 196 |
-
<SectionBandDescription>
|
| 197 |
-
Live inventory for{" "}
|
| 198 |
-
<span className="font-mono text-lumen-foreground">
|
| 199 |
-
{activeWorkspace?.brain_id}
|
| 200 |
-
</span>{" "}
|
| 201 |
-
on {activeWorkspace?.api_base_url}
|
| 202 |
-
</SectionBandDescription>
|
| 203 |
-
</SectionBandHeader>
|
| 204 |
-
<SectionBandContent>
|
| 205 |
-
<Inline gap="md" className="flex-wrap items-center">
|
| 206 |
-
<StatusIndicator status="success">
|
| 207 |
-
{healthy} endpoints responding
|
| 208 |
-
</StatusIndicator>
|
| 209 |
-
{failed > 0 ? (
|
| 210 |
-
<StatusIndicator status="warning">
|
| 211 |
-
{failed} unavailable
|
| 212 |
-
</StatusIndicator>
|
| 213 |
-
) : (
|
| 214 |
-
<StatusIndicator status="neutral">
|
| 215 |
-
All sampled counts available
|
| 216 |
-
</StatusIndicator>
|
| 217 |
-
)}
|
| 218 |
-
<RouterLink
|
| 219 |
-
to={workspacePath(activeWorkspace!.slug, "ingest")}
|
| 220 |
-
className="inline-flex h-9 items-center border border-lumen-control-border bg-lumen-action-secondary px-3 text-sm font-medium text-lumen-on-action-secondary"
|
| 221 |
-
>
|
| 222 |
-
Ingest data
|
| 223 |
-
</RouterLink>
|
| 224 |
-
</Inline>
|
| 225 |
-
</SectionBandContent>
|
| 226 |
-
</SectionBand>
|
| 227 |
-
|
| 228 |
-
<SectionBand>
|
| 229 |
-
<SectionBandHeader>
|
| 230 |
-
<SectionBandEyebrow>Inventory</SectionBandEyebrow>
|
| 231 |
-
<SectionBandTitle>Memory footprint</SectionBandTitle>
|
| 232 |
-
<SectionBandDescription>
|
| 233 |
-
Counts are sampled with limit=1 queries against retrieve and task
|
| 234 |
-
endpoints.
|
| 235 |
-
</SectionBandDescription>
|
| 236 |
-
</SectionBandHeader>
|
| 237 |
-
<SectionBandContent className="p-0">
|
| 238 |
-
<div className="grid grid-cols-2 border border-lumen-border md:grid-cols-3">
|
| 239 |
-
{stats.map((stat) => (
|
| 240 |
-
<RouterLink
|
| 241 |
-
key={stat.label}
|
| 242 |
-
to={workspacePath(activeWorkspace!.slug, stat.to.slice(1))}
|
| 243 |
-
className="border-b border-r border-lumen-border p-5 transition-colors last:border-r-0 md:[&:nth-child(3n)]:border-r-0 hover:bg-lumen-muted focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-lumen-primary"
|
| 244 |
-
>
|
| 245 |
-
<Stat>
|
| 246 |
-
<StatLabel className="text-sm text-lumen-muted-foreground">
|
| 247 |
-
{stat.label}
|
| 248 |
-
</StatLabel>
|
| 249 |
-
<StatValue className="mt-1 text-3xl font-semibold text-lumen-foreground">
|
| 250 |
-
{stat.loading ? "…" : stat.value}
|
| 251 |
-
</StatValue>
|
| 252 |
-
{stat.loading ? (
|
| 253 |
-
<StatusIndicator status="neutral" className="mt-2">
|
| 254 |
-
Loading
|
| 255 |
-
</StatusIndicator>
|
| 256 |
-
) : stat.failed ? (
|
| 257 |
-
<StatusIndicator status="warning" className="mt-2">
|
| 258 |
-
Unavailable
|
| 259 |
-
</StatusIndicator>
|
| 260 |
-
) : (
|
| 261 |
-
<StatusIndicator status="success" className="mt-2">
|
| 262 |
-
Open {stat.label.toLowerCase()}
|
| 263 |
-
</StatusIndicator>
|
| 264 |
-
)}
|
| 265 |
-
</Stat>
|
| 266 |
-
</RouterLink>
|
| 267 |
-
))}
|
| 268 |
-
</div>
|
| 269 |
-
</SectionBandContent>
|
| 270 |
-
</SectionBand>
|
| 271 |
-
|
| 272 |
-
<SectionBand tone="muted">
|
| 273 |
-
<SectionBandHeader>
|
| 274 |
-
<SectionBandEyebrow>Workflows</SectionBandEyebrow>
|
| 275 |
-
<SectionBandTitle>Common console paths</SectionBandTitle>
|
| 276 |
-
<SectionBandDescription>
|
| 277 |
-
Jump into the densest operational views for this brain.
|
| 278 |
-
</SectionBandDescription>
|
| 279 |
-
</SectionBandHeader>
|
| 280 |
-
<SectionBandContent>
|
| 281 |
-
<div className="grid gap-0 border border-lumen-border md:grid-cols-3">
|
| 282 |
-
{shortcuts.map((item) => (
|
| 283 |
-
<RouterLink
|
| 284 |
-
key={item.to}
|
| 285 |
-
to={workspacePath(activeWorkspace!.slug, item.to.slice(1))}
|
| 286 |
-
className="border-b border-lumen-border p-4 last:border-b-0 md:border-b-0 md:border-r md:last:border-r-0"
|
| 287 |
-
>
|
| 288 |
-
<div className="text-sm font-semibold text-lumen-foreground">
|
| 289 |
-
{item.title}
|
| 290 |
-
</div>
|
| 291 |
-
<p className="mt-1 text-sm text-lumen-muted-foreground">
|
| 292 |
-
{item.description}
|
| 293 |
-
</p>
|
| 294 |
-
</RouterLink>
|
| 295 |
-
))}
|
| 296 |
-
</div>
|
| 297 |
-
</SectionBandContent>
|
| 298 |
-
</SectionBand>
|
| 299 |
-
</SectionStack>
|
| 300 |
-
</PageFrame>
|
| 301 |
-
);
|
| 302 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|