fix(pi): strengthen final release proof

This commit is contained in:
2026-08-24 15:47:57 +08:00
parent c7e777246e
commit df1151b5eb
19 changed files with 1120 additions and 149 deletions

View File

@@ -82,6 +82,7 @@ export function parseProbeArgs(argv) {
return value;
};
if (argument === '--') continue;
if (argument === '--samples') options.samples = parsePositiveInteger(next(), argument);
else if (argument === '--timeout-ms') options.timeoutMs = parsePositiveInteger(next(), argument);
else if (argument === '--stage') options.stage = true;
@@ -422,7 +423,10 @@ export class PiRpcWorker {
});
await new Promise((resolveSpawn, rejectSpawn) => {
this.child.once('spawn', resolveSpawn);
this.child.once('spawn', () => {
this.spawnedAt = performance.now();
resolveSpawn();
});
this.child.once('error', rejectSpawn);
});
return this;
@@ -583,9 +587,11 @@ async function runReadySample(runtime, paths, timeoutMs) {
await worker.start();
const response = await worker.request({ type: 'get_state' });
const readyMs = response.receivedAt - worker.startedAt;
const workerSpawnMs = worker.spawnedAt - worker.startedAt;
const rpcReadyMs = response.receivedAt - worker.spawnedAt;
const rssKb = await getProcessRssKb(worker.child.pid);
const stop = await worker.stop();
return { readyMs, rssKb, stop, sessionId: response.data.sessionId };
return { readyMs, workerSpawnMs, rpcReadyMs, rssKb, stop, sessionId: response.data.sessionId };
} finally {
await worker.stop().catch(() => undefined);
}
@@ -611,9 +617,15 @@ async function runPerformanceSamples(runtime, scratchRoot, sampleCount, timeoutM
definition: {
cold: 'fresh Pi config, session, and project directories per process',
warm: 'shared primed Pi config directory with fresh session and project directories per process',
workerSpawn: 'final product executable spawn event minus spawn request',
rpcReady: 'first successful get_state response minus child spawn event',
},
coldReadyMs: summarizeMeasurements(cold.map((sample) => sample.readyMs)),
warmReadyMs: summarizeMeasurements(warm.map((sample) => sample.readyMs)),
coldWorkerSpawnMs: summarizeMeasurements(cold.map((sample) => sample.workerSpawnMs)),
warmWorkerSpawnMs: summarizeMeasurements(warm.map((sample) => sample.workerSpawnMs)),
coldRpcReadyMs: summarizeMeasurements(cold.map((sample) => sample.rpcReadyMs)),
warmRpcReadyMs: summarizeMeasurements(warm.map((sample) => sample.rpcReadyMs)),
rssKb: summarizeMeasurements([...cold, ...warm].flatMap((sample) => sample.rssKb == null ? [] : [sample.rssKb])),
exitMs: summarizeMeasurements([...cold, ...warm].map((sample) => sample.stop.exitMs)),
exitModes: [...new Set([...cold, ...warm].map((sample) => sample.stop.mode))],
@@ -867,6 +879,7 @@ async function promptAndSettle(worker, message, images, timeoutMs) {
providerFirstEventMs: firstProviderEvent
? firstProviderEvent.receivedAt - agentStart.receivedAt
: settled.receivedAt - agentStart.receivedAt,
agentSettledMs: settled.receivedAt - accepted.receivedAt,
startedAt: agentStart.receivedAt,
settledAt: settled.receivedAt,
stopReason: lastAssistantStopReason(messagesResponse),
@@ -908,6 +921,20 @@ export async function runProviderQualification(runtime, scratchRoot, fixturePath
throw new Error('Provider workers shared a session id');
}
const [leftWarmTurn, rightWarmTurn] = await Promise.all([
promptAndSettle(left, 'Reply with exactly PI_PROVIDER_LEFT_WARM_OK. Do not use tools.', undefined, timeoutMs),
promptAndSettle(right, 'Reply with exactly PI_PROVIDER_RIGHT_WARM_OK. Do not use tools.', undefined, timeoutMs),
]);
if (!successfulReasons.has(leftWarmTurn.stopReason)
|| !successfulReasons.has(rightWarmTurn.stopReason)) {
throw new Error(
`Warm provider turns did not succeed: left=${leftWarmTurn.stopReason}, right=${rightWarmTurn.stopReason}`,
);
}
const warmOverlapMs = Math.min(leftWarmTurn.settledAt, rightWarmTurn.settledAt)
- Math.max(leftWarmTurn.startedAt, rightWarmTurn.startedAt);
if (warmOverlapMs <= 0) throw new Error('Warm provider turns did not overlap');
const abortStartIndex = left.events.length;
const abortedPrompt = left.request({
type: 'prompt',
@@ -950,20 +977,49 @@ export async function runProviderQualification(runtime, scratchRoot, fixturePath
apiKeyEnv: fixture.apiKeyEnv,
imageInput: Boolean(imagePath),
distinctSessionIds: true,
cold: {
promptAcceptedSamplesMs: [leftTurn.acceptedMs, rightTurn.acceptedMs].map(Math.round),
promptAcceptedMs: summarizeMeasurements([leftTurn.acceptedMs, rightTurn.acceptedMs]),
agentStartSamplesMs: [leftTurn.agentStartMs, rightTurn.agentStartMs].map(Math.round),
agentStartMs: summarizeMeasurements([leftTurn.agentStartMs, rightTurn.agentStartMs]),
providerFirstEventSamplesMs: [
leftTurn.providerFirstEventMs,
rightTurn.providerFirstEventMs,
].map(Math.round),
providerFirstEventMs: summarizeMeasurements([
leftTurn.providerFirstEventMs,
rightTurn.providerFirstEventMs,
]),
agentSettledSamplesMs: [leftTurn.agentSettledMs, rightTurn.agentSettledMs].map(Math.round),
agentSettledMs: summarizeMeasurements([leftTurn.agentSettledMs, rightTurn.agentSettledMs]),
},
warm: {
promptAcceptedSamplesMs: [leftWarmTurn.acceptedMs, rightWarmTurn.acceptedMs].map(Math.round),
promptAcceptedMs: summarizeMeasurements([leftWarmTurn.acceptedMs, rightWarmTurn.acceptedMs]),
agentStartSamplesMs: [leftWarmTurn.agentStartMs, rightWarmTurn.agentStartMs].map(Math.round),
agentStartMs: summarizeMeasurements([leftWarmTurn.agentStartMs, rightWarmTurn.agentStartMs]),
providerFirstEventSamplesMs: [
leftWarmTurn.providerFirstEventMs,
rightWarmTurn.providerFirstEventMs,
].map(Math.round),
providerFirstEventMs: summarizeMeasurements([
leftWarmTurn.providerFirstEventMs,
rightWarmTurn.providerFirstEventMs,
]),
agentSettledSamplesMs: [leftWarmTurn.agentSettledMs, rightWarmTurn.agentSettledMs].map(Math.round),
agentSettledMs: summarizeMeasurements([
leftWarmTurn.agentSettledMs,
rightWarmTurn.agentSettledMs,
]),
},
overlapMs: Math.round(overlapMs),
promptAcceptedSamplesMs: [leftTurn.acceptedMs, rightTurn.acceptedMs].map(Math.round),
promptAcceptedMs: summarizeMeasurements([leftTurn.acceptedMs, rightTurn.acceptedMs]),
agentStartSamplesMs: [leftTurn.agentStartMs, rightTurn.agentStartMs].map(Math.round),
agentStartMs: summarizeMeasurements([leftTurn.agentStartMs, rightTurn.agentStartMs]),
providerFirstEventSamplesMs: [
leftTurn.providerFirstEventMs,
rightTurn.providerFirstEventMs,
].map(Math.round),
providerFirstEventMs: summarizeMeasurements([
leftTurn.providerFirstEventMs,
rightTurn.providerFirstEventMs,
]),
stopReasons: [leftTurn.stopReason, rightTurn.stopReason],
warmOverlapMs: Math.round(warmOverlapMs),
stopReasons: [
leftTurn.stopReason,
rightTurn.stopReason,
leftWarmTurn.stopReason,
rightWarmTurn.stopReason,
],
abortIsolation: {
abortedStopReason,
unaffectedStopReason: unaffected.stopReason,