agl 41.1.0 → 41.2.0

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.
@@ -13,23 +13,29 @@ export interface IMetricsBoardSender {
13
13
  ): Promise<TRequest['response']>;
14
14
  }
15
15
 
16
- type TUsageCounts = Pick<interfaces.IControllerUsageBucket,
17
- 'calls' | 'inputTokens' | 'outputTokens' | 'cacheReadTokens' | 'cacheWriteTokens'>;
16
+ type TUsageCounts = interfaces.IControllerUsageCounts;
17
+
18
+ /**
19
+ * A table row's counts: its buckets' counts, their total tokens and whether that total is a lower bound
20
+ * (some bucket of it has unknown cache counts and a provider `controllerUsageTotalIsLowerBound` marks).
21
+ */
22
+ interface IMetricsRowCounts extends TUsageCounts {
23
+ totalTokens: number;
24
+ totalLowerBound: boolean;
25
+ }
18
26
 
19
27
  /** A model's usage over the window: one row of the models table. */
20
- export interface IMetricsModelRow extends TUsageCounts {
28
+ export interface IMetricsModelRow extends IMetricsRowCounts {
21
29
  key: string;
22
30
  provider: interfaces.TControllerUsageProvider;
23
31
  model: string;
24
- totalTokens: number;
25
32
  }
26
33
 
27
34
  /** One session on one harness of the opened model: one row of the drill-down table. */
28
- export interface IMetricsSessionRow extends TUsageCounts {
35
+ export interface IMetricsSessionRow extends IMetricsRowCounts {
29
36
  key: string;
30
37
  harness: string;
31
38
  session: string;
32
- totalTokens: number;
33
39
  }
34
40
 
35
41
  const windowOptions: { key: interfaces.TControllerUsageWindow; label: string; description: string }[] = [
@@ -42,12 +48,15 @@ const modelKey = (bucketArg: Pick<interfaces.IControllerUsageBucket, 'provider'
42
48
  `${bucketArg.provider}/${bucketArg.model}`
43
49
  );
44
50
 
45
- /** Every token a call counted: input, output, cache read and cache write. */
51
+ /** Every token a call counted: input, output and the cache reads and writes it stated. */
46
52
  const totalTokens = (countsArg: TUsageCounts): number => (
47
53
  countsArg.inputTokens + countsArg.outputTokens + countsArg.cacheReadTokens + countsArg.cacheWriteTokens
48
54
  );
49
55
 
50
- const emptyCounts = (): TUsageCounts => ({ calls: 0, inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0 });
56
+ const emptyCounts = (): TUsageCounts => ({ calls: 0, inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0,
57
+ cacheReadUnknownCalls: 0, cacheWriteUnknownCalls: 0 });
58
+
59
+ const emptyRowCounts = (): IMetricsRowCounts => ({ ...emptyCounts(), totalTokens: 0, totalLowerBound: false });
51
60
 
52
61
  const addCounts = (targetArg: TUsageCounts, bucketArg: TUsageCounts): void => {
53
62
  targetArg.calls += bucketArg.calls;
@@ -55,6 +64,31 @@ const addCounts = (targetArg: TUsageCounts, bucketArg: TUsageCounts): void => {
55
64
  targetArg.outputTokens += bucketArg.outputTokens;
56
65
  targetArg.cacheReadTokens += bucketArg.cacheReadTokens;
57
66
  targetArg.cacheWriteTokens += bucketArg.cacheWriteTokens;
67
+ targetArg.cacheReadUnknownCalls += bucketArg.cacheReadUnknownCalls;
68
+ targetArg.cacheWriteUnknownCalls += bucketArg.cacheWriteUnknownCalls;
69
+ };
70
+
71
+ const addBucket = (rowArg: IMetricsRowCounts, bucketArg: interfaces.IControllerUsageBucket): void => {
72
+ addCounts(rowArg, bucketArg);
73
+ rowArg.totalTokens += totalTokens(bucketArg);
74
+ rowArg.totalLowerBound ||= interfaces.controllerUsageTotalIsLowerBound[bucketArg.provider]
75
+ && (bucketArg.cacheReadUnknownCalls > 0 || bucketArg.cacheWriteUnknownCalls > 0);
76
+ };
77
+
78
+ /** The cache counts the board states: reads and writes, each with the calls that did not state theirs. */
79
+ const cacheCounts = {
80
+ cacheReadTokens: 'cacheReadUnknownCalls',
81
+ cacheWriteTokens: 'cacheWriteUnknownCalls',
82
+ } as const satisfies Record<string, keyof TUsageCounts>;
83
+
84
+ /**
85
+ * A cache count as the board states it: what the calls stated; "≥" before it when some calls did not state theirs, as
86
+ * those read or wrote an unknown number, not none; and "Unknown" when no call stated any.
87
+ */
88
+ const cacheText = (countsArg: TUsageCounts, keyArg: keyof typeof cacheCounts, formatArg: Intl.NumberFormat): string => {
89
+ const stated = countsArg[keyArg], unknownCalls = countsArg[cacheCounts[keyArg]];
90
+ return unknownCalls === 0 ? formatArg.format(stated)
91
+ : stated === 0 && unknownCalls === countsArg.calls ? 'Unknown' : `≥ ${formatArg.format(stated)}`;
58
92
  };
59
93
 
60
94
  const compactNumber = new Intl.NumberFormat(undefined, { notation: 'compact', maximumFractionDigits: 1 });
@@ -68,7 +102,8 @@ const pointTimeFormatters: Record<interfaces.TControllerUsageSpan, (epochMsArg:
68
102
  hour: (epochMsArg) => momentFormat.formatRange(epochMsArg, epochMsArg + interfaces.controllerUsageSpanMs.hour),
69
103
  };
70
104
 
71
- const countColumns = <TRow extends TUsageCounts & { totalTokens: number }>(): plugins.deesCatalog.Column<TRow>[] => (
105
+ /** The count columns; a cache count as `cacheText` states it, and the total with "≥" when it is a lower bound. */
106
+ const countColumns = <TRow extends IMetricsRowCounts>(): plugins.deesCatalog.Column<TRow>[] => (
72
107
  ([
73
108
  ['calls', 'Calls'],
74
109
  ['inputTokens', 'Input'],
@@ -80,7 +115,9 @@ const countColumns = <TRow extends TUsageCounts & { totalTokens: number }>(): pl
80
115
  key: keyArg,
81
116
  header: headerArg,
82
117
  align: 'end' as const,
83
- renderer: (valueArg: number) => fullNumber.format(valueArg),
118
+ renderer: (valueArg: number, rowArg: TRow) => (keyArg === 'cacheReadTokens' || keyArg === 'cacheWriteTokens'
119
+ ? cacheText(rowArg, keyArg, fullNumber)
120
+ : keyArg === 'totalTokens' && rowArg.totalLowerBound ? `≥ ${fullNumber.format(valueArg)}` : fullNumber.format(valueArg)),
84
121
  }))
85
122
  );
86
123
 
@@ -375,11 +412,10 @@ export class AglMetricsBoard extends plugins.deesElement.DeesElement {
375
412
  const key = modelKey(bucket);
376
413
  let row = rows.get(key);
377
414
  if (!row) {
378
- row = { key, provider: bucket.provider, model: bucket.model, ...emptyCounts(), totalTokens: 0 };
415
+ row = { key, provider: bucket.provider, model: bucket.model, ...emptyRowCounts() };
379
416
  rows.set(key, row);
380
417
  }
381
- addCounts(row, bucket);
382
- row.totalTokens += totalTokens(bucket);
418
+ addBucket(row, bucket);
383
419
  }
384
420
  return [...rows.values()];
385
421
  }
@@ -393,11 +429,10 @@ export class AglMetricsBoard extends plugins.deesElement.DeesElement {
393
429
  const key = JSON.stringify([bucket.harness, bucket.session]);
394
430
  let row = rows.get(key);
395
431
  if (!row) {
396
- row = { key, harness, session, ...emptyCounts(), totalTokens: 0 };
432
+ row = { key, harness, session, ...emptyRowCounts() };
397
433
  rows.set(key, row);
398
434
  }
399
- addCounts(row, bucket);
400
- row.totalTokens += totalTokens(bucket);
435
+ addBucket(row, bucket);
401
436
  }
402
437
  return [...rows.values()];
403
438
  }
@@ -409,12 +444,27 @@ export class AglMetricsBoard extends plugins.deesElement.DeesElement {
409
444
  return [
410
445
  { id: 'input', title: 'Input', value: value(counts.inputTokens), unit: 'tokens', type: 'number', icon: 'lucide:ArrowDownToLine' },
411
446
  { id: 'output', title: 'Output', value: value(counts.outputTokens), unit: 'tokens', type: 'number', icon: 'lucide:ArrowUpFromLine' },
412
- { id: 'cacheRead', title: 'Cache read', value: value(counts.cacheReadTokens), unit: 'tokens', type: 'number', icon: 'lucide:DatabaseZap' },
413
- { id: 'cacheWrite', title: 'Cache write', value: value(counts.cacheWriteTokens), unit: 'tokens', type: 'number', icon: 'lucide:Database' },
447
+ this.cacheTile(counts, knownArg, 'cacheReadTokens'),
448
+ this.cacheTile(counts, knownArg, 'cacheWriteTokens'),
414
449
  { id: 'calls', title: 'Calls', value: value(counts.calls), type: 'number', icon: 'lucide:Activity' },
415
450
  ];
416
451
  }
417
452
 
453
+ /**
454
+ * A cache read or write tile: "Unknown" without a unit when no call stated its count, "≥" the stated count when some
455
+ * did not, and then how many calls left it unknown.
456
+ */
457
+ private cacheTile(countsArg: TUsageCounts, knownArg: boolean, keyArg: keyof typeof cacheCounts): plugins.deesCatalog.IStatsTile {
458
+ const text = knownArg ? cacheText(countsArg, keyArg, compactNumber) : '–';
459
+ const unknownCalls = countsArg[cacheCounts[keyArg]];
460
+ const tile = keyArg === 'cacheReadTokens' ? { id: 'cacheRead', title: 'Cache read', icon: 'lucide:DatabaseZap' }
461
+ : { id: 'cacheWrite', title: 'Cache write', icon: 'lucide:Database' };
462
+ return { ...tile, value: text, ...(text === 'Unknown' ? {} : { unit: 'tokens' }), type: 'number',
463
+ ...(knownArg && unknownCalls > 0
464
+ ? { description: `Unknown for ${fullNumber.format(unknownCalls)} of ${fullNumber.format(countsArg.calls)} calls` }
465
+ : {}) };
466
+ }
467
+
418
468
  /**
419
469
  * One series per model, the largest first, each drawn from zero: its tokens per minute in every span of the window,
420
470
  * the empty ones as 0. A minute that is still open is left out, as its count is partial; the totals and tables keep