feat(proxy): add proxy pool and per-connection binding + strictProxy support

- Centralize proxy management with reusable proxy pools
- Per-connection proxy binding with legacy fallback
- Add strictProxy option: fail hard instead of silently falling back to direct
- Resolve alicode-intl conflict: keep alicode-intl support + proxy support

Made-with: Cursor
This commit is contained in:
decolua
2026-03-09 15:46:06 +07:00
parent 4c469291a1
commit 880f4eca91
22 changed files with 1811 additions and 146 deletions

View File

@@ -1,5 +1,63 @@
import { NextResponse } from "next/server";
import { getProviderConnectionById, updateProviderConnection, deleteProviderConnection } from "@/models";
import {
getProviderConnectionById,
getProxyPoolById,
updateProviderConnection,
deleteProviderConnection,
} from "@/models";
function normalizeProxyConfig(body = {}) {
const hasAnyProxyField =
Object.prototype.hasOwnProperty.call(body, "connectionProxyEnabled") ||
Object.prototype.hasOwnProperty.call(body, "connectionProxyUrl") ||
Object.prototype.hasOwnProperty.call(body, "connectionNoProxy");
if (!hasAnyProxyField) return { hasAnyProxyField: false };
const enabled = body?.connectionProxyEnabled === true;
const url = typeof body?.connectionProxyUrl === "string" ? body.connectionProxyUrl.trim() : "";
const noProxy = typeof body?.connectionNoProxy === "string" ? body.connectionNoProxy.trim() : "";
if (enabled && !url) {
return {
hasAnyProxyField: true,
error: "Connection proxy URL is required when connection proxy is enabled",
};
}
return {
hasAnyProxyField: true,
connectionProxyEnabled: enabled,
connectionProxyUrl: url,
connectionNoProxy: noProxy,
};
}
async function normalizeProxyPoolUpdate(proxyPoolIdInput) {
if (proxyPoolIdInput === undefined) {
return { hasProxyPoolField: false, proxyPoolId: null };
}
if (proxyPoolIdInput === null || proxyPoolIdInput === "" || proxyPoolIdInput === "__none__") {
return { hasProxyPoolField: true, proxyPoolId: null };
}
const proxyPoolId = String(proxyPoolIdInput).trim();
if (!proxyPoolId) {
return { hasProxyPoolField: true, proxyPoolId: null };
}
const proxyPool = await getProxyPoolById(proxyPoolId);
if (!proxyPool) {
return { hasProxyPoolField: true, error: "Proxy pool not found" };
}
return { hasProxyPoolField: true, proxyPoolId };
}
function shouldMergeProviderSpecificData(existing, incoming, hasLegacyProxy, hasProxyPoolField) {
return existing !== undefined || incoming !== undefined || hasLegacyProxy || hasProxyPoolField;
}
// GET /api/providers/[id] - Get single connection
export async function GET(request, { params }) {
@@ -48,6 +106,16 @@ export async function PUT(request, { params }) {
return NextResponse.json({ error: "Connection not found" }, { status: 404 });
}
const proxyConfig = normalizeProxyConfig(body);
if (proxyConfig.error) {
return NextResponse.json({ error: proxyConfig.error }, { status: 400 });
}
const proxyPoolResult = await normalizeProxyPoolUpdate(body.proxyPoolId);
if (proxyPoolResult.error) {
return NextResponse.json({ error: proxyPoolResult.error }, { status: 400 });
}
const updateData = {};
if (name !== undefined) updateData.name = name;
if (priority !== undefined) updateData.priority = priority;
@@ -58,11 +126,33 @@ export async function PUT(request, { params }) {
if (testStatus !== undefined) updateData.testStatus = testStatus;
if (lastError !== undefined) updateData.lastError = lastError;
if (lastErrorAt !== undefined) updateData.lastErrorAt = lastErrorAt;
if (providerSpecificData !== undefined) {
if (
shouldMergeProviderSpecificData(
existing.providerSpecificData,
providerSpecificData,
proxyConfig.hasAnyProxyField,
proxyPoolResult.hasProxyPoolField
)
) {
updateData.providerSpecificData = {
...(existing.providerSpecificData || {}),
...providerSpecificData,
...(providerSpecificData || {}),
};
if (proxyConfig.hasAnyProxyField) {
updateData.providerSpecificData.connectionProxyEnabled = proxyConfig.connectionProxyEnabled;
updateData.providerSpecificData.connectionProxyUrl = proxyConfig.connectionProxyUrl;
updateData.providerSpecificData.connectionNoProxy = proxyConfig.connectionNoProxy;
}
if (proxyPoolResult.hasProxyPoolField) {
if (proxyPoolResult.proxyPoolId === null) {
delete updateData.providerSpecificData.proxyPoolId;
} else {
updateData.providerSpecificData.proxyPoolId = proxyPoolResult.proxyPoolId;
}
}
}
const updated = await updateProviderConnection(id, updateData);

View File

@@ -1,4 +1,6 @@
import { getProviderConnectionById, updateProviderConnection } from "@/lib/localDb";
import { resolveConnectionProxyConfig } from "@/lib/network/connectionProxy";
import { testProxyUrl } from "@/lib/network/proxyTest";
import { isOpenAICompatibleProvider, isAnthropicCompatibleProvider } from "@/shared/constants/providers";
import { getDefaultModel } from "open-sse/config/providerModels.js";
import {
@@ -206,7 +208,7 @@ function isTokenExpired(connection) {
return expiresAt <= Date.now() + buffer;
}
async function testOAuthConnection(connection) {
async function testOAuthConnection(connection, effectiveProxy = null) {
const config = OAUTH_TEST_CONFIG[connection.provider];
if (!config) return { valid: false, error: "Provider test not supported", refreshed: false };
if (!connection.accessToken) return { valid: false, error: "No access token", refreshed: false };
@@ -268,7 +270,7 @@ async function testOAuthConnection(connection) {
const headers = config.noAuth
? { ...config.extraHeaders }
: { [config.authHeader]: `${config.authPrefix}${accessToken}`, ...config.extraHeaders };
const res = await fetch(testUrl, { method: config.method, headers });
const res = await fetchWithConnectionProxy(testUrl, { method: config.method, headers }, effectiveProxy);
if (res.ok) return { valid: true, error: null, refreshed, newTokens };
@@ -279,10 +281,10 @@ async function testOAuthConnection(connection) {
const retryHeaders = config.noAuth
? { ...config.extraHeaders }
: { [config.authHeader]: `${config.authPrefix}${tokens.accessToken}`, ...config.extraHeaders };
const retryRes = await fetch(retryUrl, {
const retryRes = await fetchWithConnectionProxy(retryUrl, {
method: config.method,
headers: retryHeaders,
});
}, effectiveProxy);
if (retryRes.ok) return { valid: true, error: null, refreshed: true, newTokens: tokens };
}
return { valid: false, error: "Token invalid or revoked", refreshed: false };
@@ -296,14 +298,27 @@ async function testOAuthConnection(connection) {
}
}
async function testApiKeyConnection(connection) {
async function fetchWithConnectionProxy(url, options = {}, effectiveProxy = null) {
if (!effectiveProxy?.connectionProxyEnabled || !effectiveProxy?.connectionProxyUrl) {
return fetch(url, options);
}
const { proxyAwareFetch } = await import("open-sse/utils/proxyFetch.js");
return proxyAwareFetch(url, options, {
connectionProxyEnabled: true,
connectionProxyUrl: effectiveProxy.connectionProxyUrl,
connectionNoProxy: effectiveProxy.connectionNoProxy || "",
});
}
async function testApiKeyConnection(connection, effectiveProxy = null) {
if (isOpenAICompatibleProvider(connection.provider)) {
const modelsBase = connection.providerSpecificData?.baseUrl;
if (!modelsBase) return { valid: false, error: "Missing base URL" };
try {
const res = await fetch(`${modelsBase.replace(/\/$/, "")}/models`, {
const res = await fetchWithConnectionProxy(`${modelsBase.replace(/\/$/, "")}/models`, {
headers: { "Authorization": `Bearer ${connection.apiKey}` },
});
}, effectiveProxy);
return { valid: res.ok, error: res.ok ? null : "Invalid API key or base URL" };
} catch (err) {
return { valid: false, error: err.message };
@@ -316,9 +331,9 @@ async function testApiKeyConnection(connection) {
try {
modelsBase = modelsBase.replace(/\/$/, "");
if (modelsBase.endsWith("/messages")) modelsBase = modelsBase.slice(0, -9);
const res = await fetch(`${modelsBase}/models`, {
const res = await fetchWithConnectionProxy(`${modelsBase}/models`, {
headers: { "x-api-key": connection.apiKey, "anthropic-version": "2023-06-01", "Authorization": `Bearer ${connection.apiKey}` },
});
}, effectiveProxy);
return { valid: res.ok, error: res.ok ? null : "Invalid API key or base URL" };
} catch (err) {
return { valid: false, error: err.message };
@@ -328,61 +343,61 @@ async function testApiKeyConnection(connection) {
try {
switch (connection.provider) {
case "openai": {
const res = await fetch("https://api.openai.com/v1/models", { headers: { Authorization: `Bearer ${connection.apiKey}` } });
const res = await fetchWithConnectionProxy("https://api.openai.com/v1/models", { headers: { Authorization: `Bearer ${connection.apiKey}` } }, effectiveProxy);
return { valid: res.ok, error: res.ok ? null : "Invalid API key" };
}
case "anthropic": {
const res = await fetch("https://api.anthropic.com/v1/messages", {
const res = await fetchWithConnectionProxy("https://api.anthropic.com/v1/messages", {
method: "POST",
headers: { "x-api-key": connection.apiKey, "anthropic-version": "2023-06-01", "content-type": "application/json" },
body: JSON.stringify({ model: "claude-3-haiku-20240307", max_tokens: 1, messages: [{ role: "user", content: "test" }] }),
});
}, effectiveProxy);
const valid = res.status !== 401;
return { valid, error: valid ? null : "Invalid API key" };
}
case "gemini": {
const res = await fetch(`https://generativelanguage.googleapis.com/v1/models?key=${connection.apiKey}`);
const res = await fetchWithConnectionProxy(`https://generativelanguage.googleapis.com/v1/models?key=${connection.apiKey}`, {}, effectiveProxy);
return { valid: res.ok, error: res.ok ? null : "Invalid API key" };
}
case "openrouter": {
const res = await fetch("https://openrouter.ai/api/v1/auth/key", { headers: { Authorization: `Bearer ${connection.apiKey}` } });
const res = await fetchWithConnectionProxy("https://openrouter.ai/api/v1/auth/key", { headers: { Authorization: `Bearer ${connection.apiKey}` } }, effectiveProxy);
return { valid: res.ok, error: res.ok ? null : "Invalid API key" };
}
case "glm": {
const res = await fetch("https://api.z.ai/api/anthropic/v1/messages", {
const res = await fetchWithConnectionProxy("https://api.z.ai/api/anthropic/v1/messages", {
method: "POST",
headers: { "x-api-key": connection.apiKey, "anthropic-version": "2023-06-01", "content-type": "application/json" },
body: JSON.stringify({ model: "glm-4.7", max_tokens: 1, messages: [{ role: "user", content: "test" }] }),
});
}, effectiveProxy);
const valid = res.status !== 401 && res.status !== 403;
return { valid, error: valid ? null : "Invalid API key" };
}
case "glm-cn": {
const res = await fetch("https://open.bigmodel.cn/api/coding/paas/v4/chat/completions", {
const res = await fetchWithConnectionProxy("https://open.bigmodel.cn/api/coding/paas/v4/chat/completions", {
method: "POST",
headers: { "Authorization": `Bearer ${connection.apiKey}`, "content-type": "application/json" },
body: JSON.stringify({ model: "glm-4.7", max_tokens: 1, messages: [{ role: "user", content: "test" }] }),
});
}, effectiveProxy);
const valid = res.status !== 401 && res.status !== 403;
return { valid, error: valid ? null : "Invalid API key" };
}
case "minimax":
case "minimax-cn": {
const endpoints = { minimax: "https://api.minimax.io/anthropic/v1/messages", "minimax-cn": "https://api.minimaxi.com/anthropic/v1/messages" };
const res = await fetch(endpoints[connection.provider], {
const res = await fetchWithConnectionProxy(endpoints[connection.provider], {
method: "POST",
headers: { "x-api-key": connection.apiKey, "anthropic-version": "2023-06-01", "content-type": "application/json" },
body: JSON.stringify({ model: "minimax-m2", max_tokens: 1, messages: [{ role: "user", content: "test" }] }),
});
}, effectiveProxy);
const valid = res.status !== 401 && res.status !== 403;
return { valid, error: valid ? null : "Invalid API key" };
}
case "kimi": {
const res = await fetch("https://api.kimi.com/coding/v1/messages", {
const res = await fetchWithConnectionProxy("https://api.kimi.com/coding/v1/messages", {
method: "POST",
headers: { "x-api-key": connection.apiKey, "anthropic-version": "2023-06-01", "content-type": "application/json" },
body: JSON.stringify({ model: "kimi-latest", max_tokens: 1, messages: [{ role: "user", content: "test" }] }),
});
}, effectiveProxy);
const valid = res.status !== 401 && res.status !== 403;
return { valid, error: valid ? null : "Invalid API key" };
}
@@ -392,80 +407,80 @@ async function testApiKeyConnection(connection) {
const aliBaseUrl = connection.provider === "alicode-intl"
? "https://coding-intl.dashscope.aliyuncs.com/v1/chat/completions"
: "https://coding.dashscope.aliyuncs.com/v1/chat/completions";
const res = await fetch(aliBaseUrl, {
const res = await fetchWithConnectionProxy(aliBaseUrl, {
method: "POST",
headers: { "Authorization": `Bearer ${connection.apiKey}`, "content-type": "application/json" },
body: JSON.stringify({ model: getDefaultModel(connection.provider), max_tokens: 1, messages: [{ role: "user", content: "test" }] }),
});
}, effectiveProxy);
const valid = res.status !== 401 && res.status !== 403;
return { valid, error: valid ? null : "Invalid API key" };
}
case "deepseek": {
const res = await fetch("https://api.deepseek.com/models", { headers: { Authorization: `Bearer ${connection.apiKey}` } });
const res = await fetchWithConnectionProxy("https://api.deepseek.com/models", { headers: { Authorization: `Bearer ${connection.apiKey}` } }, effectiveProxy);
return { valid: res.ok, error: res.ok ? null : "Invalid API key" };
}
case "groq": {
const res = await fetch("https://api.groq.com/openai/v1/models", { headers: { Authorization: `Bearer ${connection.apiKey}` } });
const res = await fetchWithConnectionProxy("https://api.groq.com/openai/v1/models", { headers: { Authorization: `Bearer ${connection.apiKey}` } }, effectiveProxy);
return { valid: res.ok, error: res.ok ? null : "Invalid API key" };
}
case "mistral": {
const res = await fetch("https://api.mistral.ai/v1/models", { headers: { Authorization: `Bearer ${connection.apiKey}` } });
const res = await fetchWithConnectionProxy("https://api.mistral.ai/v1/models", { headers: { Authorization: `Bearer ${connection.apiKey}` } }, effectiveProxy);
return { valid: res.ok, error: res.ok ? null : "Invalid API key" };
}
case "xai": {
const res = await fetch("https://api.x.ai/v1/models", { headers: { Authorization: `Bearer ${connection.apiKey}` } });
const res = await fetchWithConnectionProxy("https://api.x.ai/v1/models", { headers: { Authorization: `Bearer ${connection.apiKey}` } }, effectiveProxy);
return { valid: res.ok, error: res.ok ? null : "Invalid API key" };
}
case "nvidia": {
const res = await fetch("https://integrate.api.nvidia.com/v1/models", { headers: { Authorization: `Bearer ${connection.apiKey}` } });
const res = await fetchWithConnectionProxy("https://integrate.api.nvidia.com/v1/models", { headers: { Authorization: `Bearer ${connection.apiKey}` } }, effectiveProxy);
return { valid: res.ok, error: res.ok ? null : "Invalid API key" };
}
case "perplexity": {
const res = await fetch("https://api.perplexity.ai/models", { headers: { Authorization: `Bearer ${connection.apiKey}` } });
const res = await fetchWithConnectionProxy("https://api.perplexity.ai/models", { headers: { Authorization: `Bearer ${connection.apiKey}` } }, effectiveProxy);
return { valid: res.ok, error: res.ok ? null : "Invalid API key" };
}
case "together": {
const res = await fetch("https://api.together.xyz/v1/models", { headers: { Authorization: `Bearer ${connection.apiKey}` } });
const res = await fetchWithConnectionProxy("https://api.together.xyz/v1/models", { headers: { Authorization: `Bearer ${connection.apiKey}` } }, effectiveProxy);
return { valid: res.ok, error: res.ok ? null : "Invalid API key" };
}
case "fireworks": {
const res = await fetch("https://api.fireworks.ai/inference/v1/models", { headers: { Authorization: `Bearer ${connection.apiKey}` } });
const res = await fetchWithConnectionProxy("https://api.fireworks.ai/inference/v1/models", { headers: { Authorization: `Bearer ${connection.apiKey}` } }, effectiveProxy);
return { valid: res.ok, error: res.ok ? null : "Invalid API key" };
}
case "cerebras": {
const res = await fetch("https://api.cerebras.ai/v1/models", { headers: { Authorization: `Bearer ${connection.apiKey}` } });
const res = await fetchWithConnectionProxy("https://api.cerebras.ai/v1/models", { headers: { Authorization: `Bearer ${connection.apiKey}` } }, effectiveProxy);
return { valid: res.ok, error: res.ok ? null : "Invalid API key" };
}
case "cohere": {
const res = await fetch("https://api.cohere.ai/v1/models", { headers: { Authorization: `Bearer ${connection.apiKey}` } });
const res = await fetchWithConnectionProxy("https://api.cohere.ai/v1/models", { headers: { Authorization: `Bearer ${connection.apiKey}` } }, effectiveProxy);
return { valid: res.ok, error: res.ok ? null : "Invalid API key" };
}
case "nebius": {
const res = await fetch("https://api.studio.nebius.ai/v1/models", { headers: { Authorization: `Bearer ${connection.apiKey}` } });
const res = await fetchWithConnectionProxy("https://api.studio.nebius.ai/v1/models", { headers: { Authorization: `Bearer ${connection.apiKey}` } }, effectiveProxy);
return { valid: res.ok, error: res.ok ? null : "Invalid API key" };
}
case "siliconflow": {
const res = await fetch("https://api.siliconflow.cn/v1/models", { headers: { Authorization: `Bearer ${connection.apiKey}` } });
const res = await fetchWithConnectionProxy("https://api.siliconflow.cn/v1/models", { headers: { Authorization: `Bearer ${connection.apiKey}` } }, effectiveProxy);
return { valid: res.ok, error: res.ok ? null : "Invalid API key" };
}
case "hyperbolic": {
const res = await fetch("https://api.hyperbolic.xyz/v1/models", { headers: { Authorization: `Bearer ${connection.apiKey}` } });
const res = await fetchWithConnectionProxy("https://api.hyperbolic.xyz/v1/models", { headers: { Authorization: `Bearer ${connection.apiKey}` } }, effectiveProxy);
return { valid: res.ok, error: res.ok ? null : "Invalid API key" };
}
case "deepgram": {
const res = await fetch("https://api.deepgram.com/v1/projects", { headers: { Authorization: `Token ${connection.apiKey}` } });
const res = await fetchWithConnectionProxy("https://api.deepgram.com/v1/projects", { headers: { Authorization: `Token ${connection.apiKey}` } }, effectiveProxy);
return { valid: res.ok, error: res.ok ? null : "Invalid API key" };
}
case "assemblyai": {
const res = await fetch("https://api.assemblyai.com/v1/account", { headers: { Authorization: `Bearer ${connection.apiKey}` } });
const res = await fetchWithConnectionProxy("https://api.assemblyai.com/v1/account", { headers: { Authorization: `Bearer ${connection.apiKey}` } }, effectiveProxy);
return { valid: res.ok, error: res.ok ? null : "Invalid API key" };
}
case "nanobanana": {
const res = await fetch("https://api.nanobananaapi.ai/v1/models", { headers: { Authorization: `Bearer ${connection.apiKey}` } });
const res = await fetchWithConnectionProxy("https://api.nanobananaapi.ai/v1/models", { headers: { Authorization: `Bearer ${connection.apiKey}` } }, effectiveProxy);
return { valid: res.ok, error: res.ok ? null : "Invalid API key" };
}
case "chutes": {
const res = await fetch("https://llm.chutes.ai/v1/models", { headers: { Authorization: `Bearer ${connection.apiKey}` } });
const res = await fetchWithConnectionProxy("https://llm.chutes.ai/v1/models", { headers: { Authorization: `Bearer ${connection.apiKey}` } }, effectiveProxy);
return { valid: res.ok, error: res.ok ? null : "Invalid API key" };
}
default:
@@ -483,13 +498,28 @@ export async function testSingleConnection(id) {
const connection = await getProviderConnectionById(id);
if (!connection) return { valid: false, error: "Connection not found", latencyMs: 0, testedAt: new Date().toISOString() };
const effectiveProxy = await resolveConnectionProxyConfig(connection.providerSpecificData || {});
if (effectiveProxy.connectionProxyEnabled && effectiveProxy.connectionProxyUrl) {
const proxyResult = await testProxyUrl({ proxyUrl: effectiveProxy.connectionProxyUrl });
if (!proxyResult.ok) {
const proxyError = proxyResult.error || `Proxy test failed with status ${proxyResult.status}`;
await updateProviderConnection(id, {
testStatus: "error",
lastError: proxyError,
lastErrorAt: new Date().toISOString(),
});
return { valid: false, error: proxyError, latencyMs: 0, testedAt: new Date().toISOString() };
}
}
const start = Date.now();
let result;
if (connection.authType === "apikey") {
result = await testApiKeyConnection(connection);
result = await testApiKeyConnection(connection, effectiveProxy);
} else {
result = await testOAuthConnection(connection);
result = await testOAuthConnection(connection, effectiveProxy);
}
const latencyMs = Date.now() - start;

View File

@@ -1,10 +1,50 @@
import { NextResponse } from "next/server";
import { getProviderConnections, createProviderConnection, getProviderNodeById, getProviderNodes } from "@/models";
import {
getProviderConnections,
createProviderConnection,
getProviderNodeById,
getProviderNodes,
getProxyPoolById,
} from "@/models";
import { APIKEY_PROVIDERS } from "@/shared/constants/config";
import { isOpenAICompatibleProvider, isAnthropicCompatibleProvider } from "@/shared/constants/providers";
export const dynamic = "force-dynamic";
function normalizeProxyConfig(body = {}) {
const enabled = body?.connectionProxyEnabled === true;
const url = typeof body?.connectionProxyUrl === "string" ? body.connectionProxyUrl.trim() : "";
const noProxy = typeof body?.connectionNoProxy === "string" ? body.connectionNoProxy.trim() : "";
if (enabled && !url) {
return { error: "Connection proxy URL is required when connection proxy is enabled" };
}
return {
connectionProxyEnabled: enabled,
connectionProxyUrl: url,
connectionNoProxy: noProxy,
};
}
async function normalizeProxyPoolId(proxyPoolId) {
if (proxyPoolId === undefined || proxyPoolId === null || proxyPoolId === "" || proxyPoolId === "__none__") {
return { proxyPoolId: null };
}
const normalizedId = String(proxyPoolId).trim();
if (!normalizedId) {
return { proxyPoolId: null };
}
const proxyPool = await getProxyPoolById(normalizedId);
if (!proxyPool) {
return { error: "Proxy pool not found" };
}
return { proxyPoolId: normalizedId };
}
// GET /api/providers - List all connections
export async function GET() {
try {
@@ -47,6 +87,16 @@ export async function POST(request) {
try {
const body = await request.json();
const { provider, apiKey, name, priority, globalPriority, defaultModel, testStatus } = body;
const proxyConfig = normalizeProxyConfig(body);
if (proxyConfig.error) {
return NextResponse.json({ error: proxyConfig.error }, { status: 400 });
}
const proxyPoolResult = await normalizeProxyPoolId(body.proxyPoolId);
if (proxyPoolResult.error) {
return NextResponse.json({ error: proxyPoolResult.error }, { status: 400 });
}
const proxyPoolId = proxyPoolResult.proxyPoolId;
// Validation
const isValidProvider = APIKEY_PROVIDERS[provider] ||
@@ -100,6 +150,17 @@ export async function POST(request) {
};
}
const mergedProviderSpecificData = {
...(providerSpecificData || {}),
connectionProxyEnabled: proxyConfig.connectionProxyEnabled,
connectionProxyUrl: proxyConfig.connectionProxyUrl,
connectionNoProxy: proxyConfig.connectionNoProxy,
};
if (proxyPoolId !== null) {
mergedProviderSpecificData.proxyPoolId = proxyPoolId;
}
const newConnection = await createProviderConnection({
provider,
authType: "apikey",
@@ -108,7 +169,7 @@ export async function POST(request) {
priority: priority || 1,
globalPriority: globalPriority || null,
defaultModel: defaultModel || null,
providerSpecificData,
providerSpecificData: mergedProviderSpecificData,
isActive: true,
testStatus: testStatus || "unknown",
});