From a50aef82ba326f183efef140deaa91534d11f30b Mon Sep 17 00:00:00 2001 From: Shakker Date: Fri, 24 Apr 2026 04:19:04 +0100 Subject: [PATCH] perf: generalize provider-filtered catalog fast path --- src/commands/models/list.list-command.ts | 7 +++- src/commands/models/list.provider-catalog.ts | 19 ++++++++- src/commands/models/list.row-sources.ts | 43 ++++++++++++++++---- src/commands/models/list.rows.ts | 7 +++- 4 files changed, 64 insertions(+), 12 deletions(-) diff --git a/src/commands/models/list.list-command.ts b/src/commands/models/list.list-command.ts index 2ee17c2881e..e1c26a4cbbe 100644 --- a/src/commands/models/list.list-command.ts +++ b/src/commands/models/list.list-command.ts @@ -4,6 +4,7 @@ import type { RuntimeEnv } from "../../runtime.js"; import { normalizeLowercaseStringOrEmpty } from "../../shared/string-coerce.js"; import { resolveConfiguredEntries } from "./list.configured.js"; import { formatErrorWithStack } from "./list.errors.js"; +import { hasProviderStaticCatalogForFilter } from "./list.provider-catalog.js"; import { loadConfiguredListModelRegistry, loadListModelRegistry } from "./list.registry-load.js"; import { appendAllModelRowSources, @@ -58,11 +59,15 @@ export async function modelsListCommand( let discoveredKeys = new Set(); let availableKeys: Set | undefined; let availabilityErrorMessage: string | undefined; - const useProviderCatalogFastPath = Boolean(opts.all && providerFilter === "codex"); const { entries } = resolveConfiguredEntries(cfg); const configuredByKey = new Map(entries.map((entry) => [entry.key, entry])); + const useProviderCatalogFastPath = + opts.all && providerFilter + ? await hasProviderStaticCatalogForFilter({ cfg, providerFilter }) + : false; const shouldLoadRegistry = modelRowSourcesRequireRegistry({ all: opts.all, + providerFilter, useProviderCatalogFastPath, }); try { diff --git a/src/commands/models/list.provider-catalog.ts b/src/commands/models/list.provider-catalog.ts index b8d83e00f93..8da1e6dc31f 100644 --- a/src/commands/models/list.provider-catalog.ts +++ b/src/commands/models/list.provider-catalog.ts @@ -4,6 +4,7 @@ import type { ModelProviderConfig } from "../../config/types.models.js"; import type { OpenClawConfig } from "../../config/types.openclaw.js"; import { formatErrorMessage } from "../../infra/errors.js"; import { createSubsystemLogger } from "../../logging/subsystem.js"; +import { loadPluginManifestRegistry } from "../../plugins/manifest-registry.js"; import { groupPluginDiscoveryProvidersByOrder, normalizePluginDiscoveryResult, @@ -45,6 +46,21 @@ export async function resolveProviderCatalogPluginIdsForFilter(params: { return undefined; } +export async function hasProviderStaticCatalogForFilter(params: { + cfg: OpenClawConfig; + env?: NodeJS.ProcessEnv; + providerFilter: string; +}): Promise { + const pluginIds = await resolveProviderCatalogPluginIdsForFilter(params); + if (!pluginIds || pluginIds.length === 0) { + return false; + } + const pluginIdSet = new Set(pluginIds); + return loadPluginManifestRegistry({ config: params.cfg, env: params.env }).plugins.some( + (plugin) => pluginIdSet.has(plugin.id) && typeof plugin.providerDiscoverySource === "string", + ); +} + function modelFromProviderCatalog(params: { provider: string; providerConfig: ModelProviderConfig; @@ -72,6 +88,7 @@ export async function loadProviderCatalogModelsForList(params: { agentDir: string; env?: NodeJS.ProcessEnv; providerFilter?: string; + staticOnly?: boolean; }): Promise[]> { const env = params.env ?? process.env; const providerFilter = params.providerFilter ? normalizeProviderId(params.providerFilter) : ""; @@ -104,7 +121,7 @@ export async function loadProviderCatalogModelsForList(params: { env, onlyPluginIds: scopedPluginIds, includeUntrustedWorkspacePlugins: false, - requireCompleteDiscoveryEntryCoverage: true, + requireCompleteDiscoveryEntryCoverage: params.staticOnly === true, }) ).filter( (provider) => diff --git a/src/commands/models/list.row-sources.ts b/src/commands/models/list.row-sources.ts index 39f360d3fbb..9389748e9d8 100644 --- a/src/commands/models/list.row-sources.ts +++ b/src/commands/models/list.row-sources.ts @@ -18,15 +18,42 @@ type AllModelRowSources = { export function modelRowSourcesRequireRegistry(params: { all?: boolean; + providerFilter?: string; useProviderCatalogFastPath: boolean; }): boolean { if (!params.all) { return false; } - return !params.useProviderCatalogFastPath; + if (params.providerFilter && params.useProviderCatalogFastPath) { + return false; + } + return true; } export async function appendAllModelRowSources(params: AllModelRowSources): Promise { + if (params.context.filter.provider && params.useProviderCatalogFastPath) { + let seenKeys = new Set(); + const catalogRows = await appendProviderCatalogRows({ + rows: params.rows, + context: params.context, + seenKeys, + staticOnly: true, + }); + if (catalogRows === 0) { + seenKeys = appendDiscoveredRows({ + rows: params.rows, + models: params.modelRegistry?.getAll() ?? [], + context: params.context, + }); + } + appendConfiguredProviderRows({ + rows: params.rows, + context: params.context, + seenKeys, + }); + return; + } + const seenKeys = appendDiscoveredRows({ rows: params.rows, models: params.modelRegistry?.getAll() ?? [], @@ -39,7 +66,7 @@ export async function appendAllModelRowSources(params: AllModelRowSources): Prom seenKeys, }); - if (params.modelRegistry) { + if (params.modelRegistry && !params.context.filter.provider) { await appendCatalogSupplementRows({ rows: params.rows, modelRegistry: params.modelRegistry, @@ -49,13 +76,11 @@ export async function appendAllModelRowSources(params: AllModelRowSources): Prom return; } - if (params.useProviderCatalogFastPath) { - await appendProviderCatalogRows({ - rows: params.rows, - context: params.context, - seenKeys, - }); - } + await appendProviderCatalogRows({ + rows: params.rows, + context: params.context, + seenKeys, + }); } export function appendConfiguredModelRowSources(params: { diff --git a/src/commands/models/list.rows.ts b/src/commands/models/list.rows.ts index 2c7e69ea448..dba142e5642 100644 --- a/src/commands/models/list.rows.ts +++ b/src/commands/models/list.rows.ts @@ -245,11 +245,14 @@ export async function appendProviderCatalogRows(params: { rows: ModelRow[]; context: RowBuilderContext; seenKeys: Set; -}): Promise { + staticOnly?: boolean; +}): Promise { + let appended = 0; for (const model of await loadProviderCatalogModelsForList({ cfg: params.context.cfg, agentDir: params.context.agentDir, providerFilter: params.context.filter.provider, + staticOnly: params.staticOnly, })) { if (!matchesRowFilter(params.context.filter, model)) { continue; @@ -270,7 +273,9 @@ export async function appendProviderCatalogRows(params: { }), ); params.seenKeys.add(key); + appended += 1; } + return appended; } export function appendConfiguredRows(params: {