Enhance token refresh functionality across multiple executors

- Updated refreshCredentials methods in various executors (Antigravity, Base, Default, Github, Kiro) to accept optional proxyOptions for improved proxy handling.
- Modified token refresh logic to utilize proxy-aware fetch for better network management.
- Enhanced usage retrieval functions to support proxy options, ensuring seamless integration with proxy configurations.
- Updated ModelSelectModal and ProviderInfoCard components to incorporate kind filtering for improved user experience in model selection.
- Added validation for API keys in the provider validation route, including support for webSearch/webFetch providers.
This commit is contained in:
decolua
2026-04-28 17:28:57 +07:00
parent 1bb621317d
commit 8f81363675
45 changed files with 2924 additions and 289 deletions
+53 -3
View File
@@ -1,17 +1,53 @@
import { NextResponse } from "next/server";
import { getProviderNodeById } from "@/models";
import { isOpenAICompatibleProvider, isAnthropicCompatibleProvider, isCustomEmbeddingProvider } from "@/shared/constants/providers";
import { isOpenAICompatibleProvider, isAnthropicCompatibleProvider, isCustomEmbeddingProvider, AI_PROVIDERS } from "@/shared/constants/providers";
import { getDefaultModel } from "open-sse/config/providerModels.js";
import { resolveOllamaLocalHost } from "open-sse/config/providers.js";
import { PROVIDER_ENDPOINTS } from "@/shared/constants/config";
// Probe a webSearch/webFetch provider using its searchConfig/fetchConfig.
// Returns true if API key is accepted (status !== 401 && !== 403).
async function probeWebProvider(provider, apiKey) {
const p = AI_PROVIDERS[provider];
if (!p) return null;
// Skip if provider has dual-purpose (LLM + search), let LLM validate handle it
const kinds = p.serviceKinds || ["llm"];
const isWebOnly = kinds.every((k) => k === "webSearch" || k === "webFetch");
if (!isWebOnly) return null;
const cfg = p.searchConfig || p.fetchConfig;
if (!cfg) return null;
if (cfg.authType === "none") return true; // no-auth (e.g. searxng)
let url = cfg.baseUrl;
const headers = { "Content-Type": "application/json" };
let body;
// Apply auth based on authHeader
switch (cfg.authHeader) {
case "bearer": headers["Authorization"] = `Bearer ${apiKey}`; break;
case "x-api-key": headers["x-api-key"] = apiKey; break;
case "x-subscription-token":headers["x-subscription-token"] = apiKey; break;
case "key": url += `?key=${encodeURIComponent(apiKey)}&q=ping&cx=test`; break; // google-pse
case "api_key": url += `?api_key=${encodeURIComponent(apiKey)}&q=ping&engine=google`; break; // searchapi
}
// Minimal body for POST endpoints; GET sends nothing
if (cfg.method === "POST") {
body = JSON.stringify({ query: "ping", q: "ping", url: "https://example.com" });
}
const res = await fetch(url, { method: cfg.method, headers, body, signal: AbortSignal.timeout(8000) });
return res.status !== 401 && res.status !== 403;
}
// POST /api/providers/validate - Validate API key with provider
export async function POST(request) {
try {
const body = await request.json();
const { provider, apiKey, providerSpecificData } = body;
if (!provider || (!apiKey && provider !== "ollama-local")) {
const isNoAuth = AI_PROVIDERS[provider]?.noAuth === true;
if (!provider || (!apiKey && provider !== "ollama-local" && !isNoAuth)) {
return NextResponse.json({ error: "Provider and API key required" }, { status: 400 });
}
@@ -147,6 +183,15 @@ export async function POST(request) {
});
}
// Generic probe for webSearch/webFetch providers (config-driven)
const webResult = await probeWebProvider(provider, apiKey);
if (webResult !== null) {
return NextResponse.json({
valid: webResult,
error: webResult ? null : "Invalid API key",
});
}
switch (provider) {
case "openai":
const openaiRes = await fetch("https://api.openai.com/v1/models", {
@@ -294,7 +339,12 @@ export async function POST(request) {
const headers = {};
if (apiKey) headers["Authorization"] = `Bearer ${apiKey}`;
const res = await fetch(endpoints[provider], { headers });
isValid = res.ok;
// xai returns 400 for bad key, 403 for valid-but-no-credit. Other providers use 401.
if (provider === "xai") {
isValid = res.status === 200 || res.status === 403;
} else {
isValid = res.ok;
}
break;
}