perf: deduplicate _goose/providers/list RPC call at startup (#8873)

Signed-off-by: Matt Toohey <contact@matttoohey.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Matt Toohey
2026-04-29 15:49:50 +10:00
committed by GitHub
parent 56b845904f
commit 0bb87daefd
3 changed files with 60 additions and 35 deletions
+19 -24
View File
@@ -2,6 +2,7 @@ import { useEffect } from "react";
import { useAgentStore } from "@/features/agents/stores/agentStore";
import { useChatSessionStore } from "@/features/chat/stores/chatSessionStore";
import { useProviderInventoryStore } from "@/features/providers/stores/providerInventoryStore";
import { discoverAcpProvidersFromEntries } from "@/shared/api/acp";
import { setNotificationHandler, getClient } from "@/shared/api/acpConnection";
import notificationHandler from "@/shared/api/acpNotificationHandler";
import { perfLog } from "@/shared/lib/perfLog";
@@ -47,46 +48,41 @@ export function useAppStartup() {
}
};
const loadProviders = async () => {
const loadProvidersAndInventory = async () => {
const t0 = performance.now();
store.setProvidersLoading(true);
try {
const { discoverAcpProviders } = await import("@/shared/api/acp");
const providers = await discoverAcpProviders();
store.setProviders(providers);
perfLog(
`[perf:startup] loadProviders done in ${(performance.now() - t0).toFixed(1)}ms (n=${providers.length})`,
);
} catch (err) {
console.error("Failed to load ACP providers on startup:", err);
} finally {
store.setProvidersLoading(false);
}
};
const loadProviderInventory = async () => {
const t0 = performance.now();
inventoryStore.setLoading(true);
try {
const { getProviderInventory } = await import(
"@/features/providers/api/inventory"
);
const entries = await getProviderInventory();
// Populate inventory store
inventoryStore.setEntries(entries);
// Derive ACP providers from the same response
const providers = discoverAcpProvidersFromEntries(entries);
store.setProviders(providers);
perfLog(
`[perf:startup] loadProviderInventory done in ${(performance.now() - t0).toFixed(1)}ms (n=${entries.length})`,
`[perf:startup] loadProvidersAndInventory done in ${(performance.now() - t0).toFixed(1)}ms (entries=${entries.length}, providers=${providers.length})`,
);
return entries;
} catch (err) {
console.error("Failed to load provider inventory on startup:", err);
console.error(
"Failed to load providers and inventory on startup:",
err,
);
return [];
} finally {
store.setProvidersLoading(false);
inventoryStore.setLoading(false);
}
};
const refreshConfiguredProviderInventory = async (
initialEntries?: Awaited<ReturnType<typeof loadProviderInventory>>,
initialEntries?: Awaited<ReturnType<typeof loadProvidersAndInventory>>,
) => {
try {
const entries =
@@ -146,15 +142,14 @@ export function useAppStartup() {
setActiveSession(null);
};
const inventoryLoad = loadProviderInventory();
const providersAndInventoryLoad = loadProvidersAndInventory();
await Promise.allSettled([
loadPersonas(),
loadProviders(),
inventoryLoad,
providersAndInventoryLoad,
loadSessionState(),
]);
void inventoryLoad.then((entries) =>
void providersAndInventoryLoad.then((entries) =>
refreshConfiguredProviderInventory(entries),
);
perfLog(
+16
View File
@@ -39,6 +39,22 @@ export interface AcpCreateSessionOptions extends AcpPrepareSessionOptions {
/** Discover ACP providers installed on the system. */
export async function discoverAcpProviders(): Promise<AcpProvider[]> {
const providers = await directAcp.listProviders();
return resolveProvidersCatalog(providers);
}
/**
* Derive ACP providers from already-fetched inventory entries,
* avoiding a duplicate `_goose/providers/list` RPC.
*/
export function discoverAcpProvidersFromEntries(
entries: Array<{ providerId: string; providerName: string }>,
): AcpProvider[] {
return resolveProvidersCatalog(
directAcp.buildProviderListFromEntries(entries),
);
}
function resolveProvidersCatalog(providers: AcpProvider[]): AcpProvider[] {
const seen = new Set<string>();
return providers
+25 -11
View File
@@ -27,26 +27,40 @@ export interface AcpSessionInfo {
personaId: string | null;
}
const DEPRECATED_PROVIDER_IDS = new Set(["claude-code", "codex", "gemini-cli"]);
const DEFAULT_PROVIDER: AcpProvider = {
export const DEPRECATED_PROVIDER_IDS = new Set([
"claude-code",
"codex",
"gemini-cli",
]);
export const DEFAULT_PROVIDER: AcpProvider = {
id: "goose",
label: "Goose (Default)",
};
/**
* Build the ACP provider list from raw inventory entries.
*
* Shared by both `listProviders` (which fetches entries via RPC) and
* `discoverAcpProvidersFromEntries` in acp.ts (which reuses
* already-fetched entries at startup).
*/
export function buildProviderListFromEntries(
entries: Array<{ providerId: string; providerName: string }>,
): AcpProvider[] {
return [
DEFAULT_PROVIDER,
...entries
.filter((entry) => !DEPRECATED_PROVIDER_IDS.has(entry.providerId))
.map((entry) => ({ id: entry.providerId, label: entry.providerName })),
];
}
export async function listProviders(): Promise<AcpProvider[]> {
const client = await getClient();
const result = await client.goose.GooseProvidersList({
providerIds: [],
});
const providers = result.entries
.filter((entry) => !DEPRECATED_PROVIDER_IDS.has(entry.providerId))
.map((entry) => ({
id: entry.providerId,
label: entry.providerName,
}));
return [DEFAULT_PROVIDER, ...providers];
return buildProviderListFromEntries(result.entries);
}
export async function listSessions(): Promise<AcpSessionInfo[]> {