fix(validation): strip trailing /models in Gemini validator to avoid /models/models 404 (#2545)

This commit is contained in:
diegosouzapw
2026-05-21 23:58:02 -03:00
parent e3ba13bd09
commit fd7bafe392
2 changed files with 30 additions and 1 deletions

View File

@@ -741,11 +741,15 @@ async function validateGeminiLikeProvider({
return { valid: false, error: "Missing base URL" };
}
// Strip a trailing /models before appending — the default Gemini registry baseUrl is
// `.../v1beta/models` (for the chat urlBuilder), so naively appending /models produced
// `.../v1beta/models/models` → upstream 404 on connection validation (#2545).
const baseForModels = baseUrl.replace(/\/models\/?$/, "");
const requestUrl =
typeof providerSpecificData?.modelsUrl === "string" &&
providerSpecificData.modelsUrl.trim() !== ""
? providerSpecificData.modelsUrl.trim()
: `${baseUrl}/models`;
: `${baseForModels}/models`;
const urlWithKey =
authType === "query" ? `${requestUrl}?key=${encodeURIComponent(apiKey)}` : requestUrl;

View File

@@ -163,3 +163,28 @@ test("#2463 openai-compatible validation does not throw on non-string modelsUrl"
});
assert.equal(typeof result.valid, "boolean");
});
// Regression for #2545: the default Gemini (AI Studio) base URL ends in /v1beta/models,
// so the validator must not append a second /models (which produced /models/models → 404).
test("#2545 gemini validation does not produce /models/models", async () => {
const calls: string[] = [];
globalThis.fetch = async (url: any) => {
calls.push(String(url));
return new Response(JSON.stringify({ models: [] }), { status: 200 });
};
const result = await validateProviderApiKey({
provider: "gemini",
apiKey: "AIzaTestKey",
providerSpecificData: {},
});
assert.equal(typeof result.valid, "boolean");
assert.ok(calls.length > 0, "validator must make a request");
assert.ok(
!calls.some((u) => u.includes("/models/models")),
`outbound URL must not contain /models/models — got ${calls.join(", ")}`
);
assert.ok(
calls.some((u) => /\/v1beta\/models(\?|$)/.test(u)),
`outbound URL must hit a single /models segment — got ${calls.join(", ")}`
);
});