From e4edefd0fcac8e40dda939777b9072511bf7c34a Mon Sep 17 00:00:00 2001 From: "clawsweeper[bot]" <274271284+clawsweeper[bot]@users.noreply.github.com> Date: Wed, 29 Apr 2026 14:07:42 -0700 Subject: [PATCH] fix: Found one regression in model-list availability heuristics for pr (#74524) Co-authored-by: openclaw-clawsweeper[bot] <280122609+openclaw-clawsweeper[bot]@users.noreply.github.com> --- src/commands/models/list.auth-index.test.ts | 25 ++++++++++++++++-- src/commands/models/list.auth-index.ts | 28 +++++++++++++++------ 2 files changed, 44 insertions(+), 9 deletions(-) diff --git a/src/commands/models/list.auth-index.test.ts b/src/commands/models/list.auth-index.test.ts index aa5f0b081f3..c96085d2fec 100644 --- a/src/commands/models/list.auth-index.test.ts +++ b/src/commands/models/list.auth-index.test.ts @@ -1,4 +1,4 @@ -import { describe, expect, it, vi } from "vitest"; +import { beforeEach, describe, expect, it, vi } from "vitest"; import type { AuthProfileStore } from "../../agents/auth-profiles/types.js"; import { createModelListAuthIndex } from "./list.auth-index.js"; @@ -20,6 +20,21 @@ const pluginRegistryMocks = vi.hoisted(() => ({ ), })); +const envCandidateMocks = vi.hoisted(() => ({ + resolveProviderEnvApiKeyCandidates: vi.fn(), +})); + +vi.mock("../../agents/model-auth-env-vars.js", async (importOriginal) => { + const actual = await importOriginal(); + envCandidateMocks.resolveProviderEnvApiKeyCandidates.mockImplementation( + actual.resolveProviderEnvApiKeyCandidates, + ); + return { + ...actual, + resolveProviderEnvApiKeyCandidates: envCandidateMocks.resolveProviderEnvApiKeyCandidates, + }; +}); + vi.mock("../../plugins/plugin-registry.js", async (importOriginal) => { const actual = await importOriginal(); return { @@ -47,6 +62,11 @@ function modelConfig(id: string) { } describe("createModelListAuthIndex", () => { + beforeEach(() => { + envCandidateMocks.resolveProviderEnvApiKeyCandidates.mockClear(); + pluginRegistryMocks.loadPluginRegistrySnapshotWithMetadata.mockClear(); + }); + it("normalizes auth aliases from profiles", () => { const index = createModelListAuthIndex({ cfg: {}, @@ -80,7 +100,8 @@ describe("createModelListAuthIndex", () => { expect(index.hasProviderAuth("openai")).toBe(false); }); - it("uses manifest env metadata for google vertex auth", () => { + it("checks resolver-only env auth on demand", () => { + envCandidateMocks.resolveProviderEnvApiKeyCandidates.mockReturnValueOnce({}); const index = createModelListAuthIndex({ cfg: {}, authStore: emptyStore, diff --git a/src/commands/models/list.auth-index.ts b/src/commands/models/list.auth-index.ts index ba64321ca7b..9d1c90bbb04 100644 --- a/src/commands/models/list.auth-index.ts +++ b/src/commands/models/list.auth-index.ts @@ -58,6 +58,7 @@ export function createModelListAuthIndex( const envCandidateMap = resolveProviderEnvApiKeyCandidates({ config: params.cfg, env }); const authenticatedProviders = new Set(); const syntheticAuthProviders = new Set(); + const envProviderAuthCache = new Map(); const addProvider = (provider: string | undefined) => { if (!provider?.trim()) { return; @@ -81,11 +82,6 @@ export function createModelListAuthIndex( addProvider(provider); } } - // Google Vertex ADC is still represented by resolveEnvApiKey's compatibility - // path. Move this into manifest auth signals once that contract exists. - if (resolveEnvApiKey("google-vertex", env, { aliasMap, candidateMap: envCandidateMap })) { - addProvider("google-vertex"); - } if (resolveAwsSdkEnvVarName(env)) { addProvider("amazon-bedrock"); @@ -105,11 +101,29 @@ export function createModelListAuthIndex( addSyntheticProvider(provider); } + const hasEnvProviderAuth = (provider: string): boolean => { + const normalized = normalizeAuthProvider(provider, aliasMap); + const cached = envProviderAuthCache.get(normalized); + if (cached !== undefined) { + return cached; + } + const hasAuth = Boolean( + resolveEnvApiKey(provider, env, { aliasMap, candidateMap: envCandidateMap }), + ); + envProviderAuthCache.set(normalized, hasAuth); + if (hasAuth) { + authenticatedProviders.add(normalized); + } + return hasAuth; + }; + return { hasProviderAuth(provider: string): boolean { + const normalizedProvider = normalizeAuthProvider(provider, aliasMap); return ( - authenticatedProviders.has(normalizeAuthProvider(provider, aliasMap)) || - syntheticAuthProviders.has(normalizeProviderIdForAuth(provider)) + authenticatedProviders.has(normalizedProvider) || + syntheticAuthProviders.has(normalizeProviderIdForAuth(provider)) || + hasEnvProviderAuth(provider) ); }, };