@cesarus777/opencode-cgp 0.4.1 → 0.4.3
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 +13 -0
- package/README.md +8 -8
- package/dist/cli.js +417 -181
- package/dist/core.js +88 -59
- package/dist/index.js +710 -166
- package/dist/tui.js +7 -3
- package/dist/types/archive.d.ts +40 -0
- package/dist/types/core.d.ts +1 -1
- package/dist/types/dashboard/controller.d.ts +1 -0
- package/dist/types/dashboard/inventory.d.ts +1 -1
- package/dist/types/dashboard/terminal.d.ts +6 -0
- package/dist/types/dashboard/terminalview.d.ts +2 -0
- package/dist/types/dashboard/view.d.ts +1 -1
- package/dist/types/history.d.ts +39 -2
- package/dist/types/historyserver.d.ts +2 -1
- package/dist/types/modelanalytics.d.ts +11 -0
- package/dist/types/modelanalyticsserver.d.ts +2 -1
- package/package.json +2 -1
package/dist/cli.js
CHANGED
|
@@ -474,15 +474,109 @@ async function segments(directory) {
|
|
|
474
474
|
return (await Promise.all(names.filter((n) => filename.test(n)).map(async (name) => ({
|
|
475
475
|
name,
|
|
476
476
|
time: Number(filename.exec(name)?.[1]),
|
|
477
|
-
|
|
477
|
+
...fileIdentity(await stat(join(directory, name)))
|
|
478
478
|
})))).sort((a, b) => a.time - b.time || a.name.localeCompare(b.name));
|
|
479
479
|
}
|
|
480
|
+
function fileIdentity(info) {
|
|
481
|
+
return {
|
|
482
|
+
bytes: info.size,
|
|
483
|
+
dev: info.dev,
|
|
484
|
+
ino: info.ino,
|
|
485
|
+
mtimeMs: info.mtimeMs,
|
|
486
|
+
ctimeMs: info.ctimeMs
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
function scanScheduler(signal, options = {}) {
|
|
490
|
+
const now = options.now ?? (() => performance.now());
|
|
491
|
+
const pause = options.yield ?? (() => new Promise((done) => setTimeout(done, 0)));
|
|
492
|
+
let started = now();
|
|
493
|
+
return () => {
|
|
494
|
+
signal?.throwIfAborted();
|
|
495
|
+
if (now() - started >= 4)
|
|
496
|
+
return pause().then(() => {
|
|
497
|
+
signal?.throwIfAborted();
|
|
498
|
+
started = now();
|
|
499
|
+
});
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
function matchesHistory(value, filter) {
|
|
503
|
+
return !(filter.sessionId !== undefined && value.sessionId !== filter.sessionId && value.sessionId !== "__history__" || value.type !== "gap" && filter.agent !== undefined && (value.performance?.requestAgent ?? value.agent) !== filter.agent || value.type !== "gap" && filter.model !== undefined && (value.performance?.requestModel ?? value.model) !== filter.model || filter.from !== undefined && value.time < filter.from || filter.to !== undefined && value.time > filter.to);
|
|
504
|
+
}
|
|
505
|
+
async function scanHistorySegment(handle, size, filter = {}, options = {}, start = 0) {
|
|
506
|
+
const records = [];
|
|
507
|
+
let malformedRecords = 0, position = start, boundary = start;
|
|
508
|
+
let pending = Buffer.alloc(0);
|
|
509
|
+
const checkpoint = scanScheduler(filter.signal, options);
|
|
510
|
+
function parse(line) {
|
|
511
|
+
if (!line.length)
|
|
512
|
+
return;
|
|
513
|
+
options.onParse?.();
|
|
514
|
+
let value;
|
|
515
|
+
try {
|
|
516
|
+
value = JSON.parse(line.toString("utf8"));
|
|
517
|
+
} catch {
|
|
518
|
+
malformedRecords++;
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
if (!isHistoryRecord(value)) {
|
|
522
|
+
malformedRecords++;
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
if (matchesHistory(value, filter))
|
|
526
|
+
records.push(value);
|
|
527
|
+
}
|
|
528
|
+
while (position < size) {
|
|
529
|
+
{
|
|
530
|
+
const waiting = checkpoint();
|
|
531
|
+
if (waiting)
|
|
532
|
+
await waiting;
|
|
533
|
+
}
|
|
534
|
+
const buffer = Buffer.alloc(Math.min(options.chunkBytes ?? 65536, size - position));
|
|
535
|
+
const { bytesRead } = await handle.read(buffer, 0, buffer.length, position);
|
|
536
|
+
if (!bytesRead)
|
|
537
|
+
throw new Error("Archive changed during read");
|
|
538
|
+
options.onRead?.(bytesRead);
|
|
539
|
+
position += bytesRead;
|
|
540
|
+
const chunk = pending.length ? Buffer.concat([pending, buffer.subarray(0, bytesRead)]) : buffer.subarray(0, bytesRead);
|
|
541
|
+
let offset = 0;
|
|
542
|
+
for (;; ) {
|
|
543
|
+
const end = chunk.indexOf(10, offset);
|
|
544
|
+
if (end < 0)
|
|
545
|
+
break;
|
|
546
|
+
{
|
|
547
|
+
const waiting = checkpoint();
|
|
548
|
+
if (waiting)
|
|
549
|
+
await waiting;
|
|
550
|
+
}
|
|
551
|
+
parse(chunk.subarray(offset, end));
|
|
552
|
+
offset = end + 1;
|
|
553
|
+
}
|
|
554
|
+
pending = Buffer.from(chunk.subarray(offset));
|
|
555
|
+
boundary = position - pending.length;
|
|
556
|
+
}
|
|
557
|
+
const completeRecords = records.length, completeMalformed = malformedRecords;
|
|
558
|
+
{
|
|
559
|
+
const waiting = checkpoint();
|
|
560
|
+
if (waiting)
|
|
561
|
+
await waiting;
|
|
562
|
+
}
|
|
563
|
+
parse(pending);
|
|
564
|
+
filter.signal?.throwIfAborted();
|
|
565
|
+
return {
|
|
566
|
+
records,
|
|
567
|
+
malformedRecords,
|
|
568
|
+
boundary,
|
|
569
|
+
completeRecords,
|
|
570
|
+
completeMalformed
|
|
571
|
+
};
|
|
572
|
+
}
|
|
480
573
|
|
|
481
574
|
class PerformanceHistory {
|
|
482
575
|
path;
|
|
483
576
|
config;
|
|
484
577
|
now;
|
|
485
578
|
report;
|
|
579
|
+
changed;
|
|
486
580
|
pending = [];
|
|
487
581
|
working;
|
|
488
582
|
active;
|
|
@@ -492,11 +586,13 @@ class PerformanceHistory {
|
|
|
492
586
|
dropped = 0;
|
|
493
587
|
droppedTotal = 0;
|
|
494
588
|
constructor(path, config, now = Date.now, report = () => {
|
|
589
|
+
}, changed = () => {
|
|
495
590
|
}) {
|
|
496
591
|
this.path = path;
|
|
497
592
|
this.config = config;
|
|
498
593
|
this.now = now;
|
|
499
594
|
this.report = report;
|
|
595
|
+
this.changed = changed;
|
|
500
596
|
}
|
|
501
597
|
emit(record2) {
|
|
502
598
|
if (!this.config.enabled || this.disabled)
|
|
@@ -545,6 +641,7 @@ class PerformanceHistory {
|
|
|
545
641
|
if (file.time >= this.now() - this.config.retentionDays * 86400000 && total + incoming <= this.config.maxBytes)
|
|
546
642
|
break;
|
|
547
643
|
await unlink(join(this.path, file.name));
|
|
644
|
+
this.changed(file.name);
|
|
548
645
|
if (file.name === this.active) {
|
|
549
646
|
this.active = undefined;
|
|
550
647
|
this.bytes = 0;
|
|
@@ -595,6 +692,7 @@ class PerformanceHistory {
|
|
|
595
692
|
const file = await open(join(this.path, this.active), "a", 384);
|
|
596
693
|
try {
|
|
597
694
|
await file.writeFile(line);
|
|
695
|
+
this.changed(this.active);
|
|
598
696
|
} finally {
|
|
599
697
|
await file.close();
|
|
600
698
|
}
|
|
@@ -649,7 +747,8 @@ function scalarPerformance(p) {
|
|
|
649
747
|
}))
|
|
650
748
|
};
|
|
651
749
|
}
|
|
652
|
-
async function readPerformanceHistory(directory, filter = {}) {
|
|
750
|
+
async function readPerformanceHistory(directory, filter = {}, options = {}) {
|
|
751
|
+
filter.signal?.throwIfAborted();
|
|
653
752
|
const records = [];
|
|
654
753
|
const files = await segments(directory);
|
|
655
754
|
filter.signal?.throwIfAborted();
|
|
@@ -667,36 +766,16 @@ async function readPerformanceHistory(directory, filter = {}) {
|
|
|
667
766
|
for (const file of selected) {
|
|
668
767
|
filter.signal?.throwIfAborted();
|
|
669
768
|
const handle = await open(join(directory, file.name), "r");
|
|
670
|
-
let source;
|
|
671
769
|
try {
|
|
672
|
-
const
|
|
673
|
-
const
|
|
674
|
-
|
|
770
|
+
const result = await scanHistorySegment(handle, file.bytes, filter, options);
|
|
771
|
+
for (const record2 of result.records)
|
|
772
|
+
records.push(record2);
|
|
773
|
+
malformedRecords += result.malformedRecords;
|
|
675
774
|
} finally {
|
|
676
775
|
await handle.close();
|
|
677
776
|
}
|
|
678
|
-
let scannedLines = 0;
|
|
679
|
-
for (const line of source.split("\n")) {
|
|
680
|
-
if (filter.signal && ++scannedLines % 256 === 0) {
|
|
681
|
-
await new Promise((done) => setTimeout(done, 0));
|
|
682
|
-
filter.signal.throwIfAborted();
|
|
683
|
-
}
|
|
684
|
-
if (!line)
|
|
685
|
-
continue;
|
|
686
|
-
try {
|
|
687
|
-
const value = JSON.parse(line);
|
|
688
|
-
if (!isHistoryRecord(value)) {
|
|
689
|
-
malformedRecords++;
|
|
690
|
-
continue;
|
|
691
|
-
}
|
|
692
|
-
if (filter.sessionId !== undefined && value.sessionId !== filter.sessionId && value.sessionId !== "__history__" || value.type !== "gap" && filter.agent !== undefined && (value.performance?.requestAgent ?? value.agent) !== filter.agent || value.type !== "gap" && filter.model !== undefined && (value.performance?.requestModel ?? value.model) !== filter.model || filter.from !== undefined && value.time < filter.from || filter.to !== undefined && value.time > filter.to)
|
|
693
|
-
continue;
|
|
694
|
-
records.push(value);
|
|
695
|
-
} catch {
|
|
696
|
-
malformedRecords++;
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
777
|
}
|
|
778
|
+
filter.signal?.throwIfAborted();
|
|
700
779
|
const metadata = {
|
|
701
780
|
schema: 1,
|
|
702
781
|
...files[0] ? { retainedFrom: files[0].time } : {},
|
|
@@ -714,7 +793,13 @@ function isHistoryRecord(value) {
|
|
|
714
793
|
if (!isRecord(value))
|
|
715
794
|
return false;
|
|
716
795
|
const r = value;
|
|
717
|
-
|
|
796
|
+
return Object.keys(r).every((k) => historyFields.has(k)) && r.schema === 1 && typeof r.recordId === "string" && typeof r.time === "number" && Number.isSafeInteger(r.time) && r.time >= 0 && typeof r.sessionId === "string" && typeof r.type === "string" && ["sample", "request", "lifecycle", "gap", "terminal"].includes(r.type) && ["parentId", "agent", "model", "event"].every((k) => r[k] === undefined || typeof r[k] === "string") && (r.performance === undefined || isPerformanceSnapshot(r.performance)) && (r.droppedRecords === undefined || typeof r.droppedRecords === "number" && Number.isSafeInteger(r.droppedRecords) && r.droppedRecords >= 0);
|
|
797
|
+
}
|
|
798
|
+
var filename, historyFields;
|
|
799
|
+
var init_history = __esm(() => {
|
|
800
|
+
init_rpc();
|
|
801
|
+
filename = /^([0-9]+)-[a-f0-9-]+\.jsonl$/;
|
|
802
|
+
historyFields = new Set([
|
|
718
803
|
"schema",
|
|
719
804
|
"recordId",
|
|
720
805
|
"time",
|
|
@@ -727,12 +812,6 @@ function isHistoryRecord(value) {
|
|
|
727
812
|
"performance",
|
|
728
813
|
"droppedRecords"
|
|
729
814
|
]);
|
|
730
|
-
return Object.keys(r).every((k) => allowed.has(k)) && r.schema === 1 && typeof r.recordId === "string" && typeof r.time === "number" && Number.isSafeInteger(r.time) && r.time >= 0 && typeof r.sessionId === "string" && typeof r.type === "string" && ["sample", "request", "lifecycle", "gap", "terminal"].includes(r.type) && ["parentId", "agent", "model", "event"].every((k) => r[k] === undefined || typeof r[k] === "string") && (r.performance === undefined || isPerformanceSnapshot(r.performance)) && (r.droppedRecords === undefined || typeof r.droppedRecords === "number" && Number.isSafeInteger(r.droppedRecords) && r.droppedRecords >= 0);
|
|
731
|
-
}
|
|
732
|
-
var filename;
|
|
733
|
-
var init_history = __esm(() => {
|
|
734
|
-
init_rpc();
|
|
735
|
-
filename = /^([0-9]+)-[a-f0-9-]+\.jsonl$/;
|
|
736
815
|
});
|
|
737
816
|
|
|
738
817
|
// src/dashboard/command.ts
|
|
@@ -1261,7 +1340,7 @@ function validateInventoryLimits(limits) {
|
|
|
1261
1340
|
if (!Number.isInteger(limits.maxNodes) || limits.maxNodes < 1 || limits.maxNodes > 2048 || !Number.isInteger(limits.maxPages) || limits.maxPages < 1 || limits.maxPages > 100 || !Number.isInteger(limits.maxDepth) || limits.maxDepth < 0 || limits.maxDepth > 64)
|
|
1262
1341
|
throw new Error("Invalid dashboard inventory limits");
|
|
1263
1342
|
}
|
|
1264
|
-
async function discoverInventory(port, signal, limits = inventoryLimits) {
|
|
1343
|
+
async function discoverInventory(port, signal, limits = inventoryLimits, progress) {
|
|
1265
1344
|
validateInventoryLimits(limits);
|
|
1266
1345
|
const sessions = new Map;
|
|
1267
1346
|
const notices = new Set;
|
|
@@ -1269,59 +1348,75 @@ async function discoverInventory(port, signal, limits = inventoryLimits) {
|
|
|
1269
1348
|
let pages = 0;
|
|
1270
1349
|
while (queue.length && pages < limits.maxPages) {
|
|
1271
1350
|
signal.throwIfAborted();
|
|
1272
|
-
const
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1351
|
+
const batch = queue.splice(0, Math.min(queue[0]?.parent === null ? 1 : 4, limits.maxPages - pages));
|
|
1352
|
+
pages += batch.length;
|
|
1353
|
+
const results = await Promise.allSettled(batch.map(async (branch) => port.listSessions({
|
|
1354
|
+
parentID: branch.parent,
|
|
1355
|
+
limit: 100,
|
|
1356
|
+
...branch.cursor ? { cursor: branch.cursor } : {}
|
|
1357
|
+
}, signal)));
|
|
1358
|
+
signal.throwIfAborted();
|
|
1359
|
+
for (const [index, branch] of batch.entries()) {
|
|
1360
|
+
try {
|
|
1361
|
+
const result = results[index];
|
|
1362
|
+
if (!result || result.status === "rejected")
|
|
1363
|
+
throw new Error("Inventory unavailable");
|
|
1364
|
+
const page = result.value;
|
|
1365
|
+
signal.throwIfAborted();
|
|
1366
|
+
for (const session of page.data) {
|
|
1367
|
+
if (session.directory !== port.location.directory) {
|
|
1368
|
+
notices.add("foreign-location");
|
|
1369
|
+
continue;
|
|
1370
|
+
}
|
|
1371
|
+
if ((session.parentId ?? null) !== branch.parent || sessions.has(session.sessionId)) {
|
|
1372
|
+
notices.add("invalid-hierarchy");
|
|
1373
|
+
continue;
|
|
1374
|
+
}
|
|
1375
|
+
if (sessions.size >= limits.maxNodes) {
|
|
1376
|
+
notices.add("node-limit");
|
|
1377
|
+
return {
|
|
1378
|
+
sessions: [...sessions.values()],
|
|
1379
|
+
complete: false,
|
|
1380
|
+
pages,
|
|
1381
|
+
notices: [...notices]
|
|
1382
|
+
};
|
|
1383
|
+
}
|
|
1384
|
+
sessions.set(session.sessionId, session);
|
|
1385
|
+
if (branch.depth < limits.maxDepth)
|
|
1386
|
+
queue.push({
|
|
1387
|
+
parent: session.sessionId,
|
|
1388
|
+
depth: branch.depth + 1,
|
|
1389
|
+
cursors: []
|
|
1390
|
+
});
|
|
1391
|
+
else
|
|
1392
|
+
notices.add("depth-limit");
|
|
1291
1393
|
}
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1394
|
+
const cursor = page.cursor?.next;
|
|
1395
|
+
if (cursor) {
|
|
1396
|
+
if (cursor === branch.cursor || branch.cursors.includes(cursor))
|
|
1397
|
+
notices.add("repeated-cursor");
|
|
1398
|
+
else
|
|
1399
|
+
queue.unshift({
|
|
1400
|
+
...branch,
|
|
1401
|
+
cursor,
|
|
1402
|
+
cursors: [...branch.cursors, cursor]
|
|
1403
|
+
});
|
|
1300
1404
|
}
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
parent: session.sessionId,
|
|
1305
|
-
depth: branch.depth + 1,
|
|
1306
|
-
cursors: []
|
|
1307
|
-
});
|
|
1308
|
-
else
|
|
1309
|
-
notices.add("depth-limit");
|
|
1405
|
+
} catch {
|
|
1406
|
+
signal.throwIfAborted();
|
|
1407
|
+
notices.add("inventory-unavailable");
|
|
1310
1408
|
}
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1409
|
+
}
|
|
1410
|
+
if (progress) {
|
|
1411
|
+
try {
|
|
1412
|
+
progress({
|
|
1413
|
+
sessions: [...sessions.values()],
|
|
1414
|
+
complete: false,
|
|
1415
|
+
pages,
|
|
1416
|
+
notices: [...notices]
|
|
1417
|
+
});
|
|
1418
|
+
} catch {
|
|
1321
1419
|
}
|
|
1322
|
-
} catch {
|
|
1323
|
-
signal.throwIfAborted();
|
|
1324
|
-
notices.add("inventory-unavailable");
|
|
1325
1420
|
}
|
|
1326
1421
|
}
|
|
1327
1422
|
if (queue.length)
|
|
@@ -1587,16 +1682,13 @@ var init_models = __esm(() => {
|
|
|
1587
1682
|
|
|
1588
1683
|
// src/dashboard/view.ts
|
|
1589
1684
|
function immutableView(value) {
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
};
|
|
1598
|
-
freeze(copy);
|
|
1599
|
-
return copy;
|
|
1685
|
+
if (value === null || typeof value !== "object" || owned.has(value))
|
|
1686
|
+
return value;
|
|
1687
|
+
const copy = Object.assign(Array.isArray(value) ? [] : {}, value);
|
|
1688
|
+
for (const key in copy)
|
|
1689
|
+
copy[key] = immutableView(copy[key]);
|
|
1690
|
+
owned.add(copy);
|
|
1691
|
+
return Object.freeze(copy);
|
|
1600
1692
|
}
|
|
1601
1693
|
function projectHistory(result, filters) {
|
|
1602
1694
|
const match = (point) => (!filters.agent || point.agent === filters.agent) && (!filters.model || point.model === filters.model);
|
|
@@ -1648,13 +1740,15 @@ function projectHistory(result, filters) {
|
|
|
1648
1740
|
notices
|
|
1649
1741
|
};
|
|
1650
1742
|
}
|
|
1651
|
-
var historyWindows;
|
|
1743
|
+
var historyWindows, owned;
|
|
1652
1744
|
var init_view = __esm(() => {
|
|
1653
1745
|
historyWindows = [300000, 3600000, 86400000, 604800000];
|
|
1746
|
+
owned = new WeakSet;
|
|
1654
1747
|
});
|
|
1655
1748
|
|
|
1656
1749
|
// src/dashboard/controller.ts
|
|
1657
1750
|
class DashboardController {
|
|
1751
|
+
snapshots = new WeakMap;
|
|
1658
1752
|
locations = new Map;
|
|
1659
1753
|
cache = new Map;
|
|
1660
1754
|
listeners = new Set;
|
|
@@ -1860,7 +1954,33 @@ class DashboardController {
|
|
|
1860
1954
|
signal,
|
|
1861
1955
|
AbortSignal.timeout(15000)
|
|
1862
1956
|
]);
|
|
1863
|
-
const result = await discoverInventory(state.port, scanSignal, this.limits)
|
|
1957
|
+
const result = await discoverInventory(state.port, scanSignal, this.limits, (partial) => {
|
|
1958
|
+
if (!this.current(state, generation, scanSignal))
|
|
1959
|
+
return;
|
|
1960
|
+
for (const session of partial.sessions) {
|
|
1961
|
+
const previous = state.nodes.get(session.sessionId);
|
|
1962
|
+
if (!previous && state.nodes.size >= this.limits.maxNodes)
|
|
1963
|
+
continue;
|
|
1964
|
+
state.nodes.set(session.sessionId, {
|
|
1965
|
+
...session,
|
|
1966
|
+
status: previous?.status ?? "unknown",
|
|
1967
|
+
inventoryStale: true
|
|
1968
|
+
});
|
|
1969
|
+
}
|
|
1970
|
+
state.complete = false;
|
|
1971
|
+
state.stale = true;
|
|
1972
|
+
state.pages = partial.pages;
|
|
1973
|
+
state.notices = partial.notices;
|
|
1974
|
+
if (!state.selected) {
|
|
1975
|
+
const first = state.nodes.keys().next().value;
|
|
1976
|
+
if (first !== undefined) {
|
|
1977
|
+
state.selected = first;
|
|
1978
|
+
this.queueHistory();
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
state.client?.setIds([...state.nodes.keys()]);
|
|
1982
|
+
this.publish();
|
|
1983
|
+
});
|
|
1864
1984
|
let active;
|
|
1865
1985
|
try {
|
|
1866
1986
|
active = new Set(await state.port.activeSessions(result.sessions.map((session) => session.sessionId), scanSignal));
|
|
@@ -1935,7 +2055,14 @@ class DashboardController {
|
|
|
1935
2055
|
return;
|
|
1936
2056
|
state.connection = client.connection;
|
|
1937
2057
|
if (client.health) {
|
|
1938
|
-
state.rows = new Map(client.rows)
|
|
2058
|
+
state.rows = new Map([...client.rows].map(([id, row2]) => {
|
|
2059
|
+
let snapshot = this.snapshots.get(row2);
|
|
2060
|
+
if (!snapshot) {
|
|
2061
|
+
snapshot = immutableView(row2);
|
|
2062
|
+
this.snapshots.set(row2, snapshot);
|
|
2063
|
+
}
|
|
2064
|
+
return [id, snapshot];
|
|
2065
|
+
}));
|
|
1939
2066
|
state.health = client.health;
|
|
1940
2067
|
state.serverAt = client.now();
|
|
1941
2068
|
state.receivedAt = this.elapsed();
|
|
@@ -2645,7 +2772,7 @@ function performanceLines(row2, width) {
|
|
|
2645
2772
|
return [];
|
|
2646
2773
|
const value = (n) => n === undefined ? "?" : n.toFixed(1);
|
|
2647
2774
|
return [
|
|
2648
|
-
...wrap(`TPS ${p.status === "waiting" ? "waiting" : `${p.status === "streaming" ? "~" : ""}${value(p.liveTps)}`} \xB7 ${p.status}${p.partial ? " \xB7 partial" : ""}`, width),
|
|
2775
|
+
...wrap(`Live TPS ${p.status === "waiting" ? "waiting" : `${p.status === "streaming" ? "~" : ""}${value(p.liveTps)}`} \xB7 ${p.status}${p.partial ? " \xB7 partial" : ""}`, width),
|
|
2649
2776
|
...wrap(`Avg model ${value(p.averageModelTps)} \xB7 Avg session ${value(p.averageSessionTps)}`, width),
|
|
2650
2777
|
...wrap(`TTFT ${p.ttftMs === undefined ? "?" : `~${(p.ttftMs / 1000).toFixed(2)}s`}${p.latestRequestTps === undefined ? "" : ` \xB7 Last ${value(p.latestRequestTps)} t/s`}`, width)
|
|
2651
2778
|
];
|
|
@@ -2920,6 +3047,7 @@ function panelRowLines(row2, width, selected) {
|
|
|
2920
3047
|
`${pad(identity, Math.max(1, width - status.length - 1))} ${status}`,
|
|
2921
3048
|
clip(`${percent} context \xB7 ${distance}`, width)
|
|
2922
3049
|
];
|
|
3050
|
+
lines.push(...performanceLines(row2, width));
|
|
2923
3051
|
const exception = [
|
|
2924
3052
|
freshnessLabel(row2),
|
|
2925
3053
|
compactionLabel(row2),
|
|
@@ -2968,12 +3096,30 @@ function overviewLabels(rows) {
|
|
|
2968
3096
|
counts.set(suffix, (counts.get(suffix) ?? 0) + 1);
|
|
2969
3097
|
return suffixes.map((suffix, index) => counts.get(suffix) === 1 ? suffix : `#${index + 1}`);
|
|
2970
3098
|
}
|
|
2971
|
-
function
|
|
3099
|
+
function overviewMetrics(row2) {
|
|
2972
3100
|
const valid2 = row2.freshness === "fresh" || row2.freshness === "stale";
|
|
2973
3101
|
const percent = `${valid2 && row2.snapshot?.measurement?.confidence === "approximate" ? "~" : ""}${valid2 && row2.utilizationPercent !== undefined ? Math.round(row2.utilizationPercent) : "?"}%`;
|
|
2974
3102
|
const context = `${row2.freshness === "stale" ? "Last measured " : ""}${percent}`;
|
|
2975
3103
|
const distance = thresholdDistance(row2, true);
|
|
2976
3104
|
const threshold = (distance === "At threshold \xB7 requires more" ? "At threshold; requires more" : distance).replace(" \xB7 Threshold reached", " \xB7 reached");
|
|
3105
|
+
const metric2 = `${context} context \xB7 ${threshold}`;
|
|
3106
|
+
const shortThreshold = threshold.replace("pressure unknown", "?").replace("At threshold; requires more", "At; needs more").replace(" \xB7 Near threshold", " near").replace(" \xB7 reached", " reached").replace("Threshold reached", "Reached");
|
|
3107
|
+
const shortMetric = `${context} \xB7 T ${shortThreshold}`;
|
|
3108
|
+
const pressure = row2.freshness !== "fresh" || row2.pressure === "unknown" ? "Threshold ?" : row2.pressure === "crossed" ? "Reached" : row2.triggerDeltaTokens === 0 ? "At; needs more" : row2.pressure === "near" ? "Near threshold" : "Below threshold";
|
|
3109
|
+
const metrics = [metric2, shortMetric, `${context} \xB7 ${pressure}`];
|
|
3110
|
+
return { percent, context, threshold, pressure, metrics };
|
|
3111
|
+
}
|
|
3112
|
+
function overviewRowHeight(row2, width, maxLines) {
|
|
3113
|
+
const base = width >= 72 ? 1 : 2;
|
|
3114
|
+
const exception = row2.freshness !== "fresh" || row2.node.status === "failed" || row2.node.interrupted || !!compactionLabel(row2) || row2.inventoryStale || row2.liveStale || row2.hidden > 0;
|
|
3115
|
+
if (exception)
|
|
3116
|
+
return Math.min(maxLines, base + 1);
|
|
3117
|
+
if (width < 72 && !overviewMetrics(row2).metrics.some((line) => cells(line) <= width))
|
|
3118
|
+
return Math.min(maxLines, base + 1);
|
|
3119
|
+
return Math.min(maxLines, base);
|
|
3120
|
+
}
|
|
3121
|
+
function overviewRowLines(row2, width, selected, label, position, maxLines = 3) {
|
|
3122
|
+
const { percent, context, threshold, pressure, metrics } = overviewMetrics(row2);
|
|
2977
3123
|
const state = width < 28 ? {
|
|
2978
3124
|
running: "RUN",
|
|
2979
3125
|
idle: "IDLE",
|
|
@@ -2987,11 +3133,6 @@ function overviewRowLines(row2, width, selected, label, position, maxLines = 3)
|
|
|
2987
3133
|
const indent = row2.depth && identityWidth >= 22 ? `${" ".repeat(Math.min(row2.depth, Math.max(0, Math.floor(identityWidth / 8) - 1)))}\u2514 ` : "";
|
|
2988
3134
|
const name = `${row2.node.agent ?? "unknown agent"}${width >= 40 ? ` / ${row2.node.model ?? "unknown model"}` : ""}`;
|
|
2989
3135
|
const identity = marker + indent + clip(name, Math.max(1, identityWidth - cells(marker + indent + tag))) + tag;
|
|
2990
|
-
const metric2 = `${context} context \xB7 ${threshold}`;
|
|
2991
|
-
const shortThreshold = threshold.replace("pressure unknown", "?").replace("At threshold; requires more", "At; needs more").replace(" \xB7 Near threshold", " near").replace(" \xB7 reached", " reached").replace("Threshold reached", "Reached");
|
|
2992
|
-
const shortMetric = `${context} \xB7 T ${shortThreshold}`;
|
|
2993
|
-
const pressure = row2.freshness !== "fresh" || row2.pressure === "unknown" ? "Threshold ?" : row2.pressure === "crossed" ? "Reached" : row2.triggerDeltaTokens === 0 ? "At; needs more" : row2.pressure === "near" ? "Near threshold" : "Below threshold";
|
|
2994
|
-
const metrics = [metric2, shortMetric, `${context} \xB7 ${pressure}`];
|
|
2995
3136
|
const fittedMetric = metrics.find((line) => cells(line) <= width);
|
|
2996
3137
|
const lines = width >= 72 ? [
|
|
2997
3138
|
overviewColumns(identity, state, row2.freshness === "stale" ? "Last" : percent, threshold, width)
|
|
@@ -3234,6 +3375,7 @@ import { isAbsolute as isAbsolute3 } from "path";
|
|
|
3234
3375
|
class TerminalShell {
|
|
3235
3376
|
controller;
|
|
3236
3377
|
quit;
|
|
3378
|
+
work = { formattedOverviewRows: 0 };
|
|
3237
3379
|
scroll = 0;
|
|
3238
3380
|
metric = 0;
|
|
3239
3381
|
sample = 0;
|
|
@@ -3249,6 +3391,9 @@ class TerminalShell {
|
|
|
3249
3391
|
rowOffset = 0;
|
|
3250
3392
|
overviewScope = "";
|
|
3251
3393
|
overviewPage = 1;
|
|
3394
|
+
labelKey = "";
|
|
3395
|
+
labels = [];
|
|
3396
|
+
layouts = new Map;
|
|
3252
3397
|
historyIdentity = "";
|
|
3253
3398
|
constructor(controller, quit) {
|
|
3254
3399
|
this.controller = controller;
|
|
@@ -3504,27 +3649,75 @@ class TerminalShell {
|
|
|
3504
3649
|
const noticeBudget = Math.min(notices.length, Math.floor((available - heading.length) / 4));
|
|
3505
3650
|
const status = notices.slice(0, noticeBudget).map((notice, index) => clip(index === noticeBudget - 1 && notices.length > noticeBudget ? `${notices.length} notices \xB7 see Details` : notice, width));
|
|
3506
3651
|
const budget = Math.max(1, available - heading.length - status.length);
|
|
3507
|
-
const
|
|
3508
|
-
|
|
3652
|
+
const labelKey = view.rows.map((row2) => row2.node.sessionId).join("\0");
|
|
3653
|
+
if (labelKey !== this.labelKey) {
|
|
3654
|
+
this.labels = overviewLabels(view.rows);
|
|
3655
|
+
this.labelKey = labelKey;
|
|
3656
|
+
}
|
|
3657
|
+
const ids = new Set(view.rows.map((row2) => row2.node.sessionId));
|
|
3658
|
+
for (const id of this.layouts.keys())
|
|
3659
|
+
if (!ids.has(id))
|
|
3660
|
+
this.layouts.delete(id);
|
|
3661
|
+
const heights = view.rows.map((row2) => {
|
|
3662
|
+
const key = JSON.stringify([
|
|
3663
|
+
width,
|
|
3664
|
+
budget,
|
|
3665
|
+
row2.node,
|
|
3666
|
+
row2.depth,
|
|
3667
|
+
row2.freshness,
|
|
3668
|
+
row2.pressure,
|
|
3669
|
+
row2.triggerDeltaTokens,
|
|
3670
|
+
row2.utilizationPercent,
|
|
3671
|
+
row2.snapshot?.measurement?.confidence,
|
|
3672
|
+
row2.snapshot?.compaction,
|
|
3673
|
+
row2.inventoryStale,
|
|
3674
|
+
row2.liveStale,
|
|
3675
|
+
row2.hidden
|
|
3676
|
+
]);
|
|
3677
|
+
let layout = this.layouts.get(row2.node.sessionId);
|
|
3678
|
+
if (!layout || layout.key !== key) {
|
|
3679
|
+
layout = { key, height: overviewRowHeight(row2, width, budget) };
|
|
3680
|
+
this.layouts.set(row2.node.sessionId, layout);
|
|
3681
|
+
}
|
|
3682
|
+
return layout.height;
|
|
3683
|
+
});
|
|
3509
3684
|
const selected = view.rows.findIndex((row2) => row2.node.sessionId === view.selectedSessionId);
|
|
3510
|
-
const start =
|
|
3511
|
-
const end = start + (
|
|
3685
|
+
const start = heights.slice(0, Math.max(0, selected)).reduce((n, h) => n + h, 0);
|
|
3686
|
+
const end = start + (heights[selected] ?? 0);
|
|
3512
3687
|
if (start < this.rowOffset)
|
|
3513
3688
|
this.rowOffset = start;
|
|
3514
3689
|
if (end > this.rowOffset + budget)
|
|
3515
3690
|
this.rowOffset = Math.max(start, end - budget);
|
|
3516
|
-
const
|
|
3517
|
-
this.rowOffset = Math.max(0, Math.min(this.rowOffset, Math.max(0,
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3691
|
+
const total = heights.reduce((n, h) => n + h, 0);
|
|
3692
|
+
this.rowOffset = Math.max(0, Math.min(this.rowOffset, Math.max(0, total - budget)));
|
|
3693
|
+
const visible = [];
|
|
3694
|
+
let cursor = 0, page = 0;
|
|
3695
|
+
for (const [index, row2] of view.rows.entries()) {
|
|
3696
|
+
const rowHeight = heights[index] ?? 1;
|
|
3697
|
+
if (cursor >= this.rowOffset && cursor + rowHeight <= this.rowOffset + budget)
|
|
3698
|
+
page++;
|
|
3699
|
+
if (cursor + rowHeight > this.rowOffset && cursor < this.rowOffset + budget) {
|
|
3700
|
+
const layout = this.layouts.get(row2.node.sessionId);
|
|
3701
|
+
const chosen = row2.node.sessionId === view.selectedSessionId;
|
|
3702
|
+
const label = this.labels[index] ?? `#${index + 1}`;
|
|
3703
|
+
if (layout) {
|
|
3704
|
+
if (!layout.lines || layout.selected !== chosen || layout.label !== label || layout.position !== index) {
|
|
3705
|
+
this.work.formattedOverviewRows++;
|
|
3706
|
+
layout.lines = overviewRowLines(row2, width, chosen, label, index, budget);
|
|
3707
|
+
layout.selected = chosen;
|
|
3708
|
+
layout.label = label;
|
|
3709
|
+
layout.position = index;
|
|
3710
|
+
}
|
|
3711
|
+
visible.push(...layout.lines.slice(Math.max(0, this.rowOffset - cursor), this.rowOffset + budget - cursor));
|
|
3712
|
+
}
|
|
3713
|
+
}
|
|
3714
|
+
cursor += rowHeight;
|
|
3715
|
+
}
|
|
3716
|
+
this.overviewPage = Math.max(1, page);
|
|
3524
3717
|
body = [
|
|
3525
3718
|
...status,
|
|
3526
3719
|
...heading,
|
|
3527
|
-
...
|
|
3720
|
+
...total ? visible : [
|
|
3528
3721
|
clip("No matching sessions; / search, f filters, r refresh.", width)
|
|
3529
3722
|
]
|
|
3530
3723
|
];
|
|
@@ -3569,7 +3762,7 @@ class TerminalShell {
|
|
|
3569
3762
|
}
|
|
3570
3763
|
}
|
|
3571
3764
|
function mountTerminal(controller, renderer, text2, finished) {
|
|
3572
|
-
let closed = false;
|
|
3765
|
+
let closed = false, pendingDraw = false, drawing = false;
|
|
3573
3766
|
let unsubscribe = () => {
|
|
3574
3767
|
};
|
|
3575
3768
|
const shutdown = (failed = false) => {
|
|
@@ -3600,25 +3793,38 @@ function mountTerminal(controller, renderer, text2, finished) {
|
|
|
3600
3793
|
const onSignal = () => shutdown();
|
|
3601
3794
|
const signals = ["SIGINT", "SIGTERM", "SIGHUP"];
|
|
3602
3795
|
const draw = () => {
|
|
3603
|
-
if (closed)
|
|
3796
|
+
if (closed || drawing)
|
|
3604
3797
|
return;
|
|
3798
|
+
pendingDraw = false;
|
|
3799
|
+
drawing = true;
|
|
3605
3800
|
try {
|
|
3606
3801
|
text2.content = shell.frame(renderer.width, renderer.height);
|
|
3607
3802
|
} catch {
|
|
3608
3803
|
fail();
|
|
3804
|
+
} finally {
|
|
3805
|
+
drawing = false;
|
|
3609
3806
|
}
|
|
3610
3807
|
};
|
|
3808
|
+
const scheduleDraw = () => {
|
|
3809
|
+
if (pendingDraw || closed || drawing)
|
|
3810
|
+
return;
|
|
3811
|
+
pendingDraw = true;
|
|
3812
|
+
queueMicrotask(() => {
|
|
3813
|
+
if (pendingDraw)
|
|
3814
|
+
draw();
|
|
3815
|
+
});
|
|
3816
|
+
};
|
|
3611
3817
|
const key = (event) => {
|
|
3612
3818
|
try {
|
|
3613
3819
|
shell.key(event, Math.max(1, renderer.height - 4));
|
|
3614
|
-
|
|
3820
|
+
scheduleDraw();
|
|
3615
3821
|
} catch {
|
|
3616
3822
|
fail();
|
|
3617
3823
|
}
|
|
3618
3824
|
};
|
|
3619
3825
|
const paste = (event) => {
|
|
3620
3826
|
shell.paste(new TextDecoder().decode(event.bytes));
|
|
3621
|
-
|
|
3827
|
+
scheduleDraw();
|
|
3622
3828
|
};
|
|
3623
3829
|
try {
|
|
3624
3830
|
renderer.root.add(text2);
|
|
@@ -3632,7 +3838,8 @@ function mountTerminal(controller, renderer, text2, finished) {
|
|
|
3632
3838
|
process.on(signal, onSignal);
|
|
3633
3839
|
process.on("uncaughtException", fail);
|
|
3634
3840
|
process.on("unhandledRejection", fail);
|
|
3635
|
-
unsubscribe = controller.subscribe(
|
|
3841
|
+
unsubscribe = controller.subscribe(scheduleDraw);
|
|
3842
|
+
draw();
|
|
3636
3843
|
if (closed) {
|
|
3637
3844
|
unsubscribe();
|
|
3638
3845
|
return { shell, shutdown };
|
|
@@ -3737,6 +3944,11 @@ function reconcile(a, b) {
|
|
|
3737
3944
|
};
|
|
3738
3945
|
}
|
|
3739
3946
|
function analyzeModelRequests(records, options) {
|
|
3947
|
+
const accumulator = createModelAccumulator(options);
|
|
3948
|
+
accumulator.append(records);
|
|
3949
|
+
return accumulator.result();
|
|
3950
|
+
}
|
|
3951
|
+
function createModelAccumulator(options) {
|
|
3740
3952
|
const maxRequests = options.maxRequests ?? 1e5;
|
|
3741
3953
|
if (!Number.isSafeInteger(maxRequests) || maxRequests < 1 || maxRequests > 1e6)
|
|
3742
3954
|
throw new Error("maxRequests must be an integer from 1 to 1000000");
|
|
@@ -3747,7 +3959,7 @@ function analyzeModelRequests(records, options) {
|
|
|
3747
3959
|
throw new Error("Invalid analytics timestamp");
|
|
3748
3960
|
if (options.from !== undefined && options.to !== undefined && options.from > options.to)
|
|
3749
3961
|
throw new Error("--from must precede --to");
|
|
3750
|
-
const
|
|
3962
|
+
const counters = {
|
|
3751
3963
|
source: "retained-request-records",
|
|
3752
3964
|
scannedRecords: 0,
|
|
3753
3965
|
unscannedRecords: 0,
|
|
@@ -3761,74 +3973,98 @@ function analyzeModelRequests(records, options) {
|
|
|
3761
3973
|
partial: false
|
|
3762
3974
|
};
|
|
3763
3975
|
const requests = new Map;
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
}
|
|
3770
|
-
if (record2.type !== "request")
|
|
3771
|
-
continue;
|
|
3772
|
-
const fact = requestFact(record2);
|
|
3773
|
-
if (!fact) {
|
|
3774
|
-
coverage.missingIdentityRecords++;
|
|
3775
|
-
continue;
|
|
3776
|
-
}
|
|
3777
|
-
fact.location = options.location;
|
|
3778
|
-
const key = JSON.stringify([
|
|
3779
|
-
fact.sessionId,
|
|
3780
|
-
fact.requestId,
|
|
3781
|
-
fact.startedAt
|
|
3782
|
-
]);
|
|
3783
|
-
const previous = requests.get(key);
|
|
3784
|
-
if (previous) {
|
|
3785
|
-
coverage.duplicateRecords++;
|
|
3786
|
-
const merged = reconcile(previous.fact, fact);
|
|
3787
|
-
if (!merged)
|
|
3788
|
-
previous.conflict = true;
|
|
3789
|
-
else
|
|
3790
|
-
previous.fact = merged;
|
|
3791
|
-
} else {
|
|
3792
|
-
if (requests.size === maxRequests) {
|
|
3793
|
-
coverage.scannedRecords--;
|
|
3794
|
-
coverage.unscannedRecords = records.length - coverage.scannedRecords;
|
|
3976
|
+
let totalRecords = 0, stopped = false;
|
|
3977
|
+
function append(records) {
|
|
3978
|
+
totalRecords += records.length;
|
|
3979
|
+
for (const record2 of records) {
|
|
3980
|
+
if (stopped)
|
|
3795
3981
|
break;
|
|
3982
|
+
counters.scannedRecords++;
|
|
3983
|
+
if (record2.type === "gap") {
|
|
3984
|
+
counters.gapRecords++;
|
|
3985
|
+
continue;
|
|
3986
|
+
}
|
|
3987
|
+
if (record2.type !== "request")
|
|
3988
|
+
continue;
|
|
3989
|
+
const fact = requestFact(record2);
|
|
3990
|
+
if (!fact) {
|
|
3991
|
+
counters.missingIdentityRecords++;
|
|
3992
|
+
continue;
|
|
3993
|
+
}
|
|
3994
|
+
fact.location = options.location;
|
|
3995
|
+
const key = JSON.stringify([
|
|
3996
|
+
fact.sessionId,
|
|
3997
|
+
fact.requestId,
|
|
3998
|
+
fact.startedAt
|
|
3999
|
+
]);
|
|
4000
|
+
const previous = requests.get(key);
|
|
4001
|
+
if (previous) {
|
|
4002
|
+
counters.duplicateRecords++;
|
|
4003
|
+
const merged = reconcile(previous.fact, fact);
|
|
4004
|
+
if (!merged)
|
|
4005
|
+
previous.conflict = true;
|
|
4006
|
+
else
|
|
4007
|
+
previous.fact = merged;
|
|
4008
|
+
} else {
|
|
4009
|
+
if (requests.size === maxRequests) {
|
|
4010
|
+
counters.scannedRecords--;
|
|
4011
|
+
stopped = true;
|
|
4012
|
+
break;
|
|
4013
|
+
}
|
|
4014
|
+
requests.set(key, { fact, conflict: false });
|
|
3796
4015
|
}
|
|
3797
|
-
requests.set(key, { fact, conflict: false });
|
|
3798
4016
|
}
|
|
3799
4017
|
}
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
|
|
4018
|
+
function* steps() {
|
|
4019
|
+
const coverage = {
|
|
4020
|
+
...counters,
|
|
4021
|
+
unscannedRecords: totalRecords - counters.scannedRecords
|
|
4022
|
+
};
|
|
4023
|
+
const accepted = [];
|
|
4024
|
+
for (const { fact: r, conflict } of requests.values()) {
|
|
4025
|
+
yield;
|
|
4026
|
+
if (conflict) {
|
|
4027
|
+
coverage.conflictingRequests++;
|
|
4028
|
+
continue;
|
|
4029
|
+
}
|
|
4030
|
+
if (r.completedAt === undefined) {
|
|
4031
|
+
coverage.incompleteRequests++;
|
|
4032
|
+
continue;
|
|
4033
|
+
}
|
|
4034
|
+
const duration = r.completedAt - r.startedAt;
|
|
4035
|
+
if (!nonnegative2(r.completedAt) || !Number.isFinite(duration) || duration <= 0) {
|
|
4036
|
+
coverage.invalidTimingRequests++;
|
|
4037
|
+
continue;
|
|
4038
|
+
}
|
|
4039
|
+
if (options.from !== undefined && r.completedAt < options.from || options.to !== undefined && r.completedAt >= options.to || options.model !== undefined && r.model !== options.model || options.agent !== undefined && r.agent !== options.agent)
|
|
4040
|
+
continue;
|
|
4041
|
+
accepted.push({ ...r, completedAt: r.completedAt });
|
|
3814
4042
|
}
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
4043
|
+
const models = yield* summarizeModelRequestSteps(accepted);
|
|
4044
|
+
coverage.partial = coverage.unscannedRecords > 0 || coverage.conflictingRequests > 0 || coverage.missingIdentityRecords > 0 || coverage.incompleteRequests > 0 || coverage.invalidTimingRequests > 0 || coverage.gapRecords > 0 || models.some((s) => s.partialRequests > 0 || s.legacyAttributionRequests > 0 || s.excludedTpsRequests > 0 || s.excludedTtftRequests > 0 || !s.model);
|
|
4045
|
+
return {
|
|
4046
|
+
schemaVersion: 1,
|
|
4047
|
+
location: options.location,
|
|
4048
|
+
models,
|
|
4049
|
+
coverage,
|
|
4050
|
+
requests: accepted
|
|
4051
|
+
};
|
|
3818
4052
|
}
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
requests: accepted
|
|
3827
|
-
};
|
|
4053
|
+
return { append, steps, result: () => finishSteps(steps()) };
|
|
4054
|
+
}
|
|
4055
|
+
function finishSteps(steps) {
|
|
4056
|
+
let step = steps.next();
|
|
4057
|
+
while (!step.done)
|
|
4058
|
+
step = steps.next();
|
|
4059
|
+
return step.value;
|
|
3828
4060
|
}
|
|
3829
4061
|
function summarizeModelRequests(requests) {
|
|
4062
|
+
return finishSteps(summarizeModelRequestSteps(requests));
|
|
4063
|
+
}
|
|
4064
|
+
function* summarizeModelRequestSteps(requests) {
|
|
3830
4065
|
const groups = new Map;
|
|
3831
4066
|
for (const r of requests) {
|
|
4067
|
+
yield;
|
|
3832
4068
|
const duration = r.completedAt - r.startedAt;
|
|
3833
4069
|
let group = groups.get(r.model);
|
|
3834
4070
|
if (!group) {
|