fix: improve startup readiness performance

This commit is contained in:
Bohdan Triapitsyn
2026-06-05 15:09:24 +03:00
parent 0b881fec8e
commit e192359da9
32 changed files with 502 additions and 110 deletions
+2 -2
View File
@@ -620,10 +620,10 @@ async function performConfigRefresh(options: {
const sdkRefreshTasks: Promise<void>[] = [];
for (const directory of directoriesToRefresh) {
if (refreshProviders) {
sdkRefreshTasks.push(configStore.loadProviders({ directory }).then(() => undefined));
sdkRefreshTasks.push(configStore.loadProviders({ directory, source: 'agentsStore:refreshConfig' }).then(() => undefined));
}
if (refreshSdkAgents) {
sdkRefreshTasks.push(configStore.loadAgents({ directory }).then(() => undefined));
sdkRefreshTasks.push(configStore.loadAgents({ directory, source: 'agentsStore:refreshConfig' }).then(() => undefined));
}
}
+180 -30
View File
@@ -15,6 +15,7 @@ import { useDirectoryStore } from "@/stores/useDirectoryStore";
import { streamDebugEnabled } from "@/stores/utils/streamDebug";
import { parseModelIdentifier } from "@/lib/modelIdentifier";
import { runtimeFetch } from "@/lib/runtime-fetch";
import { markStartupTrace, measureStartupTrace } from "@/lib/startupTrace";
const MODELS_DEV_API_URL = "https://models.dev/api.json";
const MODELS_DEV_PROXY_URL = "/api/openchamber/models-metadata";
@@ -61,6 +62,18 @@ interface OpenChamberDefaults {
}
const fetchOpenChamberDefaults = async (): Promise<OpenChamberDefaults> => {
markStartupTrace('config.defaults:start');
const started = typeof performance !== 'undefined' ? performance.now() : Date.now();
const finish = (source: string, result: OpenChamberDefaults) => {
const ended = typeof performance !== 'undefined' ? performance.now() : Date.now();
markStartupTrace('config.defaults:end', {
source,
durationMs: Math.round(ended - started),
hasDefaultModel: Boolean(result.defaultModel),
hasDefaultAgent: Boolean(result.defaultAgent),
});
return result;
};
try {
// 1. Runtime settings API (VSCode)
const runtimeSettings = getRegisteredRuntimeAPIs()?.settings;
@@ -86,7 +99,7 @@ const fetchOpenChamberDefaults = async (): Promise<OpenChamberDefaults> => {
const sttSilenceThresholdDb = normalizeSttSilenceThresholdDb(data?.sttSilenceThresholdDb);
const sttSilenceHoldMs = normalizeSttSilenceHoldMs(data?.sttSilenceHoldMs);
return {
return finish('runtime-settings', {
defaultModel: defaultModel.length > 0 ? defaultModel : undefined,
defaultVariant: defaultVariant.length > 0 ? defaultVariant : undefined,
defaultAgent: defaultAgent.length > 0 ? defaultAgent : undefined,
@@ -101,7 +114,7 @@ const fetchOpenChamberDefaults = async (): Promise<OpenChamberDefaults> => {
sttLanguage,
sttSilenceThresholdDb,
sttSilenceHoldMs,
};
});
}
} catch {
// Fall through to fetch
@@ -114,7 +127,7 @@ const fetchOpenChamberDefaults = async (): Promise<OpenChamberDefaults> => {
headers: { Accept: 'application/json' },
});
if (!response.ok) {
return {};
return finish('settings-route-not-ok', {});
}
const data = await response.json();
const defaultModel = typeof data?.defaultModel === 'string' ? data.defaultModel.trim() : '';
@@ -134,7 +147,7 @@ const fetchOpenChamberDefaults = async (): Promise<OpenChamberDefaults> => {
const sttSilenceThresholdDb = normalizeSttSilenceThresholdDb(data?.sttSilenceThresholdDb);
const sttSilenceHoldMs = normalizeSttSilenceHoldMs(data?.sttSilenceHoldMs);
return {
return finish('settings-route', {
defaultModel: defaultModel.length > 0 ? defaultModel : undefined,
defaultVariant: defaultVariant.length > 0 ? defaultVariant : undefined,
defaultAgent: defaultAgent.length > 0 ? defaultAgent : undefined,
@@ -149,9 +162,10 @@ const fetchOpenChamberDefaults = async (): Promise<OpenChamberDefaults> => {
sttLanguage,
sttSilenceThresholdDb,
sttSilenceHoldMs,
};
} catch {
return {};
});
} catch (error) {
markStartupTrace('config.defaults:error', { error: error instanceof Error ? error.message : String(error) });
return finish('error', {});
}
};
@@ -458,9 +472,11 @@ const ensureModelsMetadataFetch = (
return;
}
modelsMetadataInFlight = fetchModelsDevMetadata()
markStartupTrace('modelsMetadata:queued');
modelsMetadataInFlight = measureStartupTrace('modelsMetadata', fetchModelsDevMetadata)
.then((metadata) => {
if (metadata.size > 0) {
markStartupTrace('modelsMetadata:set', { entries: metadata.size });
setModelsMetadata(metadata);
}
return metadata;
@@ -602,8 +618,8 @@ interface ConfigStore {
activateDirectory: (directory: string | null | undefined) => Promise<void>;
loadProviders: (options?: { directory?: string | null }) => Promise<void>;
loadAgents: (options?: { directory?: string | null }) => Promise<boolean>;
loadProviders: (options?: { directory?: string | null; source?: string }) => Promise<void>;
loadAgents: (options?: { directory?: string | null; source?: string }) => Promise<boolean>;
invalidateModelMetadataCache: () => void;
setProvider: (providerId: string) => void;
setModel: (modelId: string) => void;
@@ -906,10 +922,14 @@ export const useConfigStore = create<ConfigStore>()(
})(),
activateDirectory: async (directory) => {
const directoryKey = toDirectoryKey(directory);
let snapshotHadProviders = false;
let snapshotHadAgents = false;
set((state) => {
const snapshot = state.directoryScoped[directoryKey];
if (snapshot) {
snapshotHadProviders = snapshot.providers.length > 0;
snapshotHadAgents = snapshot.agents.length > 0;
return {
activeDirectoryKey: directoryKey,
providers: snapshot.providers,
@@ -941,18 +961,36 @@ export const useConfigStore = create<ConfigStore>()(
return;
}
await get().loadProviders({ directory: fromDirectoryKey(directoryKey) });
await get().loadAgents({ directory: fromDirectoryKey(directoryKey) });
if (snapshotHadProviders) {
markStartupTrace('activateDirectory:skipProviders', { directoryKey });
} else {
await get().loadProviders({ directory: fromDirectoryKey(directoryKey), source: 'activateDirectory' });
}
if (snapshotHadAgents) {
markStartupTrace('activateDirectory:skipAgents', { directoryKey });
} else {
await get().loadAgents({ directory: fromDirectoryKey(directoryKey), source: 'activateDirectory' });
}
},
loadProviders: async (options) => {
const directoryKey = toDirectoryKey(options?.directory ?? fromDirectoryKey(get().activeDirectoryKey));
const requestedDirectory = options?.directory ?? fromDirectoryKey(get().activeDirectoryKey);
const effectiveDirectory = requestedDirectory ?? opencodeClient.getDirectory() ?? null;
const directoryKey = toDirectoryKey(requestedDirectory);
const source = options?.source ?? 'unknown';
markStartupTrace('loadProviders:called', { directoryKey, source, requestedDirectory, effectiveDirectory });
// Dedup: if a load is already in-flight for this directory, reuse it
const existing = _inFlightProviders.get(directoryKey);
if (existing) return existing;
if (existing) {
markStartupTrace('loadProviders:deduped', { directoryKey, source, requestedDirectory, effectiveDirectory });
return existing;
}
const promise = (async () => {
const loaderStarted = typeof performance !== 'undefined' ? performance.now() : Date.now();
markStartupTrace('loadProviders:start', { directoryKey, source, requestedDirectory, effectiveDirectory });
const existingSnapshot = get().directoryScoped[directoryKey];
const previousProviders = existingSnapshot?.providers ?? (get().activeDirectoryKey === directoryKey ? get().providers : []);
const previousDefaults = existingSnapshot?.defaultProviders ?? (get().activeDirectoryKey === directoryKey ? get().defaultProviders : {});
@@ -964,9 +1002,13 @@ export const useConfigStore = create<ConfigStore>()(
() => get().modelsMetadata,
(metadata) => set({ modelsMetadata: metadata }),
);
const apiResult = await opencodeClient.withDirectory(
fromDirectoryKey(directoryKey),
() => opencodeClient.getProviders()
const apiResult = await measureStartupTrace(
'loadProviders:api',
() => opencodeClient.withDirectory(
fromDirectoryKey(directoryKey),
() => opencodeClient.getProviders()
),
{ directoryKey, source, requestedDirectory, effectiveDirectory, attempt: attempt + 1 },
);
const providers = Array.isArray(apiResult?.providers) ? apiResult.providers : [];
const defaults = apiResult?.default || {};
@@ -1036,15 +1078,40 @@ export const useConfigStore = create<ConfigStore>()(
return nextState;
});
const loaderEnded = typeof performance !== 'undefined' ? performance.now() : Date.now();
markStartupTrace('loadProviders:end', {
directoryKey,
source,
requestedDirectory,
effectiveDirectory,
durationMs: Math.round(loaderEnded - loaderStarted),
providers: processedProviders.length,
models: processedProviders.reduce((count, provider) => count + provider.models.length, 0),
});
return;
} catch (error) {
lastError = error;
markStartupTrace('loadProviders:attemptError', {
directoryKey,
source,
requestedDirectory,
effectiveDirectory,
attempt: attempt + 1,
error: error instanceof Error ? error.message : String(error),
});
const waitMs = 200 * (attempt + 1);
await new Promise((resolve) => setTimeout(resolve, waitMs));
}
}
console.error("Failed to load providers:", lastError);
markStartupTrace('loadProviders:error', {
directoryKey,
source,
requestedDirectory,
effectiveDirectory,
error: lastError instanceof Error ? lastError.message : String(lastError),
});
set((state) => {
const baseSnapshot: DirectoryScopedConfig = state.directoryScoped[directoryKey] ?? {
@@ -1314,13 +1381,22 @@ export const useConfigStore = create<ConfigStore>()(
},
loadAgents: async (options) => {
const directoryKey = toDirectoryKey(options?.directory ?? fromDirectoryKey(get().activeDirectoryKey));
const requestedDirectory = options?.directory ?? fromDirectoryKey(get().activeDirectoryKey);
const effectiveDirectory = requestedDirectory ?? opencodeClient.getDirectory() ?? null;
const directoryKey = toDirectoryKey(requestedDirectory);
const source = options?.source ?? 'unknown';
markStartupTrace('loadAgents:called', { directoryKey, source, requestedDirectory, effectiveDirectory });
// Dedup: if a load is already in-flight for this directory, reuse it
const existing = _inFlightAgents.get(directoryKey);
if (existing) return existing;
if (existing) {
markStartupTrace('loadAgents:deduped', { directoryKey, source, requestedDirectory, effectiveDirectory });
return existing;
}
const promise = (async (): Promise<boolean> => {
const loaderStarted = typeof performance !== 'undefined' ? performance.now() : Date.now();
markStartupTrace('loadAgents:start', { directoryKey, source, requestedDirectory, effectiveDirectory });
const existingSnapshot = get().directoryScoped[directoryKey];
const previousAgents = existingSnapshot?.agents ?? (get().activeDirectoryKey === directoryKey ? get().agents : []);
let lastError: unknown = null;
@@ -1329,12 +1405,22 @@ export const useConfigStore = create<ConfigStore>()(
try {
// Fetch agents and OpenChamber settings in parallel
const [agents, openChamberDefaults] = await Promise.all([
opencodeClient.withDirectory(fromDirectoryKey(directoryKey), () => opencodeClient.listAgents()),
measureStartupTrace(
'loadAgents:api',
() => opencodeClient.withDirectory(fromDirectoryKey(directoryKey), () => opencodeClient.listAgents()),
{ directoryKey, source, requestedDirectory, effectiveDirectory, attempt: attempt + 1 },
),
fetchOpenChamberDefaults(),
]);
const safeAgents = Array.isArray(agents) ? agents : [];
const providerLoad = _inFlightProviders.get(directoryKey);
if (providerLoad) {
markStartupTrace('loadAgents:awaitProviders', { directoryKey, source });
await providerLoad;
}
const providers = get().activeDirectoryKey === directoryKey
? get().providers
: (get().directoryScoped[directoryKey]?.providers ?? []);
@@ -1452,6 +1538,15 @@ export const useConfigStore = create<ConfigStore>()(
return nextState;
});
const loaderEnded = typeof performance !== 'undefined' ? performance.now() : Date.now();
markStartupTrace('loadAgents:end', {
directoryKey,
source,
requestedDirectory,
effectiveDirectory,
durationMs: Math.round(loaderEnded - loaderStarted),
agents: safeAgents.length,
});
return true;
}
@@ -1592,15 +1687,39 @@ export const useConfigStore = create<ConfigStore>()(
});
}
const loaderEnded = typeof performance !== 'undefined' ? performance.now() : Date.now();
markStartupTrace('loadAgents:end', {
directoryKey,
source,
requestedDirectory,
effectiveDirectory,
durationMs: Math.round(loaderEnded - loaderStarted),
agents: safeAgents.length,
});
return true;
} catch (error) {
lastError = error;
markStartupTrace('loadAgents:attemptError', {
directoryKey,
source,
requestedDirectory,
effectiveDirectory,
attempt: attempt + 1,
error: error instanceof Error ? error.message : String(error),
});
const waitMs = 200 * (attempt + 1);
await new Promise((resolve) => setTimeout(resolve, waitMs));
}
}
console.error("Failed to load agents:", lastError);
markStartupTrace('loadAgents:error', {
directoryKey,
source,
requestedDirectory,
effectiveDirectory,
error: lastError instanceof Error ? lastError.message : String(lastError),
});
set((state) => {
const providers = state.activeDirectoryKey === directoryKey
@@ -2064,13 +2183,31 @@ export const useConfigStore = create<ConfigStore>()(
},
checkConnection: async () => {
markStartupTrace('checkConnection:start');
const maxAttempts = 5;
let attempt = 0;
let lastError: unknown = null;
while (attempt < maxAttempts) {
try {
const isHealthy = await opencodeClient.checkHealth();
markStartupTrace('checkConnection:attempt', { attempt: attempt + 1 });
const isHealthy = await measureStartupTrace(
'checkConnection:health',
() => opencodeClient.checkHealth(),
{ attempt: attempt + 1 },
);
if (!isHealthy && attempt < maxAttempts - 1) {
const hasEverConnected = get().hasEverConnected;
set({
isConnected: false,
connectionPhase: hasEverConnected ? "reconnecting" : "connecting",
lastDisconnectReason: 'health_check_unhealthy',
});
attempt += 1;
await sleep(400 * attempt);
continue;
}
const hasEverConnected = get().hasEverConnected;
set(isHealthy
? { isConnected: true, hasEverConnected: true, connectionPhase: "connected" }
@@ -2079,6 +2216,7 @@ export const useConfigStore = create<ConfigStore>()(
connectionPhase: hasEverConnected ? "reconnecting" : "connecting",
lastDisconnectReason: 'health_check_unhealthy',
});
markStartupTrace('checkConnection:end', { healthy: isHealthy, attempts: attempt + 1 });
return isHealthy;
} catch (error) {
lastError = error;
@@ -2096,15 +2234,19 @@ export const useConfigStore = create<ConfigStore>()(
connectionPhase: get().hasEverConnected ? "reconnecting" : "connecting",
lastDisconnectReason: 'health_check_failed',
});
markStartupTrace('checkConnection:end', { healthy: false, attempts: maxAttempts });
return false;
},
initializeApp: async () => {
if (_initializeAppInFlight) {
markStartupTrace('initializeApp:deduped');
return _initializeAppInFlight;
}
const run = (async () => {
const initStarted = typeof performance !== 'undefined' ? performance.now() : Date.now();
markStartupTrace('initializeApp:start');
try {
const debug = streamDebugEnabled();
if (debug) console.log("Starting app initialization...");
@@ -2123,15 +2265,21 @@ export const useConfigStore = create<ConfigStore>()(
}
if (debug) console.log("Initializing app...");
await opencodeClient.initApp();
markStartupTrace('initApp:skipped', { reason: 'checkConnection already verified health' });
if (debug) console.log("Loading providers...");
await get().loadProviders();
if (debug) console.log("Loading agents...");
await get().loadAgents();
if (debug) console.log("Loading providers and agents...");
await Promise.all([
get().loadProviders({ source: 'initializeApp' }),
get().loadAgents({ source: 'initializeApp' }),
]);
set({ isInitialized: true, isConnected: true, hasEverConnected: true, connectionPhase: "connected" });
const initEnded = typeof performance !== 'undefined' ? performance.now() : Date.now();
markStartupTrace('initializeApp:end', {
durationMs: Math.round(initEnded - initStarted),
providers: get().providers.length,
agents: get().agents.length,
});
if (debug) console.log("App initialized successfully");
} catch (error) {
console.error("Failed to initialize app:", error);
@@ -2141,6 +2289,7 @@ export const useConfigStore = create<ConfigStore>()(
connectionPhase: get().hasEverConnected ? "reconnecting" : "connecting",
lastDisconnectReason: 'init_error',
});
markStartupTrace('initializeApp:error', { error: error instanceof Error ? error.message : String(error) });
}
})().finally(() => {
_initializeAppInFlight = null;
@@ -2235,16 +2384,16 @@ let unsubscribeConfigStoreChanges: (() => void) | null = null;
if (!unsubscribeConfigStoreChanges) {
unsubscribeConfigStoreChanges = subscribeToConfigChanges(async (event) => {
const tasks: Promise<void>[] = [];
const tasks: Promise<void>[] = [];
if (scopeMatches(event, "agents")) {
const { loadAgents } = useConfigStore.getState();
tasks.push(loadAgents().then(() => {}));
tasks.push(loadAgents({ source: 'configChange:agents' }).then(() => {}));
}
if (scopeMatches(event, "providers")) {
const { loadProviders } = useConfigStore.getState();
tasks.push(loadProviders());
tasks.push(loadProviders({ source: 'configChange:providers' }));
}
if (tasks.length > 0) {
@@ -2263,6 +2412,7 @@ if (typeof window !== "undefined" && !unsubscribeConfigStoreDirectoryChanges) {
return;
}
markStartupTrace('directoryStore:changed', { previous: prevKey, next: nextKey });
void useConfigStore.getState().activateDirectory(state.currentDirectory);
});
}