freegate 0.6.0

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.
@@ -0,0 +1,215 @@
1
+ // lib/providers.js
2
+ const https = require('https');
3
+ const http = require('http');
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+ const logger = require('./logger');
7
+
8
+ // Load .env if present — prefer cwd (user's project) then package dir
9
+ const ENV_CANDIDATES = [path.join(process.cwd(), '.env'), path.join(__dirname, '..', '.env')];
10
+ for (const envPath of ENV_CANDIDATES) {
11
+ try {
12
+ if (fs.existsSync(envPath)) {
13
+ for (const line of fs.readFileSync(envPath, 'utf8').split('\n')) {
14
+ const trimmed = line.trim();
15
+ if (!trimmed || trimmed.startsWith('#')) continue;
16
+ const eq = trimmed.indexOf('=');
17
+ if (eq > 0) process.env[trimmed.slice(0, eq).trim()] = trimmed.slice(eq + 1).trim();
18
+ }
19
+ }
20
+ } catch {}
21
+ }
22
+
23
+ // Config candidates: cwd first (user's project), then package dir
24
+ const CONFIG_CANDIDATES = [path.join(process.cwd(), 'config.json'), path.join(__dirname, '..', 'config.json')];
25
+ const CONFIG_PATH = CONFIG_CANDIDATES.find(p => fs.existsSync(p)) || CONFIG_CANDIDATES[1];
26
+
27
+ const PROVIDERS_CATALOG_PATH = path.join(__dirname, '..', 'providers.json');
28
+ const DEFAULT_CONFIG = { port: 4000, auth: '', rateLimit: { maxRequests: 100, windowMs: 60000 }, providers: {} };
29
+
30
+ function loadConfig() {
31
+ let cfg = { ...DEFAULT_CONFIG };
32
+ // 1. Start from the catalog
33
+ try {
34
+ const catalog = JSON.parse(fs.readFileSync(PROVIDERS_CATALOG_PATH, 'utf8'));
35
+ cfg.providers = { ...catalog };
36
+ } catch { /* no catalog — empty providers */ }
37
+ // 2. Overlay user config.json
38
+ try {
39
+ const userCfg = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8'));
40
+ if (userCfg && typeof userCfg === 'object') {
41
+ cfg.port = userCfg.port || cfg.port;
42
+ cfg.auth = userCfg.auth || cfg.auth;
43
+ if (userCfg.rateLimit) cfg.rateLimit = { ...cfg.rateLimit, ...userCfg.rateLimit };
44
+ if (userCfg.providers) {
45
+ for (const [k, v] of Object.entries(userCfg.providers)) {
46
+ if (v.enabled === false) { delete cfg.providers[k]; continue; }
47
+ cfg.providers[k] = { ...(cfg.providers[k] || {}), ...v, key: k };
48
+ }
49
+ }
50
+ }
51
+ } catch (err) {
52
+ console.error(`config.json invalid, using catalog only: ${err.message}`);
53
+ }
54
+ // 3. Default catalog providers to enabled=true (they may lack the field)
55
+ for (const [k, p] of Object.entries(cfg.providers)) {
56
+ if (p.enabled === undefined) p.enabled = true;
57
+ }
58
+ return cfg;
59
+ }
60
+
61
+ const config = loadConfig();
62
+ const PROVIDERS = config.providers;
63
+
64
+ // Add key to each provider from env
65
+ for (const [key, provider] of Object.entries(PROVIDERS)) {
66
+ provider.key = key;
67
+ // Honor explicit envVar from the catalog/config first
68
+ const explicitVar = provider.envVar;
69
+ const derivedVar = `PROVIDER_${key.toUpperCase().replace(/-/g, '_')}_APIKEY`;
70
+ const prefix = key.split('-')[0].toUpperCase();
71
+ // Map known prefixes to their real env vars
72
+ const prefixMap = { OR: 'OPENROUTER', NIM: 'NIM', GROQ: 'GROQ', MISTRAL: 'MISTRAL', GEMINI: 'GEMINI', ZAI: 'ZAI' };
73
+ const resolvedPrefix = prefixMap[prefix] || prefix;
74
+ const envKey = process.env[explicitVar] || process.env[derivedVar] || process.env[`PROVIDER_${resolvedPrefix}_APIKEY`];
75
+ if (envKey) provider.apiKey = envKey;
76
+ }
77
+
78
+ const MODEL_MAP = {
79
+ 'glm-4.7-flash': 'zai', 'glm-4.5': 'zai', 'glm-4.5-air': 'zai',
80
+ 'glm-4.7': 'zai', 'glm-5': 'zai', 'glm-5-turbo': 'zai',
81
+ 'deepseek-ai/deepseek-v4-flash-0731': 'nim-deepseek',
82
+ 'meta/llama-3.1-8b-instruct': 'nim-llama',
83
+ 'openai/gpt-oss-120b': 'groq-gpt', 'qwen/qwen3.6-27b': 'groq-qwen',
84
+ 'allam-2-7b': 'groq-allam', 'groq/compound': 'groq-compound',
85
+ 'gemini-3.6-flash': 'gemini-flash',
86
+ 'codestral-latest': 'mistral-codestral', 'mistral-small-latest': 'mistral-small',
87
+ 'mistral-medium-latest': 'mistral-small', 'mistral-large-latest': 'mistral-small',
88
+ 'tier-splus': 'or-ox-alpha', 'tier-s': 'mistral-codestral',
89
+ 'tier-a': 'mistral-small', 'tier-b': 'groq-qwen',
90
+ 'tier-xl': 'or-nemotron-550b', 'tier-l': 'mistral-codestral',
91
+ 'openrouter-hermes': 'openrouter-hermes',
92
+ 'openrouter-mistral': 'openrouter-mistral',
93
+ 'nvidia/llama-3.1-nemotron-ultra-253b-v1': 'nim-nemotron',
94
+ 'cohere/north-mini-code:free': 'openrouter-hermes',
95
+ 'z-ai/glm-5.2:free': 'openrouter-mistral',
96
+ 'nvidia/nemotron-3-ultra-550b-a55b:free': 'or-nemotron-550b',
97
+ 'nvidia/nemotron-3-super-120b-a12b:free': 'or-nemotron-120b',
98
+ 'openai/gpt-oss-20b:free': 'or-gpt-oss-20b',
99
+ 'stealth/ox-alpha': 'or-ox-alpha',
100
+ 'nvidia/nemotron-3.5-lightning:free': 'or-nemotron-35',
101
+ 'poolside/laguna-s-2.1:free': 'or-laguna-s',
102
+ 'gemini-3.6-flash:vision': 'gemini-vision',
103
+ 'meta/llama-3.2-11b-vision-instruct': 'nim-vision',
104
+ 'ollama/llama3.2': 'ollama',
105
+ 'llama3.2': 'ollama',
106
+ 'ollama/qwen2.5:0.5b': 'ollama',
107
+ 'qwen2.5:0.5b': 'ollama',
108
+ 'lmstudio/local-model': 'lmstudio',
109
+ 'dots-studio/dots-3-note-preview:free': 'or-dots-3',
110
+ 'liquid/lfm-2.5-2.6b:free': 'or-lfm',
111
+ 'poolside/laguna-xs-2.1:free': 'or-laguna-xs',
112
+ 'cerebras/gpt-oss-120b': 'cerebras-gpt',
113
+ 'cerebras-gpt': 'cerebras-gpt',
114
+ 'cerebras/gemma-4-31b': 'cerebras-gemma',
115
+ 'cerebras-gemma': 'cerebras-gemma',
116
+ 'deepseek-v4-flash': 'deepseek',
117
+ 'deepseek-chat': 'deepseek',
118
+ 'deepseek-v4-flash-vision-exp': 'deepseek-vision',
119
+ };
120
+
121
+ function callProvider(provider, body, timeout = 30000, retries = 1) {
122
+ const isStreaming = body.stream === true;
123
+
124
+ return new Promise((resolve, reject) => {
125
+ const attempt = (remaining) => {
126
+ const startTime = Date.now();
127
+ const postData = JSON.stringify(body);
128
+ const url = new URL(provider.endpoint);
129
+
130
+ const options = {
131
+ hostname: url.hostname,
132
+ port: url.port || (url.protocol === 'https:' ? 443 : 80),
133
+ path: url.pathname,
134
+ method: 'POST',
135
+ timeout,
136
+ headers: {
137
+ 'Content-Type': 'application/json',
138
+ ...(provider.apiKey ? { 'Authorization': `Bearer ${provider.apiKey}` } : {}),
139
+ 'Content-Length': Buffer.byteLength(postData),
140
+ ...(isStreaming ? { 'Accept': 'text/event-stream' } : {}),
141
+ },
142
+ };
143
+
144
+ const transport = url.protocol === 'https:' ? https : http;
145
+ const req = transport.request(options, (res) => {
146
+ const latency = Date.now() - startTime;
147
+
148
+ if (res.statusCode !== 200) {
149
+ let data = '';
150
+ res.on('data', chunk => data += chunk);
151
+ res.on('end', () => {
152
+ let errorMsg;
153
+ try {
154
+ const errData = JSON.parse(data);
155
+ errorMsg = errData.error?.message || errData.message || data.slice(0, 200);
156
+ } catch {
157
+ errorMsg = data.slice(0, 200);
158
+ }
159
+ const err = new Error(`${provider.key}: ${res.statusCode} — ${errorMsg}`);
160
+ err.statusCode = res.statusCode;
161
+ err.providerKey = provider.key;
162
+ handleRetry(err, remaining);
163
+ });
164
+ return;
165
+ }
166
+
167
+ if (isStreaming) {
168
+ resolve({ stream: res, statusCode: 200, latency });
169
+ return;
170
+ }
171
+
172
+ let data = '';
173
+ res.on('data', chunk => data += chunk);
174
+ res.on('end', () => {
175
+ try {
176
+ const parsed = JSON.parse(data);
177
+ resolve({ data: parsed, statusCode: 200, latency, usage: parsed.usage });
178
+ } catch (err) {
179
+ handleRetry(new Error(`${provider.key}: Invalid JSON from provider`), remaining);
180
+ }
181
+ });
182
+ });
183
+
184
+ req.on('timeout', () => {
185
+ req.destroy();
186
+ handleRetry(new Error(`${provider.key}: timeout`), remaining);
187
+ });
188
+
189
+ req.on('error', (err) => {
190
+ handleRetry(new Error(`${provider.key}: ${err.message}`), remaining);
191
+ });
192
+
193
+ req.write(postData);
194
+ req.end();
195
+
196
+ function handleRetry(err, remaining) {
197
+ // Never retry rate limits (429) — it just burns quota faster.
198
+ // Also skip 401/404 (auth/model errors won't fix themselves).
199
+ const status = err.statusCode || 0;
200
+ const noRetry = status === 429 || status === 401 || status === 403 || status === 404;
201
+ if (remaining > 0 && !isStreaming && !noRetry) {
202
+ const delay = remaining === 2 ? 1000 : 2000;
203
+ logger.warn('Retrying provider call', { key: provider.key, attempt: retries - remaining + 2, error: err.message });
204
+ setTimeout(() => attempt(remaining - 1), delay);
205
+ } else {
206
+ reject(err);
207
+ }
208
+ }
209
+ };
210
+
211
+ attempt(retries);
212
+ });
213
+ }
214
+
215
+ module.exports = { PROVIDERS, MODEL_MAP, callProvider };
@@ -0,0 +1,16 @@
1
+ // lib/rateLimit.js
2
+ const rateLimits = {};
3
+
4
+ function checkRateLimit(serviceKey, limit, windowMs) {
5
+ const now = Date.now();
6
+ if (!rateLimits[serviceKey] || now - rateLimits[serviceKey].windowStart > windowMs) {
7
+ rateLimits[serviceKey] = { count: 1, windowStart: now };
8
+ return true;
9
+ }
10
+ rateLimits[serviceKey].count++;
11
+ return rateLimits[serviceKey].count <= limit;
12
+ }
13
+
14
+ function getStats() { return rateLimits; }
15
+
16
+ module.exports = { checkRateLimit, getStats };
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "freegate",
3
+ "version": "0.6.0",
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
+ "license": "MIT",
6
+ "bin": {
7
+ "freegate": "./bin/freegate.js"
8
+ },
9
+ "scripts": {
10
+ "start": "node server.js",
11
+ "test": "node --test --test-concurrency=1 test/proxy.test.js test/clean.test.js"
12
+ },
13
+ "engines": {
14
+ "node": ">=18"
15
+ },
16
+ "keywords": [
17
+ "llm",
18
+ "proxy",
19
+ "gateway",
20
+ "free",
21
+ "openai",
22
+ "failover",
23
+ "groq",
24
+ "mistral",
25
+ "gemini",
26
+ "deepseek",
27
+ "llm-gateway",
28
+ "multi-provider",
29
+ "self-hosted",
30
+ "vision"
31
+ ],
32
+ "files": [
33
+ "server.js",
34
+ "lib/",
35
+ "providers.json",
36
+ "config.example.json",
37
+ ".env.example",
38
+ "bin/",
39
+ "assets/",
40
+ "README.md",
41
+ "README.ru.md"
42
+ ]
43
+ }
package/providers.json ADDED
@@ -0,0 +1,298 @@
1
+ {
2
+ "groq-gpt": {
3
+ "endpoint": "https://api.groq.com/openai/v1/chat/completions",
4
+ "model": "openai/gpt-oss-120b",
5
+ "priority": 1,
6
+ "dailyLimit": 1000,
7
+ "keyHint": "console.groq.com \u2192 API Keys",
8
+ "envVar": "PROVIDER_GROQ_APIKEY",
9
+ "free": true,
10
+ "category": "general"
11
+ },
12
+ "groq-qwen": {
13
+ "endpoint": "https://api.groq.com/openai/v1/chat/completions",
14
+ "model": "qwen/qwen3.6-27b",
15
+ "priority": 2,
16
+ "dailyLimit": 1000,
17
+ "keyHint": "console.groq.com \u2192 API Keys",
18
+ "envVar": "PROVIDER_GROQ_APIKEY",
19
+ "free": true,
20
+ "category": "general"
21
+ },
22
+ "groq-allam": {
23
+ "endpoint": "https://api.groq.com/openai/v1/chat/completions",
24
+ "model": "allam-2-7b",
25
+ "priority": 3,
26
+ "dailyLimit": 1000,
27
+ "keyHint": "console.groq.com \u2192 API Keys (allam-2-7b, \u043e\u0447\u0435\u043d\u044c \u0431\u044b\u0441\u0442\u0440\u0430\u044f 229ms)",
28
+ "envVar": "PROVIDER_GROQ_APIKEY",
29
+ "free": true,
30
+ "category": "general"
31
+ },
32
+ "groq-compound": {
33
+ "endpoint": "https://api.groq.com/openai/v1/chat/completions",
34
+ "model": "groq/compound",
35
+ "priority": 12,
36
+ "dailyLimit": 1000,
37
+ "keyHint": "console.groq.com \u2192 API Keys (compound)",
38
+ "envVar": "PROVIDER_GROQ_APIKEY",
39
+ "free": true,
40
+ "category": "general"
41
+ },
42
+ "mistral-codestral": {
43
+ "endpoint": "https://api.mistral.ai/v1/chat/completions",
44
+ "model": "codestral-latest",
45
+ "priority": 3,
46
+ "dailyLimit": 500000,
47
+ "keyHint": "console.mistral.ai \u2192 API Keys",
48
+ "envVar": "PROVIDER_MISTRAL_APIKEY",
49
+ "free": true,
50
+ "category": "coding"
51
+ },
52
+ "mistral-small": {
53
+ "endpoint": "https://api.mistral.ai/v1/chat/completions",
54
+ "model": "mistral-small-latest",
55
+ "priority": 4,
56
+ "dailyLimit": 500000,
57
+ "keyHint": "console.mistral.ai \u2192 API Keys",
58
+ "envVar": "PROVIDER_MISTRAL_APIKEY",
59
+ "free": true,
60
+ "category": "general"
61
+ },
62
+ "gemini-flash": {
63
+ "endpoint": "https://generativelanguage.googleapis.com/v1beta/openai/chat/completions",
64
+ "model": "gemini-3.6-flash",
65
+ "priority": 5,
66
+ "dailyLimit": 1500,
67
+ "keyHint": "aistudio.google.com \u2192 Get API key",
68
+ "envVar": "PROVIDER_GEMINI_APIKEY",
69
+ "free": true,
70
+ "category": "general"
71
+ },
72
+ "nim-deepseek": {
73
+ "endpoint": "https://integrate.api.nvidia.com/v1/chat/completions",
74
+ "model": "deepseek-ai/deepseek-v4-flash-0731",
75
+ "priority": 6,
76
+ "dailyLimit": 40,
77
+ "keyHint": "build.nvidia.com \u2192 Get API key",
78
+ "envVar": "PROVIDER_NIM_APIKEY",
79
+ "free": true,
80
+ "category": "general"
81
+ },
82
+ "nim-llama": {
83
+ "endpoint": "https://integrate.api.nvidia.com/v1/chat/completions",
84
+ "model": "meta/llama-3.1-8b-instruct",
85
+ "priority": 7,
86
+ "dailyLimit": 40,
87
+ "keyHint": "build.nvidia.com \u2192 Get API key",
88
+ "envVar": "PROVIDER_NIM_APIKEY",
89
+ "free": true,
90
+ "category": "general"
91
+ },
92
+ "zai": {
93
+ "endpoint": "https://open.bigmodel.cn/api/paas/v4/chat/completions",
94
+ "model": "glm-4.7-flash",
95
+ "priority": 8,
96
+ "dailyLimit": 1000,
97
+ "keyHint": "open.bigmodel.cn \u2192 API Keys",
98
+ "envVar": "PROVIDER_ZAI_APIKEY",
99
+ "free": true,
100
+ "category": "general"
101
+ },
102
+ "openrouter-hermes": {
103
+ "endpoint": "https://openrouter.ai/api/v1/chat/completions",
104
+ "model": "cohere/north-mini-code:free",
105
+ "priority": 17,
106
+ "dailyLimit": 50,
107
+ "keyHint": "openrouter.ai \u2192 Keys (\u043c\u0435\u0434\u043b\u0435\u043d\u043d\u044b\u0439, \u0437\u0430\u043f\u0430\u0441\u043d\u043e\u0439)",
108
+ "envVar": "PROVIDER_OPENROUTER_APIKEY",
109
+ "free": true,
110
+ "category": "coding"
111
+ },
112
+ "openrouter-mistral": {
113
+ "endpoint": "https://openrouter.ai/api/v1/chat/completions",
114
+ "model": "z-ai/glm-5.2:free",
115
+ "priority": 10,
116
+ "dailyLimit": 50,
117
+ "keyHint": "openrouter.ai \u2192 Keys",
118
+ "envVar": "PROVIDER_OPENROUTER_APIKEY",
119
+ "free": true,
120
+ "category": "general"
121
+ },
122
+ "or-nemotron-550b": {
123
+ "endpoint": "https://openrouter.ai/api/v1/chat/completions",
124
+ "model": "nvidia/nemotron-3-ultra-550b-a55b:free",
125
+ "priority": 11,
126
+ "dailyLimit": 50,
127
+ "keyHint": "openrouter.ai \u2192 Keys",
128
+ "envVar": "PROVIDER_OPENROUTER_APIKEY",
129
+ "free": true,
130
+ "category": "reasoning"
131
+ },
132
+ "or-nemotron-120b": {
133
+ "endpoint": "https://openrouter.ai/api/v1/chat/completions",
134
+ "model": "nvidia/nemotron-3-super-120b-a12b:free",
135
+ "priority": 12,
136
+ "dailyLimit": 50,
137
+ "keyHint": "openrouter.ai \u2192 Keys",
138
+ "envVar": "PROVIDER_OPENROUTER_APIKEY",
139
+ "free": true,
140
+ "category": "reasoning"
141
+ },
142
+ "or-gpt-oss-20b": {
143
+ "endpoint": "https://openrouter.ai/api/v1/chat/completions",
144
+ "model": "openai/gpt-oss-20b:free",
145
+ "priority": 13,
146
+ "dailyLimit": 50,
147
+ "keyHint": "openrouter.ai \u2192 Keys",
148
+ "envVar": "PROVIDER_OPENROUTER_APIKEY",
149
+ "free": true,
150
+ "category": "general"
151
+ },
152
+ "or-ox-alpha": {
153
+ "endpoint": "https://openrouter.ai/api/v1/chat/completions",
154
+ "model": "stealth/ox-alpha",
155
+ "priority": 3,
156
+ "dailyLimit": 100,
157
+ "keyHint": "openrouter.ai \u2192 Keys (Ox Alpha: 1M \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442, reasoning, \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u043e)",
158
+ "envVar": "PROVIDER_OPENROUTER_APIKEY",
159
+ "free": true,
160
+ "reasoning": true,
161
+ "category": "reasoning"
162
+ },
163
+ "or-nemotron-35": {
164
+ "endpoint": "https://openrouter.ai/api/v1/chat/completions",
165
+ "model": "nvidia/nemotron-3.5-lightning:free",
166
+ "priority": 14,
167
+ "dailyLimit": 100,
168
+ "keyHint": "openrouter.ai \u2192 Keys (Nemotron 3.5 Lightning: 977K \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442)",
169
+ "envVar": "PROVIDER_OPENROUTER_APIKEY",
170
+ "free": true,
171
+ "category": "reasoning"
172
+ },
173
+ "or-laguna-s": {
174
+ "endpoint": "https://openrouter.ai/api/v1/chat/completions",
175
+ "model": "poolside/laguna-s-2.1:free",
176
+ "priority": 15,
177
+ "dailyLimit": 100,
178
+ "keyHint": "openrouter.ai \u2192 Keys (Laguna S: \u043a\u043e\u0434\u0438\u043d\u0433, 118B)",
179
+ "envVar": "PROVIDER_OPENROUTER_APIKEY",
180
+ "free": true,
181
+ "category": "coding"
182
+ },
183
+ "gemini-vision": {
184
+ "endpoint": "https://generativelanguage.googleapis.com/v1beta/openai/chat/completions",
185
+ "model": "gemini-3.6-flash",
186
+ "priority": 5,
187
+ "dailyLimit": 1500,
188
+ "keyHint": "aistudio.google.com \u2192 Get API key (vision: \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0441\u043e \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442\u0430\u043c\u0438)",
189
+ "envVar": "PROVIDER_GEMINI_APIKEY",
190
+ "free": true,
191
+ "vision": true,
192
+ "category": "vision"
193
+ },
194
+ "nim-vision": {
195
+ "endpoint": "https://integrate.api.nvidia.com/v1/chat/completions",
196
+ "model": "meta/llama-3.2-11b-vision-instruct",
197
+ "priority": 18,
198
+ "dailyLimit": 40,
199
+ "keyHint": "build.nvidia.com \u2192 Get API key (vision, \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u044b\u0439, \u0437\u0430\u043f\u0430\u0441\u043d\u043e\u0439)",
200
+ "envVar": "PROVIDER_NIM_APIKEY",
201
+ "free": true,
202
+ "vision": true,
203
+ "category": "vision"
204
+ },
205
+ "ollama": {
206
+ "endpoint": "http://localhost:11434/v1/chat/completions",
207
+ "model": "qwen2.5:0.5b",
208
+ "priority": 17,
209
+ "dailyLimit": 100000,
210
+ "keyHint": "\u041b\u043e\u043a\u0430\u043b\u044c\u043d\u0430\u044f \u043c\u043e\u0434\u0435\u043b\u044c. \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438 Ollama \u0438 \u0441\u043a\u0430\u0447\u0430\u0439 \u043c\u043e\u0434\u0435\u043b\u044c: ollama pull qwen2.5:0.5b (\u0431\u0435\u0437 \u043a\u043b\u044e\u0447\u0430, \u0431\u0435\u0437\u043b\u0438\u043c\u0438\u0442, \u043f\u0440\u0438\u0432\u0430\u0442\u043d\u043e)",
211
+ "envVar": "",
212
+ "free": true,
213
+ "local": true,
214
+ "category": "local"
215
+ },
216
+ "lmstudio": {
217
+ "endpoint": "http://localhost:1234/v1/chat/completions",
218
+ "model": "local-model",
219
+ "priority": 18,
220
+ "dailyLimit": 100000,
221
+ "keyHint": "\u041b\u043e\u043a\u0430\u043b\u044c\u043d\u0430\u044f \u043c\u043e\u0434\u0435\u043b\u044c. \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438 LM Studio \u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u0438 \u043c\u043e\u0434\u0435\u043b\u044c (\u0431\u0435\u0437 \u043a\u043b\u044e\u0447\u0430, \u0431\u0435\u0437\u043b\u0438\u043c\u0438\u0442)",
222
+ "envVar": "",
223
+ "free": true,
224
+ "local": true,
225
+ "category": "local"
226
+ },
227
+ "cerebras-gpt": {
228
+ "endpoint": "https://api.cerebras.ai/v1/chat/completions",
229
+ "model": "gpt-oss-120b",
230
+ "priority": 2,
231
+ "dailyLimit": 1000,
232
+ "keyHint": "cloud.cerebras.ai \u2192 API Keys (gpt-oss-120b, \u043e\u0447\u0435\u043d\u044c \u0431\u044b\u0441\u0442\u0440\u0430\u044f)",
233
+ "envVar": "PROVIDER_CEREBRAS_APIKEY",
234
+ "free": true,
235
+ "category": "general"
236
+ },
237
+ "cerebras-gemma": {
238
+ "endpoint": "https://api.cerebras.ai/v1/chat/completions",
239
+ "model": "gemma-4-31b",
240
+ "priority": 8,
241
+ "dailyLimit": 1000,
242
+ "keyHint": "cloud.cerebras.ai \u2192 API Keys (gemma-4-31b)",
243
+ "envVar": "PROVIDER_CEREBRAS_APIKEY",
244
+ "free": true,
245
+ "category": "general"
246
+ },
247
+ "deepseek": {
248
+ "endpoint": "https://api.deepseek.com/chat/completions",
249
+ "model": "deepseek-v4-flash",
250
+ "priority": 4,
251
+ "dailyLimit": 1000,
252
+ "keyHint": "platform.deepseek.com \u2192 API Keys (deepseek-v4-flash)",
253
+ "envVar": "PROVIDER_DEEPSEEK_APIKEY",
254
+ "free": true,
255
+ "category": "general"
256
+ },
257
+ "deepseek-vision": {
258
+ "endpoint": "https://api.deepseek.com/chat/completions",
259
+ "model": "deepseek-v4-flash-vision-exp",
260
+ "priority": 14,
261
+ "dailyLimit": 1000,
262
+ "keyHint": "platform.deepseek.com \u2192 API Keys (vision)",
263
+ "envVar": "PROVIDER_DEEPSEEK_APIKEY",
264
+ "free": true,
265
+ "vision": true,
266
+ "category": "vision"
267
+ },
268
+ "or-dots-3": {
269
+ "endpoint": "https://openrouter.ai/api/v1/chat/completions",
270
+ "model": "dots-studio/dots-3-note-preview:free",
271
+ "priority": 19,
272
+ "dailyLimit": 50,
273
+ "keyHint": "openrouter.ai \u2192 Keys (Dots 3: 280B MoE, \u043c\u043e\u0449\u043d\u0430\u044f)",
274
+ "envVar": "PROVIDER_OPENROUTER_APIKEY",
275
+ "free": true,
276
+ "category": "reasoning"
277
+ },
278
+ "or-lfm": {
279
+ "endpoint": "https://openrouter.ai/api/v1/chat/completions",
280
+ "model": "liquid/lfm-2.5-2.6b:free",
281
+ "priority": 20,
282
+ "dailyLimit": 50,
283
+ "keyHint": "openrouter.ai \u2192 Keys (LFM 2.5: \u0431\u044b\u0441\u0442\u0440\u0430\u044f reasoning)",
284
+ "envVar": "PROVIDER_OPENROUTER_APIKEY",
285
+ "free": true,
286
+ "category": "reasoning"
287
+ },
288
+ "or-laguna-xs": {
289
+ "endpoint": "https://openrouter.ai/api/v1/chat/completions",
290
+ "model": "poolside/laguna-xs-2.1:free",
291
+ "priority": 21,
292
+ "dailyLimit": 50,
293
+ "keyHint": "openrouter.ai \u2192 Keys (Laguna XS: \u043a\u043e\u0434\u0438\u043d\u0433)",
294
+ "envVar": "PROVIDER_OPENROUTER_APIKEY",
295
+ "free": true,
296
+ "category": "coding"
297
+ }
298
+ }