@maxiaochao/pi-toolkit 0.2.4 → 0.2.6
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/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
## 2026-09-09 - v0.2.6
|
|
2
|
+
|
|
3
|
+
- fix cache export: segment-view tooltip and event markers now show global request numbers (aligned with the x-axis) instead of relative 1-based; e2e asserts real tooltip values
|
|
4
|
+
|
|
5
|
+
## 2026-09-09 - v0.2.5
|
|
6
|
+
|
|
7
|
+
- fix cache export: segment views lost the hover tooltip (it rendered inside the hidden all-history section); move to one page-level tooltip; e2e now asserts real rendering
|
|
8
|
+
|
|
1
9
|
## 2026-09-09 - v0.2.4
|
|
2
10
|
|
|
3
11
|
- fix cache export: hover tooltips now work across segment breaks; context segments also break on model changes; add headless-browser e2e tests
|
|
@@ -205,8 +205,12 @@ function lineChart(title, series, opts = {}) {
|
|
|
205
205
|
];
|
|
206
206
|
|
|
207
207
|
const tsAttr = timestamps ? ` data-timestamps="${esc(timestamps.join("|"), true)}"` : "";
|
|
208
|
+
// Global per-request numbers (same length as the series) so a small page
|
|
209
|
+
// script can rewrite tau's relative "Request N" tooltip line to the global
|
|
210
|
+
// number inside segment views (tau hardcodes index + 1).
|
|
211
|
+
const reqAttr = requestNumbers ? ` data-requestnumbers="${esc(requestNumbers.join("|"), true)}"` : "";
|
|
208
212
|
const parts = [
|
|
209
|
-
`<svg class="usage-chart" viewBox="0 0 ${width} ${height}" role="img" aria-label="${esc(title, true)}" data-left="${left}" data-right="${right}" data-count="${count}"${tsAttr}>`,
|
|
213
|
+
`<svg class="usage-chart" viewBox="0 0 ${width} ${height}" role="img" aria-label="${esc(title, true)}" data-left="${left}" data-right="${right}" data-count="${count}"${tsAttr}${reqAttr}>`,
|
|
210
214
|
`<text class="chart-title" x="${left}" y="24">${esc(title)}</text>`,
|
|
211
215
|
];
|
|
212
216
|
for (let tick = 0; tick < 6; tick++) {
|
|
@@ -233,7 +237,10 @@ function lineChart(title, series, opts = {}) {
|
|
|
233
237
|
.forEach((event, eventIndex) => {
|
|
234
238
|
const [x] = point(Math.max(0, Math.min(count - 1, event.requestNumber - 1)), 0);
|
|
235
239
|
const markerY = top + 7 + (eventIndex % 3) * 9;
|
|
236
|
-
|
|
240
|
+
// data-request stays RELATIVE (tau's tooltip matches index + 1); the human
|
|
241
|
+
// description shows the GLOBAL request number (chartEvents keeps it).
|
|
242
|
+
const shown = event.globalRequestNumber ?? event.requestNumber;
|
|
243
|
+
const description = `${event.label} before request ${shown} at ${event.timestamp}`;
|
|
237
244
|
parts.push(
|
|
238
245
|
`<g class="usage-event usage-event-${esc(event.kind, true)}" data-request="${event.requestNumber}" ` +
|
|
239
246
|
`data-event-info="${esc(description, true)}" role="img" aria-label="${esc(description, true)}">` +
|
|
@@ -542,10 +549,14 @@ function eventsForRequests(events, requests) {
|
|
|
542
549
|
return events.filter((event) => event.requestNumber >= from && event.requestNumber <= to);
|
|
543
550
|
}
|
|
544
551
|
|
|
545
|
-
function chartEvents(events, requests) {
|
|
552
|
+
export function chartEvents(events, requests) {
|
|
546
553
|
if (!requests.length) return [];
|
|
547
554
|
const first = requests[0].number;
|
|
548
|
-
return events.map((event) => ({
|
|
555
|
+
return events.map((event) => ({
|
|
556
|
+
...event,
|
|
557
|
+
requestNumber: event.requestNumber - first + 1, // relative, for tau's data-request matching
|
|
558
|
+
globalRequestNumber: event.requestNumber, // absolute, for readable labels
|
|
559
|
+
}));
|
|
549
560
|
}
|
|
550
561
|
|
|
551
562
|
function renderDashboardView(requests, events, toolCalls, compactions, scopeLabel) {
|
|
@@ -657,7 +668,7 @@ function renderDashboardView(requests, events, toolCalls, compactions, scopeLabe
|
|
|
657
668
|
`<th>Fresh</th><th>Cached</th><th>Written</th><th>Prompt</th><th>Hit rate</th><th>Output</th>` +
|
|
658
669
|
`<th>Stop</th></tr></thead><tbody>${rows}</tbody></table></div></div>` +
|
|
659
670
|
`<div class="usage-panel"><h2>Tool calls</h2>${toolRows}</div>` +
|
|
660
|
-
`</div
|
|
671
|
+
`</div>`
|
|
661
672
|
);
|
|
662
673
|
}
|
|
663
674
|
|
|
@@ -697,10 +708,16 @@ export function renderDashboard(data) {
|
|
|
697
708
|
...segmentViews.map((view) => `<section class="usage-context-view" data-context-view="${view.id}"${view.id === `segment-${latest.number}` ? "" : " hidden"}>${view.html}</section>`),
|
|
698
709
|
].join("");
|
|
699
710
|
|
|
711
|
+
// ONE shared tooltip for the whole page. tau's script grabs the first
|
|
712
|
+
// `.usage-tooltip` in the document; with one per view that element lived in
|
|
713
|
+
// the (hidden) all-history section, so segment views set text on an element
|
|
714
|
+
// whose ancestor was display:none -> nothing painted. A single tooltip
|
|
715
|
+
// outside the view sections renders under every view.
|
|
700
716
|
return `<div class="usage-view-controls"><label for="context-segment-select">View</label>` +
|
|
701
717
|
`<select id="context-segment-select" data-context-segment-select>${options}</select>` +
|
|
702
718
|
`<span>Default: latest Context segment. All-history values remain available; cache metrics reset where compaction rewrites the prefix or the model changes.</span></div>` +
|
|
703
|
-
views
|
|
719
|
+
views +
|
|
720
|
+
`<div class="usage-tooltip" role="status" aria-live="polite"></div>`;
|
|
704
721
|
}
|
|
705
722
|
|
|
706
723
|
// ---------------------------------------------------------------------------
|
|
@@ -750,6 +767,23 @@ for (const select of document.querySelectorAll("[data-context-segment-select]"))
|
|
|
750
767
|
select.addEventListener("change", show);
|
|
751
768
|
show();
|
|
752
769
|
}
|
|
770
|
+
// tau's tooltip hardcodes "Request " + (index + 1), which is relative inside a
|
|
771
|
+
// segment view while the x-axis shows global request numbers. Rewrite the
|
|
772
|
+
// tooltip's leading request number to the chart's global number on hover.
|
|
773
|
+
// Registered after tau's own pointermove listener, so it runs after tau writes.
|
|
774
|
+
document.querySelectorAll(".usage-chart").forEach((chart) => {
|
|
775
|
+
const nums = (chart.dataset.requestnumbers || "").split("|").map(Number);
|
|
776
|
+
chart.addEventListener("pointermove", () => {
|
|
777
|
+
const tooltip = document.querySelector(".usage-tooltip");
|
|
778
|
+
if (!tooltip) return;
|
|
779
|
+
const m = tooltip.textContent.match(/^Request (\\d+)/);
|
|
780
|
+
if (!m) return;
|
|
781
|
+
const global = nums[Number(m[1]) - 1];
|
|
782
|
+
if (global && global !== Number(m[1])) {
|
|
783
|
+
tooltip.textContent = tooltip.textContent.replace(/^Request \\d+/, "Request " + global);
|
|
784
|
+
}
|
|
785
|
+
});
|
|
786
|
+
});
|
|
753
787
|
</script>
|
|
754
788
|
</main></body></html>`;
|
|
755
789
|
}
|
|
@@ -158,7 +158,10 @@ const HOVER_SCRIPT = `
|
|
|
158
158
|
clientX: r.left + 30 + r.width * frac, clientY: r.top + r.height / 2, bubbles: true,
|
|
159
159
|
}));
|
|
160
160
|
const t = document.querySelector(".usage-tooltip");
|
|
161
|
-
|
|
161
|
+
// Actually-rendered check: getClientRects() returns 0 when the element or
|
|
162
|
+
// any ancestor is display:none, even if the element's own computed
|
|
163
|
+
// display is block (the old per-view tooltip blind spot).
|
|
164
|
+
return { visible: t.getClientRects().length > 0 && getComputedStyle(t).display !== "none", text: t.textContent || "" };
|
|
162
165
|
}
|
|
163
166
|
const requestOf = (r) => {
|
|
164
167
|
const m = (r.text || "").match(/Request (\\d+)/);
|
|
@@ -266,7 +269,8 @@ async function run() {
|
|
|
266
269
|
check("e2e no IndexSizeError / uncaught errors in All view",
|
|
267
270
|
!allCharts.errs.some((e) => /IndexSizeError|Uncaught/i.test(e)), JSON.stringify(allCharts.errs));
|
|
268
271
|
|
|
269
|
-
// 3. latest segment view (no internal breaks): full-width tooltips
|
|
272
|
+
// 3. latest segment view (no internal breaks): full-width tooltips aligned
|
|
273
|
+
// to GLOBAL request numbers (fixture segment-4 = requests 40..45)
|
|
270
274
|
await evalJs(`window.__probe.setView("segment-4")`);
|
|
271
275
|
const seg4 = await evalJs(`
|
|
272
276
|
(() => {
|
|
@@ -278,9 +282,28 @@ async function run() {
|
|
|
278
282
|
check("e2e segment-4 tooltip works at start and end",
|
|
279
283
|
seg4.near.visible && seg4.far.visible && seg4.farReq !== seg4.nearReq,
|
|
280
284
|
JSON.stringify(seg4));
|
|
285
|
+
check("e2e segment-4 tooltip aligns to GLOBAL request numbers (40..45, not 1..6)",
|
|
286
|
+
seg4.nearReq === 40 && seg4.farReq === 45,
|
|
287
|
+
JSON.stringify(seg4));
|
|
281
288
|
check("e2e segment view has no uncaught errors",
|
|
282
289
|
!seg4.errs.some((e) => /IndexSizeError|Uncaught/i.test(e)), JSON.stringify(seg4.errs));
|
|
283
290
|
|
|
291
|
+
// 4. event markers in a segment view keep relative data-request for tau
|
|
292
|
+
// matching but describe the GLOBAL request number
|
|
293
|
+
await evalJs(`window.__probe.setView("segment-2")`);
|
|
294
|
+
const ev = await evalJs(`
|
|
295
|
+
(() => {
|
|
296
|
+
const chart = [...document.querySelectorAll("svg.usage-chart")]
|
|
297
|
+
.find((s) => s.getAttribute("aria-label") === "Prompt input by request" &&
|
|
298
|
+
s.closest(".usage-context-view") && !s.closest(".usage-context-view").hidden);
|
|
299
|
+
const g = chart.querySelector(".usage-event");
|
|
300
|
+
return g ? { dataRequest: g.getAttribute("data-request"), info: g.getAttribute("data-event-info") } : null;
|
|
301
|
+
})()
|
|
302
|
+
`);
|
|
303
|
+
check("e2e segment event marker: data-request relative, description global",
|
|
304
|
+
ev && ev.dataRequest === "1" && ev.info.includes("before request 25"),
|
|
305
|
+
JSON.stringify(ev));
|
|
306
|
+
|
|
284
307
|
cdp.close();
|
|
285
308
|
if (usesWindowsChrome && windowsPid) {
|
|
286
309
|
try { execFileSync("taskkill.exe", ["/PID", String(windowsPid), "/T", "/F"]); } catch { /* best effort */ }
|
|
@@ -4,7 +4,7 @@ import { mkdtempSync, readFileSync, rmSync } from "node:fs";
|
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { tmpdir } from "node:os";
|
|
6
6
|
import registerCacheExport from "../index.ts";
|
|
7
|
-
import { parseEntries, analyzeStreaks, analyzeMisses, buildContextSegments, lineChartForTest, renderDashboard } from "../render.ts";
|
|
7
|
+
import { parseEntries, analyzeStreaks, analyzeMisses, buildContextSegments, lineChartForTest, renderDashboard, chartEvents } from "../render.ts";
|
|
8
8
|
|
|
9
9
|
let pass = 0, fail = 0;
|
|
10
10
|
function check(name, cond, detail = "") {
|
|
@@ -346,6 +346,23 @@ const hitOf = (r) => r.cached / (r.fresh + r.cached + r.cacheWrite);
|
|
|
346
346
|
dashboard.slice(0, 500));
|
|
347
347
|
}
|
|
348
348
|
|
|
349
|
+
// P3. segment-view request numbers align to global (tooltip override + event labels)
|
|
350
|
+
{
|
|
351
|
+
const chart = lineChartForTest("seg", [["cached", [1, 2, 3]]], { requestNumbers: [40, 41, 42] });
|
|
352
|
+
check("P3 chart carries global data-requestnumbers", chart.includes('data-requestnumbers="40|41|42"'), chart.slice(0, 220));
|
|
353
|
+
const ev = chartEvents([{ kind: "model", requestNumber: 310, label: "Model changed to b", timestamp: "12:00:00" }], [{ number: 310 }, { number: 311 }, { number: 312 }]);
|
|
354
|
+
check("P3 chartEvents keeps relative requestNumber (tau matching) and global label number",
|
|
355
|
+
ev[0].requestNumber === 1 && ev[0].globalRequestNumber === 310,
|
|
356
|
+
JSON.stringify(ev[0]));
|
|
357
|
+
const labeled = lineChartForTest("seg", [["cached", [1, 2, 3]]], {
|
|
358
|
+
requestNumbers: [310, 311, 312],
|
|
359
|
+
events: [{ kind: "model", requestNumber: 1, label: "Model changed to b", timestamp: "12:00:00", globalRequestNumber: 310 }],
|
|
360
|
+
});
|
|
361
|
+
check("P3 event description shows global request while data-request stays relative",
|
|
362
|
+
labeled.includes("before request 310 at 12:00:00") && labeled.includes('data-request="1"'),
|
|
363
|
+
labeled.slice(0, 700));
|
|
364
|
+
}
|
|
365
|
+
|
|
349
366
|
// Q. subagent results remain visible as main-session tool input -----------
|
|
350
367
|
{
|
|
351
368
|
const entries = [
|
package/package.json
CHANGED