free-coding-models 0.1.63 → 0.1.64

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/README.md CHANGED
@@ -2,8 +2,8 @@
2
2
  <img src="https://img.shields.io/npm/v/free-coding-models?color=76b900&label=npm&logo=npm" alt="npm version">
3
3
  <img src="https://img.shields.io/node/v/free-coding-models?color=76b900&logo=node.js" alt="node version">
4
4
  <img src="https://img.shields.io/npm/l/free-coding-models?color=76b900" alt="license">
5
- <img src="https://img.shields.io/badge/models-101-76b900?logo=nvidia" alt="models count">
6
- <img src="https://img.shields.io/badge/providers-9-blue" alt="providers count">
5
+ <img src="https://img.shields.io/badge/models-111-76b900?logo=nvidia" alt="models count">
6
+ <img src="https://img.shields.io/badge/providers-13-blue" alt="providers count">
7
7
  </p>
8
8
 
9
9
  <h1 align="center">free-coding-models</h1>
@@ -15,7 +15,7 @@
15
15
  <p align="center">
16
16
 
17
17
  ```
18
- 1. Create a free API key (NVIDIA, Groq, or Cerebras)
18
+ 1. Create a free API key (NVIDIA, OpenRouter, Hugging Face, etc.)
19
19
  2. npm i -g free-coding-models
20
20
  3. free-coding-models
21
21
  ```
@@ -24,7 +24,7 @@
24
24
 
25
25
  <p align="center">
26
26
  <strong>Find the fastest coding LLM models in seconds</strong><br>
27
- <sub>Ping free models from NVIDIA NIM, Groq, Cerebras, and SambaNova in real-time — pick the best one for OpenCode, OpenClaw, or any AI coding assistant</sub>
27
+ <sub>Ping free coding models from 13 providers in real-time — pick the best one for OpenCode, OpenClaw, or any AI coding assistant</sub>
28
28
  </p>
29
29
 
30
30
  <p align="center">
@@ -47,7 +47,7 @@
47
47
  ## ✨ Features
48
48
 
49
49
  - **🎯 Coding-focused** — Only LLM models optimized for code generation, not chat or vision
50
- - **🌐 Multi-provider** — 101 models from NVIDIA NIM, Groq, Cerebras, SambaNova, OpenRouter, Codestral, Hyperbolic, Scaleway, and Google AI — all free to use
50
+ - **🌐 Multi-provider** — 111 models from NVIDIA NIM, Groq, Cerebras, SambaNova, OpenRouter, Hugging Face Inference, Replicate, DeepInfra, Fireworks AI, Codestral, Hyperbolic, Scaleway, and Google AI — all free to use
51
51
  - **⚙️ Settings screen** — Press `P` to manage provider API keys, enable/disable providers, and test keys live
52
52
  - **🚀 Parallel pings** — All models tested simultaneously via native `fetch`
53
53
  - **📊 Real-time animation** — Watch latency appear live in alternate screen buffer
