diff --git a/src/agents/pi-model-discovery.ts b/src/agents/pi-model-discovery.ts index 7c6dbdec4cc..1744305b117 100644 --- a/src/agents/pi-model-discovery.ts +++ b/src/agents/pi-model-discovery.ts @@ -35,6 +35,7 @@ type DiscoveredProviderRuntimeModelLike = Omit type DiscoverModelsOptions = { providerFilter?: string; + normalizeModels?: boolean; }; type InMemoryAuthStorageBackendLike = { @@ -149,17 +150,24 @@ function createOpenClawModelRegistry( const providerFilter = options?.providerFilter ? normalizeProviderId(options.providerFilter) : ""; const matchesProviderFilter = (entry: Model) => !providerFilter || normalizeProviderId(entry.provider) === providerFilter; + const shouldNormalize = options?.normalizeModels !== false; - registry.getAll = () => - getAll() - .filter((entry: Model) => matchesProviderFilter(entry)) - .map((entry: Model) => normalizeDiscoveredPiModel(entry, agentDir)); - registry.getAvailable = () => - getAvailable() - .filter((entry: Model) => matchesProviderFilter(entry)) - .map((entry: Model) => normalizeDiscoveredPiModel(entry, agentDir)); + registry.getAll = () => { + const entries = getAll().filter((entry: Model) => matchesProviderFilter(entry)); + return shouldNormalize + ? entries.map((entry: Model) => normalizeDiscoveredPiModel(entry, agentDir)) + : entries; + }; + registry.getAvailable = () => { + const entries = getAvailable().filter((entry: Model) => matchesProviderFilter(entry)); + return shouldNormalize + ? entries.map((entry: Model) => normalizeDiscoveredPiModel(entry, agentDir)) + : entries; + }; registry.find = (provider: string, modelId: string) => - normalizeDiscoveredPiModel(find(provider, modelId), agentDir); + shouldNormalize + ? normalizeDiscoveredPiModel(find(provider, modelId), agentDir) + : find(provider, modelId); return registry; } diff --git a/src/commands/models/list.list-command.forward-compat.test.ts b/src/commands/models/list.list-command.forward-compat.test.ts index 2e424bfd39f..be36882bef5 100644 --- a/src/commands/models/list.list-command.forward-compat.test.ts +++ b/src/commands/models/list.list-command.forward-compat.test.ts @@ -153,7 +153,7 @@ function installModelsListCommandForwardCompatMocks() { vi.doMock("./list.registry-load.js", () => ({ loadListModelRegistry: async ( cfg: unknown, - opts?: { providerFilter?: string }, + opts?: { providerFilter?: string; normalizeModels?: boolean }, ): Promise<{ models: Array<{ provider: string; id: string }>; availableKeys?: Set; @@ -173,7 +173,7 @@ function installModelsListCommandForwardCompatMocks() { loadConfiguredListModelRegistry: ( _cfg: unknown, _entries: unknown, - opts?: { providerFilter?: string }, + opts?: { providerFilter?: string; normalizeModels?: boolean }, ) => { mocks.loadModelRegistry(mocks.resolvedConfig, opts); return { @@ -618,6 +618,7 @@ describe("modelsListCommand forward-compat", () => { expect(mocks.loadModelRegistry).toHaveBeenCalledWith(mocks.resolvedConfig, { providerFilter: undefined, + normalizeModels: false, }); expect(mocks.loadProviderCatalogModelsForList).not.toHaveBeenCalled(); expect(mocks.resolveModelWithRegistry).not.toHaveBeenCalled(); @@ -653,6 +654,7 @@ describe("modelsListCommand forward-compat", () => { mocks.resolvedConfig, expect.objectContaining({ providerFilter: "openai-codex", + normalizeModels: true, }), ); expect(mocks.loadProviderCatalogModelsForList).toHaveBeenNthCalledWith(1, { diff --git a/src/commands/models/list.list-command.ts b/src/commands/models/list.list-command.ts index 03d7e0c6ec1..7be2486d9c0 100644 --- a/src/commands/models/list.list-command.ts +++ b/src/commands/models/list.list-command.ts @@ -92,7 +92,10 @@ export async function modelsListCommand( const shouldLoadRegistry = sourcePlan?.requiresInitialRegistry ?? false; const loadRegistryState = async () => { const { loadListModelRegistry } = await loadRegistryLoadModule(); - const loaded = await loadListModelRegistry(cfg, { providerFilter }); + const loaded = await loadListModelRegistry(cfg, { + providerFilter, + normalizeModels: Boolean(providerFilter), + }); modelRegistry = loaded.registry; discoveredKeys = loaded.discoveredKeys; availableKeys = loaded.availableKeys; diff --git a/src/commands/models/list.registry-load.ts b/src/commands/models/list.registry-load.ts index d7e683c3c38..b6b65f7c674 100644 --- a/src/commands/models/list.registry-load.ts +++ b/src/commands/models/list.registry-load.ts @@ -10,7 +10,7 @@ import { modelKey } from "./shared.js"; export async function loadListModelRegistry( cfg: OpenClawConfig, - opts?: { providerFilter?: string }, + opts?: { providerFilter?: string; normalizeModels?: boolean }, ) { const loaded = await loadModelRegistry(cfg, opts); return { diff --git a/src/commands/models/list.registry.ts b/src/commands/models/list.registry.ts index 972b8ab43ac..f2d3b39a7e0 100644 --- a/src/commands/models/list.registry.ts +++ b/src/commands/models/list.registry.ts @@ -107,11 +107,15 @@ function loadAvailableModels(registry: ModelRegistry, cfg: OpenClawConfig): Mode } } -export async function loadModelRegistry(cfg: OpenClawConfig, opts?: { providerFilter?: string }) { +export async function loadModelRegistry( + cfg: OpenClawConfig, + opts?: { providerFilter?: string; normalizeModels?: boolean }, +) { const agentDir = resolveOpenClawAgentDir(); const authStorage = discoverAuthStorage(agentDir, { readOnly: true }); const registry = discoverModels(authStorage, agentDir, { providerFilter: opts?.providerFilter, + normalizeModels: opts?.normalizeModels, }); const models = registry.getAll().filter( (model) =>