Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion packages/core/src/telemetry/memory-monitor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,18 @@ import {
_resetGlobalMemoryMonitorForTests,
} from './memory-monitor.js';
import type { Config } from '../config/config.js';
import { recordMemoryUsage, isPerformanceMonitoringActive } from './metrics.js';
import {
recordMemoryUsage,
recordCpuUsage,
isPerformanceMonitoringActive,
} from './metrics.js';
import { HighWaterMarkTracker } from './high-water-mark-tracker.js';
import { RateLimiter } from './rate-limiter.js';

// Mock dependencies
vi.mock('./metrics.js', () => ({
recordMemoryUsage: vi.fn(),
recordCpuUsage: vi.fn(),
isPerformanceMonitoringActive: vi.fn(),
MemoryMetricType: {
HEAP_USED: 'heap_used',
Expand All @@ -50,6 +55,7 @@ vi.mock('node:process', () => ({
}));

const mockRecordMemoryUsage = vi.mocked(recordMemoryUsage);
const mockRecordCpuUsage = vi.mocked(recordCpuUsage);
const mockIsPerformanceMonitoringActive = vi.mocked(
isPerformanceMonitoringActive,
);
Expand Down Expand Up @@ -192,6 +198,13 @@ describe('MemoryMonitor', () => {
component: 'test_context',
},
);
expect(mockRecordCpuUsage).toHaveBeenCalledWith(
mockConfig,
expect.any(Number),
{
component: 'test_context',
},
);
});

it('should not record metrics when performance monitoring is inactive', () => {
Expand Down
9 changes: 9 additions & 0 deletions packages/core/src/telemetry/memory-monitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { isUserActive } from './activity-detector.js';
import { HighWaterMarkTracker } from './high-water-mark-tracker.js';
import {
recordMemoryUsage,
recordCpuUsage,
MemoryMetricType,
isPerformanceMonitoringActive,
} from './metrics.js';
Expand All @@ -37,6 +38,7 @@ export class MemoryMonitor {
private intervalId: NodeJS.Timeout | null = null;
private isRunning = false;
private lastSnapshot: MemorySnapshot | null = null;
private lastCpuUsage: NodeJS.CpuUsage | null = null;
private monitoringInterval: number = 10000;
private highWaterMarkTracker: HighWaterMarkTracker;
private rateLimiter: RateLimiter;
Expand Down Expand Up @@ -191,6 +193,13 @@ export class MemoryMonitor {
memory_type: MemoryMetricType.RSS,
component: context,
});

// Record delta CPU usage (in microseconds)
const cpuUsage = process.cpuUsage(this.lastCpuUsage ?? undefined);
this.lastCpuUsage = process.cpuUsage();
recordCpuUsage(config, cpuUsage.user + cpuUsage.system, {
component: context,
});
}

this.lastSnapshot = snapshot;
Expand Down
4 changes: 3 additions & 1 deletion packages/test-utils/src/perf-test-harness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,9 @@ export class PerfTestHarness {
throw new Error(`No active timer found for label "${label}"`);
}

const wallClockMs = performance.now() - timer.startTime;
// Round wall-clock time to nearest 0.1 ms
const wallClockMs =
Math.round((performance.now() - timer.startTime) * 10) / 10;
const cpuDelta = process.cpuUsage(timer.startCpuUsage);
this.activeTimers.delete(label);

Expand Down
34 changes: 28 additions & 6 deletions packages/test-utils/src/test-rig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,28 @@ export function checkModelOutputContent(
return isValid;
}

export interface MetricDataPoint {
attributes?: Record<string, unknown>;
value?: {
sum?: number;
min?: number;
max?: number;
count?: number;
};
startTime?: [number, number];
endTime?: string;
}

export interface TelemetryMetric {
descriptor: {
name: string;
type?: string;
description?: string;
unit?: string;
};
dataPoints: MetricDataPoint[];
}

export interface ParsedLog {
attributes?: {
'event.name'?: string;
Expand All @@ -213,11 +235,7 @@ export interface ParsedLog {
prompt_id?: string;
};
scopeMetrics?: {
metrics: {
descriptor: {
name: string;
};
}[];
metrics: TelemetryMetric[];
}[];
}

Expand Down Expand Up @@ -1297,6 +1315,10 @@ export class TestRig {
return logs;
}

readTelemetryLogs(): ParsedLog[] {
return this._readAndParseTelemetryLog();
}

private _readAndParseTelemetryLog(): ParsedLog[] {
// Telemetry is always written to the test directory
const logFilePath = join(this.homeDir!, 'telemetry.log');
Expand Down Expand Up @@ -1450,7 +1472,7 @@ export class TestRig {
);
}

readMetric(metricName: string): Record<string, unknown> | null {
readMetric(metricName: string): TelemetryMetric | null {
const logs = this._readAndParseTelemetryLog();
for (const logData of logs) {
if (logData.scopeMetrics) {
Expand Down
33 changes: 29 additions & 4 deletions perf-tests/baselines.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
{
"version": 1,
"updatedAt": "2026-04-09T02:30:22.000Z",
"updatedAt": "2026-04-14T14:04:02.662Z",
"scenarios": {
"cold-startup-time": {
"wallClockMs": 927.553249999999,
"wallClockMs": 927.6,
"cpuTotalUs": 1470,
"timestamp": "2026-04-08T22:27:54.871Z"
},
"idle-cpu-usage": {
"wallClockMs": 5000.460750000002,
"wallClockMs": 5000.5,
"cpuTotalUs": 12157,
"timestamp": "2026-04-08T22:28:19.098Z"
},
Expand All @@ -18,14 +18,39 @@
"timestamp": "2026-04-14T15:22:56.133Z"
},
"skill-loading-time": {
"wallClockMs": 930.0920409999962,
"wallClockMs": 930.1,
"cpuTotalUs": 1323,
"timestamp": "2026-04-08T22:28:23.290Z"
},
"high-volume-shell-output": {
"wallClockMs": 1119.9,
"cpuTotalUs": 2100,
"timestamp": "2026-04-09T02:30:22.000Z"
},
"long-conversation-resume": {
"wallClockMs": 4212.5,
"cpuTotalUs": 351393,
"timestamp": "2026-04-14T14:02:53.268Z"
},
"long-conversation-typing": {
"wallClockMs": 113.7,
"cpuTotalUs": 3304,
"timestamp": "2026-04-14T14:03:12.525Z"
},
"long-conversation-execution": {
"wallClockMs": 248.7,
"cpuTotalUs": 3825,
"timestamp": "2026-04-14T14:03:28.575Z"
},
"long-conversation-terminal-scrolling": {
"wallClockMs": 362.4,
"cpuTotalUs": 12755860,
"timestamp": "2026-04-14T14:03:45.687Z"
},
"long-conversation-alternate-scrolling": {
"wallClockMs": 362.4,
"cpuTotalUs": 12755860,
"timestamp": "2026-04-14T14:04:02.662Z"
}
}
}
Loading
Loading