@@ -77,8 +77,12 @@ Before using `free-coding-models`, make sure you have:
77
77
  - **NVIDIA NIM** — [build.nvidia.com](https://build.nvidia.com) → Profile → API Keys → Generate
78
78
  - **Groq** — [console.groq.com/keys](https://console.groq.com/keys) → Create API Key
79
79
  - **Cerebras** — [cloud.cerebras.ai](https://cloud.cerebras.ai) → API Keys → Create
80
- - **SambaNova** — [cloud.sambanova.ai/apis](https://cloud.sambanova.ai/apis) → API KeysCreate ($5 free trial, 3 months)
81
- - **OpenRouter** — [openrouter.ai/settings/keys](https://openrouter.ai/settings/keys) → Create key (50 free req/day)
80
+ - **SambaNova** — [sambanova.ai/developers](https://sambanova.ai/developers) → Developers portalAPI key (dev tier generous)
81
+ - **OpenRouter** — [openrouter.ai/keys](https://openrouter.ai/keys) → Create key (50 req/day, 20/min on `:free`)
82
+ - **Hugging Face Inference** — [huggingface.co/settings/tokens](https://huggingface.co/settings/tokens) → Access Tokens (free monthly credits)
83
+ - **Replicate** — [replicate.com/account/api-tokens](https://replicate.com/account/api-tokens) → Create token (dev quota)
84
+ - **DeepInfra** — [deepinfra.com/login](https://deepinfra.com/login) → Login → API key (free dev tier)
85
+ - **Fireworks AI** — [fireworks.ai](https://fireworks.ai) → Settings → Access Tokens ($1 free credits)
82
86
  - **Mistral Codestral** — [codestral.mistral.ai](https://codestral.mistral.ai) → API Keys (30 req/min, 2000/day — phone required)
83
87
  - **Hyperbolic** — [app.hyperbolic.ai/settings](https://app.hyperbolic.ai/settings) → API Keys ($1 free trial)
84
88
  - **Scaleway** — [console.scaleway.com/iam/api-keys](https://console.scaleway.com/iam/api-keys) → IAM → API Keys (1M free tokens)
@@ -86,7 +90,7 @@ Before using `free-coding-models`, make sure you have:
86
90
  3. **OpenCode** *(optional)* — [Install OpenCode](https://github.com/opencode-ai/opencode) to use the OpenCode integration
87
91
  4. **OpenClaw** *(optional)* — [Install OpenClaw](https://openclaw.ai) to use the OpenClaw integration
88
92
 
89
- > 💡 **Tip:** You don't need all nine providers. One key is enough to get started. Add more later via the Settings screen (`P` key). Models without a key still show real latency (`🔑 NO KEY`) so you can evaluate providers before signing up.
93
+ > 💡 **Tip:** You don't need all thirteen providers. One key is enough to get started. Add more later via the Settings screen (`P` key). Models without a key still show real latency (`🔑 NO KEY`) so you can evaluate providers before signing up.
90
94
 
91
95
  ---
92
96
 
@@ -167,13 +171,13 @@ When you run `free-coding-models` without `--opencode` or `--openclaw`, you get
167
171
  Use `↑↓` arrows to select, `Enter` to confirm. Then the TUI launches with your chosen mode shown in the header badge.
168
172
 
169
173
  **How it works:**
170
- 1. **Ping phase** — All enabled models are pinged in parallel (up to 101 across 9 providers)
174
+ 1. **Ping phase** — All enabled models are pinged in parallel (up to 111 across 13 providers)
171
175
  2. **Continuous monitoring** — Models are re-pinged every 2 seconds forever
172
176
  3. **Real-time updates** — Watch "Latest", "Avg", and "Up%" columns update live
173
177
  4. **Select anytime** — Use ↑↓ arrows to navigate, press Enter on a model to act
174
178
  5. **Smart detection** — Automatically detects if NVIDIA NIM is configured in OpenCode or OpenClaw
175
179
 
176
- Setup wizard (first run — walks through all 9 providers):
180
+ Setup wizard (first run — walks through all 13 providers):
177
181
 
178
182
  ```
179
183
  🔑 First-time setup — API keys
@@ -203,7 +207,7 @@ Setup wizard (first run — walks through all 9 providers):
203
207
  You can add or change keys anytime with the P key in the TUI.
204
208
  ```
205
209
 
206
- You don't need all nine — skip any provider by pressing Enter. At least one key is required.
210
+ You don't need all thirteen — skip any provider by pressing Enter. At least one key is required.
207
211
 
208
212
  ### Adding or changing keys later
209
213
 
@@ -214,9 +218,14 @@ Press **`P`** to open the Settings screen at any time:
214
218
 
215
219
  Providers
216
220
 
217
- ❯ [ ✅ ] NIM nvapi-••••••••••••3f9a [Test ✅]
218
- [ ✅ ] Groq (no key set) [Test —]
219
- [ ✅ ] Cerebras (no key set) [Test —]
221
+ ❯ [ ✅ ] NVIDIA NIM nvapi-••••••••••••3f9a [Test ✅] Free tier (provider quota by model)
222
+ [ ✅ ] OpenRouter (no key set) [Test —] 50 req/day, 20/min (:free shared quota)
223
+ [ ✅ ] Hugging Face Inference (no key set) [Test —] Free monthly credits (~$0.10)
224
+
225
+ Setup Instructions — NVIDIA NIM
226
+ 1) Create a NVIDIA NIM account: https://build.nvidia.com
227
+ 2) Profile → API Keys → Generate
228
+ 3) Press T to test your key
220
229
 
221
230
  ↑↓ Navigate • Enter Edit key • Space Toggle enabled • T Test key • Esc Close
222
231
  ```
@@ -239,6 +248,11 @@ Env vars always take priority over the config file:
239
248
  NVIDIA_API_KEY=nvapi-xxx free-coding-models
240
249
  GROQ_API_KEY=gsk_xxx free-coding-models
241
250
  CEREBRAS_API_KEY=csk_xxx free-coding-models
251
+ OPENROUTER_API_KEY=sk-or-xxx free-coding-models
252
+ HUGGINGFACE_API_KEY=hf_xxx free-coding-models
253
+ REPLICATE_API_TOKEN=r8_xxx free-coding-models
254
+ DEEPINFRA_API_KEY=di_xxx free-coding-models
255
+ FIREWORKS_API_KEY=fw_xxx free-coding-models
242
256
  FREE_CODING_MODELS_TELEMETRY=0 free-coding-models
243
257
  ```
244
258
 
@@ -268,13 +282,33 @@ When enabled, telemetry events include: event name, app version, selected mode,
268
282
  1. Sign up at [cloud.cerebras.ai](https://cloud.cerebras.ai)
269
283
  2. Go to API Keys → Create
270
284
 
271
- > 💡 **Free credits** — All three providers offer free tiers for developers.
285
+ **OpenRouter** (`:free` models):
286
+ 1. Sign up at [openrouter.ai/keys](https://openrouter.ai/keys)
287
+ 2. Create API key (`sk-or-...`)
288
+
289
+ **Hugging Face Inference**:
290
+ 1. Sign up at [huggingface.co/settings/tokens](https://huggingface.co/settings/tokens)
291
+ 2. Create Access Token (`hf_...`)
292
+
293
+ **Replicate**:
294
+ 1. Sign up at [replicate.com/account/api-tokens](https://replicate.com/account/api-tokens)
295
+ 2. Create API token (`r8_...`)
296
+
297
+ **DeepInfra**:
298
+ 1. Sign up at [deepinfra.com/login](https://deepinfra.com/login)
299
+ 2. Create API key from your account dashboard
300
+
301
+ **Fireworks AI**:
302
+ 1. Sign up at [fireworks.ai](https://fireworks.ai)
303
+ 2. Open Settings → Access Tokens and create a token
304
+
305
+ > 💡 **Free tiers** — each provider exposes a dev/free tier with its own quotas.
272
306
 
273
307
  ---
274
308
 
275
309
  ## 🤖 Coding Models
276
310
 
277
- **101 coding models** across 9 providers and 8 tiers, ranked by [SWE-bench Verified](https://www.swebench.com) — the industry-standard benchmark measuring real GitHub issue resolution. Scores are self-reported by providers unless noted.
311
+ **111 coding models** across 13 providers and 8 tiers, ranked by [SWE-bench Verified](https://www.swebench.com) — the industry-standard benchmark measuring real GitHub issue resolution. Scores are self-reported by providers unless noted.
278
312
 
279
313
  ### NVIDIA NIM (44 models)
280
314
 
@@ -347,6 +381,19 @@ Current tier filter is shown in the header badge (e.g., `[Tier S]`)
347
381
  - Sets your selected model as default in `~/.config/opencode/opencode.json`
348
382
  - Launches OpenCode with the model ready to use
349
383
 
384
+ ### tmux sub-agent panes
385
+
386
+ When launched from an existing `tmux` session, `free-coding-models` now auto-adds an OpenCode `--port` argument so OpenCode/oh-my-opencode can spawn sub-agents in panes.
387
+
388
+ - Priority 1: reuse `OPENCODE_PORT` if it is valid and free
389
+ - Priority 2: auto-pick the first free port in `4096-5095`
390
+
391
+ You can force a specific port:
392
+
393
+ ```bash
394
+ OPENCODE_PORT=4098 free-coding-models --opencode
395
+ ```
396
+
350
397
  ### Manual OpenCode Setup (Optional)
351
398
 
352
399
  Create or edit `~/.config/opencode/opencode.json`:
@@ -522,6 +569,15 @@ This script:
522
569
  | `NVIDIA_API_KEY` | NVIDIA NIM key |
523
570
  | `GROQ_API_KEY` | Groq key |
524
571
  | `CEREBRAS_API_KEY` | Cerebras key |
572
+ | `SAMBANOVA_API_KEY` | SambaNova key |
573
+ | `OPENROUTER_API_KEY` | OpenRouter key |
574
+ | `HUGGINGFACE_API_KEY` / `HF_TOKEN` | Hugging Face token |
575
+ | `REPLICATE_API_TOKEN` | Replicate token |
576
+ | `DEEPINFRA_API_KEY` / `DEEPINFRA_TOKEN` | DeepInfra key |
577
+ | `CODESTRAL_API_KEY` | Mistral Codestral key |
578
+ | `HYPERBOLIC_API_KEY` | Hyperbolic key |
579
+ | `SCALEWAY_API_KEY` | Scaleway key |
580
+ | `GOOGLE_API_KEY` | Google AI Studio key |
525
581
  | `FREE_CODING_MODELS_TELEMETRY` | `0` disables analytics, `1` enables analytics |
526
582
  | `FREE_CODING_MODELS_POSTHOG_KEY` | PostHog project API key used for anonymous event capture |
527
583
  | `FREE_CODING_MODELS_POSTHOG_HOST` | Optional PostHog ingest host (`https://eu.i.posthog.com` default) |
@@ -533,12 +589,20 @@ This script:
533
589
  "apiKeys": {
534
590
  "nvidia": "nvapi-xxx",
535
591
  "groq": "gsk_xxx",
536
- "cerebras": "csk_xxx"
592
+ "cerebras": "csk_xxx",
593
+ "openrouter": "sk-or-xxx",
594
+ "huggingface": "hf_xxx",
595
+ "replicate": "r8_xxx",
596
+ "deepinfra": "di_xxx"
537
597
  },
538
598
  "providers": {
539
599
  "nvidia": { "enabled": true },
540
600
  "groq": { "enabled": true },
541
- "cerebras": { "enabled": true }
601
+ "cerebras": { "enabled": true },
602
+ "openrouter": { "enabled": true },
603
+ "huggingface": { "enabled": true },
604
+ "replicate": { "enabled": true },
605
+ "deepinfra": { "enabled": true }
542
606
  },
543
607
  "telemetry": {
544
608
  "enabled": true,
@@ -10,7 +10,7 @@
10
10
  * During benchmarking, users can navigate with arrow keys and press Enter to act on the selected model.
11
11
  *
12
12
  * 🎯 Key features:
13
- * - Parallel pings across all models with animated real-time updates (3 providers: NIM, Groq, Cerebras)
13
+ * - Parallel pings across all models with animated real-time updates (multi-provider)
14
14
  * - Continuous monitoring with 2-second ping intervals (never stops)
15
15
  * - Rolling averages calculated from ALL successful pings since start
16
16
  * - Best-per-tier highlighting with medals (🥇🥈🥉)
@@ -19,7 +19,7 @@
19
19
  * - Startup mode menu (OpenCode CLI vs OpenCode Desktop vs OpenClaw) when no flag is given
20
20
  * - Automatic config detection and model setup for both tools
21
21
  * - JSON config stored in ~/.free-coding-models.json (auto-migrates from old plain-text)
22
- * - Multi-provider support via sources.js (NIM, Groq, Cerebras — extensible)
22
+ * - Multi-provider support via sources.js (NIM/Groq/Cerebras/OpenRouter/Hugging Face/Replicate/DeepInfra/... — extensible)
23
23
  * - Settings screen (P key) to manage API keys per provider, enable/disable, test keys
24
24
  * - Uptime percentage tracking (successful pings / total pings)
25
25
  * - Sortable columns (R/Y/O/M/L/A/S/N/H/V/U keys)
@@ -32,15 +32,16 @@
32
32
  * - `getTelemetryTerminal`: Infer terminal family (Terminal.app, iTerm2, kitty, etc.)
33
33
  * - `isTelemetryDebugEnabled` / `telemetryDebug`: Optional runtime telemetry diagnostics via env
34
34
  * - `sendUsageTelemetry`: Fire-and-forget anonymous app-start event
35
- * - `promptApiKey`: Interactive wizard for first-time NVIDIA API key setup
35
+ * - `promptApiKey`: Interactive wizard for first-time multi-provider API key setup
36
36
  * - `promptModeSelection`: Startup menu to choose OpenCode vs OpenClaw
37
- * - `ping`: Perform HTTP request to NIM endpoint with timeout handling
37
+ * - `buildPingRequest` / `ping`: Build provider-specific probe requests and measure latency
38
38
  * - `renderTable`: Generate ASCII table with colored latency indicators and status emojis
39
39
  * - `getAvg`: Calculate average latency from all successful pings
40
40
  * - `getVerdict`: Determine verdict string based on average latency (Overloaded for 429)
41
41
  * - `getUptime`: Calculate uptime percentage from ping history
42
42
  * - `sortResults`: Sort models by various columns
43
43
  * - `checkNvidiaNimConfig`: Check if NVIDIA NIM provider is configured in OpenCode
44
+ * - `isTcpPortAvailable` / `resolveOpenCodeTmuxPort`: Pick a safe OpenCode port when running in tmux
44
45
  * - `startOpenCode`: Launch OpenCode CLI with selected model (configures if needed)
45
46
  * - `startOpenCodeDesktop`: Set model in shared config & open OpenCode Desktop app
46
47
  * - `loadOpenClawConfig` / `saveOpenClawConfig`: Manage ~/.openclaw/openclaw.json
@@ -57,8 +58,8 @@
57
58
  * ⚙️ Configuration:
58
59
  * - API keys stored per-provider in ~/.free-coding-models.json (0600 perms)
59
60
  * - Old ~/.free-coding-models plain-text auto-migrated as nvidia key on first run
60
- * - Env vars override config: NVIDIA_API_KEY, GROQ_API_KEY, CEREBRAS_API_KEY
61
- * - Models loaded from sources.js — 53 models across NIM, Groq, Cerebras
61
+ * - Env vars override config: NVIDIA_API_KEY, GROQ_API_KEY, CEREBRAS_API_KEY, OPENROUTER_API_KEY, HUGGINGFACE_API_KEY/HF_TOKEN, REPLICATE_API_TOKEN, DEEPINFRA_API_KEY/DEEPINFRA_TOKEN, FIREWORKS_API_KEY, etc.
62
+ * - Models loaded from sources.js — all provider/model definitions are centralized there
62
63
  * - OpenCode config: ~/.config/opencode/opencode.json
63
64
  * - OpenClaw config: ~/.openclaw/openclaw.json
64
65
  * - Ping timeout: 15s per attempt
@@ -86,6 +87,7 @@ import { readFileSync, writeFileSync, existsSync, copyFileSync, mkdirSync } from
86
87
  import { randomUUID } from 'crypto'
87
88
  import { homedir } from 'os'
88
89
  import { join, dirname } from 'path'
90
+ import { createServer } from 'net'
89
91
  import { MODELS, sources } from '../sources.js'
90
92
  import { patchOpenClawModelsJson } from '../patch-openclaw-models.js'
91
93
  import { getAvg, getVerdict, getUptime, sortResults, filterByTier, findBestModel, parseArgs, TIER_ORDER, VERDICT_ORDER, TIER_LETTER_MAP } from '../lib/utils.js'
@@ -486,7 +488,7 @@ function runUpdate(latestVersion) {
486
488
 
487
489
  // ─── First-run wizard ─────────────────────────────────────────────────────────
488
490
  // 📖 Shown when NO provider has a key configured yet.
489
- // 📖 Steps through all 3 providers sequentially — each is optional (Enter to skip).
491
+ // 📖 Steps through all configured providers sequentially — each is optional (Enter to skip).
490
492
  // 📖 At least one key must be entered to proceed. Keys saved to ~/.free-coding-models.json.
491
493
  // 📖 Returns the nvidia key (or null) for backward-compat with the rest of main().
492
494
  async function promptApiKey(config) {
@@ -495,81 +497,17 @@ async function promptApiKey(config) {
495
497
  console.log(chalk.dim(' Enter keys for any provider you want to use. Press Enter to skip one.'))
496
498
  console.log()
497
499
 
498
- // 📖 Provider definitions: label, key field, url for getting the key
499
- const providers = [
500
- {
501
- key: 'nvidia',
502
- label: 'NVIDIA NIM',
503
- color: chalk.rgb(118, 185, 0),
504
- url: 'https://build.nvidia.com',
505
- hint: 'Profile API Keys → Generate',
506
- prefix: 'nvapi-',
507
- },
508
- {
509
- key: 'groq',
510
- label: 'Groq',
511
- color: chalk.rgb(249, 103, 20),
512
- url: 'https://console.groq.com/keys',
513
- hint: 'API Keys → Create API Key',
514
- prefix: 'gsk_',
515
- },
516
- {
517
- key: 'cerebras',
518
- label: 'Cerebras',
519
- color: chalk.rgb(0, 180, 255),
520
- url: 'https://cloud.cerebras.ai',
521
- hint: 'API Keys → Create',
522
- prefix: 'csk_ / cauth_',
523
- },
524
- {
525
- key: 'sambanova',
526
- label: 'SambaNova',
527
- color: chalk.rgb(255, 165, 0),
528
- url: 'https://cloud.sambanova.ai/apis',
529
- hint: 'API Keys → Create ($5 free trial, 3 months)',
530
- prefix: 'sn-',
531
- },
532
- {
533
- key: 'openrouter',
534
- label: 'OpenRouter',
535
- color: chalk.rgb(120, 80, 255),
536
- url: 'https://openrouter.ai/settings/keys',
537
- hint: 'API Keys → Create key (50 free req/day, shared quota)',
538
- prefix: 'sk-or-',
539
- },
540
- {
541
- key: 'codestral',
542
- label: 'Mistral Codestral',
543
- color: chalk.rgb(255, 100, 100),
544
- url: 'https://codestral.mistral.ai',
545
- hint: 'API Keys → Create key (30 req/min, 2000/day — phone required)',
546
- prefix: 'csk-',
547
- },
548
- {
549
- key: 'hyperbolic',
550
- label: 'Hyperbolic',
551
- color: chalk.rgb(0, 200, 150),
552
- url: 'https://app.hyperbolic.ai/settings',
553
- hint: 'Settings → API Keys ($1 free trial)',
554
- prefix: 'eyJ',
555
- },
556
- {
557
- key: 'scaleway',
558
- label: 'Scaleway',
559
- color: chalk.rgb(130, 0, 250),
560
- url: 'https://console.scaleway.com/iam/api-keys',
561
- hint: 'IAM → API Keys (1M free tokens)',
562
- prefix: 'scw-',
563
- },
564
- {
565
- key: 'googleai',
566
- label: 'Google AI Studio',
567
- color: chalk.rgb(66, 133, 244),
568
- url: 'https://aistudio.google.com/apikey',
569
- hint: 'Get API key (free Gemma models, 14.4K req/day)',
570
- prefix: 'AIza',
571
- },
572
- ]
500
+ // 📖 Build providers from sources to keep setup in sync with actual supported providers.
501
+ const providers = Object.keys(sources).map((key) => {
502
+ const meta = PROVIDER_METADATA[key] || {}
503
+ return {
504
+ key,
505
+ label: meta.label || sources[key]?.name || key,
506
+ color: meta.color || chalk.white,
507
+ url: meta.signupUrl || 'https://example.com',
508
+ hint: meta.signupHint || 'Create API key',
509
+ }
510
+ })
573
511
 
574
512
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout })
575
513
 
@@ -1121,23 +1059,50 @@ function renderTable(results, pendingPings, frame, cursor = null, sortColumn = '
1121
1059
  // ─── HTTP ping ────────────────────────────────────────────────────────────────
1122
1060
 
1123
1061
  // 📖 ping: Send a single chat completion request to measure model availability and latency.
1124
- // 📖 url param is the provider's endpoint URL — differs per provider (NIM, Groq, Cerebras).
1062
+ // 📖 providerKey and url determine provider-specific request format.
1125
1063
  // 📖 apiKey can be null — in that case no Authorization header is sent.
1126
1064
  // 📖 A 401 response still tells us the server is UP and gives us real latency.
1127
- async function ping(apiKey, modelId, url) {
1065
+ function buildPingRequest(apiKey, modelId, providerKey, url) {
1066
+ if (providerKey === 'replicate') {
1067
+ // 📖 Replicate uses /v1/predictions with a different payload than OpenAI chat-completions.
1068
+ const replicateHeaders = { 'Content-Type': 'application/json', Prefer: 'wait=4' }
1069
+ if (apiKey) replicateHeaders.Authorization = `Token ${apiKey}`
1070
+ return {
1071
+ url,
1072
+ headers: replicateHeaders,
1073
+ body: { version: modelId, input: { prompt: 'hi' } },
1074
+ }
1075
+ }
1076
+
1077
+ const headers = { 'Content-Type': 'application/json' }
1078
+ if (apiKey) headers.Authorization = `Bearer ${apiKey}`
1079
+ if (providerKey === 'openrouter') {
1080
+ // 📖 OpenRouter recommends optional app identification headers.
1081
+ headers['HTTP-Referer'] = 'https://github.com/vava-nessa/free-coding-models'
1082
+ headers['X-Title'] = 'free-coding-models'
1083
+ }
1084
+
1085
+ return {
1086
+ url,
1087
+ headers,
1088
+ body: { model: modelId, messages: [{ role: 'user', content: 'hi' }], max_tokens: 1 },
1089
+ }
1090
+ }
1091
+
1092
+ async function ping(apiKey, modelId, providerKey, url) {
1128
1093
  const ctrl = new AbortController()
1129
1094
  const timer = setTimeout(() => ctrl.abort(), PING_TIMEOUT)
1130
1095
  const t0 = performance.now()
1131
1096
  try {
1132
- // 📖 Only attach Authorization header when a key is available
1133
- const headers = { 'Content-Type': 'application/json' }
1134
- if (apiKey) headers['Authorization'] = `Bearer ${apiKey}`
1135
- const resp = await fetch(url, {
1097
+ const req = buildPingRequest(apiKey, modelId, providerKey, url)
1098
+ const resp = await fetch(req.url, {
1136
1099
  method: 'POST', signal: ctrl.signal,
1137
- headers,
1138
- body: JSON.stringify({ model: modelId, messages: [{ role: 'user', content: 'hi' }], max_tokens: 1 }),
1100
+ headers: req.headers,
1101
+ body: JSON.stringify(req.body),
1139
1102
  })
1140
- return { code: String(resp.status), ms: Math.round(performance.now() - t0) }
1103
+ // 📖 Normalize all HTTP 2xx statuses to "200" so existing verdict/avg logic still works.
1104
+ const code = resp.status >= 200 && resp.status < 300 ? '200' : String(resp.status)
1105
+ return { code, ms: Math.round(performance.now() - t0) }
1141
1106
  } catch (err) {
1142
1107
  const isTimeout = err.name === 'AbortError'
1143
1108
  return {
@@ -1178,12 +1143,112 @@ const ENV_VAR_NAMES = {
1178
1143
  cerebras: 'CEREBRAS_API_KEY',
1179
1144
  sambanova: 'SAMBANOVA_API_KEY',
1180
1145
  openrouter: 'OPENROUTER_API_KEY',
1146
+ huggingface:'HUGGINGFACE_API_KEY',
1147
+ replicate: 'REPLICATE_API_TOKEN',
1148
+ deepinfra: 'DEEPINFRA_API_KEY',
1149
+ fireworks: 'FIREWORKS_API_KEY',
1181
1150
  codestral: 'CODESTRAL_API_KEY',
1182
1151
  hyperbolic: 'HYPERBOLIC_API_KEY',
1183
1152
  scaleway: 'SCALEWAY_API_KEY',
1184
1153
  googleai: 'GOOGLE_API_KEY',
1185
1154
  }
1186
1155
 
1156
+ // 📖 Provider metadata used by the setup wizard and Settings details panel.
1157
+ // 📖 Keeps signup links + rate limits centralized so UI stays consistent.
1158
+ const PROVIDER_METADATA = {
1159
+ nvidia: {
1160
+ label: 'NVIDIA NIM',
1161
+ color: chalk.rgb(118, 185, 0),
1162
+ signupUrl: 'https://build.nvidia.com',
1163
+ signupHint: 'Profile → API Keys → Generate',
1164
+ rateLimits: 'Free tier (provider quota by model)',
1165
+ },
1166
+ groq: {
1167
+ label: 'Groq',
1168
+ color: chalk.rgb(249, 103, 20),
1169
+ signupUrl: 'https://console.groq.com/keys',
1170
+ signupHint: 'API Keys → Create API Key',
1171
+ rateLimits: 'Free dev tier (provider quota)',
1172
+ },
1173
+ cerebras: {
1174
+ label: 'Cerebras',
1175
+ color: chalk.rgb(0, 180, 255),
1176
+ signupUrl: 'https://cloud.cerebras.ai',
1177
+ signupHint: 'API Keys → Create',
1178
+ rateLimits: 'Free dev tier (provider quota)',
1179
+ },
1180
+ sambanova: {
1181
+ label: 'SambaNova',
1182
+ color: chalk.rgb(255, 165, 0),
1183
+ signupUrl: 'https://sambanova.ai/developers',
1184
+ signupHint: 'Developers portal → Create API key',
1185
+ rateLimits: 'Dev tier generous quota',
1186
+ },
1187
+ openrouter: {
1188
+ label: 'OpenRouter',
1189
+ color: chalk.rgb(120, 80, 255),
1190
+ signupUrl: 'https://openrouter.ai/keys',
1191
+ signupHint: 'API Keys → Create',
1192
+ rateLimits: '50 req/day, 20/min (:free shared quota)',
1193
+ },
1194
+ huggingface: {
1195
+ label: 'Hugging Face Inference',
1196
+ color: chalk.rgb(255, 182, 0),
1197
+ signupUrl: 'https://huggingface.co/settings/tokens',
1198
+ signupHint: 'Settings → Access Tokens',
1199
+ rateLimits: 'Free monthly credits (~$0.10)',
1200
+ },
1201
+ replicate: {
1202
+ label: 'Replicate',
1203
+ color: chalk.rgb(120, 160, 255),
1204
+ signupUrl: 'https://replicate.com/account/api-tokens',
1205
+ signupHint: 'Account → API Tokens',
1206
+ rateLimits: 'Developer free quota',
1207
+ },
1208
+ deepinfra: {
1209
+ label: 'DeepInfra',
1210
+ color: chalk.rgb(0, 180, 140),
1211
+ signupUrl: 'https://deepinfra.com/login',
1212
+ signupHint: 'Login → API keys',
1213
+ rateLimits: 'Free dev tier (low-latency quota)',
1214
+ },
1215
+ fireworks: {
1216
+ label: 'Fireworks AI',
1217
+ color: chalk.rgb(255, 80, 50),
1218
+ signupUrl: 'https://fireworks.ai',
1219
+ signupHint: 'Create account → Generate API key',
1220
+ rateLimits: '$1 free credits (new dev accounts)',
1221
+ },
1222
+ codestral: {
1223
+ label: 'Mistral Codestral',
1224
+ color: chalk.rgb(255, 100, 100),
1225
+ signupUrl: 'https://codestral.mistral.ai',
1226
+ signupHint: 'API Keys → Create',
1227
+ rateLimits: '30 req/min, 2000/day',
1228
+ },
1229
+ hyperbolic: {
1230
+ label: 'Hyperbolic',
1231
+ color: chalk.rgb(0, 200, 150),
1232
+ signupUrl: 'https://app.hyperbolic.ai/settings',
1233
+ signupHint: 'Settings → API Keys',
1234
+ rateLimits: '$1 free trial credits',
1235
+ },
1236
+ scaleway: {
1237
+ label: 'Scaleway',
1238
+ color: chalk.rgb(130, 0, 250),
1239
+ signupUrl: 'https://console.scaleway.com/iam/api-keys',
1240
+ signupHint: 'IAM → API Keys',
1241
+ rateLimits: '1M free tokens',
1242
+ },
1243
+ googleai: {
1244
+ label: 'Google AI Studio',
1245
+ color: chalk.rgb(66, 133, 244),
1246
+ signupUrl: 'https://aistudio.google.com/apikey',
1247
+ signupHint: 'Get API key',
1248
+ rateLimits: '14.4K req/day, 30/min',
1249
+ },
1250
+ }
1251
+
1187
1252
  // 📖 OpenCode config location varies by platform
1188
1253
  // 📖 Windows: %APPDATA%\opencode\opencode.json (or sometimes ~/.config/opencode)
1189
1254
  // 📖 macOS/Linux: ~/.config/opencode/opencode.json
@@ -1193,6 +1258,45 @@ const OPENCODE_CONFIG = isWindows
1193
1258
 
1194
1259
  // 📖 Fallback to .config on Windows if AppData doesn't exist
1195
1260
  const OPENCODE_CONFIG_FALLBACK = join(homedir(), '.config', 'opencode', 'opencode.json')
1261
+ const OPENCODE_PORT_RANGE_START = 4096
1262
+ const OPENCODE_PORT_RANGE_END = 5096
1263
+
1264
+ // 📖 isTcpPortAvailable: checks if a local TCP port is free for OpenCode.
1265
+ // 📖 Used to avoid tmux sub-agent port conflicts when multiple projects run in parallel.
1266
+ function isTcpPortAvailable(port) {
1267
+ return new Promise((resolve) => {
1268
+ const server = createServer()
1269
+ server.once('error', () => resolve(false))
1270
+ server.once('listening', () => {
1271
+ server.close(() => resolve(true))
1272
+ })
1273
+ server.listen(port)
1274
+ })
1275
+ }
1276
+
1277
+ // 📖 resolveOpenCodeTmuxPort: selects a safe port for OpenCode when inside tmux.
1278
+ // 📖 Priority:
1279
+ // 📖 1) OPENCODE_PORT from env (if valid and available)
1280
+ // 📖 2) First available port in 4096-5095
1281
+ async function resolveOpenCodeTmuxPort() {
1282
+ const envPortRaw = process.env.OPENCODE_PORT
1283
+ const envPort = Number.parseInt(envPortRaw || '', 10)
1284
+
1285
+ if (Number.isInteger(envPort) && envPort > 0 && envPort <= 65535) {
1286
+ if (await isTcpPortAvailable(envPort)) {
1287
+ return { port: envPort, source: 'env' }
1288
+ }
1289
+ console.log(chalk.yellow(` ⚠ OPENCODE_PORT=${envPort} is already in use; selecting another port for this run.`))
1290
+ }
1291
+
1292
+ for (let port = OPENCODE_PORT_RANGE_START; port < OPENCODE_PORT_RANGE_END; port++) {
1293
+ if (await isTcpPortAvailable(port)) {
1294
+ return { port, source: 'auto' }
1295
+ }
1296
+ }
1297
+
1298
+ return null
1299
+ }
1196
1300
 
1197
1301
  function getOpenCodeConfigPath() {
1198
1302
  if (existsSync(OPENCODE_CONFIG)) return OPENCODE_CONFIG
@@ -1243,10 +1347,30 @@ async function spawnOpenCode(args, providerKey, fcmConfig) {
1243
1347
  const envVarName = ENV_VAR_NAMES[providerKey]
1244
1348
  const resolvedKey = getApiKey(fcmConfig, providerKey)
1245
1349
  const childEnv = { ...process.env }
1350
+ const finalArgs = [...args]
1351
+ const hasExplicitPortArg = finalArgs.includes('--port')
1246
1352
  if (envVarName && resolvedKey) childEnv[envVarName] = resolvedKey
1247
1353
 
1354
+ // 📖 In tmux, OpenCode sub-agents need a listening port to open extra panes.
1355
+ // 📖 We auto-pick one if the user did not provide --port explicitly.
1356
+ if (process.env.TMUX && !hasExplicitPortArg) {
1357
+ const tmuxPort = await resolveOpenCodeTmuxPort()
1358
+ if (tmuxPort) {
1359
+ const portValue = String(tmuxPort.port)
1360
+ childEnv.OPENCODE_PORT = portValue
1361
+ finalArgs.push('--port', portValue)
1362
+ if (tmuxPort.source === 'env') {
1363
+ console.log(chalk.dim(` 📺 tmux detected — using OPENCODE_PORT=${portValue}.`))
1364
+ } else {
1365
+ console.log(chalk.dim(` 📺 tmux detected — using OpenCode port ${portValue} for sub-agent panes.`))
1366
+ }
1367
+ } else {
1368
+ console.log(chalk.yellow(` ⚠ tmux detected but no free OpenCode port found in ${OPENCODE_PORT_RANGE_START}-${OPENCODE_PORT_RANGE_END - 1}; launching without --port.`))
1369
+ }
1370
+ }
1371
+
1248
1372
  const { spawn } = await import('child_process')
1249
- const child = spawn('opencode', args, {
1373
+ const child = spawn('opencode', finalArgs, {
1250
1374
  stdio: 'inherit',
1251
1375
  shell: true,
1252
1376
  detached: false,
@@ -1269,7 +1393,7 @@ async function spawnOpenCode(args, providerKey, fcmConfig) {
1269
1393
 
1270
1394
  // ─── Start OpenCode ────────────────────────────────────────────────────────────
1271
1395
  // 📖 Launches OpenCode with the selected model.
1272
- // 📖 Handles all 3 providers: nvidia (needs custom provider config), groq & cerebras (built-in in OpenCode).
1396
+ // 📖 Handles nvidia + all OpenAI-compatible providers defined in sources.js.
1273
1397
  // 📖 For nvidia: checks if NIM is configured, sets provider.models entry, spawns with nvidia/model-id.
1274
1398
  // 📖 For groq/cerebras: OpenCode has built-in support -- just sets model in config and spawns.
1275
1399
  // 📖 Model format: { modelId, label, tier, providerKey }
@@ -1357,6 +1481,14 @@ After installation, you can use: opencode --model ${modelRef}`
1357
1481
  await spawnOpenCode([], providerKey, fcmConfig)
1358
1482
  }
1359
1483
  } else {
1484
+ if (providerKey === 'replicate') {
1485
+ console.log(chalk.yellow(' ⚠ Replicate models are monitor-only for now in OpenCode mode.'))
1486
+ console.log(chalk.dim(' Reason: Replicate uses /v1/predictions instead of OpenAI chat-completions.'))
1487
+ console.log(chalk.dim(' You can still benchmark this model in the TUI and use other providers for OpenCode launch.'))
1488
+ console.log()
1489
+ return
1490
+ }
1491
+
1360
1492
  // 📖 Groq: built-in OpenCode provider -- needs provider block with apiKey in opencode.json.
1361
1493
  // 📖 Cerebras: NOT built-in -- needs @ai-sdk/openai-compatible + baseURL, like NVIDIA.
1362
1494
  // 📖 Both need the model registered in provider.<key>.models so OpenCode can find it.
@@ -1413,6 +1545,36 @@ After installation, you can use: opencode --model ${modelRef}`
1413
1545
  },
1414
1546
  models: {}
1415
1547
  }
1548
+ } else if (providerKey === 'huggingface') {
1549
+ config.provider.huggingface = {
1550
+ npm: '@ai-sdk/openai-compatible',
1551
+ name: 'Hugging Face Inference',
1552
+ options: {
1553
+ baseURL: 'https://router.huggingface.co/v1',
1554
+ apiKey: '{env:HUGGINGFACE_API_KEY}'
1555
+ },
1556
+ models: {}
1557
+ }
1558
+ } else if (providerKey === 'deepinfra') {
1559
+ config.provider.deepinfra = {
1560
+ npm: '@ai-sdk/openai-compatible',
1561
+ name: 'DeepInfra',
1562
+ options: {
1563
+ baseURL: 'https://api.deepinfra.com/v1/openai',
1564
+ apiKey: '{env:DEEPINFRA_API_KEY}'
1565
+ },
1566
+ models: {}
1567
+ }
1568
+ } else if (providerKey === 'fireworks') {
1569
+ config.provider.fireworks = {
1570
+ npm: '@ai-sdk/openai-compatible',
1571
+ name: 'Fireworks AI',
1572
+ options: {
1573
+ baseURL: 'https://api.fireworks.ai/inference/v1',
1574
+ apiKey: '{env:FIREWORKS_API_KEY}'
1575
+ },
1576
+ models: {}
1577
+ }
1416
1578
  } else if (providerKey === 'codestral') {
1417
1579
  config.provider.codestral = {
1418
1580
  npm: '@ai-sdk/openai-compatible',
@@ -1488,7 +1650,7 @@ After installation, you can use: opencode --model ${modelRef}`
1488
1650
  // ─── Start OpenCode Desktop ─────────────────────────────────────────────────────
1489
1651
  // 📖 startOpenCodeDesktop: Same config logic as startOpenCode, but opens the Desktop app.
1490
1652
  // 📖 OpenCode Desktop shares config at the same location as CLI.
1491
- // 📖 Handles all 3 providers: nvidia (needs custom provider config), groq & cerebras (built-in).
1653
+ // 📖 Handles nvidia + all OpenAI-compatible providers defined in sources.js.
1492
1654
  // 📖 No need to wait for exit — Desktop app stays open independently.
1493
1655
  async function startOpenCodeDesktop(model, fcmConfig) {
1494
1656
  const providerKey = model.providerKey ?? 'nvidia'
@@ -1589,6 +1751,14 @@ ${isWindows ? 'set NVIDIA_API_KEY=your_key_here' : 'export NVIDIA_API_KEY=your_k
1589
1751
  console.log()
1590
1752
  }
1591
1753
  } else {
1754
+ if (providerKey === 'replicate') {
1755
+ console.log(chalk.yellow(' ⚠ Replicate models are monitor-only for now in OpenCode Desktop mode.'))
1756
+ console.log(chalk.dim(' Reason: Replicate uses /v1/predictions instead of OpenAI chat-completions.'))
1757
+ console.log(chalk.dim(' You can still benchmark this model in the TUI and use other providers for Desktop launch.'))
1758
+ console.log()
1759
+ return
1760
+ }
1761
+
1592
1762
  // 📖 Groq: built-in OpenCode provider — needs provider block with apiKey in opencode.json.
1593
1763
  // 📖 Cerebras: NOT built-in — needs @ai-sdk/openai-compatible + baseURL, like NVIDIA.
1594
1764
  // 📖 Both need the model registered in provider.<key>.models so OpenCode can find it.
@@ -1643,6 +1813,36 @@ ${isWindows ? 'set NVIDIA_API_KEY=your_key_here' : 'export NVIDIA_API_KEY=your_k
1643
1813
  },
1644
1814
  models: {}
1645
1815
  }
1816
+ } else if (providerKey === 'huggingface') {
1817
+ config.provider.huggingface = {
1818
+ npm: '@ai-sdk/openai-compatible',
1819
+ name: 'Hugging Face Inference',
1820
+ options: {
1821
+ baseURL: 'https://router.huggingface.co/v1',
1822
+ apiKey: '{env:HUGGINGFACE_API_KEY}'
1823
+ },
1824
+ models: {}
1825
+ }
1826
+ } else if (providerKey === 'deepinfra') {
1827
+ config.provider.deepinfra = {
1828
+ npm: '@ai-sdk/openai-compatible',
1829
+ name: 'DeepInfra',
1830
+ options: {
1831
+ baseURL: 'https://api.deepinfra.com/v1/openai',
1832
+ apiKey: '{env:DEEPINFRA_API_KEY}'
1833
+ },
1834
+ models: {}
1835
+ }
1836
+ } else if (providerKey === 'fireworks') {
1837
+ config.provider.fireworks = {
1838
+ npm: '@ai-sdk/openai-compatible',
1839
+ name: 'Fireworks AI',
1840
+ options: {
1841
+ baseURL: 'https://api.fireworks.ai/inference/v1',
1842
+ apiKey: '{env:FIREWORKS_API_KEY}'
1843
+ },
1844
+ models: {}
1845
+ }
1646
1846
  } else if (providerKey === 'codestral') {
1647
1847
  config.provider.codestral = {
1648
1848
  npm: '@ai-sdk/openai-compatible',
@@ -1854,7 +2054,7 @@ async function runFiableMode(config) {
1854
2054
  const pingPromises = results.map(r => {
1855
2055
  const rApiKey = getApiKey(config, r.providerKey)
1856
2056
  const url = sources[r.providerKey]?.url
1857
- return ping(rApiKey, r.modelId, url).then(({ code, ms }) => {
2057
+ return ping(rApiKey, r.modelId, r.providerKey, url).then(({ code, ms }) => {
1858
2058
  r.pings.push({ ms, code })
1859
2059
  if (code === '200') {
1860
2060
  r.status = 'up'
@@ -2111,12 +2311,14 @@ async function main() {
2111
2311
  lines.push('')
2112
2312
  lines.push(` ${chalk.bold('⚙ Settings')} ${chalk.dim('— free-coding-models v' + LOCAL_VERSION)}`)
2113
2313
  lines.push('')
2114
- lines.push(` ${chalk.bold('Providers')}`)
2314
+ lines.push(` ${chalk.bold('🧩 Providers')}`)
2315
+ lines.push(` ${chalk.dim(' ' + '─'.repeat(112))}`)
2115
2316
  lines.push('')
2116
2317
 
2117
2318
  for (let i = 0; i < providerKeys.length; i++) {
2118
2319
  const pk = providerKeys[i]
2119
2320
  const src = sources[pk]
2321
+ const meta = PROVIDER_METADATA[pk] || {}
2120
2322
  const isCursor = i === state.settingsCursor
2121
2323
  const enabled = isProviderEnabled(state.config, pk)
2122
2324
  const keyVal = state.config.apiKeys?.[pk] ?? ''
@@ -2140,22 +2342,37 @@ async function main() {
2140
2342
  if (testResult === 'pending') testBadge = chalk.yellow('[Testing…]')
2141
2343
  else if (testResult === 'ok') testBadge = chalk.greenBright('[Test ✅]')
2142
2344
  else if (testResult === 'fail') testBadge = chalk.red('[Test ❌]')
2345
+ const rateSummary = chalk.dim((meta.rateLimits || 'No limit info').slice(0, 36))
2143
2346
 
2144
- const enabledBadge = enabled ? chalk.greenBright('✅') : chalk.dim('')
2145
- const providerName = chalk.bold(src.name.padEnd(10))
2347
+ const enabledBadge = enabled ? chalk.greenBright('✅') : chalk.redBright('')
2348
+ const providerName = chalk.bold((meta.label || src.name || pk).slice(0, 22).padEnd(22))
2146
2349
  const bullet = isCursor ? chalk.bold.cyan(' ❯ ') : chalk.dim(' ')
2147
2350
 
2148
- const row = `${bullet}[ ${enabledBadge} ] ${providerName} ${keyDisplay.padEnd(30)} ${testBadge}`
2351
+ const row = `${bullet}[ ${enabledBadge} ] ${providerName} ${keyDisplay.padEnd(30)} ${testBadge} ${rateSummary}`
2149
2352
  lines.push(isCursor ? chalk.bgRgb(30, 30, 60)(row) : row)
2150
2353
  }
2151
2354
 
2152
2355
  lines.push('')
2153
- lines.push(` ${chalk.bold('Analytics')}`)
2356
+ const selectedProviderKey = providerKeys[Math.min(state.settingsCursor, providerKeys.length - 1)]
2357
+ const selectedSource = sources[selectedProviderKey]
2358
+ const selectedMeta = PROVIDER_METADATA[selectedProviderKey] || {}
2359
+ if (selectedSource && state.settingsCursor < telemetryRowIdx) {
2360
+ const selectedKey = getApiKey(state.config, selectedProviderKey)
2361
+ const setupStatus = selectedKey ? chalk.green('API key detected ✅') : chalk.yellow('API key missing ⚠')
2362
+ lines.push(` ${chalk.bold('Setup Instructions')} — ${selectedMeta.label || selectedSource.name || selectedProviderKey}`)
2363
+ lines.push(chalk.dim(` 1) Create a ${selectedMeta.label || selectedSource.name} account: ${selectedMeta.signupUrl || 'signup link missing'}`))
2364
+ lines.push(chalk.dim(` 2) ${selectedMeta.signupHint || 'Generate an API key and paste it with Enter on this row'}`))
2365
+ lines.push(chalk.dim(` 3) Press ${chalk.yellow('T')} to test your key. Status: ${setupStatus}`))
2366
+ lines.push('')
2367
+ }
2368
+
2369
+ lines.push(` ${chalk.bold('📊 Analytics')}`)
2370
+ lines.push(` ${chalk.dim(' ' + '─'.repeat(112))}`)
2154
2371
  lines.push('')
2155
2372
 
2156
2373
  const telemetryCursor = state.settingsCursor === telemetryRowIdx
2157
2374
  const telemetryEnabled = state.config.telemetry?.enabled === true
2158
- const telemetryStatus = telemetryEnabled ? chalk.greenBright('✅ Enabled') : chalk.dim(' Disabled')
2375
+ const telemetryStatus = telemetryEnabled ? chalk.greenBright('✅ Enabled') : chalk.redBright(' Disabled')
2159
2376
  const telemetryRowBullet = telemetryCursor ? chalk.bold.cyan(' ❯ ') : chalk.dim(' ')
2160
2377
  const telemetryEnv = parseTelemetryEnv(process.env.FREE_CODING_MODELS_TELEMETRY)
2161
2378
  const telemetrySource = telemetryEnv === null
@@ -2227,7 +2444,7 @@ async function main() {
2227
2444
  if (!testModel) { state.settingsTestResults[providerKey] = 'fail'; return }
2228
2445
 
2229
2446
  state.settingsTestResults[providerKey] = 'pending'
2230
- const { code } = await ping(testKey, testModel, src.url)
2447
+ const { code } = await ping(testKey, testModel, providerKey, src.url)
2231
2448
  state.settingsTestResults[providerKey] = code === '200' ? 'ok' : 'fail'
2232
2449
  }
2233
2450
 
@@ -2566,7 +2783,7 @@ async function main() {
2566
2783
  const pingModel = async (r) => {
2567
2784
  const providerApiKey = getApiKey(state.config, r.providerKey) ?? null
2568
2785
  const providerUrl = sources[r.providerKey]?.url ?? sources.nvidia.url
2569
- const { code, ms } = await ping(providerApiKey, r.modelId, providerUrl)
2786
+ const { code, ms } = await ping(providerApiKey, r.modelId, r.providerKey, providerUrl)
2570
2787
 
2571
2788
  // 📖 Store ping result as object with ms and code
2572
2789
  // 📖 ms = actual response time (even for errors like 429)
package/lib/config.js CHANGED
@@ -17,6 +17,10 @@
17
17
  * "cerebras": "csk_xxx",
18
18
  * "sambanova": "sn-xxx",
19
19
  * "openrouter": "sk-or-xxx",
20
+ * "huggingface":"hf_xxx",
21
+ * "replicate": "r8_xxx",
22
+ * "deepinfra": "di_xxx",
23
+ * "fireworks": "fw_xxx",
20
24
  * "codestral": "csk-xxx",
21
25
  * "hyperbolic": "eyJ...",
22
26
  * "scaleway": "scw-xxx",
@@ -28,6 +32,10 @@
28
32
  * "cerebras": { "enabled": true },
29
33
  * "sambanova": { "enabled": true },
30
34
  * "openrouter": { "enabled": true },
35
+ * "huggingface":{ "enabled": true },
36
+ * "replicate": { "enabled": true },
37
+ * "deepinfra": { "enabled": true },
38
+ * "fireworks": { "enabled": true },
31
39
  * "codestral": { "enabled": true },
32
40
  * "hyperbolic": { "enabled": true },
33
41
  * "scaleway": { "enabled": true },
@@ -74,6 +82,10 @@ const ENV_VARS = {
74
82
  cerebras: 'CEREBRAS_API_KEY',
75
83
  sambanova: 'SAMBANOVA_API_KEY',
76
84
  openrouter: 'OPENROUTER_API_KEY',
85
+ huggingface:['HUGGINGFACE_API_KEY', 'HF_TOKEN'],
86
+ replicate: 'REPLICATE_API_TOKEN',
87
+ deepinfra: ['DEEPINFRA_API_KEY', 'DEEPINFRA_TOKEN'],
88
+ fireworks: 'FIREWORKS_API_KEY',
77
89
  codestral: 'CODESTRAL_API_KEY',
78
90
  hyperbolic: 'HYPERBOLIC_API_KEY',
79
91
  scaleway: 'SCALEWAY_API_KEY',
@@ -163,7 +175,10 @@ export function saveConfig(config) {
163
175
  export function getApiKey(config, providerKey) {
164
176
  // 📖 Env var override — takes precedence over everything
165
177
  const envVar = ENV_VARS[providerKey]
166
- if (envVar && process.env[envVar]) return process.env[envVar]
178
+ const envCandidates = Array.isArray(envVar) ? envVar : [envVar]
179
+ for (const candidate of envCandidates) {
180
+ if (candidate && process.env[candidate]) return process.env[candidate]
181
+ }
167
182
 
168
183
  // 📖 Config file value
169
184
  const key = config?.apiKeys?.[providerKey]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "free-coding-models",
3
- "version": "0.1.63",
3
+ "version": "0.1.64",
4
4
  "description": "Find the fastest coding LLM models in seconds — ping free models from multiple providers, pick the best one for OpenCode, Cursor, or any AI coding assistant.",
5
5
  "keywords": [
6
6
  "nvidia",
package/sources.js CHANGED
@@ -27,8 +27,8 @@
27
27
  * 📖 Secondary: https://swe-rebench.com (independent evals, scores are lower)
28
28
  * 📖 Leaderboard tracker: https://www.marc0.dev/en/leaderboard
29
29
  *
30
- * @exports nvidiaNim, groq, cerebras, sambanova, openrouter, codestral, hyperbolic, scaleway, googleai — model arrays per provider
31
- * @exports sources — map of { nvidia, groq, cerebras, sambanova, openrouter, codestral, hyperbolic, scaleway, googleai } each with { name, url, models }
30
+ * @exports nvidiaNim, groq, cerebras, sambanova, openrouter, huggingface, replicate, deepinfra, fireworks, codestral, hyperbolic, scaleway, googleai — model arrays per provider
31
+ * @exports sources — map of { nvidia, groq, cerebras, sambanova, openrouter, huggingface, replicate, deepinfra, fireworks, codestral, hyperbolic, scaleway, googleai } each with { name, url, models }
32
32
  * @exports MODELS — flat array of [modelId, label, tier, sweScore, ctx, providerKey]
33
33
  *
34
34
  * 📖 MODELS now includes providerKey as 6th element so ping() knows which
@@ -139,13 +139,17 @@ export const sambanova = [
139
139
  ['Meta-Llama-3.3-70B-Instruct', 'Llama 3.3 70B', 'A-', '39.5%', '128k'],
140
140
  // ── B tier ──
141
141
  ['Meta-Llama-3.1-8B-Instruct', 'Llama 3.1 8B', 'B', '28.8%', '128k'],
142
+ // ── A tier — requested Llama3-Groq coding tuned family ──
143
+ ['Llama-3-Groq-70B-Tool-Use', 'Llama3-Groq 70B', 'A', '43.0%', '128k'],
142
144
  ]
143
145
 
144
146
  // 📖 OpenRouter source - https://openrouter.ai
145
147
  // 📖 Free :free models with shared quota — 50 free req/day
146
- // 📖 API keys at https://openrouter.ai/settings/keys
148
+ // 📖 API keys at https://openrouter.ai/keys
147
149
  export const openrouter = [
148
- ['qwen/qwen3-coder:free', 'Qwen3 Coder', 'S+', '70.6%', '256k'],
150
+ ['qwen/qwen3-coder:480b-free', 'Qwen3 Coder 480B', 'S+', '70.6%', '256k'],
151
+ ['mistralai/devstral-2-free', 'Devstral 2', 'S+', '72.2%', '256k'],
152
+ ['mimo-v2-flash-free', 'Mimo V2 Flash', 'A', '45.0%', '128k'],
149
153
  ['stepfun/step-3.5-flash:free', 'Step 3.5 Flash', 'S+', '74.4%', '256k'],
150
154
  ['deepseek/deepseek-r1-0528:free', 'DeepSeek R1 0528', 'S', '61.0%', '128k'],
151
155
  ['qwen/qwen3-next-80b-a3b-instruct:free', 'Qwen3 80B Instruct', 'S', '65.0%', '128k'],
@@ -155,6 +159,35 @@ export const openrouter = [
155
159
  ['meta-llama/llama-3.3-70b-instruct:free', 'Llama 3.3 70B', 'A-', '39.5%', '128k'],
156
160
  ]
157
161
 
162
+ // 📖 Hugging Face Inference source - https://huggingface.co
163
+ // 📖 OpenAI-compatible endpoint via router.huggingface.co/v1
164
+ // 📖 Free monthly credits on developer accounts (~$0.10) — token at https://huggingface.co/settings/tokens
165
+ export const huggingface = [
166
+ ['deepseek-ai/DeepSeek-V3-Coder', 'DeepSeek V3 Coder', 'S', '62.0%', '128k'],
167
+ ['bigcode/starcoder2-15b', 'StarCoder2 15B', 'B', '25.0%', '16k'],
168
+ ]
169
+
170
+ // 📖 Replicate source - https://replicate.com
171
+ // 📖 Uses predictions endpoint (not OpenAI chat-completions) with token auth
172
+ export const replicate = [
173
+ ['codellama/CodeLlama-70b-Instruct-hf', 'CodeLlama 70B', 'A-', '39.0%', '16k'],
174
+ ]
175
+
176
+ // 📖 DeepInfra source - https://deepinfra.com
177
+ // 📖 OpenAI-compatible endpoint: https://api.deepinfra.com/v1/openai/chat/completions
178
+ export const deepinfra = [
179
+ ['mistralai/Mixtral-8x22B-Instruct-v0.1', 'Mixtral Code', 'B+', '32.0%', '64k'],
180
+ ['meta-llama/Meta-Llama-3.1-70B-Instruct', 'Llama 3.1 70B', 'A-', '39.5%', '128k'],
181
+ ]
182
+
183
+ // 📖 Fireworks AI source - https://fireworks.ai
184
+ // 📖 OpenAI-compatible endpoint: https://api.fireworks.ai/inference/v1/chat/completions
185
+ // 📖 Free trial credits: $1 for new developers
186
+ export const fireworks = [
187
+ ['accounts/fireworks/models/deepseek-v3', 'DeepSeek V3', 'S', '62.0%', '128k'],
188
+ ['accounts/fireworks/models/deepseek-r1', 'DeepSeek R1', 'S', '61.0%', '128k'],
189
+ ]
190
+
158
191
  // 📖 Mistral Codestral source - https://codestral.mistral.ai
159
192
  // 📖 Free coding model — 30 req/min, 2000/day (phone number required for key)
160
193
  // 📖 API keys at https://codestral.mistral.ai
@@ -225,6 +258,26 @@ export const sources = {
225
258
  url: 'https://openrouter.ai/api/v1/chat/completions',
226
259
  models: openrouter,
227
260
  },
261
+ huggingface: {
262
+ name: 'Hugging Face',
263
+ url: 'https://router.huggingface.co/v1/chat/completions',
264
+ models: huggingface,
265
+ },
266
+ replicate: {
267
+ name: 'Replicate',
268
+ url: 'https://api.replicate.com/v1/predictions',
269
+ models: replicate,
270
+ },
271
+ deepinfra: {
272
+ name: 'DeepInfra',
273
+ url: 'https://api.deepinfra.com/v1/openai/chat/completions',
274
+ models: deepinfra,
275
+ },
276
+ fireworks: {
277
+ name: 'Fireworks',
278
+ url: 'https://api.fireworks.ai/inference/v1/chat/completions',
279
+ models: fireworks,
280
+ },
228
281
  codestral: {
229
282
  name: 'Codestral',
230
283
  url: 'https://codestral.mistral.ai/v1/chat/completions',