feat(proxy-fitness): pool fitness registry + Smart rotation + pool-scoped retry

Pool/IP fitness registry (globalThis-backed, provider::model scopes, 5-min
cooldown, provider::* wildcard) fed by pool-scoped failures: freebuff
limited-IP / model-locked gates and opencode free per-IP limits. chatCore
retries a failed pool via another pool without locking the account; new
Smart rotation strategy (per-connection + no-auth providers) skips unfit
pools. Proxy Fitness dashboard page: active-block table with provider/IP
filters, per-record Clear and provider-scoped Clear All; egress column via
pool geo enrichment. Unit tests for the registry.
This commit is contained in:
MUH. IQRAM BAHRING
2026-08-07 03:51:19 +08:00
parent 90ff8191ff
commit fd22290a34
17 changed files with 1000 additions and 35 deletions
+22 -3
View File
@@ -47,10 +47,16 @@ export async function getProviderCredentials(provider, excludeConnectionIds = nu
const override = (settings.providerStrategies || {})[providerId] || {};
const strategy = override.rotateStrategy || "none";
let pickedId = override.proxyPoolId || null;
let poolIds = [];
if (strategy !== "none") {
const allPools = await getProxyPools({ isActive: true });
const poolIds = allPools.filter(p => p.proxyUrl).map(p => p.id);
pickedId = pickProxyPoolId(poolIds, strategy, providerId);
poolIds = allPools.filter(p => p.proxyUrl).map(p => p.id);
// Scope region-aware ("smart") filtering to this provider/model so
// pools marked unfit here are skipped.
const scope = `${providerId}::${model || "*"}`;
pickedId = pickProxyPoolId(poolIds, strategy, providerId, { scope });
} else if (override.proxyPoolId) {
poolIds = [override.proxyPoolId];
}
const resolvedProxy = await resolveConnectionProxyConfig({ proxyPoolId: pickedId || "" });
return {
@@ -64,6 +70,13 @@ export async function getProviderCredentials(provider, excludeConnectionIds = nu
connectionNoProxy: resolvedProxy.connectionNoProxy,
connectionProxyPoolId: resolvedProxy.proxyPoolId || null,
vercelRelayUrl: resolvedProxy.vercelRelayUrl || "",
proxyPoolId: resolvedProxy.proxyPoolId || null,
strictProxy: resolvedProxy.strictProxy === true,
// Let chatCore's pool-scoped retry rotate across the same candidate
// pool set (excluding the failed pool) instead of reusing it — this
// is what makes per-IP limit retries work for no-auth providers.
proxyPoolIds: poolIds,
proxyRotationStrategy: strategy,
},
};
}
@@ -172,7 +185,11 @@ export async function getProviderCredentials(provider, excludeConnectionIds = nu
connection = availableConnections[0];
}
const resolvedProxy = await resolveConnectionProxyConfig(connection.providerSpecificData || {}, connection.id);
// Scope the region-aware picker to this provider/model (e.g. freebuff::gpt-5.6-luna)
const psdForProxy = connection.providerSpecificData?.proxyPoolIds?.length
? { ...connection.providerSpecificData, proxyPoolScope: `${providerId}::${model || ""}` }
: connection.providerSpecificData;
const resolvedProxy = await resolveConnectionProxyConfig(psdForProxy || {}, connection.id);
return {
authType: connection.authType,
@@ -193,6 +210,8 @@ export async function getProviderCredentials(provider, excludeConnectionIds = nu
connectionNoProxy: resolvedProxy.connectionNoProxy,
connectionProxyPoolId: resolvedProxy.proxyPoolId || null,
vercelRelayUrl: resolvedProxy.vercelRelayUrl || "",
proxyPoolId: resolvedProxy.proxyPoolId || null,
strictProxy: resolvedProxy.strictProxy === true,
},
connectionId: connection.id,
// Include current status for optimization check