fix(test): use POST /v1/messages to validate anthropic-compatible connections

GET /models is not part of the Anthropic API spec and many compatible
proxies do not implement it, causing the connection test to always fail
even with a valid API key.

Switch to POST /v1/messages with max_tokens=1 — the same approach used
for the built-in anthropic provider — and treat any non-401/403 response
as valid, since 400/529 still confirms the key was accepted.

Use node.defaultModel / connection.defaultModel when set so the test
respects the configured model rather than always falling back to
claude-3-haiku-20240307.

Co-authored-by: Rehan Choirul <rehanchrl@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Rehan Choirul
2026-06-20 15:53:03 +07:00
committed by decolua
parent 6c10edf8ba
commit 584cf66a70
2 changed files with 31 additions and 7 deletions

View File

@@ -355,10 +355,25 @@ async function testApiKeyConnection(connection, effectiveProxy = null) {
try {
modelsBase = modelsBase.replace(/\/$/, "");
if (modelsBase.endsWith("/messages")) modelsBase = modelsBase.slice(0, -9);
const res = await fetchWithConnectionProxy(`${modelsBase}/models`, {
headers: { "x-api-key": connection.apiKey, "anthropic-version": "2023-06-01", "Authorization": `Bearer ${connection.apiKey}` },
const messagesUrl = `${modelsBase}/v1/messages`;
const model = connection.defaultModel || "claude-3-haiku-20240307";
const res = await fetchWithConnectionProxy(messagesUrl, {
method: "POST",
headers: {
"x-api-key": connection.apiKey,
"anthropic-version": "2023-06-01",
"content-type": "application/json",
"Authorization": `Bearer ${connection.apiKey}`,
},
body: JSON.stringify({
model,
max_tokens: 1,
messages: [{ role: "user", content: "test" }],
}),
}, effectiveProxy);
return { valid: res.ok, error: res.ok ? null : "Invalid API key or base URL" };
// 400/529 still confirms key accepted; only 401/403 = bad key
const valid = res.status !== 401 && res.status !== 403;
return { valid, error: valid ? null : "Invalid API key or base URL" };
} catch (err) {
return { valid: false, error: err.message };
}

View File

@@ -155,17 +155,26 @@ export async function POST(request) {
normalizedBase = normalizedBase.slice(0, -9); // remove /messages
}
const modelsUrl = `${normalizedBase}/models`;
const messagesUrl = `${normalizedBase}/v1/messages`;
const model = node.defaultModel || "claude-3-haiku-20240307";
const res = await fetch(modelsUrl, {
const res = await fetch(messagesUrl, {
method: "POST",
headers: {
"x-api-key": apiKey,
"anthropic-version": "2023-06-01",
"Authorization": `Bearer ${apiKey}`
"content-type": "application/json",
"Authorization": `Bearer ${apiKey}`,
},
body: JSON.stringify({
model,
max_tokens: 1,
messages: [{ role: "user", content: "test" }],
}),
});
isValid = res.ok;
// 400/529 still confirms key accepted; only 401/403 = bad key
isValid = res.status !== 401 && res.status !== 403;
return NextResponse.json({
valid: isValid,
error: isValid ? null : "Invalid API key",