@firstpick/pi-extension-git-footer-status 0.2.4 → 0.2.5
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/index.ts +88 -63
- package/package.json +1 -1
package/index.ts
CHANGED
|
@@ -101,6 +101,26 @@ type PromptEstimateCache = {
|
|
|
101
101
|
tokens: number;
|
|
102
102
|
};
|
|
103
103
|
|
|
104
|
+
type FooterUsageSnapshot = {
|
|
105
|
+
totalInput: number;
|
|
106
|
+
totalOutput: number;
|
|
107
|
+
totalCacheRead: number;
|
|
108
|
+
totalCacheWrite: number;
|
|
109
|
+
totalCost: number;
|
|
110
|
+
historicalTokenSpeed: number | null;
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
function emptyFooterUsageSnapshot(): FooterUsageSnapshot {
|
|
114
|
+
return {
|
|
115
|
+
totalInput: 0,
|
|
116
|
+
totalOutput: 0,
|
|
117
|
+
totalCacheRead: 0,
|
|
118
|
+
totalCacheWrite: 0,
|
|
119
|
+
totalCost: 0,
|
|
120
|
+
historicalTokenSpeed: null,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
104
124
|
function formatTokenSpeed(tokensPerSecond: number): string {
|
|
105
125
|
if (tokensPerSecond < 100) {
|
|
106
126
|
if (tokensPerSecond >= 10) return tokensPerSecond.toFixed(1);
|
|
@@ -368,6 +388,7 @@ export default function gitFooterStatus(pi: ExtensionAPI) {
|
|
|
368
388
|
let latestMeasuredTokenSpeed: number | null = null;
|
|
369
389
|
let promptEstimateCache: PromptEstimateCache | null = null;
|
|
370
390
|
let promptEstimateRequestId = 0;
|
|
391
|
+
let footerUsageSnapshot: FooterUsageSnapshot = emptyFooterUsageSnapshot();
|
|
371
392
|
let requestFooterRender: (() => void) | null = null;
|
|
372
393
|
|
|
373
394
|
const getPromptCalibration = (ctx: ExtensionContext) => collectInitialPromptCalibration(ctx.sessionManager.getSessionDir());
|
|
@@ -409,13 +430,62 @@ export default function gitFooterStatus(pi: ExtensionAPI) {
|
|
|
409
430
|
}
|
|
410
431
|
};
|
|
411
432
|
|
|
412
|
-
const getFooterPromptInjectionTokens = (
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
433
|
+
const getFooterPromptInjectionTokens = (): number => promptEstimateCache?.tokens ?? 0;
|
|
434
|
+
|
|
435
|
+
const recomputeFooterUsageSnapshot = (ctx: ExtensionContext): FooterUsageSnapshot => {
|
|
436
|
+
const snapshot = emptyFooterUsageSnapshot();
|
|
437
|
+
const entries = ctx.sessionManager.getEntries();
|
|
438
|
+
|
|
439
|
+
for (let i = 0; i < entries.length; i++) {
|
|
440
|
+
const entry = entries[i];
|
|
441
|
+
if (entry.type !== "message" || entry.message.role !== "assistant") continue;
|
|
442
|
+
|
|
443
|
+
const message = entry.message as AssistantMessage;
|
|
444
|
+
snapshot.totalInput += message.usage?.input ?? 0;
|
|
445
|
+
snapshot.totalOutput += message.usage?.output ?? 0;
|
|
446
|
+
snapshot.totalCacheRead += message.usage?.cacheRead ?? 0;
|
|
447
|
+
snapshot.totalCacheWrite += message.usage?.cacheWrite ?? 0;
|
|
448
|
+
snapshot.totalCost += message.usage?.cost?.total ?? 0;
|
|
449
|
+
|
|
450
|
+
const outputTokens = message.usage?.output ?? 0;
|
|
451
|
+
if (outputTokens <= 0) continue;
|
|
452
|
+
|
|
453
|
+
const endMs = getEntryTimestampMs(entry);
|
|
454
|
+
if (endMs === null) continue;
|
|
455
|
+
|
|
456
|
+
let fallbackSpeed: number | null = null;
|
|
457
|
+
for (let j = i - 1; j >= 0; j--) {
|
|
458
|
+
const previous = entries[j];
|
|
459
|
+
if (previous.type !== "message") continue;
|
|
460
|
+
|
|
461
|
+
// Skip assistant-to-assistant deltas (too noisy for speed).
|
|
462
|
+
if (previous.message.role === "assistant") continue;
|
|
463
|
+
|
|
464
|
+
const startMs = getEntryTimestampMs(previous);
|
|
465
|
+
if (startMs === null || endMs <= startMs) continue;
|
|
466
|
+
|
|
467
|
+
const elapsedSeconds = (endMs - startMs) / 1000;
|
|
468
|
+
if (elapsedSeconds <= 0) continue;
|
|
469
|
+
|
|
470
|
+
const speed = outputTokens / elapsedSeconds;
|
|
471
|
+
if (!isReasonableTokenSpeed(speed)) continue;
|
|
472
|
+
|
|
473
|
+
// Prefer user-anchored speed (best approximation of full turn latency).
|
|
474
|
+
if (previous.message.role === "user") {
|
|
475
|
+
snapshot.historicalTokenSpeed = speed;
|
|
476
|
+
break;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// Keep first non-assistant speed as fallback if no user message is found.
|
|
480
|
+
if (fallbackSpeed === null) fallbackSpeed = speed;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
if (fallbackSpeed !== null && snapshot.historicalTokenSpeed === null) {
|
|
484
|
+
snapshot.historicalTokenSpeed = fallbackSpeed;
|
|
485
|
+
}
|
|
417
486
|
}
|
|
418
|
-
|
|
487
|
+
|
|
488
|
+
return snapshot;
|
|
419
489
|
};
|
|
420
490
|
|
|
421
491
|
const recordAssistantSpeed = (message: AssistantMessage, endMs = Date.now()): boolean => {
|
|
@@ -476,6 +546,7 @@ export default function gitFooterStatus(pi: ExtensionAPI) {
|
|
|
476
546
|
|
|
477
547
|
pi.on("session_start", async (_event, ctx) => {
|
|
478
548
|
promptEstimateCache = null;
|
|
549
|
+
footerUsageSnapshot = recomputeFooterUsageSnapshot(ctx);
|
|
479
550
|
void refreshPromptInjectionEstimate(ctx);
|
|
480
551
|
|
|
481
552
|
ctx.ui.setFooter((tui, theme, footerData) => {
|
|
@@ -490,70 +561,22 @@ export default function gitFooterStatus(pi: ExtensionAPI) {
|
|
|
490
561
|
},
|
|
491
562
|
invalidate() {},
|
|
492
563
|
render(width: number): string[] {
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
564
|
+
const {
|
|
565
|
+
totalInput,
|
|
566
|
+
totalOutput,
|
|
567
|
+
totalCacheRead,
|
|
568
|
+
totalCacheWrite,
|
|
569
|
+
totalCost,
|
|
570
|
+
historicalTokenSpeed,
|
|
571
|
+
} = footerUsageSnapshot;
|
|
498
572
|
const liveOutputTokens = currentAssistantStartMs !== null ? currentAssistantEstimatedOutputTokens : 0;
|
|
499
573
|
let latestTokenSpeed: number | null = currentAssistantStartMs !== null ? currentAssistantLiveTokenSpeed : latestMeasuredTokenSpeed;
|
|
500
|
-
let historicalTokenSpeed: number | null = null;
|
|
501
|
-
|
|
502
|
-
const entries = ctx.sessionManager.getEntries();
|
|
503
|
-
for (let i = 0; i < entries.length; i++) {
|
|
504
|
-
const entry = entries[i];
|
|
505
|
-
if (entry.type === "message" && entry.message.role === "assistant") {
|
|
506
|
-
const message = entry.message as AssistantMessage;
|
|
507
|
-
totalInput += message.usage?.input ?? 0;
|
|
508
|
-
totalOutput += message.usage?.output ?? 0;
|
|
509
|
-
totalCacheRead += message.usage?.cacheRead ?? 0;
|
|
510
|
-
totalCacheWrite += message.usage?.cacheWrite ?? 0;
|
|
511
|
-
totalCost += message.usage?.cost?.total ?? 0;
|
|
512
|
-
|
|
513
|
-
if (latestMeasuredTokenSpeed === null && (message.usage?.output ?? 0) > 0) {
|
|
514
|
-
const endMs = getEntryTimestampMs(entry);
|
|
515
|
-
if (endMs !== null) {
|
|
516
|
-
let fallbackSpeed: number | null = null;
|
|
517
|
-
|
|
518
|
-
for (let j = i - 1; j >= 0; j--) {
|
|
519
|
-
const previous = entries[j];
|
|
520
|
-
if (previous.type !== "message") continue;
|
|
521
|
-
|
|
522
|
-
// Skip assistant-to-assistant deltas (too noisy for speed).
|
|
523
|
-
if (previous.message.role === "assistant") continue;
|
|
524
|
-
|
|
525
|
-
const startMs = getEntryTimestampMs(previous);
|
|
526
|
-
if (startMs === null || endMs <= startMs) continue;
|
|
527
|
-
|
|
528
|
-
const elapsedSeconds = (endMs - startMs) / 1000;
|
|
529
|
-
if (elapsedSeconds <= 0) continue;
|
|
530
|
-
|
|
531
|
-
const speed = (message.usage?.output ?? 0) / elapsedSeconds;
|
|
532
|
-
if (!isReasonableTokenSpeed(speed)) continue;
|
|
533
|
-
|
|
534
|
-
// Prefer user-anchored speed (best approximation of full turn latency).
|
|
535
|
-
if (previous.message.role === "user") {
|
|
536
|
-
historicalTokenSpeed = speed;
|
|
537
|
-
break;
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
// Keep first non-assistant speed as fallback if no user message is found.
|
|
541
|
-
if (fallbackSpeed === null) fallbackSpeed = speed;
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
if (fallbackSpeed !== null && historicalTokenSpeed === null) {
|
|
545
|
-
historicalTokenSpeed = fallbackSpeed;
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
574
|
|
|
552
575
|
if (latestTokenSpeed === null && historicalTokenSpeed !== null) {
|
|
553
576
|
latestTokenSpeed = historicalTokenSpeed;
|
|
554
577
|
}
|
|
555
578
|
|
|
556
|
-
const currentPromptInjectionTokens = getFooterPromptInjectionTokens(
|
|
579
|
+
const currentPromptInjectionTokens = getFooterPromptInjectionTokens();
|
|
557
580
|
|
|
558
581
|
const contextUsage = ctx.getContextUsage();
|
|
559
582
|
const contextWindow = contextUsage?.contextWindow ?? ctx.model?.contextWindow ?? 0;
|
|
@@ -730,6 +753,8 @@ export default function gitFooterStatus(pi: ExtensionAPI) {
|
|
|
730
753
|
recordAssistantSpeed(event.message as AssistantMessage);
|
|
731
754
|
resetLiveAssistantState();
|
|
732
755
|
}
|
|
756
|
+
footerUsageSnapshot = recomputeFooterUsageSnapshot(ctx);
|
|
757
|
+
requestFooterRender?.();
|
|
733
758
|
void refreshPromptInjectionEstimate(ctx);
|
|
734
759
|
await refresh(ctx);
|
|
735
760
|
});
|
package/package.json
CHANGED