freegate 0.6.14 → 0.6.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/bandit.js +8 -1
- package/package.json +1 -1
- package/server.js +7 -6
package/lib/bandit.js
CHANGED
|
@@ -69,4 +69,11 @@ function recordOutcome(bucketPriors, key, success) {
|
|
|
69
69
|
else bucketPriors[key].b += 1;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
|
|
72
|
+
// Временные лимиты (429/403/402/401) — это НЕ качество провайдера, а временное
|
|
73
|
+
// состояние (лимит запросов, квота, баланс). Bandit не должен наказывать за них.
|
|
74
|
+
const TRANSIENT_LIMITS = new Set([401, 402, 403, 408, 429]);
|
|
75
|
+
function isTransientLimit(status) {
|
|
76
|
+
return typeof status === 'number' && TRANSIENT_LIMITS.has(status);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
module.exports = { bucket, sampleBeta, pick, recordOutcome, BUCKET_THRESHOLDS, isTransientLimit };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "freegate",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.15",
|
|
4
4
|
"description": "Free multi-provider LLM gateway with automatic failover. One OpenAI-compatible endpoint routes to 25 free models (Groq, Mistral, Gemini, NIM, OpenRouter, ZAI, Cerebras, DeepSeek). Never pay for LLMs.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|
package/server.js
CHANGED
|
@@ -10,7 +10,7 @@ const { handleDashboard } = require('./lib/dashboard');
|
|
|
10
10
|
const { acquire, stats: poolStats } = require('./lib/pool');
|
|
11
11
|
const { stripThink, cleanDelta, cleanMessage, fixReasoningMessage, isTooShort, MIN_ANSWER_LEN } = require('./lib/clean');
|
|
12
12
|
const { classifyComplexity, maybeUpgradeTier, classifyVisionComplexity } = require('./lib/routing');
|
|
13
|
-
const { bucket, pick: banditPick } = require('./lib/bandit');
|
|
13
|
+
const { bucket, pick: banditPick, isTransientLimit } = require('./lib/bandit');
|
|
14
14
|
const logger = require('./lib/logger');
|
|
15
15
|
|
|
16
16
|
// Load persisted state
|
|
@@ -441,7 +441,7 @@ async function handleChatCompletion(req, res, body) {
|
|
|
441
441
|
});
|
|
442
442
|
result.stream.on('error', (err) => {
|
|
443
443
|
logger.error('Stream error', { key, error: err.message });
|
|
444
|
-
recordBandit(complexityBucket, key, false);
|
|
444
|
+
if (!isTransientLimit(err.statusCode)) recordBandit(complexityBucket, key, false);
|
|
445
445
|
res.end();
|
|
446
446
|
});
|
|
447
447
|
result.stream.pipe(cleaner).pipe(res);
|
|
@@ -527,7 +527,7 @@ async function handleChatCompletion(req, res, body) {
|
|
|
527
527
|
});
|
|
528
528
|
result.stream.on('error', (err) => {
|
|
529
529
|
logger.error('Stream error', { key, error: err.message });
|
|
530
|
-
recordBandit(complexityBucket, key, false);
|
|
530
|
+
if (!isTransientLimit(err.statusCode)) recordBandit(complexityBucket, key, false);
|
|
531
531
|
res.end();
|
|
532
532
|
});
|
|
533
533
|
return;
|
|
@@ -551,7 +551,8 @@ async function handleChatCompletion(req, res, body) {
|
|
|
551
551
|
const statusCode = err.statusCode || 502;
|
|
552
552
|
errors.push(err.message);
|
|
553
553
|
recordRequest(key, false, err.message);
|
|
554
|
-
|
|
554
|
+
// Временные лимиты (429/403/402) — не наказываем провайдера в bandit.
|
|
555
|
+
if (!isTransientLimit(statusCode)) recordBandit(complexityBucket, key, false);
|
|
555
556
|
recordRecent({ model: requestedModel, provider: key, status: statusCode, latency: 0, cached: false });
|
|
556
557
|
initHealth(key);
|
|
557
558
|
// Do NOT flip provider to 'error' on a single failed request — transient
|
|
@@ -672,14 +673,14 @@ async function handleChatCompletion(req, res, body) {
|
|
|
672
673
|
});
|
|
673
674
|
result.stream.on('error', (err) => {
|
|
674
675
|
logger.error('Stream error (retry)', { key, error: err.message });
|
|
675
|
-
recordBandit(complexityBucket, key, false);
|
|
676
|
+
if (!isTransientLimit(err.statusCode)) recordBandit(complexityBucket, key, false);
|
|
676
677
|
res.end();
|
|
677
678
|
});
|
|
678
679
|
return;
|
|
679
680
|
}
|
|
680
681
|
} catch (err2) {
|
|
681
682
|
recordRequest(key, false, err2.message);
|
|
682
|
-
recordBandit(complexityBucket, key, false);
|
|
683
|
+
if (!isTransientLimit(err2.statusCode)) recordBandit(complexityBucket, key, false);
|
|
683
684
|
recordFailure(key, err2.statusCode);
|
|
684
685
|
}
|
|
685
686
|
}
|