freegate 0.6.4 → 0.6.6
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/health.js +13 -0
- package/package.json +1 -1
- package/server.js +4 -2
package/lib/health.js
CHANGED
|
@@ -8,6 +8,7 @@ const STATE_PATH = path.join(__dirname, '..', 'state.json');
|
|
|
8
8
|
let health = {};
|
|
9
9
|
let circuitBreakers = {};
|
|
10
10
|
let stats = { totalRequests: 0, successfulRequests: 0, failedRequests: 0, providerUsage: {}, errors: {}, startTime: Date.now(), tokenUsage: {} };
|
|
11
|
+
let lastSelection = null;
|
|
11
12
|
|
|
12
13
|
const MAX_RECENT = 50;
|
|
13
14
|
let recent = [];
|
|
@@ -37,6 +38,17 @@ function loadState() {
|
|
|
37
38
|
}
|
|
38
39
|
}
|
|
39
40
|
|
|
41
|
+
function recordSelection(providerKey, modelName, requestedModel) {
|
|
42
|
+
lastSelection = {
|
|
43
|
+
provider: providerKey,
|
|
44
|
+
model: modelName,
|
|
45
|
+
requested: requestedModel,
|
|
46
|
+
at: Date.now(),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function getLastSelection() { return lastSelection; }
|
|
51
|
+
|
|
40
52
|
function saveState() {
|
|
41
53
|
try {
|
|
42
54
|
fs.writeFileSync(STATE_PATH, JSON.stringify({ health, circuitBreakers, stats }, null, 2));
|
|
@@ -175,5 +187,6 @@ module.exports = {
|
|
|
175
187
|
getHealth, getStats,
|
|
176
188
|
recordRecent, recordRpm, getRecent, getRpm,
|
|
177
189
|
getDailyUsage, getReliability,
|
|
190
|
+
recordSelection, getLastSelection,
|
|
178
191
|
_stopTimers,
|
|
179
192
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "freegate",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.6",
|
|
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
|
@@ -4,7 +4,7 @@ const fs = require('fs');
|
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const { LRUCache } = require('./lib/cache');
|
|
6
6
|
const { PROVIDERS, MODEL_MAP, callProvider } = require('./lib/providers');
|
|
7
|
-
const { loadState, initHealth, isCircuitOpen, recordSuccess, recordFailure, recordRequest, recordTokens, getHealth, getStats, recordRecent, recordRpm, getRecent, getRpm } = require('./lib/health');
|
|
7
|
+
const { loadState, initHealth, isCircuitOpen, recordSuccess, recordFailure, recordRequest, recordTokens, getHealth, getStats, recordRecent, recordRpm, getRecent, getRpm, recordSelection, getLastSelection } = require('./lib/health');
|
|
8
8
|
const { checkRateLimit } = require('./lib/rateLimit');
|
|
9
9
|
const { handleDashboard } = require('./lib/dashboard');
|
|
10
10
|
const { acquire, stats: poolStats } = require('./lib/pool');
|
|
@@ -170,7 +170,7 @@ async function handleChatCompletion(req, res, body) {
|
|
|
170
170
|
{ type: 'text', text: 'Распознай и извлеки ВЕСЬ текст с изображения (ошибка, код, сообщение). Верни только содержимое, без комментариев. Если это код — верни код как есть.' },
|
|
171
171
|
...(Array.isArray(body.messages) ? body.messages.flatMap((m) => (Array.isArray(m.content) ? m.content.filter((c) => c && (c.type === 'image_url' || c.type === 'image' || c.type === 'input_image')).map((c) => {
|
|
172
172
|
// Normalize any image part to the universal image_url format
|
|
173
|
-
const url = c.image_url?.url || c.image?.url || (c.image && typeof c.image === 'string' ? c.image : null) || c.url;
|
|
173
|
+
const url = c.image_url?.url || c.image?.url || c.image?.data || (c.image && typeof c.image === 'string' ? c.image : null) || c.url;
|
|
174
174
|
return url ? { type: 'image_url', image_url: { url } } : null;
|
|
175
175
|
}).filter(Boolean) : [])) : []),
|
|
176
176
|
],
|
|
@@ -339,6 +339,7 @@ async function handleChatCompletion(req, res, body) {
|
|
|
339
339
|
recordRequest(key, true);
|
|
340
340
|
logger.request({ model: requestedModel, provider: key, status: 200, latency: result.latency, stream: isStreaming });
|
|
341
341
|
recordRecent({ model: requestedModel, provider: key, status: 200, latency: result.latency, cached: false });
|
|
342
|
+
recordSelection(key, provider.model, requestedModel);
|
|
342
343
|
|
|
343
344
|
if (isStreaming && result.stream) {
|
|
344
345
|
res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive' });
|
|
@@ -550,6 +551,7 @@ const server = http.createServer(async (req, res) => {
|
|
|
550
551
|
cache: cache.stats(),
|
|
551
552
|
limits,
|
|
552
553
|
pool: poolStats(),
|
|
554
|
+
last_selection: getLastSelection(),
|
|
553
555
|
}));
|
|
554
556
|
return;
|
|
555
557
|
}
|