fix(perplexity): validate API keys via /v1/models endpoint

Perplexity deprecated the unversioned `/models` endpoint (now 404),
so the OpenAI-like key validator (validateOpenAILikeProvider in
src/lib/providers/validation.ts) would derive `<baseUrl>/models`
via addModelsSuffix from the perplexity registry baseUrl and hit a
dead endpoint, misclassifying valid keys.

Pin an explicit `modelsUrl` on the perplexity registry entry so
validation probes `/v1/models` directly. The dashboard models
discovery route (src/app/api/providers/[id]/models/route.ts)
already tries `/v1/models` first via its endpoints fallback list,
so it is unaffected.

Co-authored-by: gaoliao1688 <gaoliao1688@users.noreply.github.com>
Inspired-by: https://github.com/decolua/9router/commit/db4499d6
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-06-22 12:28:41 -03:00
parent 35a3962cf0
commit 3e03daaa7a
2 changed files with 55 additions and 0 deletions

View File

@@ -6,6 +6,11 @@ export const perplexityProvider: RegistryEntry = {
format: "openai",
executor: "default",
baseUrl: "https://api.perplexity.ai/chat/completions",
// Perplexity deprecated the unversioned `/models` endpoint (returns 404), so
// pin an explicit `modelsUrl` here. Without it, validateOpenAILikeProvider
// (src/lib/providers/validation.ts) derives `<baseUrl>/models` via
// addModelsSuffix and probes the dead endpoint, misclassifying valid keys.
modelsUrl: "https://api.perplexity.ai/v1/models",
authType: "apikey",
authHeader: "bearer",
models: [

View File

@@ -0,0 +1,50 @@
// Regression test for perplexity API key validation.
//
// Perplexity deprecated the unversioned `/models` endpoint (returns 404), so
// our default validation probe — which derives `<baseUrl>/models` from the
// perplexity registry entry via `addModelsSuffix` — would always fail to
// confirm a valid key, falling through to the chat-completions probe and
// often misclassifying live keys as "Invalid". Inspired by upstream
// 9router fix (see commit message); we port it OmniRoute-style by
// declaring an explicit `modelsUrl` on the perplexity registry entry.
import { describe, it } from "node:test";
import assert from "node:assert";
describe("perplexity registry — key validation models endpoint", () => {
it("declares a modelsUrl pointing at /v1/models (not the deprecated /models)", async () => {
const { getRegistryEntry } = await import(
"../../open-sse/config/providerRegistry.ts"
);
const entry = getRegistryEntry("perplexity");
assert.ok(entry, "perplexity must be registered in the execution registry");
assert.equal(entry.format, "openai");
assert.ok(
typeof entry.modelsUrl === "string" && entry.modelsUrl.length > 0,
"perplexity registry entry must declare an explicit modelsUrl so key " +
"validation does not hit the deprecated <baseUrl>/models endpoint"
);
assert.equal(
entry.modelsUrl,
"https://api.perplexity.ai/v1/models",
"perplexity modelsUrl must point at the versioned /v1/models endpoint " +
"(unversioned /models was deprecated and now returns 404)"
);
});
it("does not derive a /models URL that ends in /chat/completions/models", async () => {
const { getRegistryEntry } = await import(
"../../open-sse/config/providerRegistry.ts"
);
const entry = getRegistryEntry("perplexity");
assert.ok(entry?.modelsUrl, "modelsUrl required");
assert.ok(
!entry.modelsUrl.includes("/chat/completions"),
"modelsUrl must not include /chat/completions"
);
assert.ok(
entry.modelsUrl.endsWith("/v1/models"),
"modelsUrl must end with /v1/models"
);
});
});