feat(metrics): add API request counter for copilot/subscription users (#1140)

This commit is contained in:
Tom Boucher 2026-03-18 10:25:41 -04:00 committed by GitHub
parent 65fe3c2adc
commit b1ce681803
5 changed files with 14 additions and 2 deletions

View file

@ -460,6 +460,7 @@ export function updateProgressWidget(
sp.push(`\u26A1${hitRate}%`);
}
if (cumulativeCost) sp.push(`$${cumulativeCost.toFixed(3)}`);
else if (autoTotals?.apiRequests) sp.push(`${autoTotals.apiRequests} reqs`);
const cxDisplay = cxPct === "?"
? `?/${formatWidgetTokens(cxWindow)}`

View file

@ -479,8 +479,12 @@ export class GSDDashboardOverlay {
lines.push(row(th.fg("text", th.bold("Cost & Usage"))));
lines.push(blank());
// Show cost or request count (for copilot/subscription users where cost is 0)
const costOrReqs = totals.cost > 0
? `${th.fg("warning", formatCost(totals.cost))} total`
: `${th.fg("text", String(totals.apiRequests))} requests`;
lines.push(row(fitColumns([
`${th.fg("warning", formatCost(totals.cost))} total`,
costOrReqs,
`${th.fg("text", formatTokenCount(totals.tokens.total))} tokens`,
`${th.fg("text", String(totals.toolCalls))} tools`,
`${th.fg("text", String(totals.units))} units`,

View file

@ -43,6 +43,7 @@ export interface UnitMetrics {
toolCalls: number;
assistantMessages: number;
userMessages: number;
apiRequests?: number; // total API requests made (useful for copilot users where cost is always 0)
// Budget fields (optional — absent in pre-M009 metrics data)
contextWindowTokens?: number;
truncationSections?: number;
@ -179,6 +180,7 @@ export function snapshotUnitMetrics(
toolCalls,
assistantMessages,
userMessages,
apiRequests: assistantMessages, // each assistant message = one API request
...(opts?.tier ? { tier: opts.tier } : {}),
...(opts?.modelDowngraded !== undefined ? { modelDowngraded: opts.modelDowngraded } : {}),
...(opts?.contextWindowTokens !== undefined ? { contextWindowTokens: opts.contextWindowTokens } : {}),
@ -247,6 +249,7 @@ export interface ProjectTotals {
toolCalls: number;
assistantMessages: number;
userMessages: number;
apiRequests: number;
totalTruncationSections: number;
continueHereFiredCount: number;
}
@ -330,6 +333,7 @@ export function getProjectTotals(units: UnitMetrics[]): ProjectTotals {
toolCalls: 0,
assistantMessages: 0,
userMessages: 0,
apiRequests: 0,
totalTruncationSections: 0,
continueHereFiredCount: 0,
};
@ -340,6 +344,7 @@ export function getProjectTotals(units: UnitMetrics[]): ProjectTotals {
totals.toolCalls += u.toolCalls;
totals.assistantMessages += u.assistantMessages;
totals.userMessages += u.userMessages;
totals.apiRequests += u.apiRequests ?? u.assistantMessages; // fallback for pre-existing data
totals.totalTruncationSections += u.truncationSections ?? 0;
if (u.continueHereFired) totals.continueHereFiredCount++;
}

View file

@ -68,6 +68,7 @@ function mockData(overrides: Partial<VisualizerData> = {}): VisualizerData {
userMessages: 12,
totalTruncationSections: 2,
continueHereFiredCount: 1,
apiRequests: 20,
},
byPhase: [
{ phase: "execution", units: 4, tokens: mockTokens(), cost: 2.50, duration: 3_600_000 },

View file

@ -387,6 +387,7 @@ console.log("\n=== renderMetricsView ===");
userMessages: 5,
totalTruncationSections: 0,
continueHereFiredCount: 0,
apiRequests: 5,
},
byPhase: [
{
@ -653,7 +654,7 @@ console.log("\n=== renderHealthView ===");
units: 10, tokens: { input: 5000, output: 2000, cacheRead: 1000, cacheWrite: 500, total: 8500 },
cost: 5.00, duration: 120000, toolCalls: 50,
assistantMessages: 30, userMessages: 15,
totalTruncationSections: 3, continueHereFiredCount: 1,
totalTruncationSections: 3, continueHereFiredCount: 1, apiRequests: 30,
},
health: {
budgetCeiling: 20.00,