free-coding-models 0.5.95 โ 0.5.96
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 +12 -8
- package/bin/free-coding-models.js +2 -0
- package/changelog/v0.5.96.md +22 -0
- package/package.json +1 -1
- package/sources.js +50 -7
- package/src/core/config.js +16 -0
- package/src/core/i18n/index.js +49 -0
- package/src/core/i18n/locales/en.js +991 -0
- package/src/core/i18n/locales/zh-CN.js +991 -0
- package/src/core/opencode.js +15 -8
- package/src/core/ping.js +31 -1
- package/src/core/playground.js +20 -19
- package/src/core/provider-key-tester.js +1 -0
- package/src/core/provider-metadata.js +9 -0
- package/src/core/quota-capabilities.js +2 -0
- package/src/core/router-daemon.js +37 -6
- package/src/core/router-dashboard.js +15 -14
- package/src/core/router-v2/tui-dashboard.js +27 -26
- package/src/tui/app.js +2 -0
- package/src/tui/cli-help.js +45 -21
- package/src/tui/command-palette.js +19 -1
- package/src/tui/key-handler.js +21 -1
- package/src/tui/overlays.js +113 -107
- package/src/tui/render-helpers.js +4 -3
- package/src/tui/render-table.js +137 -132
- package/src/tui/theme.js +2 -0
- package/web/dist/assets/index-Cq9X7sN8.js +45 -0
- package/web/dist/assets/index-DZFdxy2N.css +1 -0
- package/web/dist/index.html +2 -2
- package/web/server.js +58 -6
- package/web/dist/assets/index-BOpGJzSO.js +0 -45
- package/web/dist/assets/index-CCaxIOti.css +0 -1
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
8
|
<strong>Find the fastest free coding model in seconds.</strong><br>
|
|
9
|
-
Live latency, stability and verdicts for
|
|
9
|
+
Live latency, stability and verdicts for 259 models from 25 free AI providers, then install the one you pick straight into your favorite coding tool.<br><br>
|
|
10
10
|
<strong>Works with:</strong> OpenCode CLI / Desktop / WebUI, OpenClaw, Crush, Goose, Aider, Kilo CLI, Qwen Code, OpenHands, Amp, Hermes, Continue, Cline, Xcode, Pi, ZCode, ForgeCode, Copilot, jcode, Caveman Code and more.
|
|
11
11
|
</p>
|
|
12
12
|
|
|
@@ -24,7 +24,7 @@ free-coding-models
|
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
<p align="center">
|
|
27
|
-
<sub>Then create a free account on one of the <a href="#-providers">
|
|
27
|
+
<sub>Then create a free account on one of the <a href="#-providers">25 providers</a> to grab an API key.</sub>
|
|
28
28
|
</p>
|
|
29
29
|
|
|
30
30
|
<p align="center">
|
|
@@ -35,7 +35,7 @@ free-coding-models
|
|
|
35
35
|
|
|
36
36
|
## ๐ก Why this tool?
|
|
37
37
|
|
|
38
|
-
There is a large catalog of free and free-limited coding models (**
|
|
38
|
+
There is a large catalog of free and free-limited coding models (**25 providers / 259 live models**, generated from [`sources.js`](./sources.js)). Which one is fastest *right now*? Which one is actually stable, versus just lucky on the last ping?
|
|
39
39
|
|
|
40
40
|
`free-coding-models` (FCM) answers that by pinging every model in parallel, showing live latency, and computing a **live Stability Score (0-100)** combining p95 latency, jitter, spike rate and uptime. Average latency alone is misleading: a model that randomly spikes to 6 seconds is not reliable.
|
|
41
41
|
|
|
@@ -87,17 +87,17 @@ free-coding-models --fiable # print the single most reliable model
|
|
|
87
87
|
|
|
88
88
|
## ๐ข Providers
|
|
89
89
|
|
|
90
|
-
**
|
|
90
|
+
**25 active providers / 259 live models**, sorted by live model count. Top 8:
|
|
91
91
|
|
|
92
92
|
| Provider | Models | Best tier | Env var |
|
|
93
93
|
|----------|--------|-----------|---------|
|
|
94
94
|
| [Alibaba DashScope](https://modelstudio.console.alibabacloud.com) | 29 | S+ | `DASHSCOPE_API_KEY` |
|
|
95
|
-
| [
|
|
95
|
+
| [Kilo](https://kilo.ai) | 21 | S+ | `KILO_API_KEY` |
|
|
96
96
|
| [OpenRouter](https://openrouter.ai/keys) | 21 | S+ | `OPENROUTER_API_KEY` |
|
|
97
|
-
| [
|
|
97
|
+
| [Pollinations AI](https://enter.pollinations.ai) | 21 | S+ | `POLLINATIONS_API_KEY` |
|
|
98
98
|
| [Ollama Cloud](https://ollama.com/settings/keys) | 20 | S+ | `OLLAMA_API_KEY` |
|
|
99
|
-
| [
|
|
100
|
-
| [
|
|
99
|
+
| [Cloudflare AI](https://dash.cloudflare.com) | 15 | S | `CLOUDFLARE_API_TOKEN` |
|
|
100
|
+
| [OVHcloud AI](https://endpoints.ai.cloud.ovh.net) | 13 | S+ | `OVH_AI_ENDPOINTS_ACCESS_TOKEN` |
|
|
101
101
|
| [NVIDIA NIM](https://build.nvidia.com) | 12 | S+ | `NVIDIA_API_KEY` |
|
|
102
102
|
|
|
103
103
|
> ๐งพ **What "free" means here:** free is a property of the *(provider, model)* pair, never of the provider as a whole. A row is listed only when that exact model id costs $0 to call through that provider (permanent free tier, `:free` variant, or free plan), verified live at audit time. The same open-weights model can be free on one host and paid on another - paid siblings are deliberately excluded. Full breakdown and badge legend: [`docs/providers.md`](./docs/providers.md).
|
|
@@ -129,6 +129,10 @@ The TUI is the heart of FCM: a live, sortable table of every model with real lat
|
|
|
129
129
|
|
|
130
130
|
Mouse support: click headers to sort, double-click a row to launch, right-click to favorite, scroll to navigate.
|
|
131
131
|
|
|
132
|
+
### Languages
|
|
133
|
+
|
|
134
|
+
The TUI and Web Dashboard support English (default) and Simplified Chinese (`zh-CN`). Change **Language** in Settings; the preference is shared and saved in your local config.
|
|
135
|
+
|
|
132
136
|
> ๐ **Full key table, mouse reference and workflows:** [`docs/tui.md`](./docs/tui.md) ยท Stability score and columns: [`docs/stability.md`](./docs/stability.md)
|
|
133
137
|
|
|
134
138
|
---
|
|
@@ -32,6 +32,7 @@ if (_configDirIdx !== -1 && process.argv[_configDirIdx + 1] && !process.argv[_co
|
|
|
32
32
|
import chalk from 'chalk';
|
|
33
33
|
import { parseArgs, TIER_LETTER_MAP } from '../src/core/utils.js';
|
|
34
34
|
import { loadConfig, saveConfig } from '../src/core/config.js';
|
|
35
|
+
import { setLocale } from '../src/core/i18n/index.js';
|
|
35
36
|
import { ensureTelemetryConfig } from '../src/core/telemetry.js';
|
|
36
37
|
import { ensureFavoritesConfig } from '../src/core/favorites.js';
|
|
37
38
|
import { buildCliHelpText } from '../src/tui/cli-help.js';
|
|
@@ -69,6 +70,7 @@ async function main() {
|
|
|
69
70
|
const cliArgs = parseArgs(process.argv);
|
|
70
71
|
|
|
71
72
|
if (cliArgs.helpMode) {
|
|
73
|
+
setLocale(loadConfig().settings?.language || 'en');
|
|
72
74
|
console.log();
|
|
73
75
|
console.log(buildCliHelpText({ chalk, title: 'free-coding-models' }));
|
|
74
76
|
console.log();
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Changelog v0.5.96 - 2026-09-26
|
|
2
|
+
|
|
3
|
+
Community release: 3 issues fixed, 5 pull requests merged (4 new contributors), 1 declined with reason. The catalog moves to **25 providers / 259 models** with the addition of onomeo and 2 Mistral coding models, minus 6 Pollinations models that silently left the free tier.
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **๐จ๐ณ Simplified Chinese localization** (PR #193 by @Mavlan): the TUI and the Web Dashboard now share one i18n layer, English stays the default and `zh-CN` is one setting away. The language preference is persisted in the shared config and exposed through the web Settings API. CLI help, settings, router, install, search and command-palette text are all localized, and CJK display-width handling is reused for TUI columns and overlay clipping.
|
|
8
|
+
- **onomeo provider** (PR #187 by @Owaid0723666, who also disclosed operating the service): 9 free coding models behind a daily check-in credit system (50k credits on day 1, up to 200k/day with a streak, no card). Wired end to end: env var `ONOMEO_API_KEY`, key test against `GET /v1/models`, quota badge `limited`, theme colors, Docker env passthrough and provider card on the website. Model ids verified live against the public catalog; premium models deliberately not listed.
|
|
9
|
+
- **Mistral coding model aliases** (PR #192 by @MrTodone): `mistral-code-latest` and `mistral-code-fim-latest` join the Mistral provider (both live-verified aliases of codestral-2508, 256k context), handy for clients that address the `mistral-code-*` family directly.
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- **Daemon dashboard "[object Object]" toast on every open** (issue #189): the daemon's embedded dashboard only stubbed `GET /api/tool-mode`, so the frontend's POST (URL write-back of `?toolMode=`) fell through to the OpenAI-style 404 catch-all. The daemon now handles GET and POST like `web/server.js`: GET returns the persisted `settings.preferredToolMode` instead of a hardcoded `opencode`, POST validates and persists the mode, and the frontend hook extracts nested `error.message` so any future failure reads as a sentence, not `[object Object]`. Covered by 4 new integration tests.
|
|
14
|
+
- **"Unrecognized flag: --model" when launching OpenCode v2** (issue #194): OpenCode v2 rejects flags its entry points don't know, and `opencode web` never accepted `--model` at all. FCM already wrote your pick into `opencode.json` (`config.model`) before every launch, so the flag was redundant: all launch paths (CLI, Web, Desktop, snap/flatpak) now spawn bare and let the config default apply. Works identically on OpenCode v1 and v2.
|
|
15
|
+
- **6 Pollinations models that quietly became paid** (issue #190, reported by @holakoyad): `minimax-m2.7`, `qwen/qwen3.8-max`, `google/gemini-3.1-pro-preview`, `nvidia/nemotron-3-ultra`, `qwen/qwen3-coder-next` and `gemma-4-31b` answer HTTP 200 with the error text embedded in the completion content on free keys, so status-code probes saw them as healthy. All 6 are removed (free alternatives remain on their original providers), and probes now sniff pollinations completions for the paid-Pollen message and report `402` instead of a fake `200`. Covered by 4 new unit tests.
|
|
16
|
+
- **Router v2 failover settings wiped on restart** (PR #191 by @MrTodone): `lastResortModel`, `bodyReadTimeoutMs`, `totalBudgetMs` and `contentValidation` were silently stripped by the config normalizer on every `load() โ save()` round-trip. Unknown failover keys now survive normalization (the daemon still sanitizes them at read time), with round-trip regression tests.
|
|
17
|
+
- **Web Settings API key deletion could resurrect itself** (PR #195 by @ming076): `/api/settings` saved keys with a plain `saveConfig()` whose on-disk merge could restore a just-deleted key. Key add/update/delete now goes through the provider-scoped `persistApiKeysForProvider()` helper, and persistence failures return a real error instead of a fake success.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- Provider counts: **24 โ 25 providers, 254 โ 259 models** (minus 6 paid Pollinations, plus 9 onomeo, plus 2 Mistral aliases). `docs/providers.md` regenerated from `sources.js`, README counts and Top-8 table refreshed, website catalog mirror synced.
|
|
22
|
+
- Declined the chalk 5 โ 6 dependabot bump (PR #183): chalk 6 requires Node 22 while FCM supports Node 18+ and CI tests Node 20. It will be revisited when the minimum Node version moves to 22.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "free-coding-models",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.96",
|
|
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
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
* ๐ Secondary: https://swe-rebench.com (independent evals, scores are lower)
|
|
30
30
|
* ๐ Leaderboard tracker: https://www.marc0.dev/en/leaderboard
|
|
31
31
|
*
|
|
32
|
-
* @exports nvidiaNim, groq, cerebras, sambanova, openrouter, githubModels, mistral, codestral, scaleway, googleai, zai, qwen, cloudflare, ovhcloud, opencodeZen, kilo, llm7, routeway, novita, ollamaCloud, pollinations, siliconflow, requesty, orcarouter, vercelGateway โ model arrays per active provider
|
|
32
|
+
* @exports nvidiaNim, groq, cerebras, sambanova, openrouter, githubModels, mistral, codestral, scaleway, googleai, zai, qwen, cloudflare, ovhcloud, opencodeZen, kilo, llm7, routeway, novita, ollamaCloud, pollinations, siliconflow, requesty, orcarouter, vercelGateway, onomeo โ model arrays per active provider
|
|
33
33
|
* @exports sources โ map of active free/free-limited providers, each with { name, url, models }
|
|
34
34
|
|
|
35
35
|
* @exports MODELS โ flat array of [modelId, label, tier, sweScore, ctx, providerKey]
|
|
@@ -227,6 +227,9 @@ export const mistral = [
|
|
|
227
227
|
['labs-leanstral-1-5', 'Leanstral 1.5', 'B', '-', '256k'], // Added (2026-09-22) โ free-listed on Mistral LP; targets Lean 4 theorem proving, niche coding use
|
|
228
228
|
['ministral-3b-2512', 'Ministral 3 3B', 'B', '-', '128k'], // Fixed (2026-09-16): ministral-3-3b-25-12 โ ministral-3b-2512; ctx 256k โ 128k (max_context_length 131072)
|
|
229
229
|
// Removed (2026-09-16): mistral-small-creative-25-12 (Mistral Small Creative) โ absent from the live catalog
|
|
230
|
+
// โโ Coding models (codestral-2508 aliases, live-verified 2026-09-22 via api.mistral.ai/v1/models) โโ
|
|
231
|
+
['mistral-code-latest', 'Mistral Code', 'A', '-', '256k'], // Added (2026-09-22): aliases codestral-2508 / codestral-latest; ctx 256000
|
|
232
|
+
['mistral-code-fim-latest', 'Mistral Code FIM', 'A', '-', '256k'], // Added (2026-09-22): FIM alias of codestral-2508; ctx 256000
|
|
230
233
|
]
|
|
231
234
|
|
|
232
235
|
// ๐ Mistral Codestral source - https://codestral.mistral.ai
|
|
@@ -553,21 +556,25 @@ export const novita = [
|
|
|
553
556
|
// ๐ Note (2026-09-21): the anonymous tier (text.pollinations.ai/models) now lists ONLY openai-fast;
|
|
554
557
|
// ๐ the models below need the free API key + Pollen credits (gen.pollinations.ai). Re-check the Pollen
|
|
555
558
|
// ๐ free-grant policy at next audit: if grants stop covering these models, this list must shrink to openai-fast.
|
|
559
|
+
// ๐ Note (2026-09-26, issue #190): a subset of models now requires PAID Pollen even with a free key โ they
|
|
560
|
+
// ๐ return HTTP 200 with the error embedded in the completion content ("not enough credits / needs paid
|
|
561
|
+
// ๐ Pollen"), so status-code-only probes see them as healthy. 6 such models were removed; when auditing
|
|
562
|
+
// ๐ Pollinations, sniff the completion content for that message, not just the HTTP status.
|
|
556
563
|
export const pollinations = [
|
|
557
564
|
// โโ S+ tier โ SWE-bench Verified โฅ70% โโ
|
|
558
565
|
['laguna', 'Laguna S 2.1', 'S+', '-', '1M'], // Fixed (2026-09-21): alias now resolves to poolside/laguna-s-2.1 (Laguna S 2.1), was Laguna XS.2; score cleared (S 2.1 has no published SWE-bench Verified)
|
|
559
|
-
|
|
566
|
+
// Removed (2026-09-26): minimax-m2.7 (MiniMax M2.7) โ now requires paid Pollen: HTTP 200 with an embedded "not enough credits, this model needs paid Pollen" error on free-tier keys (issue #190); replacement: minimax (MiniMax M3, still free via Pollen grants)
|
|
560
567
|
['glm-5.3', 'Z.ai GLM-5.3', 'S+', '-', '1M'],
|
|
561
568
|
['kimi', 'Moonshot Kimi K2.6', 'S+', '80.2%', '262k'],
|
|
562
569
|
['minimax', 'MiniMax M3', 'S+', '80.5%', '524k'],
|
|
563
570
|
['moonshotai/kimi-k3', 'Moonshot Kimi K3', 'S+', '76.8%', '1M'], // Added (2026-09-21) โ canonical id, healthy on live /v1/models; score follows the Kimi K3 entry on NVIDIA
|
|
564
571
|
['deepseek/deepseek-v4-pro', 'DeepSeek V4 Pro', 'S+', '-', '1M'], // Added (2026-09-21) โ canonical id, healthy on live /v1/models
|
|
565
|
-
|
|
566
|
-
|
|
572
|
+
// Removed (2026-09-26): qwen/qwen3.8-max (Qwen3.8 Max) โ requires paid Pollen (embedded credits error on free-tier keys, issue #190); still free on DashScope
|
|
573
|
+
// Removed (2026-09-26): google/gemini-3.1-pro-preview (Gemini 3.1 Pro Preview) โ requires paid Pollen (embedded credits error on free-tier keys, issue #190); no free Gemini Pro tier anywhere
|
|
567
574
|
['openai/gpt-5.5', 'OpenAI GPT-5.5', 'S+', '-', '1M'], // Added (2026-09-21) โ canonical id, healthy on live /v1/models
|
|
568
575
|
['openai/gpt-6-astra', 'OpenAI GPT-6 Astra', 'S+', '-', '1M'], // Added (2026-09-21) โ canonical id, healthy on live /v1/models
|
|
569
576
|
['z-ai/glm-5.3-flash', 'Z.ai GLM-5.3 Flash', 'S+', '-', '1M'], // Added (2026-09-21) โ canonical id, healthy on live /v1/models
|
|
570
|
-
|
|
577
|
+
// Removed (2026-09-26): nvidia/nemotron-3-ultra (NVIDIA Nemotron 3 Ultra) โ requires paid Pollen (embedded credits error on free-tier keys, issue #190); still free on NVIDIA NIM
|
|
571
578
|
['anthropic/claude-opus-5', 'Claude Opus 5', 'S+', '-', '1M'], // Added (2026-09-22) โ healthy on live /v1/models, free via Pollen credits
|
|
572
579
|
// โโ S tier โ SWE-bench Verified 60โ70% โโ
|
|
573
580
|
['anthropic/claude-sonnet-5', 'Claude Sonnet 5', 'S', '-', '1M'], // Added (2026-09-22) โ healthy on live /v1/models, free via Pollen credits
|
|
@@ -575,12 +582,12 @@ export const pollinations = [
|
|
|
575
582
|
['deepseek', 'DeepSeek V4 Flash', 'S+', '79.0%', '1M'], // Fixed (2026-09-21): alias now resolves to deepseek/deepseek-v4-flash (V4 Flash 0731), was V3; re-scored per the V4 Flash family entry
|
|
576
583
|
['kimi-code', 'Kimi K2.7 Code', 'S', '60.4%', '262k'], // Fixed (2026-09-21): alias now resolves to moonshotai/kimi-k2.7-code, was K2 Code
|
|
577
584
|
['openai', 'OpenAI GPT-5.4 Nano', 'B+', '-', '400k'], // Fixed (2026-09-21): alias now resolves to openai/gpt-5.4-nano (was a generic GPT alias); re-tiered to the nano class
|
|
578
|
-
|
|
585
|
+
// Removed (2026-09-26): qwen/qwen3-coder-next (Qwen3 Coder Next) โ requires paid Pollen (embedded credits error on free-tier keys, issue #190); still free on DashScope
|
|
579
586
|
['openai/gpt-5.6-luna', 'OpenAI GPT-5.6 Luna', 'S', '-', '1M'], // Added (2026-09-21) โ canonical id, healthy on live /v1/models
|
|
580
587
|
// โโ A+ tier โ SWE-bench Verified 50โ60% โโ
|
|
581
588
|
['deepseek/deepseek-v4.1-flash', 'DeepSeek V4.1 Flash', 'A+', '-', '1M'], // Added (2026-09-22) โ healthy on live /v1/models, successor to V4 Flash
|
|
582
589
|
['meituan/longcat-2.0', 'LongCat 2.0', 'A', '-', '1M'], // Added (2026-09-22) โ healthy on live /v1/models, new MoE agentic/coding model on the network
|
|
583
|
-
|
|
590
|
+
// Removed (2026-09-26): gemma-4-31b (Gemma 4 31B) โ requires paid Pollen (embedded credits error on free-tier keys, issue #190); the free Gemma route is NVIDIA NIM
|
|
584
591
|
['gpt-oss', 'GPT OSS 20B', 'A+', '50.3%', '131k'],
|
|
585
592
|
['qwen3.7-flash', 'Qwen3.7 Flash', 'A+', '-', '1M'],
|
|
586
593
|
// โโ B+ tier โโ
|
|
@@ -706,6 +713,35 @@ export const ollamaCloud = [
|
|
|
706
713
|
['nemotron-3-nano:30b', 'Nemotron 3 Nano 30B', 'A-', '38.8%', '1M'],
|
|
707
714
|
]
|
|
708
715
|
|
|
716
|
+
// ๐ onomeo source - https://onomeo.com/docs
|
|
717
|
+
// ๐ OpenAI-compatible gateway: https://onomeo.com/v1/chat/completions (streaming + tool calls)
|
|
718
|
+
// ๐ Public beta: not every feature is guaranteed to work; feedback is welcome at https://onomeo.com/feedback.
|
|
719
|
+
// ๐ Free credits come from a daily check-in, no card: 50,000 on day 1, rising to 200,000/day from
|
|
720
|
+
// ๐ day 7 of a streak (a missed day resets it). Every call spends credits and the cost per reply
|
|
721
|
+
// ๐ varies by model (https://onomeo.com/models), so this is a small allowance.
|
|
722
|
+
// ๐ Premium models are not listed; each account that has not paid can spend up to 50,000 credits/day on them.
|
|
723
|
+
// ๐ Optional: $5 one-time buys 1,000,000 credits. Limits: 12 req/min per key, 60 req per 5 hours
|
|
724
|
+
// ๐ per account, 120 per 5 hours per IP, and a site-wide pool of 450 per 5 hours for accounts
|
|
725
|
+
// ๐ that have not bought credits.
|
|
726
|
+
// ๐ Some upstreams may train on prompts; each model page (https://onomeo.com/models/<id>) says which.
|
|
727
|
+
// ๐ Ids and ctx checked 2026-09-26 against the public https://onomeo.com/api/info
|
|
728
|
+
// ๐ (`models`, `modelFacts`); /v1/models needs a key.
|
|
729
|
+
export const onomeo = [
|
|
730
|
+
// โโ S+ tier โ SWE-bench Verified โฅ70% โโ
|
|
731
|
+
['deepseek-v4-flash', 'DeepSeek V4 Flash', 'S+', '79.0%', '1M'],
|
|
732
|
+
['glm-5.2', 'GLM 5.2', 'S+', '82.8%', '1M'],
|
|
733
|
+
// โโ S tier โ SWE-bench Verified 60โ70% โโ
|
|
734
|
+
['gemini-3.1-flash-lite', 'Gemini 3.1 Flash Lite', 'S', '62.8%', '1M'],
|
|
735
|
+
['@cf/openai/gpt-oss-120b', 'GPT OSS 120B', 'S', '62.4%', '128k'],
|
|
736
|
+
['nvidia/nemotron-3-super-120b-a12b', 'Nemotron 3 Super', 'S', '60.5%', '262k'],
|
|
737
|
+
// โโ A+ tier โ SWE-bench Verified 50โ60% โโ
|
|
738
|
+
['openai/gpt-oss-20b', 'GPT OSS 20B', 'A+', '50.3%', '128k'],
|
|
739
|
+
['z-ai/glm-5.3-flash-free', 'GLM-5.3 Flash', 'A+', '-', '1M'],
|
|
740
|
+
['stepfun/step-3.7-flash:free', 'Step 3.7 Flash', 'A+', '-', '256k'],
|
|
741
|
+
// โโ A tier โ SWE-bench Verified 40โ50% โโ
|
|
742
|
+
['codestral-latest', 'Codestral Latest', 'A', '40.0%', '256k'],
|
|
743
|
+
]
|
|
744
|
+
|
|
709
745
|
// ๐ All sources combined - used by the main script
|
|
710
746
|
// ๐ Each source has: name (display), url (API endpoint), models (array of model tuples)
|
|
711
747
|
// ๐ Providers ordered by generosity of free tier (most generous first)
|
|
@@ -898,6 +934,13 @@ export const sources = {
|
|
|
898
934
|
quotaCode: 'free',
|
|
899
935
|
models: ollamaCloud,
|
|
900
936
|
},
|
|
937
|
+
onomeo: {
|
|
938
|
+
name: 'onomeo',
|
|
939
|
+
url: 'https://onomeo.com/v1/chat/completions',
|
|
940
|
+
quota: 'Daily check-in credits ยท 12 RPM ยท 60 req/5h',
|
|
941
|
+
quotaCode: 'limited',
|
|
942
|
+
models: onomeo,
|
|
943
|
+
},
|
|
901
944
|
}
|
|
902
945
|
|
|
903
946
|
// ๐ Flatten all models from all sources โ each entry includes providerKey as 6th element
|
package/src/core/config.js
CHANGED
|
@@ -105,6 +105,7 @@ import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync, statSy
|
|
|
105
105
|
import { homedir } from 'node:os'
|
|
106
106
|
import { join, resolve, dirname } from 'node:path'
|
|
107
107
|
import { syncShellEnv } from './shell-env.js'
|
|
108
|
+
import { DEFAULT_LOCALE, normalizeLocale } from './i18n/index.js'
|
|
108
109
|
|
|
109
110
|
// ๐ Expand leading ~/ or bare ~ to homedir so --config-dir ~/.config/... works.
|
|
110
111
|
// ๐ resolve() alone does NOT expand tilde, it would treat ~ as a literal folder.
|
|
@@ -188,6 +189,7 @@ const ENV_VARS = {
|
|
|
188
189
|
requesty: 'REQUESTY_API_KEY',
|
|
189
190
|
orcarouter: 'ORCAROUTER_API_KEY',
|
|
190
191
|
'vercel-gateway': ['VERCEL_AI_GATEWAY_API_KEY', 'AI_GATEWAY_API_KEY'],
|
|
192
|
+
onomeo: 'ONOMEO_API_KEY',
|
|
191
193
|
}
|
|
192
194
|
|
|
193
195
|
// ๐ Smart Router defaults are intentionally conservative: balanced probing,
|
|
@@ -331,6 +333,7 @@ function normalizeSettingsSection(settings) {
|
|
|
331
333
|
delete safeSettings.footerHidden
|
|
332
334
|
return {
|
|
333
335
|
...safeSettings,
|
|
336
|
+
language: normalizeLocale(safeSettings.language),
|
|
334
337
|
hideUnconfiguredModels: typeof safeSettings.hideUnconfiguredModels === 'boolean' ? safeSettings.hideUnconfiguredModels : true,
|
|
335
338
|
favoritesPinnedAndSticky: typeof safeSettings.favoritesPinnedAndSticky === 'boolean' ? safeSettings.favoritesPinnedAndSticky : false,
|
|
336
339
|
runAiSpeedTestOnStartup: typeof safeSettings.runAiSpeedTestOnStartup === 'boolean' ? safeSettings.runAiSpeedTestOnStartup : false,
|
|
@@ -456,10 +459,22 @@ function normalizeRouterFailover(failover, routerFallback = {}) {
|
|
|
456
459
|
const rawStreamStall = numberOrNull(safeFailover.streamStallTimeoutMs) ?? numberOrNull(routerFallback.streamStallTimeoutMs)
|
|
457
460
|
const rawRequestTimeout = numberOrNull(safeFailover.requestTimeoutMs) ?? numberOrNull(routerFallback.requestTimeoutMs)
|
|
458
461
|
|
|
462
|
+
// ๐ router-v2 documented failover fields (lastResortModel,
|
|
463
|
+
// bodyReadTimeoutMs, totalBudgetMs, contentValidation โ docs/router-v2.md)
|
|
464
|
+
// are read by the daemon at runtime but are not v1 knobs normalized here.
|
|
465
|
+
// Carry any unknown keys through so they survive load()/save() round-trips
|
|
466
|
+
// instead of being silently dropped from the on-disk config.
|
|
467
|
+
const v1FailoverKeys = new Set(['maxRetries', 'streamStallTimeoutMs', 'requestTimeoutMs'])
|
|
468
|
+
const extras = {}
|
|
469
|
+
for (const key of Object.keys(safeFailover)) {
|
|
470
|
+
if (!v1FailoverKeys.has(key)) extras[key] = safeFailover[key]
|
|
471
|
+
}
|
|
472
|
+
|
|
459
473
|
return {
|
|
460
474
|
maxRetries: normalizePositiveInteger(rawMaxRetries, DEFAULT_ROUTER_SETTINGS.failover.maxRetries, { min: 1, max: 20 }),
|
|
461
475
|
streamStallTimeoutMs: normalizePositiveInteger(rawStreamStall, DEFAULT_ROUTER_SETTINGS.failover.streamStallTimeoutMs, { min: 1000, max: 120000 }),
|
|
462
476
|
requestTimeoutMs: normalizePositiveInteger(rawRequestTimeout, DEFAULT_ROUTER_SETTINGS.failover.requestTimeoutMs, { min: 1000, max: 300000 }),
|
|
477
|
+
...extras,
|
|
463
478
|
}
|
|
464
479
|
}
|
|
465
480
|
|
|
@@ -1295,6 +1310,7 @@ export function isProviderEnabled(config, providerKey) {
|
|
|
1295
1310
|
*/
|
|
1296
1311
|
export function _emptyProfileSettings() {
|
|
1297
1312
|
return {
|
|
1313
|
+
language: DEFAULT_LOCALE,
|
|
1298
1314
|
tierFilter: null, // ๐ null = show all tiers, or 'S'|'A'|'B'|'C'|'D'
|
|
1299
1315
|
sortColumn: 'avg', // ๐ default sort column
|
|
1300
1316
|
sortAsc: true, // ๐ true = ascending (fastest first for latency)
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import en from './locales/en.js'
|
|
2
|
+
import zhCN from './locales/zh-CN.js'
|
|
3
|
+
|
|
4
|
+
export const DEFAULT_LOCALE = 'en'
|
|
5
|
+
export const SUPPORTED_LOCALES = Object.freeze(['en', 'zh-CN'])
|
|
6
|
+
export const LOCALE_OPTIONS = Object.freeze([
|
|
7
|
+
Object.freeze({ value: 'en', labelKey: 'common.language.english' }),
|
|
8
|
+
Object.freeze({ value: 'zh-CN', labelKey: 'common.language.simplifiedChinese' }),
|
|
9
|
+
])
|
|
10
|
+
|
|
11
|
+
const dictionaries = Object.freeze({ en, 'zh-CN': zhCN })
|
|
12
|
+
let activeLocale = DEFAULT_LOCALE
|
|
13
|
+
|
|
14
|
+
export function normalizeLocale(locale) {
|
|
15
|
+
return SUPPORTED_LOCALES.includes(locale) ? locale : DEFAULT_LOCALE
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function getLocale() {
|
|
19
|
+
return activeLocale
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function setLocale(locale) {
|
|
23
|
+
activeLocale = normalizeLocale(locale)
|
|
24
|
+
return activeLocale
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function createTranslator(resources = dictionaries) {
|
|
28
|
+
return (locale, key, values = {}) => {
|
|
29
|
+
if (typeof key !== 'string') return ''
|
|
30
|
+
const active = normalizeLocale(locale)
|
|
31
|
+
const localized = resources[active]?.[key]
|
|
32
|
+
const fallback = resources[DEFAULT_LOCALE]?.[key]
|
|
33
|
+
const template = typeof localized === 'string' ? localized : typeof fallback === 'string' ? fallback : key
|
|
34
|
+
return template.replace(/\{([\w.]+)\}/g, (match, name) => {
|
|
35
|
+
const value = values && Object.prototype.hasOwnProperty.call(values, name) ? values[name] : undefined
|
|
36
|
+
return value === null || value === undefined ? match : String(value)
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const translate = createTranslator()
|
|
42
|
+
|
|
43
|
+
export function t(key, values = {}) {
|
|
44
|
+
return translate(activeLocale, key, values)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function hasTranslation(locale, key) {
|
|
48
|
+
return typeof dictionaries[normalizeLocale(locale)]?.[key] === 'string'
|
|
49
|
+
}
|