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:
@@ -2,6 +2,7 @@ import { useEffect } from "react";
|
|||||||
import { useAgentStore } from "@/features/agents/stores/agentStore";
|
import { useAgentStore } from "@/features/agents/stores/agentStore";
|
||||||
import { useChatSessionStore } from "@/features/chat/stores/chatSessionStore";
|
import { useChatSessionStore } from "@/features/chat/stores/chatSessionStore";
|
||||||
import { useProviderInventoryStore } from "@/features/providers/stores/providerInventoryStore";
|
import { useProviderInventoryStore } from "@/features/providers/stores/providerInventoryStore";
|
||||||
|
import { discoverAcpProvidersFromEntries } from "@/shared/api/acp";
|
||||||
import { setNotificationHandler, getClient } from "@/shared/api/acpConnection";
|
import { setNotificationHandler, getClient } from "@/shared/api/acpConnection";
|
||||||
import notificationHandler from "@/shared/api/acpNotificationHandler";
|
import notificationHandler from "@/shared/api/acpNotificationHandler";
|
||||||
import { perfLog } from "@/shared/lib/perfLog";
|
import { perfLog } from "@/shared/lib/perfLog";
|
||||||
@@ -47,46 +48,41 @@ export function useAppStartup() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const loadProviders = async () => {
|
const loadProvidersAndInventory = async () => {
|
||||||
const t0 = performance.now();
|
const t0 = performance.now();
|
||||||
store.setProvidersLoading(true);
|
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);
|
inventoryStore.setLoading(true);
|
||||||
try {
|
try {
|
||||||
const { getProviderInventory } = await import(
|
const { getProviderInventory } = await import(
|
||||||
"@/features/providers/api/inventory"
|
"@/features/providers/api/inventory"
|
||||||
);
|
);
|
||||||
const entries = await getProviderInventory();
|
const entries = await getProviderInventory();
|
||||||
|
|
||||||
|
// Populate inventory store
|
||||||
inventoryStore.setEntries(entries);
|
inventoryStore.setEntries(entries);
|
||||||
|
|
||||||
|
// Derive ACP providers from the same response
|
||||||
|
const providers = discoverAcpProvidersFromEntries(entries);
|
||||||
|
store.setProviders(providers);
|
||||||
|
|
||||||
perfLog(
|
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;
|
return entries;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Failed to load provider inventory on startup:", err);
|
console.error(
|
||||||
|
"Failed to load providers and inventory on startup:",
|
||||||
|
err,
|
||||||
|
);
|
||||||
return [];
|
return [];
|
||||||
} finally {
|
} finally {
|
||||||
|
store.setProvidersLoading(false);
|
||||||
inventoryStore.setLoading(false);
|
inventoryStore.setLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const refreshConfiguredProviderInventory = async (
|
const refreshConfiguredProviderInventory = async (
|
||||||
initialEntries?: Awaited<ReturnType<typeof loadProviderInventory>>,
|
initialEntries?: Awaited<ReturnType<typeof loadProvidersAndInventory>>,
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
const entries =
|
const entries =
|
||||||
@@ -146,15 +142,14 @@ export function useAppStartup() {
|
|||||||
setActiveSession(null);
|
setActiveSession(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
const inventoryLoad = loadProviderInventory();
|
const providersAndInventoryLoad = loadProvidersAndInventory();
|
||||||
|
|
||||||
await Promise.allSettled([
|
await Promise.allSettled([
|
||||||
loadPersonas(),
|
loadPersonas(),
|
||||||
loadProviders(),
|
providersAndInventoryLoad,
|
||||||
inventoryLoad,
|
|
||||||
loadSessionState(),
|
loadSessionState(),
|
||||||
]);
|
]);
|
||||||
void inventoryLoad.then((entries) =>
|
void providersAndInventoryLoad.then((entries) =>
|
||||||
refreshConfiguredProviderInventory(entries),
|
refreshConfiguredProviderInventory(entries),
|
||||||
);
|
);
|
||||||
perfLog(
|
perfLog(
|
||||||
|
|||||||
@@ -39,6 +39,22 @@ export interface AcpCreateSessionOptions extends AcpPrepareSessionOptions {
|
|||||||
/** Discover ACP providers installed on the system. */
|
/** Discover ACP providers installed on the system. */
|
||||||
export async function discoverAcpProviders(): Promise<AcpProvider[]> {
|
export async function discoverAcpProviders(): Promise<AcpProvider[]> {
|
||||||
const providers = await directAcp.listProviders();
|
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>();
|
const seen = new Set<string>();
|
||||||
|
|
||||||
return providers
|
return providers
|
||||||
|
|||||||
@@ -27,26 +27,40 @@ export interface AcpSessionInfo {
|
|||||||
personaId: string | null;
|
personaId: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEPRECATED_PROVIDER_IDS = new Set(["claude-code", "codex", "gemini-cli"]);
|
export const DEPRECATED_PROVIDER_IDS = new Set([
|
||||||
const DEFAULT_PROVIDER: AcpProvider = {
|
"claude-code",
|
||||||
|
"codex",
|
||||||
|
"gemini-cli",
|
||||||
|
]);
|
||||||
|
export const DEFAULT_PROVIDER: AcpProvider = {
|
||||||
id: "goose",
|
id: "goose",
|
||||||
label: "Goose (Default)",
|
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[]> {
|
export async function listProviders(): Promise<AcpProvider[]> {
|
||||||
const client = await getClient();
|
const client = await getClient();
|
||||||
const result = await client.goose.GooseProvidersList({
|
const result = await client.goose.GooseProvidersList({
|
||||||
providerIds: [],
|
providerIds: [],
|
||||||
});
|
});
|
||||||
|
return buildProviderListFromEntries(result.entries);
|
||||||
const providers = result.entries
|
|
||||||
.filter((entry) => !DEPRECATED_PROVIDER_IDS.has(entry.providerId))
|
|
||||||
.map((entry) => ({
|
|
||||||
id: entry.providerId,
|
|
||||||
label: entry.providerName,
|
|
||||||
}));
|
|
||||||
|
|
||||||
return [DEFAULT_PROVIDER, ...providers];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function listSessions(): Promise<AcpSessionInfo[]> {
|
export async function listSessions(): Promise<AcpSessionInfo[]> {
|
||||||
|
|||||||
Reference in New Issue
Block a user