Skip to content

Commit f3df863

Browse files
authored
fix(gateway): honor profile auth for SecretRef model entries (#90686)
Fixes #90685 by allowing models.list availability to use matching auth-profile credentials when provider config contains a non-env SecretRef, while preserving unavailable results for unresolved SecretRef-only providers. Adds isolated regression coverage for file SecretRefs and secretref-managed provider markers. Co-authored-by: Rohit <[email protected]>
1 parent 26b9736 commit f3df863

2 files changed

Lines changed: 84 additions & 21 deletions

File tree

src/gateway/server-methods/models-list-result.ts

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import {
1414
type AuthProfileStore,
1515
} from "../../agents/auth-profiles.js";
1616
import { DEFAULT_PROVIDER } from "../../agents/defaults.js";
17-
import { NON_ENV_SECRETREF_MARKER } from "../../agents/model-auth-markers.js";
1817
import { hasRuntimeAvailableProviderAuth } from "../../agents/model-auth.js";
1918
import {
2019
loadModelCatalogForBrowse,
@@ -57,18 +56,6 @@ function omitRuntimeModelParams(entry: ModelCatalogEntry): ModelCatalogEntry {
5756
return rest;
5857
}
5958

60-
function modelCatalogEntryHasUnknownSecretRefAvailability(
61-
cfg: OpenClawConfig,
62-
entry: ModelCatalogEntry,
63-
): boolean {
64-
const providerId = normalizeProviderId(entry.provider);
65-
const provider = Object.entries(cfg.models?.providers ?? {}).find(
66-
([id]) => normalizeProviderId(id) === providerId,
67-
)?.[1];
68-
const apiKey = provider?.apiKey;
69-
return apiKey === NON_ENV_SECRETREF_MARKER || (isSecretRef(apiKey) && apiKey.source !== "env");
70-
}
71-
7259
function createInFlightProviderAuthChecker(
7360
providerAuthChecker: ModelsListProviderAuthChecker,
7461
): ModelsListProviderAuthChecker {
@@ -219,16 +206,9 @@ async function resolveModelsListEntryAvailability(
219206

220207
async function buildPublicModelsListEntry(params: {
221208
entry: ModelCatalogEntry;
222-
cfg: OpenClawConfig;
223209
providerAuthChecker?: ModelsListProviderAuthChecker;
224210
}): Promise<ModelsListEntry> {
225211
const publicEntry = omitRuntimeModelParams(params.entry);
226-
if (modelCatalogEntryHasUnknownSecretRefAvailability(params.cfg, params.entry)) {
227-
return {
228-
...publicEntry,
229-
available: false,
230-
};
231-
}
232212
if (!params.providerAuthChecker) {
233213
return publicEntry;
234214
}
@@ -253,7 +233,6 @@ async function buildPublicModelsListEntries(params: {
253233
params.catalog.map((entry) =>
254234
buildPublicModelsListEntry({
255235
entry,
256-
cfg: params.cfg,
257236
providerAuthChecker,
258237
}),
259238
),

src/gateway/server-methods/models.test.ts

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,90 @@ describe("models.list", () => {
565565
);
566566
});
567567

568+
it("marks auth profiles available even when provider config uses non-env SecretRef markers", async () => {
569+
for (const fixture of [
570+
{
571+
name: "file",
572+
apiKey: {
573+
source: "file",
574+
provider: "mounted-json",
575+
id: "/providers/vllm/apiKey",
576+
},
577+
},
578+
{ name: "managed-marker", apiKey: "secretref-managed" },
579+
] as const) {
580+
await withOpenClawTestState(
581+
{
582+
layout: "state-only",
583+
prefix: `openclaw-models-list-provider-${fixture.name}-profile-`,
584+
agentEnv: "main",
585+
env: {
586+
OPENCLAW_TEST_PROFILE_API_KEY: "test-token",
587+
VLLM_API_KEY: undefined,
588+
},
589+
},
590+
async (state) => {
591+
await state.writeAuthProfiles({
592+
version: 1,
593+
profiles: {
594+
"vllm:env": {
595+
type: "api_key",
596+
provider: "vllm",
597+
keyRef: {
598+
source: "env",
599+
provider: "default",
600+
id: "OPENCLAW_TEST_PROFILE_API_KEY",
601+
},
602+
},
603+
},
604+
});
605+
606+
const cfg = {
607+
agents: {
608+
defaults: {
609+
models: {
610+
"vllm/*": {},
611+
},
612+
},
613+
},
614+
models: {
615+
providers: {
616+
vllm: {
617+
apiKey: fixture.apiKey,
618+
},
619+
},
620+
},
621+
} as unknown as OpenClawConfig;
622+
623+
const { request, respond } = requestModelsList({
624+
view: "all",
625+
runtimeConfig: cfg,
626+
loadGatewayModelCatalog: vi.fn(() =>
627+
Promise.resolve([{ id: "llama-secure", name: "Llama Secure", provider: "vllm" }]),
628+
),
629+
reqId: `req-models-list-provider-${fixture.name}-profile`,
630+
});
631+
await request;
632+
633+
expect(respond).toHaveBeenCalledWith(
634+
true,
635+
{
636+
models: [
637+
{
638+
id: "llama-secure",
639+
name: "Llama Secure",
640+
provider: "vllm",
641+
available: true,
642+
},
643+
],
644+
},
645+
undefined,
646+
);
647+
},
648+
);
649+
}
650+
});
651+
568652
it("preserves catalog load errors before the timeout fallback wins", async () => {
569653
const { request, respond } = requestModelsList({
570654
view: "configured",

0 commit comments

Comments
 (0)