perf: gate perf logs and dedup build_config_update on first message (#8627)

Signed-off-by: Bradley Axen <baxen@squareup.com>
This commit is contained in:
Bradley Axen
2026-04-17 12:43:36 -07:00
committed by GitHub
parent 7fa662bcac
commit 8a51e34891
22 changed files with 544 additions and 285 deletions
+25
View File
@@ -6,6 +6,7 @@ import {
clearActiveMessageId,
} from "./acpNotificationHandler";
import { searchSessionsViaExports } from "./sessionSearch";
import { perfLog } from "@/shared/lib/perfLog";
export interface AcpProvider {
id: string;
@@ -36,6 +37,8 @@ export async function acpSendMessage(
options: AcpSendMessageOptions = {},
): Promise<void> {
const { systemPrompt, personaId, images } = options;
const sid = sessionId.slice(0, 8);
const tStart = performance.now();
const gooseSessionId = sessionTracker.getGooseSessionId(sessionId, personaId);
if (!gooseSessionId) {
@@ -57,7 +60,15 @@ export async function acpSendMessage(
const messageId = crypto.randomUUID();
setActiveMessageId(gooseSessionId, messageId);
perfLog(
`[perf:send] ${sid} acpSendMessage → prompt(len=${prompt.length}, imgs=${images?.length ?? 0})`,
);
const tPrompt = performance.now();
await directAcp.prompt(gooseSessionId, content);
const tDone = performance.now();
perfLog(
`[perf:send] ${sid} prompt() resolved in ${(tDone - tPrompt).toFixed(1)}ms (total acpSendMessage ${(tDone - tStart).toFixed(1)}ms)`,
);
clearActiveMessageId(gooseSessionId);
}
@@ -69,12 +80,20 @@ export async function acpPrepareSession(
workingDir: string,
options: AcpPrepareSessionOptions = {},
): Promise<void> {
const sid = sessionId.slice(0, 8);
const t0 = performance.now();
perfLog(
`[perf:prepare] ${sid} acpPrepareSession start (provider=${providerId})`,
);
await sessionTracker.prepareSession(
sessionId,
providerId,
workingDir,
options.personaId,
);
perfLog(
`[perf:prepare] ${sid} acpPrepareSession done in ${(performance.now() - t0).toFixed(1)}ms`,
);
}
export async function acpSetModel(
@@ -125,7 +144,13 @@ export async function acpLoadSession(
workingDir?: string,
): Promise<void> {
const effectiveWorkingDir = workingDir ?? "~/.goose/artifacts";
const sid = sessionId.slice(0, 8);
const t0 = performance.now();
perfLog(`[perf:load] ${sid} acpLoadSession → client.loadSession`);
await directAcp.loadSession(gooseSessionId, effectiveWorkingDir);
perfLog(
`[perf:load] ${sid} client.loadSession resolved in ${(performance.now() - t0).toFixed(1)}ms`,
);
sessionTracker.registerSession(
sessionId,
gooseSessionId,
+33 -2
View File
@@ -5,6 +5,7 @@ import type {
PromptResponse,
} from "@agentclientprotocol/sdk";
import { getClient } from "./acpConnection";
import { perfLog } from "@/shared/lib/perfLog";
export interface AcpProvider {
id: string;
@@ -84,24 +85,36 @@ export async function setModel(
sessionId: string,
modelId: string,
): Promise<void> {
const sid = sessionId.slice(0, 8);
const tClient = performance.now();
const client = await getClient();
const tCall = performance.now();
await client.setSessionConfigOption({
sessionId,
configId: "model",
value: modelId,
});
perfLog(
`[perf:api] ${sid} setModel(${modelId}) getClient=${(tCall - tClient).toFixed(1)}ms wire=${(performance.now() - tCall).toFixed(1)}ms`,
);
}
export async function setProvider(
sessionId: string,
providerId: string,
): Promise<void> {
const sid = sessionId.slice(0, 8);
const tClient = performance.now();
const client = await getClient();
const tCall = performance.now();
await client.setSessionConfigOption({
sessionId,
configId: "provider",
value: providerId,
});
perfLog(
`[perf:api] ${sid} setProvider(${providerId}) getClient=${(tCall - tClient).toFixed(1)}ms wire=${(performance.now() - tCall).toFixed(1)}ms`,
);
}
export async function updateWorkingDir(
@@ -120,16 +133,34 @@ export async function cancelSession(sessionId: string): Promise<void> {
export async function newSession(
workingDir: string,
): Promise<NewSessionResponse> {
const tClient = performance.now();
const client = await getClient();
return client.newSession({ cwd: workingDir, mcpServers: [] });
const tCall = performance.now();
const response = await client.newSession({ cwd: workingDir, mcpServers: [] });
const sid = response.sessionId.slice(0, 8);
perfLog(
`[perf:api] ${sid} newSession getClient=${(tCall - tClient).toFixed(1)}ms wire=${(performance.now() - tCall).toFixed(1)}ms`,
);
return response;
}
export async function loadSession(
sessionId: string,
workingDir: string,
): Promise<LoadSessionResponse> {
const sid = sessionId.slice(0, 8);
const tClient = performance.now();
const client = await getClient();
return client.loadSession({ sessionId, cwd: workingDir, mcpServers: [] });
const tCall = performance.now();
const response = await client.loadSession({
sessionId,
cwd: workingDir,
mcpServers: [],
});
perfLog(
`[perf:api] ${sid} loadSession getClient=${(tCall - tClient).toFixed(1)}ms wire=${(performance.now() - tCall).toFixed(1)}ms`,
);
return response;
}
export async function prompt(
+16
View File
@@ -8,6 +8,7 @@ import {
type RequestPermissionResponse,
} from "@agentclientprotocol/sdk";
import { createWebSocketStream } from "./createWebSocketStream";
import { perfLog } from "@/shared/lib/perfLog";
let notificationHandler: AcpNotificationHandler | null = null;
@@ -63,12 +64,21 @@ function monitorConnection(client: GooseClient): void {
}
async function initializeConnection(): Promise<GooseClient> {
const tStart = performance.now();
const wsUrl: string = await invoke("get_goose_serve_url");
perfLog(
`[perf:conn] get_goose_serve_url in ${(performance.now() - tStart).toFixed(1)}ms`,
);
const tStream = performance.now();
const stream = createWebSocketStream(wsUrl);
const client = new GooseClient(createClientCallbacks(), stream);
perfLog(
`[perf:conn] ws stream + client created in ${(performance.now() - tStream).toFixed(1)}ms`,
);
const tInit = performance.now();
await client.initialize({
protocolVersion: PROTOCOL_VERSION,
clientCapabilities: {},
@@ -77,6 +87,9 @@ async function initializeConnection(): Promise<GooseClient> {
version: "0.1.0",
},
});
perfLog(
`[perf:conn] client.initialize in ${(performance.now() - tInit).toFixed(1)}ms (total ${(performance.now() - tStart).toFixed(1)}ms)`,
);
monitorConnection(client);
@@ -89,6 +102,7 @@ export async function getClient(): Promise<GooseClient> {
}
if (!clientPromise) {
perfLog("[perf:conn] getClient() → initializing new ACP connection");
clientPromise = initializeConnection()
.then((client) => {
resolvedClient = client;
@@ -98,6 +112,8 @@ export async function getClient(): Promise<GooseClient> {
clientPromise = null;
throw error;
});
} else {
perfLog("[perf:conn] getClient() awaiting in-flight initializeConnection");
}
return clientPromise;
@@ -15,19 +15,52 @@ import type {
} from "@/shared/types/messages";
import type { AcpNotificationHandler } from "./acpConnection";
import { getLocalSessionId } from "./acpSessionTracker";
import { perfLog } from "@/shared/lib/perfLog";
// Pre-set message ID for the next live stream per goose session
const presetMessageIds = new Map<string, string>();
// Per-session perf counters for replay/live streaming.
interface ReplayPerf {
firstAt: number;
lastAt: number;
count: number;
}
const replayPerf = new Map<string, ReplayPerf>();
interface LivePerf {
sendStartedAt: number;
firstChunkAt: number | null;
chunkCount: number;
}
const livePerf = new Map<string, LivePerf>();
export function setActiveMessageId(
gooseSessionId: string,
messageId: string,
): void {
presetMessageIds.set(gooseSessionId, messageId);
livePerf.set(gooseSessionId, {
sendStartedAt: performance.now(),
firstChunkAt: null,
chunkCount: 0,
});
}
export function clearActiveMessageId(gooseSessionId: string): void {
presetMessageIds.delete(gooseSessionId);
const perf = livePerf.get(gooseSessionId);
if (perf) {
const sid = gooseSessionId.slice(0, 8);
const total = performance.now() - perf.sendStartedAt;
const ttft =
perf.firstChunkAt !== null
? (perf.firstChunkAt - perf.sendStartedAt).toFixed(1)
: "n/a";
perfLog(
`[perf:stream] ${sid} stream ended — ttft=${ttft}ms total=${total.toFixed(1)}ms chunks=${perf.chunkCount}`,
);
livePerf.delete(gooseSessionId);
}
}
export async function handleSessionNotification(
@@ -39,12 +72,45 @@ export async function handleSessionNotification(
const isReplay = useChatStore.getState().loadingSessionIds.has(sessionId);
if (isReplay) {
const sid = sessionId.slice(0, 8);
let perf = replayPerf.get(sessionId);
const now = performance.now();
if (!perf) {
perf = { firstAt: now, lastAt: now, count: 0 };
replayPerf.set(sessionId, perf);
perfLog(`[perf:replay] ${sid} first notification received`);
}
perf.lastAt = now;
perf.count += 1;
handleReplay(sessionId, update);
} else {
const perf = livePerf.get(gooseSessionId);
if (perf && update.sessionUpdate === "agent_message_chunk") {
perf.chunkCount += 1;
if (perf.firstChunkAt === null) {
perf.firstChunkAt = performance.now();
const sid = gooseSessionId.slice(0, 8);
perfLog(
`[perf:stream] ${sid} first agent_message_chunk at ttft=${(perf.firstChunkAt - perf.sendStartedAt).toFixed(1)}ms`,
);
}
}
handleLive(sessionId, gooseSessionId, update);
}
}
export function getReplayPerf(
sessionId: string,
): { count: number; spanMs: number } | null {
const perf = replayPerf.get(sessionId);
if (!perf) return null;
return { count: perf.count, spanMs: perf.lastAt - perf.firstAt };
}
export function clearReplayPerf(sessionId: string): void {
replayPerf.delete(sessionId);
}
function handleReplay(sessionId: string, update: SessionUpdate): void {
switch (update.sessionUpdate) {
case "agent_message_chunk": {
+35 -5
View File
@@ -1,4 +1,5 @@
import * as acpApi from "./acpApi";
import { perfLog } from "@/shared/lib/perfLog";
interface PreparedSession {
gooseSessionId: string;
@@ -22,34 +23,63 @@ export async function prepareSession(
workingDir: string,
personaId?: string,
): Promise<string> {
const sid = sessionId.slice(0, 8);
const key = makeKey(sessionId, personaId);
const existing = prepared.get(key) ?? prepared.get(sessionId);
if (existing) {
const tReuse = performance.now();
let changed = false;
if (existing.workingDir !== workingDir) {
await acpApi.updateWorkingDir(existing.gooseSessionId, workingDir);
existing.workingDir = workingDir;
changed = true;
}
if (existing.providerId !== providerId) {
const tProv = performance.now();
await acpApi.setProvider(existing.gooseSessionId, providerId);
perfLog(
`[perf:prepare] ${sid} reuse setProvider(${providerId}) in ${(performance.now() - tProv).toFixed(1)}ms (goose_sid=${existing.gooseSessionId.slice(0, 8)})`,
);
existing.providerId = providerId;
changed = true;
}
perfLog(
`[perf:prepare] ${sid} reuse existing session (updates=${changed}) in ${(performance.now() - tReuse).toFixed(1)}ms`,
);
return existing.gooseSessionId;
}
let gooseSessionId: string | null = null;
const tLoad = performance.now();
try {
await acpApi.loadSession(sessionId, workingDir);
gooseSessionId = sessionId;
} catch {}
if (!gooseSessionId) {
const response = await acpApi.newSession(workingDir);
gooseSessionId = response.sessionId;
perfLog(
`[perf:prepare] ${sid} tracker loadSession ok in ${(performance.now() - tLoad).toFixed(1)}ms`,
);
} catch {
perfLog(
`[perf:prepare] ${sid} tracker loadSession failed in ${(performance.now() - tLoad).toFixed(1)}ms → newSession`,
);
}
if (!gooseSessionId) {
const tNew = performance.now();
const response = await acpApi.newSession(workingDir);
gooseSessionId = response.sessionId;
perfLog(
`[perf:prepare] ${sid} tracker newSession done in ${(performance.now() - tNew).toFixed(1)}ms (goose_sid=${gooseSessionId.slice(0, 8)})`,
);
}
const gooseSid = gooseSessionId.slice(0, 8);
const tProv = performance.now();
await acpApi.setProvider(gooseSessionId, providerId);
perfLog(
`[perf:prepare] ${sid} tracker setProvider(${providerId}) in ${(performance.now() - tProv).toFixed(1)}ms (goose_sid=${gooseSid})`,
);
prepared.set(key, { gooseSessionId, providerId, workingDir });
prepared.set(sessionId, { gooseSessionId, providerId, workingDir });
+39
View File
@@ -0,0 +1,39 @@
/**
* Gated performance logger for frontend timing instrumentation.
*
* Enabled when any of the following is true:
* - Running under Vite dev (`import.meta.env.DEV`)
* - `localStorage.getItem("goose.perf") === "1"`
*
* Otherwise a no-op, so perf call sites add zero runtime cost in release
* builds for users who have not opted in.
*
* Messages are prefixed with `[perf:<channel>]` by callers; this helper
* is intentionally dumb and forwards the already-formatted string.
*/
function isEnabled(): boolean {
try {
if (import.meta.env?.DEV) return true;
} catch {
// import.meta may be unavailable in some test contexts
}
try {
if (
typeof localStorage !== "undefined" &&
localStorage.getItem("goose.perf") === "1"
) {
return true;
}
} catch {
// localStorage can throw in restricted contexts
}
return false;
}
const enabled = isEnabled();
export function perfLog(message: string): void {
if (!enabled) return;
// eslint-disable-next-line no-console
console.log(message);
}