@cesarus777/opencode-cgp 0.2.1 → 0.2.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/README.md +3 -3
- package/dist/tui.js +270 -111
- package/dist/types/tui/chartformat.d.ts +37 -2
- package/dist/types/tui/charts.d.ts +1 -0
- package/dist/types/tui/compactformat.d.ts +3 -2
- package/dist/types/tui/contextchart.d.ts +19 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -41,7 +41,7 @@ Global configuration is `~/.config/opencode/opencode.jsonc` (or the OpenCode con
|
|
|
41
41
|
|
|
42
42
|
Restart/reload OpenCode and look for `context-guard: observe; diagnostics ...`. Observe mode records policy decisions and never requests compaction; OpenCode's own automatic compaction remains active. Inspect diagnostics first, then set `mode` to `enforce` to admit native compaction at runtime step boundaries. The complete [example](examples/opencode.jsonc) includes model, role, and agent policies. **Last matching rule wins**; selectors within a rule are ANDed.
|
|
43
43
|
|
|
44
|
-
Pin the exact published version in the existing entry's `package` field or use the manager. Version `0.2.
|
|
44
|
+
Pin the exact published version in the existing entry's `package` field or use the manager. Version `0.2.2` adds live context-capacity charts, single-metric performance chart navigation, panel padding, and persistent close controls.
|
|
45
45
|
|
|
46
46
|
```sh
|
|
47
47
|
opencode plugin add @cesarus777/opencode-cgp@0.2.1
|
|
@@ -96,13 +96,13 @@ A provider measurement refers to its **completed request**, not the next assembl
|
|
|
96
96
|
|
|
97
97
|
## Native TUI
|
|
98
98
|
|
|
99
|
-
Open `/context-guard` or **Open Context Guard** in the command palette for current prompt occupancy, effective input capacity, trigger/headroom, measurement freshness, compaction phase and nested agent status. A native sidebar section appears where the host mounts its sidebar; the panel also works on child routes and narrow terminals. Estimates, unknown values, stale measurements and uncertain compaction remain explicit.
|
|
99
|
+
Open `/context-guard` or **Open Context Guard** in the command palette for current prompt occupancy, effective input capacity, trigger/headroom, measurement freshness, compaction phase and nested agent status. A native sidebar section appears where the host mounts its sidebar; the panel also works on child routes and narrow terminals. The main session includes a live context chart scaled to its full effective input capacity, with a horizontal compaction threshold and recent usage samples. Key values stay visible as text. Estimates, unknown values, stale measurements and uncertain compaction remain explicit.
|
|
100
100
|
|
|
101
101
|
The TUI reads bounded snapshots over public OpenCode RPC, including when telemetry is disabled. Remote clients also need a client-side package registration in `cli.json`; see [the example](examples/cli.jsonc) and [controls, installation and bounds](docs/configuration.md#native-context-guard-view). It targets the inspected 2.0.29 API. The actual local host passes wide root, child and narrow grandchild panel checks. Use **Context Guard: open panel** above a child composer, where the host hides its usual prompt and palette shortcut. tmux, Neovim and cross-machine remote validation remain open.
|
|
102
102
|
|
|
103
103
|
Per-session live TPS, average model throughput, average session throughput and observed TTFT appear in the selected session and child rows. Live TPS and TTFT use `~` to mark estimates; completed rates use provider totals. Configurable local history defaults to seven days with a 64 MiB total cap, independently of diagnostic telemetry. See [metric definitions and history exports](docs/configuration.md#live-performance-and-retained-history).
|
|
104
104
|
|
|
105
|
-
Press **c** in the Context Guard panel for retained-history terminal charts
|
|
105
|
+
Press **c** in the Context Guard panel for retained-history terminal charts. View one metric at a time with **Left / Right**, change the time window with **[ / ]**, and select an agent instance with **Up / Down**. Press **c** to return or **q** to close the panel; controls stay visible below the scrollable content. Generate a self-contained interactive browser report with `bun run charts /absolute/history-directory --output report.html`. It includes filters, UTC timestamp tooltips, request statistics and SVG export. Installed packages expose the same command as `context-guard charts ...` (Bun required). See [chart controls and limits](docs/configuration.md#performance-charts).
|
|
106
106
|
|
|
107
107
|
## Inspect
|
|
108
108
|
|
package/dist/tui.js
CHANGED
|
@@ -313,8 +313,8 @@ function uniqueRows(value) {
|
|
|
313
313
|
|
|
314
314
|
// src/tui.tsx
|
|
315
315
|
import { effect as _$effect5 } from "@opentui/solid";
|
|
316
|
-
import { createTextNode as _$
|
|
317
|
-
import { insertNode as _$
|
|
316
|
+
import { createTextNode as _$createTextNode3 } from "@opentui/solid";
|
|
317
|
+
import { insertNode as _$insertNode5 } from "@opentui/solid";
|
|
318
318
|
import { setProp as _$setProp5 } from "@opentui/solid";
|
|
319
319
|
import { createElement as _$createElement5 } from "@opentui/solid";
|
|
320
320
|
import { createComponent as _$createComponent5 } from "@opentui/solid";
|
|
@@ -1224,6 +1224,61 @@ function performanceLines(row2, width) {
|
|
|
1224
1224
|
];
|
|
1225
1225
|
}
|
|
1226
1226
|
|
|
1227
|
+
// src/tui/contextchart.ts
|
|
1228
|
+
function updateContextTrace(previous, row2) {
|
|
1229
|
+
const snapshot = row2?.snapshot;
|
|
1230
|
+
const model = snapshot?.model ?? row2?.node.model;
|
|
1231
|
+
const capacity = snapshot?.capacityTokens;
|
|
1232
|
+
const sessionId = row2?.node.sessionId ?? "";
|
|
1233
|
+
const samples = previous?.sessionId === sessionId && previous.model === model && previous.capacity === capacity ? previous.samples : [];
|
|
1234
|
+
const measurement2 = snapshot?.measurement;
|
|
1235
|
+
const valid2 = (row2?.freshness === "fresh" || row2?.freshness === "stale") && measurement2 && Number.isFinite(measurement2.tokens) && measurement2.tokens >= 0;
|
|
1236
|
+
const at = measurement2?.at ?? snapshot?.observedAt;
|
|
1237
|
+
let next = samples;
|
|
1238
|
+
if (at !== undefined && Number.isFinite(at)) {
|
|
1239
|
+
const sample = {
|
|
1240
|
+
at,
|
|
1241
|
+
...valid2 ? { tokens: measurement2.tokens } : {}
|
|
1242
|
+
};
|
|
1243
|
+
const last = samples.at(-1);
|
|
1244
|
+
if (!last || at > last.at)
|
|
1245
|
+
next = [...samples, sample].slice(-96);
|
|
1246
|
+
else if (at === last.at && sample.tokens !== last.tokens)
|
|
1247
|
+
next = [...samples.slice(0, -1), sample];
|
|
1248
|
+
}
|
|
1249
|
+
return { sessionId, model, capacity, samples: next };
|
|
1250
|
+
}
|
|
1251
|
+
function contextChart(samples, capacity, trigger, width) {
|
|
1252
|
+
if (capacity === undefined || !Number.isFinite(capacity) || capacity <= 0 || width < 4)
|
|
1253
|
+
return [];
|
|
1254
|
+
const height = 8;
|
|
1255
|
+
const gutter = width >= 16 ? 4 : 0;
|
|
1256
|
+
const size = Math.max(1, Math.min(96, width - gutter - 2));
|
|
1257
|
+
const values = samples.slice(-size);
|
|
1258
|
+
const offset = size - values.length;
|
|
1259
|
+
const thresholdRow = trigger !== undefined && Number.isFinite(trigger) && trigger >= 0 && trigger <= capacity ? Math.round((1 - trigger / capacity) * (height - 1)) : -1;
|
|
1260
|
+
const blocks = " \u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588";
|
|
1261
|
+
const lines = Array.from({ length: height }, (_, row2) => {
|
|
1262
|
+
const threshold = row2 === thresholdRow;
|
|
1263
|
+
const axis = gutter ? (row2 === 0 ? "100%" : row2 === height - 1 ? "0%" : "").padStart(gutter) : "";
|
|
1264
|
+
const plot = Array.from({ length: size }, (_2, col) => {
|
|
1265
|
+
const value = values[col - offset]?.tokens;
|
|
1266
|
+
const fill = value === undefined ? 0 : Math.max(0, Math.min(8, Math.ceil((Math.min(value / capacity, 1) * height - (height - row2 - 1)) * 8)));
|
|
1267
|
+
if (threshold)
|
|
1268
|
+
return fill > 0 ? "\u2542" : "\u2504";
|
|
1269
|
+
if (value === 0 && row2 === height - 1)
|
|
1270
|
+
return "\u2581";
|
|
1271
|
+
return blocks[fill] ?? " ";
|
|
1272
|
+
}).join("");
|
|
1273
|
+
return { line: `${axis}\u2502${plot}\u2502`, threshold };
|
|
1274
|
+
});
|
|
1275
|
+
lines.push({
|
|
1276
|
+
line: `${" ".repeat(gutter)}\u2514${"\u2500".repeat(size)}\u2518`,
|
|
1277
|
+
threshold: false
|
|
1278
|
+
});
|
|
1279
|
+
return lines;
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1227
1282
|
// src/tui/compact.tsx
|
|
1228
1283
|
import { createTextNode as _$createTextNode } from "@opentui/solid";
|
|
1229
1284
|
import { insertNode as _$insertNode } from "@opentui/solid";
|
|
@@ -1258,7 +1313,7 @@ function progress(ratio, width) {
|
|
|
1258
1313
|
const filled = valid2 ? Math.round(Math.min(1, ratio) * size) : 0;
|
|
1259
1314
|
return clip("\u2588".repeat(filled) + "\u2591".repeat(size - filled) + " " + label, width);
|
|
1260
1315
|
}
|
|
1261
|
-
function mainSections(row2, width, age) {
|
|
1316
|
+
function mainSections(row2, width, age, samples) {
|
|
1262
1317
|
if (!row2)
|
|
1263
1318
|
return [{ tone: "base", lines: wrap("Loading session family...", width) }];
|
|
1264
1319
|
const s = row2.snapshot, m = s?.measurement;
|
|
@@ -1279,7 +1334,14 @@ function mainSections(row2, width, age) {
|
|
|
1279
1334
|
...columns("Context", `${tokens(measured)} / ${tokens(s?.capacityTokens)}`, width)
|
|
1280
1335
|
]
|
|
1281
1336
|
},
|
|
1282
|
-
{
|
|
1337
|
+
...contextChart(samples ?? (m && valid2 ? [{ at: m.at, tokens: m.tokens }] : []), s?.capacityTokens, s?.policy.triggerTokens, width).map(({ line, threshold }) => ({
|
|
1338
|
+
tone: threshold ? "threshold" : "pressure",
|
|
1339
|
+
lines: [line]
|
|
1340
|
+
})),
|
|
1341
|
+
{
|
|
1342
|
+
tone: "base",
|
|
1343
|
+
lines: wrap(`${ratio(s?.capacityTokens) === undefined ? "?" : Math.round((ratio(s?.capacityTokens) ?? 0) * 100)}% used \xB7 recent samples \u2192 now`, width)
|
|
1344
|
+
},
|
|
1283
1345
|
{
|
|
1284
1346
|
tone: "base",
|
|
1285
1347
|
lines: [
|
|
@@ -1290,7 +1352,8 @@ function mainSections(row2, width, age) {
|
|
|
1290
1352
|
{
|
|
1291
1353
|
tone: "pressure",
|
|
1292
1354
|
lines: [
|
|
1293
|
-
|
|
1355
|
+
...wrap(`${ratio(s?.policy.triggerTokens) === undefined ? "?" : Math.round((ratio(s?.policy.triggerTokens) ?? 0) * 100)}% of threshold \xB7 \u2504 trigger`, width),
|
|
1356
|
+
...s?.capacityTokens !== undefined && s.policy.triggerTokens !== undefined && s.policy.triggerTokens > s.capacityTokens ? wrap("Threshold above chart capacity", width) : [],
|
|
1294
1357
|
...wrap(remaining === undefined ? "Compaction distance unknown" : remaining < 0 ? `${tokens(-remaining)} tokens over threshold` : `${tokens(remaining)} tokens until compaction`, width)
|
|
1295
1358
|
]
|
|
1296
1359
|
},
|
|
@@ -1408,6 +1471,7 @@ function CompactSidebar(props) {
|
|
|
1408
1471
|
setCollapsed(new Set);
|
|
1409
1472
|
});
|
|
1410
1473
|
const current = createMemo(() => props.rows.find((r) => r.node.sessionId === props.sessionID));
|
|
1474
|
+
const trace = createMemo((previous) => updateContextTrace(previous, current()));
|
|
1411
1475
|
const tree = createMemo(() => subagentTree(props.rows, props.sessionID, collapsed(), hideCompleted(), byPressure()));
|
|
1412
1476
|
const entries = createMemo(() => new Map(tree().visible.map((entry) => [entry.row.node.sessionId, entry])));
|
|
1413
1477
|
const toggle = (id) => setCollapsed((previous) => {
|
|
@@ -1497,7 +1561,7 @@ function CompactSidebar(props) {
|
|
|
1497
1561
|
});
|
|
1498
1562
|
_$insert(_el$2, _$createComponent(Index, {
|
|
1499
1563
|
get each() {
|
|
1500
|
-
return mainSections(current(), width(), props.state.age(props.sessionID));
|
|
1564
|
+
return mainSections(current(), width(), props.state.age(props.sessionID), trace().samples);
|
|
1501
1565
|
},
|
|
1502
1566
|
children: (section2) => _$createComponent(Index, {
|
|
1503
1567
|
get each() {
|
|
@@ -1506,7 +1570,7 @@ function CompactSidebar(props) {
|
|
|
1506
1570
|
children: (line) => (() => {
|
|
1507
1571
|
var _el$13 = _$createElement("text");
|
|
1508
1572
|
_$insert(_el$13, () => line() || " ");
|
|
1509
|
-
_$effect((_$p) => _$setProp(_el$13, "fg", section2().tone === "execution" ? executionColor(current()) : section2().tone === "pressure" ? pressureColor(current()) : section2().tone === "measurement" ? current()?.freshness === "fresh" ? colors().muted : colors().feedback.warning.base : colors().base, _$p));
|
|
1573
|
+
_$effect((_$p) => _$setProp(_el$13, "fg", section2().tone === "execution" ? executionColor(current()) : section2().tone === "threshold" ? colors().feedback.warning.base : section2().tone === "pressure" ? pressureColor(current()) : section2().tone === "measurement" ? current()?.freshness === "fresh" ? colors().muted : colors().feedback.warning.base : colors().base, _$p));
|
|
1510
1574
|
return _el$13;
|
|
1511
1575
|
})()
|
|
1512
1576
|
})
|
|
@@ -1652,6 +1716,8 @@ function GuardView(props) {
|
|
|
1652
1716
|
onCleanup(() => consumer.release());
|
|
1653
1717
|
const rows = createMemo2(() => props.family ?? props.state.rows([], !props.expanded));
|
|
1654
1718
|
const current = createMemo2(() => rows().find((row2) => row2.node.sessionId === props.sessionID));
|
|
1719
|
+
const trace = createMemo2((previous) => updateContextTrace(previous, props.expanded ? current() : undefined));
|
|
1720
|
+
const chart = createMemo2(() => contextChart(trace().samples, current()?.snapshot?.capacityTokens, current()?.snapshot?.policy.triggerTokens, props.width));
|
|
1655
1721
|
const detail = createMemo2(() => {
|
|
1656
1722
|
const lines = detailLines(current(), props.width, props.expanded, props.state.age(props.sessionID));
|
|
1657
1723
|
if (props.expanded) {
|
|
@@ -1719,26 +1785,48 @@ function GuardView(props) {
|
|
|
1719
1785
|
_$insert2(_el$5, () => clip(`${props.state.connection()} / ${props.state.health()?.enforcement ?? "?"}`, props.width));
|
|
1720
1786
|
_$insert2(_el$, _$createComponent2(For2, {
|
|
1721
1787
|
get each() {
|
|
1722
|
-
return
|
|
1788
|
+
return chart();
|
|
1723
1789
|
},
|
|
1724
|
-
children: (
|
|
1790
|
+
children: (item) => (() => {
|
|
1725
1791
|
var _el$1 = _$createElement2("text");
|
|
1726
|
-
_$insert2(_el$1, line);
|
|
1727
|
-
_$effect2((_$p) => _$setProp2(_el$1, "fg", props.context.theme.text.base, _$p));
|
|
1792
|
+
_$insert2(_el$1, () => item.line);
|
|
1793
|
+
_$effect2((_$p) => _$setProp2(_el$1, "fg", item.threshold ? props.context.theme.text.feedback.warning.base : props.context.theme.text.muted, _$p));
|
|
1728
1794
|
return _el$1;
|
|
1729
1795
|
})()
|
|
1730
1796
|
}), _el$6);
|
|
1731
1797
|
_$insert2(_el$, _$createComponent2(For2, {
|
|
1732
1798
|
get each() {
|
|
1733
|
-
return props.
|
|
1799
|
+
return wrap("Recent context samples \u2192 now \xB7 \u2504 trigger", props.width);
|
|
1734
1800
|
},
|
|
1735
|
-
children: (
|
|
1801
|
+
children: (line) => (() => {
|
|
1736
1802
|
var _el$10 = _$createElement2("text");
|
|
1737
|
-
_$insert2(_el$10,
|
|
1738
|
-
_$effect2((_$p) => _$setProp2(_el$10, "fg", props.context.theme.text.
|
|
1803
|
+
_$insert2(_el$10, line);
|
|
1804
|
+
_$effect2((_$p) => _$setProp2(_el$10, "fg", props.context.theme.text.muted, _$p));
|
|
1739
1805
|
return _el$10;
|
|
1740
1806
|
})()
|
|
1741
1807
|
}), _el$6);
|
|
1808
|
+
_$insert2(_el$, _$createComponent2(For2, {
|
|
1809
|
+
get each() {
|
|
1810
|
+
return detail();
|
|
1811
|
+
},
|
|
1812
|
+
children: (line) => (() => {
|
|
1813
|
+
var _el$11 = _$createElement2("text");
|
|
1814
|
+
_$insert2(_el$11, line);
|
|
1815
|
+
_$effect2((_$p) => _$setProp2(_el$11, "fg", props.context.theme.text.base, _$p));
|
|
1816
|
+
return _el$11;
|
|
1817
|
+
})()
|
|
1818
|
+
}), _el$6);
|
|
1819
|
+
_$insert2(_el$, _$createComponent2(For2, {
|
|
1820
|
+
get each() {
|
|
1821
|
+
return props.state.health()?.reasons ?? [];
|
|
1822
|
+
},
|
|
1823
|
+
children: (reason) => (() => {
|
|
1824
|
+
var _el$12 = _$createElement2("text");
|
|
1825
|
+
_$insert2(_el$12, () => clip(reason, props.width));
|
|
1826
|
+
_$effect2((_$p) => _$setProp2(_el$12, "fg", props.context.theme.text.feedback.warning.base, _$p));
|
|
1827
|
+
return _el$12;
|
|
1828
|
+
})()
|
|
1829
|
+
}), _el$6);
|
|
1742
1830
|
_$insert2(_el$6, (() => {
|
|
1743
1831
|
var _c$2 = _$memo2(() => props.state.health()?.mode === "observe");
|
|
1744
1832
|
return () => _c$2() ? current()?.pressure === "crossed" ? "Observe: would compact" : "Observe: no admission" : "";
|
|
@@ -1752,9 +1840,9 @@ function GuardView(props) {
|
|
|
1752
1840
|
return visible();
|
|
1753
1841
|
},
|
|
1754
1842
|
children: (row2) => (() => {
|
|
1755
|
-
var _el$
|
|
1756
|
-
_$setProp2(_el$
|
|
1757
|
-
_$setProp2(_el$
|
|
1843
|
+
var _el$13 = _$createElement2("box");
|
|
1844
|
+
_$setProp2(_el$13, "flexDirection", "column");
|
|
1845
|
+
_$setProp2(_el$13, "onMouseDown", () => {
|
|
1758
1846
|
if (row2.hidden)
|
|
1759
1847
|
props.state.expand(row2.node.sessionId);
|
|
1760
1848
|
else {
|
|
@@ -1766,19 +1854,19 @@ function GuardView(props) {
|
|
|
1766
1854
|
});
|
|
1767
1855
|
}
|
|
1768
1856
|
});
|
|
1769
|
-
_$insert2(_el$
|
|
1857
|
+
_$insert2(_el$13, _$createComponent2(For2, {
|
|
1770
1858
|
get each() {
|
|
1771
1859
|
return rowLines(row2, Math.max(1, props.width - (props.expanded ? 2 : 0))).map((line) => props.expanded ? `${row2.node.sessionId === props.selectedId ? ">" : " "} ${line}` : line);
|
|
1772
1860
|
},
|
|
1773
1861
|
children: (line) => (() => {
|
|
1774
|
-
var _el$
|
|
1775
|
-
_$insert2(_el$
|
|
1776
|
-
_$effect2((_$p) => _$setProp2(_el$
|
|
1777
|
-
return _el$
|
|
1862
|
+
var _el$14 = _$createElement2("text");
|
|
1863
|
+
_$insert2(_el$14, line);
|
|
1864
|
+
_$effect2((_$p) => _$setProp2(_el$14, "fg", row2.pressure === "crossed" ? props.context.theme.text.feedback.error.base : row2.pressure === "near" ? props.context.theme.text.feedback.warning.base : props.context.theme.text.base, _$p));
|
|
1865
|
+
return _el$14;
|
|
1778
1866
|
})()
|
|
1779
1867
|
}));
|
|
1780
|
-
_$effect2((_$p) => _$setProp2(_el$
|
|
1781
|
-
return _el$
|
|
1868
|
+
_$effect2((_$p) => _$setProp2(_el$13, "id", `context-guard.row.${row2.node.sessionId}`, _$p));
|
|
1869
|
+
return _el$13;
|
|
1782
1870
|
})()
|
|
1783
1871
|
}), _el$0);
|
|
1784
1872
|
_$setProp2(_el$0, "onMouseDown", () => props.context.ui.panel.open("context-guard.monitor"));
|
|
@@ -1810,18 +1898,18 @@ function GuardView(props) {
|
|
|
1810
1898
|
|
|
1811
1899
|
// src/tui/panel.tsx
|
|
1812
1900
|
import { effect as _$effect4 } from "@opentui/solid";
|
|
1813
|
-
import { createTextNode as _$createTextNode3 } from "@opentui/solid";
|
|
1814
|
-
import { insertNode as _$insertNode3 } from "@opentui/solid";
|
|
1815
1901
|
import { memo as _$memo3 } from "@opentui/solid";
|
|
1902
|
+
import { insertNode as _$insertNode4 } from "@opentui/solid";
|
|
1816
1903
|
import { insert as _$insert4 } from "@opentui/solid";
|
|
1817
1904
|
import { createComponent as _$createComponent4 } from "@opentui/solid";
|
|
1818
|
-
import { setProp as _$setProp4 } from "@opentui/solid";
|
|
1819
1905
|
import { use as _$use } from "@opentui/solid";
|
|
1906
|
+
import { setProp as _$setProp4 } from "@opentui/solid";
|
|
1820
1907
|
import { createElement as _$createElement4 } from "@opentui/solid";
|
|
1821
|
-
import { createSignal as createSignal4, createMemo as createMemo3, createEffect as createEffect4, onCleanup as onCleanup3, Show as Show3 } from "solid-js";
|
|
1908
|
+
import { createSignal as createSignal4, createMemo as createMemo3, createEffect as createEffect4, onCleanup as onCleanup3, Show as Show3, For as For4 } from "solid-js";
|
|
1822
1909
|
|
|
1823
1910
|
// src/tui/charts.tsx
|
|
1824
1911
|
import { effect as _$effect3 } from "@opentui/solid";
|
|
1912
|
+
import { insertNode as _$insertNode3 } from "@opentui/solid";
|
|
1825
1913
|
import { insert as _$insert3 } from "@opentui/solid";
|
|
1826
1914
|
import { createComponent as _$createComponent3 } from "@opentui/solid";
|
|
1827
1915
|
import { setProp as _$setProp3 } from "@opentui/solid";
|
|
@@ -1829,7 +1917,7 @@ import { createElement as _$createElement3 } from "@opentui/solid";
|
|
|
1829
1917
|
import { createEffect as createEffect3, createSignal as createSignal3, onCleanup as onCleanup2, For as For3 } from "solid-js";
|
|
1830
1918
|
|
|
1831
1919
|
// src/tui/chartformat.ts
|
|
1832
|
-
function
|
|
1920
|
+
function columns2(points, from, to, width, gaps = []) {
|
|
1833
1921
|
const size = Math.max(1, Math.min(120, Math.floor(width))), values = Array.from({ length: size });
|
|
1834
1922
|
const column = (time) => Math.max(0, Math.min(size - 1, Math.floor((time - from) / Math.max(1, to - from) * (size - 1))));
|
|
1835
1923
|
let min = Infinity, max = -Infinity;
|
|
@@ -1841,6 +1929,13 @@ function sparkline(points, from, to, width, gaps = []) {
|
|
|
1841
1929
|
const i = column(point.time);
|
|
1842
1930
|
values[i] = Math.max(values[i] ?? -Infinity, point.value);
|
|
1843
1931
|
}
|
|
1932
|
+
for (const time of gaps)
|
|
1933
|
+
if (time >= from && time <= to)
|
|
1934
|
+
values[column(time)] = undefined;
|
|
1935
|
+
return { values, column, min, max };
|
|
1936
|
+
}
|
|
1937
|
+
function sparkline(points, from, to, width, gaps = []) {
|
|
1938
|
+
const { values, column, min, max } = columns2(points, from, to, width, gaps);
|
|
1844
1939
|
const bars = "\u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588";
|
|
1845
1940
|
const chars = values.map((v) => v === undefined ? " " : bars[Math.min(7, Math.floor(v / Math.max(1, max) * 7))] ?? "\u2581");
|
|
1846
1941
|
for (const time of gaps)
|
|
@@ -1848,20 +1943,58 @@ function sparkline(points, from, to, width, gaps = []) {
|
|
|
1848
1943
|
chars[column(time)] = "\u2506";
|
|
1849
1944
|
return { line: chars.join(""), ...min === Infinity ? {} : { min, max } };
|
|
1850
1945
|
}
|
|
1851
|
-
|
|
1946
|
+
var chartMetrics = [
|
|
1947
|
+
{ label: "Live TPS ~", unit: "t/s", key: "liveTps", source: "points" },
|
|
1948
|
+
{ label: "Avg model", unit: "t/s", key: "averageModelTps", source: "points" },
|
|
1949
|
+
{
|
|
1950
|
+
label: "Avg session",
|
|
1951
|
+
unit: "t/s",
|
|
1952
|
+
key: "averageSessionTps",
|
|
1953
|
+
source: "points"
|
|
1954
|
+
},
|
|
1955
|
+
{ label: "TTFT ~", unit: "s", key: "ttftMs", source: "requests" },
|
|
1956
|
+
{ label: "Request throughput", unit: "t/s", key: "tps", source: "requests" },
|
|
1957
|
+
{
|
|
1958
|
+
label: "Cumulative output",
|
|
1959
|
+
unit: "tokens",
|
|
1960
|
+
key: "sessionOutputTokens",
|
|
1961
|
+
source: "points"
|
|
1962
|
+
}
|
|
1963
|
+
];
|
|
1964
|
+
function metricPlot(points, from, to, width, gaps = []) {
|
|
1965
|
+
const label = (value) => value >= 1e6 ? `${(value / 1e6).toFixed(1)}M` : value >= 1000 ? `${(value / 1000).toFixed(1)}k` : value.toFixed(1);
|
|
1966
|
+
const peak = Math.max(0, ...points.filter((p) => p.time >= from && p.time <= to && Number.isFinite(p.value)).map((p) => p.value ?? 0));
|
|
1967
|
+
const scale = Math.max(1, peak);
|
|
1968
|
+
const gutter = width >= 20 ? Math.max(label(scale).length, 3) : 0;
|
|
1969
|
+
const { values, column } = columns2(points, from, to, Math.max(1, width - gutter - 2), gaps);
|
|
1970
|
+
const gapColumns = new Set(gaps.filter((time) => time >= from && time <= to).map(column));
|
|
1971
|
+
const height = 6;
|
|
1972
|
+
const blocks = " \u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588";
|
|
1973
|
+
const lines = Array.from({ length: height }, (_, row2) => {
|
|
1974
|
+
const axis = gutter ? (row2 === 0 ? label(scale) : row2 === height - 1 ? "0.0" : "").padStart(gutter) : "";
|
|
1975
|
+
return `${axis}\u2502${values.map((value, i) => {
|
|
1976
|
+
if (gapColumns.has(i))
|
|
1977
|
+
return "\u2506";
|
|
1978
|
+
if (value === undefined)
|
|
1979
|
+
return " ";
|
|
1980
|
+
const fill = Math.max(0, Math.min(8, Math.ceil((value / scale * height - (height - row2 - 1)) * 8)));
|
|
1981
|
+
return value === 0 && row2 === height - 1 ? "\u2581" : blocks[fill];
|
|
1982
|
+
}).join("")}\u2502`;
|
|
1983
|
+
});
|
|
1984
|
+
lines.push(`${" ".repeat(gutter)}\u2514${"\u2500".repeat(values.length)}\u2518`);
|
|
1985
|
+
const start = new Date(from).toISOString().slice(11, 16), end = new Date(to).toISOString().slice(11, 16);
|
|
1986
|
+
const axisWidth = values.length + 2;
|
|
1987
|
+
if (axisWidth >= 11)
|
|
1988
|
+
lines.push(`${" ".repeat(gutter)}${start}${" ".repeat(axisWidth - 10)}${end}`);
|
|
1989
|
+
return lines;
|
|
1990
|
+
}
|
|
1991
|
+
function historyChartLines(result, width, from, to, metricIndex = 0) {
|
|
1852
1992
|
if (!result.enabled)
|
|
1853
1993
|
return wrap("History recording is disabled on this server.", width);
|
|
1854
1994
|
const series = result.data.series[0];
|
|
1855
1995
|
if (!series)
|
|
1856
1996
|
return wrap("No retained measurements in this window.", width);
|
|
1857
1997
|
const gaps = result.data.gaps.map((g) => g.time);
|
|
1858
|
-
const chart = (label, unit, points) => {
|
|
1859
|
-
const plotted = sparkline(points, from, to, width - 2, gaps);
|
|
1860
|
-
return [
|
|
1861
|
-
...wrap(`${label}${plotted.min === undefined ? ": unavailable" : ` \xB7 ${plotted.min.toFixed(1)}\u2013${plotted.max?.toFixed(1)} ${unit}`}`, width),
|
|
1862
|
-
`\u2502${plotted.line}\u2502`
|
|
1863
|
-
];
|
|
1864
|
-
};
|
|
1865
1998
|
const observations = (key) => series.points.map((p) => ({
|
|
1866
1999
|
time: p.time,
|
|
1867
2000
|
...p[key] === undefined ? {} : { value: p[key] }
|
|
@@ -1870,20 +2003,17 @@ function historyChartLines(result, width, from, to) {
|
|
|
1870
2003
|
time: p.time,
|
|
1871
2004
|
...p[key] === undefined ? {} : { value: key === "ttftMs" ? p[key] / 1000 : p[key] }
|
|
1872
2005
|
}));
|
|
2006
|
+
const metric2 = chartMetrics[metricIndex] ?? chartMetrics[0];
|
|
2007
|
+
const points = metric2.source === "points" ? observations(metric2.key) : requests(metric2.key);
|
|
2008
|
+
const plotted = sparkline(points, from, to, width - 2, gaps);
|
|
1873
2009
|
return [
|
|
1874
|
-
...
|
|
1875
|
-
"",
|
|
1876
|
-
...chart("Avg model", "t/s", observations("averageModelTps")),
|
|
1877
|
-
"",
|
|
1878
|
-
...chart("Avg session", "t/s", observations("averageSessionTps")),
|
|
1879
|
-
"",
|
|
1880
|
-
...chart("TTFT ~", "s", requests("ttftMs")),
|
|
1881
|
-
"",
|
|
1882
|
-
...chart("Request throughput", "t/s", requests("tps")),
|
|
2010
|
+
...wrap(`\u2039 ${metric2.label} \u203A \xB7 ${metricIndex + 1}/${chartMetrics.length}`, width),
|
|
2011
|
+
...wrap(plotted.min === undefined ? "No measurements for this metric." : `${plotted.min.toFixed(1)}\u2013${plotted.max?.toFixed(1)} ${metric2.unit} \xB7 observed range`, width),
|
|
1883
2012
|
"",
|
|
1884
|
-
...
|
|
2013
|
+
...metricPlot(points, from, to, width, gaps),
|
|
1885
2014
|
"",
|
|
1886
|
-
...wrap("UTC time \u2192 \xB7
|
|
2015
|
+
...wrap("UTC time \u2192 \xB7 peaks per column", width),
|
|
2016
|
+
...wrap("blank = no sample \xB7 \u2506 = gap", width),
|
|
1887
2017
|
...wrap(`${new Date(from).toISOString()} \u2192 ${new Date(to).toISOString()}`, width),
|
|
1888
2018
|
...series.points.some((p) => p.partial) ? wrap("Partial timing coverage; averages include only matched model durations.", width) : [],
|
|
1889
2019
|
...result.truncated ? wrap("Downsampled/bounded history; some older requests or gap markers may be omitted.", width) : [],
|
|
@@ -1940,44 +2070,47 @@ function HistoryCharts(props) {
|
|
|
1940
2070
|
});
|
|
1941
2071
|
const lines = () => {
|
|
1942
2072
|
const current = result();
|
|
1943
|
-
return current ? historyChartLines(current, Math.max(4, props.width), range().from, range().to) : [];
|
|
2073
|
+
return current ? historyChartLines(current, Math.max(4, props.width), range().from, range().to, props.metricIndex) : [];
|
|
1944
2074
|
};
|
|
1945
2075
|
return (() => {
|
|
1946
|
-
var _el$ = _$createElement3("box");
|
|
2076
|
+
var _el$ = _$createElement3("box"), _el$2 = _$createElement3("box");
|
|
2077
|
+
_$insertNode3(_el$, _el$2);
|
|
1947
2078
|
_$setProp3(_el$, "flexDirection", "column");
|
|
1948
2079
|
_$insert3(_el$, _$createComponent3(For3, {
|
|
1949
2080
|
get each() {
|
|
1950
2081
|
return wrap(`Performance charts \xB7 ${props.sessionId}`, props.width);
|
|
1951
2082
|
},
|
|
1952
2083
|
children: (line) => (() => {
|
|
1953
|
-
var _el$
|
|
1954
|
-
_$insert3(_el$
|
|
1955
|
-
_$effect3((_$p) => _$setProp3(_el$
|
|
1956
|
-
return _el$
|
|
2084
|
+
var _el$3 = _$createElement3("text");
|
|
2085
|
+
_$insert3(_el$3, line);
|
|
2086
|
+
_$effect3((_$p) => _$setProp3(_el$3, "fg", props.context.theme.text.base, _$p));
|
|
2087
|
+
return _el$3;
|
|
1957
2088
|
})()
|
|
1958
|
-
}),
|
|
2089
|
+
}), _el$2);
|
|
1959
2090
|
_$insert3(_el$, _$createComponent3(For3, {
|
|
1960
2091
|
get each() {
|
|
1961
2092
|
return wrap(status(), props.width);
|
|
1962
2093
|
},
|
|
1963
2094
|
children: (line) => (() => {
|
|
1964
|
-
var _el$
|
|
1965
|
-
_$insert3(_el$
|
|
1966
|
-
_$effect3((_$p) => _$setProp3(_el$
|
|
1967
|
-
return _el$
|
|
2095
|
+
var _el$4 = _$createElement3("text");
|
|
2096
|
+
_$insert3(_el$4, line);
|
|
2097
|
+
_$effect3((_$p) => _$setProp3(_el$4, "fg", props.context.theme.text.muted, _$p));
|
|
2098
|
+
return _el$4;
|
|
1968
2099
|
})()
|
|
1969
|
-
}),
|
|
1970
|
-
_$
|
|
2100
|
+
}), _el$2);
|
|
2101
|
+
_$setProp3(_el$2, "flexDirection", "column");
|
|
2102
|
+
_$setProp3(_el$2, "paddingTop", 1);
|
|
2103
|
+
_$insert3(_el$2, _$createComponent3(For3, {
|
|
1971
2104
|
get each() {
|
|
1972
2105
|
return lines();
|
|
1973
2106
|
},
|
|
1974
2107
|
children: (line) => (() => {
|
|
1975
|
-
var _el$
|
|
1976
|
-
_$insert3(_el$
|
|
1977
|
-
_$effect3((_$p) => _$setProp3(_el$
|
|
1978
|
-
return _el$
|
|
2108
|
+
var _el$5 = _$createElement3("text");
|
|
2109
|
+
_$insert3(_el$5, line || " ");
|
|
2110
|
+
_$effect3((_$p) => _$setProp3(_el$5, "fg", props.context.theme.text.base, _$p));
|
|
2111
|
+
return _el$5;
|
|
1979
2112
|
})()
|
|
1980
|
-
})
|
|
2113
|
+
}));
|
|
1981
2114
|
return _el$;
|
|
1982
2115
|
})();
|
|
1983
2116
|
}
|
|
@@ -1985,6 +2118,8 @@ function HistoryCharts(props) {
|
|
|
1985
2118
|
// src/tui/panel.tsx
|
|
1986
2119
|
function GuardPanel(props) {
|
|
1987
2120
|
const [showCharts, setShowCharts] = createSignal4(false);
|
|
2121
|
+
const [metricIndex, setMetricIndex] = createSignal4(0);
|
|
2122
|
+
const width = () => Math.max(4, props.input.width - 4);
|
|
1988
2123
|
const windows = [300000, 3600000, 86400000, 7 * 86400000];
|
|
1989
2124
|
const [windowIndex, setWindowIndex] = createSignal4(1);
|
|
1990
2125
|
const [chartRefresh, setChartRefresh] = createSignal4(0);
|
|
@@ -2034,6 +2169,27 @@ function GuardPanel(props) {
|
|
|
2034
2169
|
props.context.keymap.layer(() => ({
|
|
2035
2170
|
enabled: () => props.input.focused,
|
|
2036
2171
|
commands: [{
|
|
2172
|
+
id: "context-guard.close",
|
|
2173
|
+
title: "Close Context Guard",
|
|
2174
|
+
bind: "q",
|
|
2175
|
+
run: () => props.input.close()
|
|
2176
|
+
}, {
|
|
2177
|
+
id: "context-guard.chart-previous",
|
|
2178
|
+
title: "Previous chart metric",
|
|
2179
|
+
bind: "left",
|
|
2180
|
+
run: () => {
|
|
2181
|
+
if (showCharts())
|
|
2182
|
+
setMetricIndex((i) => (i + chartMetrics.length - 1) % chartMetrics.length);
|
|
2183
|
+
}
|
|
2184
|
+
}, {
|
|
2185
|
+
id: "context-guard.chart-next",
|
|
2186
|
+
title: "Next chart metric",
|
|
2187
|
+
bind: "right",
|
|
2188
|
+
run: () => {
|
|
2189
|
+
if (showCharts())
|
|
2190
|
+
setMetricIndex((i) => (i + 1) % chartMetrics.length);
|
|
2191
|
+
}
|
|
2192
|
+
}, {
|
|
2037
2193
|
id: "context-guard.charts",
|
|
2038
2194
|
title: "Toggle performance charts",
|
|
2039
2195
|
bind: "c",
|
|
@@ -2104,20 +2260,26 @@ function GuardPanel(props) {
|
|
|
2104
2260
|
}
|
|
2105
2261
|
}]
|
|
2106
2262
|
}));
|
|
2263
|
+
const hints = () => showCharts() ? ["c back \xB7 q close", "\u2190 / \u2192 metric \xB7 \u2191 / \u2193 session", `[ / ] window \xB7 ${["5 minutes", "1 hour", "24 hours", "7 days"][windowIndex()]}`, "r refresh \xB7 Enter open session"] : ["c charts \xB7 q close", `\u2191 / \u2193 select ${offset() + 1} \xB7 Enter open`, "space expand \xB7 i inspect", "r refresh \xB7 l load more"];
|
|
2107
2264
|
return (() => {
|
|
2108
|
-
var _el$ = _$createElement4("
|
|
2109
|
-
_$
|
|
2110
|
-
_$
|
|
2111
|
-
_$
|
|
2112
|
-
_$
|
|
2113
|
-
_$
|
|
2114
|
-
_$
|
|
2265
|
+
var _el$ = _$createElement4("box"), _el$2 = _$createElement4("scrollbox"), _el$3 = _$createElement4("box"), _el$4 = _$createElement4("text"), _el$5 = _$createElement4("box");
|
|
2266
|
+
_$insertNode4(_el$, _el$2);
|
|
2267
|
+
_$insertNode4(_el$, _el$5);
|
|
2268
|
+
_$setProp4(_el$, "flexDirection", "column");
|
|
2269
|
+
_$setProp4(_el$, "flexGrow", 1);
|
|
2270
|
+
_$setProp4(_el$, "paddingLeft", 2);
|
|
2271
|
+
_$setProp4(_el$, "paddingRight", 2);
|
|
2272
|
+
_$setProp4(_el$, "paddingTop", 1);
|
|
2273
|
+
_$setProp4(_el$, "paddingBottom", 1);
|
|
2274
|
+
_$insertNode4(_el$2, _el$3);
|
|
2115
2275
|
_$use((value) => {
|
|
2116
2276
|
scroll = value;
|
|
2117
|
-
}, _el$);
|
|
2118
|
-
_$setProp4(_el
|
|
2119
|
-
_$setProp4(_el$2, "
|
|
2120
|
-
_$
|
|
2277
|
+
}, _el$2);
|
|
2278
|
+
_$setProp4(_el$2, "flexGrow", 1);
|
|
2279
|
+
_$setProp4(_el$2, "minHeight", 0);
|
|
2280
|
+
_$insertNode4(_el$3, _el$4);
|
|
2281
|
+
_$setProp4(_el$3, "flexDirection", "column");
|
|
2282
|
+
_$insert4(_el$3, _$createComponent4(GuardView, {
|
|
2121
2283
|
get sessionID() {
|
|
2122
2284
|
return props.input.sessionID;
|
|
2123
2285
|
},
|
|
@@ -2128,7 +2290,7 @@ function GuardPanel(props) {
|
|
|
2128
2290
|
return props.context;
|
|
2129
2291
|
},
|
|
2130
2292
|
get width() {
|
|
2131
|
-
return
|
|
2293
|
+
return width();
|
|
2132
2294
|
},
|
|
2133
2295
|
expanded: true,
|
|
2134
2296
|
get family() {
|
|
@@ -2140,24 +2302,28 @@ function GuardPanel(props) {
|
|
|
2140
2302
|
get inspectId() {
|
|
2141
2303
|
return inspectId();
|
|
2142
2304
|
}
|
|
2143
|
-
}));
|
|
2144
|
-
_$insert4(_el
|
|
2305
|
+
}), _el$4);
|
|
2306
|
+
_$insert4(_el$4, () => wrap("Guard input = prompt + caches. Native Context uses different totals.", width()).join("\n"));
|
|
2307
|
+
_$insert4(_el$2, _$createComponent4(Show3, {
|
|
2145
2308
|
get when() {
|
|
2146
2309
|
return showCharts();
|
|
2147
2310
|
},
|
|
2148
2311
|
get children() {
|
|
2149
|
-
return
|
|
2312
|
+
return _$createComponent4(HistoryCharts, {
|
|
2150
2313
|
get sessionId() {
|
|
2151
2314
|
return selected();
|
|
2152
2315
|
},
|
|
2153
2316
|
get windowMs() {
|
|
2154
2317
|
return windows[windowIndex()] ?? 3600000;
|
|
2155
2318
|
},
|
|
2319
|
+
get metricIndex() {
|
|
2320
|
+
return metricIndex();
|
|
2321
|
+
},
|
|
2156
2322
|
get refresh() {
|
|
2157
2323
|
return chartRefresh();
|
|
2158
2324
|
},
|
|
2159
2325
|
get width() {
|
|
2160
|
-
return
|
|
2326
|
+
return width();
|
|
2161
2327
|
},
|
|
2162
2328
|
get state() {
|
|
2163
2329
|
return props.state;
|
|
@@ -2165,38 +2331,31 @@ function GuardPanel(props) {
|
|
|
2165
2331
|
get context() {
|
|
2166
2332
|
return props.context;
|
|
2167
2333
|
}
|
|
2168
|
-
})
|
|
2169
|
-
var _el$3 = _$createElement4("text"), _el$4 = _$createTextNode3(`[ / ] window \xB7 `);
|
|
2170
|
-
_$insertNode3(_el$3, _el$4);
|
|
2171
|
-
_$insert4(_el$3, () => ["5 minutes", "1 hour", "24 hours", "7 days"][windowIndex()], null);
|
|
2172
|
-
_$effect4((_$p) => _$setProp4(_el$3, "fg", props.context.theme.text.muted, _$p));
|
|
2173
|
-
return _el$3;
|
|
2174
|
-
})()];
|
|
2334
|
+
});
|
|
2175
2335
|
}
|
|
2176
|
-
}),
|
|
2177
|
-
_$
|
|
2178
|
-
_$
|
|
2179
|
-
_$
|
|
2180
|
-
_$insert4(_el$
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2336
|
+
}), null);
|
|
2337
|
+
_$setProp4(_el$5, "flexDirection", "column");
|
|
2338
|
+
_$setProp4(_el$5, "flexShrink", 0);
|
|
2339
|
+
_$setProp4(_el$5, "paddingTop", 1);
|
|
2340
|
+
_$insert4(_el$5, _$createComponent4(For4, {
|
|
2341
|
+
get each() {
|
|
2342
|
+
return hints().flatMap((hint) => wrap(hint, width()));
|
|
2343
|
+
},
|
|
2344
|
+
children: (line) => (() => {
|
|
2345
|
+
var _el$6 = _$createElement4("text");
|
|
2346
|
+
_$insert4(_el$6, line);
|
|
2347
|
+
_$effect4((_$p) => _$setProp4(_el$6, "fg", props.context.theme.text.muted, _$p));
|
|
2348
|
+
return _el$6;
|
|
2349
|
+
})()
|
|
2350
|
+
}));
|
|
2184
2351
|
_$effect4((_p$) => {
|
|
2185
|
-
var _v$ = !showCharts(), _v$2 = props.context.theme.text.muted
|
|
2186
|
-
_v$ !== _p$.e && (_p$.e = _$setProp4(_el$
|
|
2187
|
-
_v$2 !== _p$.t && (_p$.t = _$setProp4(_el$
|
|
2188
|
-
_v$3 !== _p$.a && (_p$.a = _$setProp4(_el$8, "fg", _v$3, _p$.a));
|
|
2189
|
-
_v$4 !== _p$.o && (_p$.o = _$setProp4(_el$1, "fg", _v$4, _p$.o));
|
|
2190
|
-
_v$5 !== _p$.i && (_p$.i = _$setProp4(_el$11, "fg", _v$5, _p$.i));
|
|
2191
|
-
_v$6 !== _p$.n && (_p$.n = _$setProp4(_el$13, "fg", _v$6, _p$.n));
|
|
2352
|
+
var _v$ = !showCharts(), _v$2 = props.context.theme.text.muted;
|
|
2353
|
+
_v$ !== _p$.e && (_p$.e = _$setProp4(_el$3, "visible", _v$, _p$.e));
|
|
2354
|
+
_v$2 !== _p$.t && (_p$.t = _$setProp4(_el$4, "fg", _v$2, _p$.t));
|
|
2192
2355
|
return _p$;
|
|
2193
2356
|
}, {
|
|
2194
2357
|
e: undefined,
|
|
2195
|
-
t: undefined
|
|
2196
|
-
a: undefined,
|
|
2197
|
-
o: undefined,
|
|
2198
|
-
i: undefined,
|
|
2199
|
-
n: undefined
|
|
2358
|
+
t: undefined
|
|
2200
2359
|
});
|
|
2201
2360
|
return _el$;
|
|
2202
2361
|
})();
|
|
@@ -2245,7 +2404,7 @@ var tui_default = Plugin.define({
|
|
|
2245
2404
|
},
|
|
2246
2405
|
get children() {
|
|
2247
2406
|
var _el$ = _$createElement5("text");
|
|
2248
|
-
_$
|
|
2407
|
+
_$insertNode5(_el$, _$createTextNode3(`Context Guard: open panel`));
|
|
2249
2408
|
_$setProp5(_el$, "onMouseDown", () => context.ui.panel.open("context-guard.monitor"));
|
|
2250
2409
|
_$effect5((_$p) => _$setProp5(_el$, "fg", context.theme.text.muted, _$p));
|
|
2251
2410
|
return _el$;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { HistoryChartResult } from "../historycharts.js";
|
|
2
|
-
/** Each cell contains an observed peak, never an interpolated or invented zero. */
|
|
3
2
|
export declare function sparkline(points: readonly {
|
|
4
3
|
time: number;
|
|
5
4
|
value?: number;
|
|
@@ -8,4 +7,40 @@ export declare function sparkline(points: readonly {
|
|
|
8
7
|
min?: number;
|
|
9
8
|
max?: number;
|
|
10
9
|
};
|
|
11
|
-
export declare
|
|
10
|
+
export declare const chartMetrics: readonly [{
|
|
11
|
+
readonly label: "Live TPS ~";
|
|
12
|
+
readonly unit: "t/s";
|
|
13
|
+
readonly key: "liveTps";
|
|
14
|
+
readonly source: "points";
|
|
15
|
+
}, {
|
|
16
|
+
readonly label: "Avg model";
|
|
17
|
+
readonly unit: "t/s";
|
|
18
|
+
readonly key: "averageModelTps";
|
|
19
|
+
readonly source: "points";
|
|
20
|
+
}, {
|
|
21
|
+
readonly label: "Avg session";
|
|
22
|
+
readonly unit: "t/s";
|
|
23
|
+
readonly key: "averageSessionTps";
|
|
24
|
+
readonly source: "points";
|
|
25
|
+
}, {
|
|
26
|
+
readonly label: "TTFT ~";
|
|
27
|
+
readonly unit: "s";
|
|
28
|
+
readonly key: "ttftMs";
|
|
29
|
+
readonly source: "requests";
|
|
30
|
+
}, {
|
|
31
|
+
readonly label: "Request throughput";
|
|
32
|
+
readonly unit: "t/s";
|
|
33
|
+
readonly key: "tps";
|
|
34
|
+
readonly source: "requests";
|
|
35
|
+
}, {
|
|
36
|
+
readonly label: "Cumulative output";
|
|
37
|
+
readonly unit: "tokens";
|
|
38
|
+
readonly key: "sessionOutputTokens";
|
|
39
|
+
readonly source: "points";
|
|
40
|
+
}];
|
|
41
|
+
/** Plot only observed column peaks; leave missing data and gaps disconnected. */
|
|
42
|
+
export declare function metricPlot(points: readonly {
|
|
43
|
+
time: number;
|
|
44
|
+
value?: number;
|
|
45
|
+
}[], from: number, to: number, width: number, gaps?: readonly number[]): string[];
|
|
46
|
+
export declare function historyChartLines(result: HistoryChartResult, width: number, from: number, to: number, metricIndex?: number): string[];
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
+
import { type ContextSample } from "./contextchart.js";
|
|
1
2
|
import type { GuardRow } from "../family.js";
|
|
2
3
|
export declare function tokens(value: number | undefined): string;
|
|
3
4
|
export declare function columns(left: string, right: string, width: number): string[];
|
|
4
5
|
export declare function progress(ratio: number | undefined, width: number): string;
|
|
5
6
|
export interface MainSection {
|
|
6
|
-
readonly tone: "base" | "execution" | "pressure" | "measurement";
|
|
7
|
+
readonly tone: "base" | "execution" | "pressure" | "measurement" | "threshold";
|
|
7
8
|
readonly lines: readonly string[];
|
|
8
9
|
}
|
|
9
|
-
export declare function mainSections(row: GuardRow | undefined, width: number, age: number): MainSection[];
|
|
10
|
+
export declare function mainSections(row: GuardRow | undefined, width: number, age: number, samples?: readonly ContextSample[]): MainSection[];
|
|
10
11
|
export declare function mainLines(row: GuardRow | undefined, width: number, age: number): string[];
|
|
11
12
|
export declare const statusSymbol: {
|
|
12
13
|
readonly running: "●";
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { GuardRow } from "../family.js";
|
|
2
|
+
export interface ContextSample {
|
|
3
|
+
readonly at: number;
|
|
4
|
+
readonly tokens?: number;
|
|
5
|
+
}
|
|
6
|
+
export interface ContextTrace {
|
|
7
|
+
readonly sessionId: string;
|
|
8
|
+
readonly model: string | undefined;
|
|
9
|
+
readonly capacity: number | undefined;
|
|
10
|
+
readonly samples: readonly ContextSample[];
|
|
11
|
+
}
|
|
12
|
+
/** Keep only actual measurements, never copies made by age/render ticks. */
|
|
13
|
+
export declare function updateContextTrace(previous: ContextTrace | undefined, row: GuardRow | undefined): ContextTrace;
|
|
14
|
+
export interface ContextChartLine {
|
|
15
|
+
readonly line: string;
|
|
16
|
+
readonly threshold: boolean;
|
|
17
|
+
}
|
|
18
|
+
/** Fixed capacity scale and observation order; no interpolation across missing samples. */
|
|
19
|
+
export declare function contextChart(samples: readonly ContextSample[], capacity: number | undefined, trigger: number | undefined, width: number): ContextChartLine[];
|