freegate 0.6.22 → 0.6.23

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/compactor.js CHANGED
@@ -17,14 +17,19 @@ const KEEP_RECENT_TOKENS = 30000; // tokens — recent messages kept untouched (
17
17
  const MAX_COMPACT_THRESHOLD = 100000; // est tokens — cap for window-aware thresholds (≈200k real,
18
18
  // sits safely inside even 512k/1M windows without compacting too early)
19
19
  const SUMMARY_MAX_TOKENS = 2000; // tokens — summary length cap (detailed enough to not lose the work)
20
- const CHARS_PER_TOKEN = 1.5; // heuristic chars→tokens, DOUBLY conservative: real sessions
21
- // (with tool outputs, files, big system prompts) average ~0.75
22
- // chars/token, not ~4. Using 1.5 keeps us from compacting too late.
23
-
24
- // Reliability-ordered long-context summarizers (OpenRouter + HF).
20
+ const CHARS_PER_TOKEN = 1.0; // heuristic chars→tokens. Прежнее 1.5 ДВАЖДЫ завышало
21
+ // оценку (реально ~0.75-0.8 chars/token) компакции
22
+ // срабатывали почти на каждый второй запрос (53%),
23
+ // каждый — доп. LLM-вызов = медленно + риск «замирания».
24
+ // 1.0 всё ещё с запасом, но почти вдвое меньше лишних
25
+ // компакций. keep-recent и оконные пороги не меняем.
26
+
27
+ // Reliability-ordered long-context summarizers. Порядок важен: getSummary идёт
28
+ // по списку и ждёт ПЕРВЫЙ отвечающий (до 30с). Медленные/непроверенные в начале
29
+ // = «думает и замирает» (компакция тормозит каждый вызов). Бытрые и надёжные вперёд.
25
30
  const SUMMARIZERS = [
26
- 'or-nemotron-35', 'or-dots-3', 'or-minimax-m2-7-free',
27
- 'or-lfm', 'deepseek',
31
+ 'or-dots-3', 'deepseek', 'or-minimax-m2-7-free',
32
+ 'or-lfm', 'or-nemotron-35',
28
33
  ];
29
34
 
30
35
  // Summary cache: hash of compacted messages → summary string.
@@ -205,7 +210,7 @@ async function summarizeViaChain(messages) {
205
210
  { role: 'user', content: 'Сожми следующий диалог:\n\n' + textToSummarize },
206
211
  ],
207
212
  max_tokens: SUMMARY_MAX_TOKENS,
208
- }, 30000, 1);
213
+ }, 15000, 1); // 15с на summarizer — быстрое переползание, без «замирания»
209
214
  const summary = result.data?.choices?.[0]?.message?.content;
210
215
  if (summary && summary.trim().length >= 10) {
211
216
  return summary.trim();
package/lib/dashboard.js CHANGED
@@ -1112,7 +1112,7 @@ const HTML = `<!DOCTYPE html>
1112
1112
  var st = health[k] || {};
1113
1113
  return '<tr><td class="mono">' + esc(k) + '</td><td>' + errors[k] + '</td><td>' + esc(st.status || '?') + '</td><td>' + esc(st.reason || st.latency_ms || '') + '</td></tr>';
1114
1114
  }).sort(function (a, b) {
1115
- var ma = a.match(/<td>(\d+)<\/td>/), mb = b.match(/<td>(\d+)<\/td>/);
1115
+ var ma = a.match(/<td>(\\d+)<\\/td>/), mb = b.match(/<td>(\\d+)<\\/td>/);
1116
1116
  return (mb ? parseInt(mb[1], 10) : 0) - (ma ? parseInt(ma[1], 10) : 0);
1117
1117
  });
1118
1118
  document.getElementById('diagErrorsTable').innerHTML = rows.length
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "freegate",
3
- "version": "0.6.22",
3
+ "version": "0.6.23",
4
4
  "description": "Free multi-provider LLM gateway with automatic failover. One OpenAI-compatible endpoint routes to many free models (Groq, Mistral, Gemini, NIM, OpenRouter, ZAI, Cerebras, DeepSeek and more). Never pay for LLMs.",
5
5
  "license": "MIT",
6
6
  "bin": {
package/server.js CHANGED
@@ -595,6 +595,17 @@ async function handleChatCompletion(req, res, body) {
595
595
  .filter(([_, p]) => p.enabled && !isCircuitOpen(p.key) && p.vision !== true &&
596
596
  (getHealth()[p.key]?.status === 'up' || getHealth()[p.key]?.status === 'ratelimited'));
597
597
 
598
+ // Анти-«замирание»: провайдер, накопивший много ошибок сегодня (status остаётся
599
+ // 'up' — это не 429/401, а флап/долгие таймауты), по-прежнему попадает в цепочку
600
+ // и тормозит ответ. Исключаем его из активного пула до конца дня. Если так пул
601
+ // пустеет (все «ошибочные») — вернём их как крайний резерв, чтобы не отдать 503.
602
+ const todayErrors = getStats().errors || {};
603
+ const ERROR_POOL_THRESHOLD = 15;
604
+ const lowErrorProviders = healthyProviders.filter(
605
+ ([k]) => (todayErrors[k] || 0) < ERROR_POOL_THRESHOLD
606
+ );
607
+ const healthy2 = lowErrorProviders.length > 0 ? lowErrorProviders : healthyProviders;
608
+
598
609
  // Window-aware routing: estimate the request size and only consider providers
599
610
  // whose context window can actually hold it. This stops large requests from
600
611
  // burning time falling through lfm (65k) / groq (131k) providers that reject
@@ -603,10 +614,10 @@ async function handleChatCompletion(req, res, body) {
603
614
  // requests keep the full fast pool.
604
615
  const requestTokens = estimateTokens(body.messages);
605
616
  const MIN_WINDOW = 50000; // below this we don't filter (typical requests)
606
- let windowPool = healthyProviders;
617
+ let windowPool = healthy2;
607
618
  let upgradeNoCapable = false; // апгрейд, но ни один здоровый провайдер не держит запрос
608
619
  if (requestTokens > MIN_WINDOW || windowUpgraded) {
609
- const capable = healthyProviders.filter(([_, p]) => {
620
+ const capable = healthy2.filter(([_, p]) => {
610
621
  const win = p.context_window || 0;
611
622
  // Unknown/0 window providers are kept (heuristic) — better to try than drop.
612
623
  return win === 0 || win >= requestTokens;