My-Free-LLM-API / server /src /db /index.ts
tayyabimam's picture
Fresh build completely clean of binaries
35ac985
Raw
History Blame Contribute Delete
75.8 kB
import crypto from 'crypto';
import Database from 'better-sqlite3';
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import { initEncryptionKey } from '../lib/crypto.js';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const DB_PATH = path.resolve(__dirname, '../../data/freeapi.db');
let db: Database.Database;
export function getDb(): Database.Database {
if (!db) {
throw new Error('Database not initialized. Call initDb() first.');
}
return db;
}
export function initDb(dbPath?: string): Database.Database {
const resolvedPath = dbPath ?? DB_PATH;
const isMemory = resolvedPath === ':memory:';
if (!isMemory) {
const dataDir = path.dirname(resolvedPath);
if (!fs.existsSync(dataDir)) {
fs.mkdirSync(dataDir, { recursive: true });
}
}
db = new Database(resolvedPath);
if (!isMemory) db.pragma('journal_mode = WAL');
db.pragma('foreign_keys = ON');
createTables(db);
initEncryptionKey(db);
seedModels(db);
migrateModels(db);
migrateModelsV2(db);
migrateModelsV3Ranks(db);
migrateModelsV4(db);
migrateModelsV5(db);
migrateModelsV6(db);
migrateModelsV7(db);
migrateModelsV8(db);
migrateModelsV9(db);
migrateModelsV10(db);
migrateModelsV11(db);
migrateModelsV12(db);
migrateModelsV13(db);
migrateModelsV14(db);
ensureUnifiedKey(db);
console.log(`Database initialized at ${resolvedPath}`);
return db;
}
function createTables(db: Database.Database) {
db.exec(`
CREATE TABLE IF NOT EXISTS models (
id INTEGER PRIMARY KEY AUTOINCREMENT,
platform TEXT NOT NULL,
model_id TEXT NOT NULL,
display_name TEXT NOT NULL,
intelligence_rank INTEGER NOT NULL,
speed_rank INTEGER NOT NULL,
size_label TEXT NOT NULL DEFAULT '',
rpm_limit INTEGER,
rpd_limit INTEGER,
tpm_limit INTEGER,
tpd_limit INTEGER,
monthly_token_budget TEXT NOT NULL DEFAULT '',
context_window INTEGER,
enabled INTEGER NOT NULL DEFAULT 1,
UNIQUE(platform, model_id)
);
CREATE TABLE IF NOT EXISTS api_keys (
id INTEGER PRIMARY KEY AUTOINCREMENT,
platform TEXT NOT NULL,
label TEXT NOT NULL DEFAULT '',
encrypted_key TEXT NOT NULL,
iv TEXT NOT NULL,
auth_tag TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'unknown',
enabled INTEGER NOT NULL DEFAULT 1,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
last_checked_at TEXT
);
CREATE TABLE IF NOT EXISTS requests (
id INTEGER PRIMARY KEY AUTOINCREMENT,
platform TEXT NOT NULL,
model_id TEXT NOT NULL,
key_id INTEGER,
status TEXT NOT NULL,
input_tokens INTEGER NOT NULL DEFAULT 0,
output_tokens INTEGER NOT NULL DEFAULT 0,
latency_ms INTEGER NOT NULL DEFAULT 0,
error TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS rate_limit_usage (
id INTEGER PRIMARY KEY AUTOINCREMENT,
platform TEXT NOT NULL,
model_id TEXT NOT NULL,
key_id INTEGER NOT NULL,
kind TEXT NOT NULL CHECK (kind IN ('request', 'tokens')),
tokens INTEGER NOT NULL DEFAULT 0,
created_at_ms INTEGER NOT NULL,
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS rate_limit_cooldowns (
platform TEXT NOT NULL,
model_id TEXT NOT NULL,
key_id INTEGER NOT NULL,
expires_at_ms INTEGER NOT NULL,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
PRIMARY KEY (platform, model_id, key_id)
);
CREATE TABLE IF NOT EXISTS fallback_config (
id INTEGER PRIMARY KEY AUTOINCREMENT,
model_db_id INTEGER NOT NULL REFERENCES models(id),
priority INTEGER NOT NULL,
enabled INTEGER NOT NULL DEFAULT 1,
UNIQUE(model_db_id)
);
CREATE TABLE IF NOT EXISTS settings (
key TEXT PRIMARY KEY,
value TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_requests_created_at ON requests(created_at);
CREATE INDEX IF NOT EXISTS idx_requests_platform ON requests(platform);
CREATE INDEX IF NOT EXISTS idx_rate_limit_usage_lookup ON rate_limit_usage(platform, model_id, key_id, kind, created_at_ms);
CREATE INDEX IF NOT EXISTS idx_rate_limit_cooldowns_expires ON rate_limit_cooldowns(expires_at_ms);
CREATE INDEX IF NOT EXISTS idx_api_keys_platform ON api_keys(platform);
`);
ensureRequestKeyIdColumn(db);
}
function ensureRequestKeyIdColumn(db: Database.Database) {
const columns = db.prepare('PRAGMA table_info(requests)').all() as { name: string }[];
if (!columns.some(col => col.name === 'key_id')) {
db.prepare('ALTER TABLE requests ADD COLUMN key_id INTEGER').run();
}
db.prepare('CREATE INDEX IF NOT EXISTS idx_requests_key_id ON requests(key_id)').run();
}
function seedModels(db: Database.Database) {
const count = db.prepare('SELECT COUNT(*) as cnt FROM models').get() as { cnt: number };
if (count.cnt > 0) return;
const insert = db.prepare(`
INSERT INTO models (platform, model_id, display_name, intelligence_rank, speed_rank, size_label, rpm_limit, rpd_limit, tpm_limit, tpd_limit, monthly_token_budget, context_window)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`);
// NOTE: Limits current as of April 2026. See migrateModels() for in-place updates.
const models = [
// Google β€” gemini-2.5-flash free quotas were cut Dec 2025 (now ~20 RPD, budget much lower than before)
['google', 'gemini-2.5-pro', 'Gemini 2.5 Pro', 1, 8, 'Frontier', 5, 100, 250000, null, '~12M', 1048576],
['google', 'gemini-2.5-flash', 'Gemini 2.5 Flash', 4, 5, 'Large', 10, 20, 250000, null, '~3M', 1048576],
['google', 'gemini-2.5-flash-lite', 'Gemini 2.5 Flash-Lite', 8, 3, 'Medium', 15, 1000, 250000, null, '~120M', 1048576],
// OpenRouter β€” upgraded DeepSeek R1 -> V3.1 (stronger reasoning); default RPD ~200
['openrouter', 'deepseek/deepseek-v3.1:free', 'DeepSeek V3.1 (free)', 2, 10, 'Frontier', 20, 200, null, null, '~6M', 131072],
['openrouter', 'moonshotai/kimi-k2:free', 'Kimi K2 (free)', 2, 9, 'Frontier', 20, 200, null, null, '~6M', 131072],
['openrouter', 'qwen/qwen3-coder:free', 'Qwen3 Coder (free)', 3, 9, 'Frontier', 20, 200, null, null, '~6M', 262144],
['openrouter', 'z-ai/glm-4.5-air:free', 'GLM-4.5 Air (free)', 4, 9, 'Large', 20, 200, null, null, '~6M', 131072],
// Cerebras β€” same 30 RPM / 1M TPD free pool; adding frontier coder, Llama 4 Maverick, GPT-OSS
['cerebras', 'qwen-3-coder-480b', 'Qwen3-Coder 480B', 2, 1, 'Frontier', 30, null, 60000, 1000000, '~30M', 131072],
['cerebras', 'llama-4-maverick-17b-128e-instruct', 'Llama 4 Maverick', 3, 1, 'Frontier', 30, null, 60000, 1000000, '~30M', 131072],
['cerebras', 'qwen3-235b', 'Qwen3 235B', 3, 1, 'Large', 30, null, 60000, 1000000, '~30M', 8192],
['cerebras', 'gpt-oss-120b', 'GPT-OSS 120B', 3, 1, 'Large', 30, null, 60000, 1000000, '~30M', 131072],
// GitHub Models β€” GPT-4o replaced with GPT-5 (same free tier key)
['github', 'openai/gpt-5', 'GPT-5 (GitHub)', 1, 7, 'Frontier', 10, 50, null, null, '~18M', 128000],
// SambaNova β€” 70B RPM bumped to 20
['sambanova', 'Meta-Llama-3.3-70B-Instruct', 'Llama 3.3 70B', 6, 9, 'Large', 20, null, null, 200000, '~6M', 8192],
// Mistral β€” Experiment pool ~1B tokens/mo shared across all models
['mistral', 'mistral-large-latest', 'Mistral Large 3', 7, 8, 'Large', 2, null, 500000, null, '~50-100M', 131072],
['mistral', 'magistral-medium-latest', 'Magistral Medium', 4, 8, 'Large', 2, null, 500000, null, '~50-100M', 40000],
['mistral', 'codestral-latest', 'Codestral', 6, 6, 'Medium', 2, null, 500000, null, '~50-100M', 32000],
// Groq β€” scout TPM corrected to 6k (not 30k)
['groq', 'llama-3.3-70b-versatile', 'Llama 3.3 70B', 9, 2, 'Medium', 30, 1000, 6000, 500000, '~15M', 131072],
['groq', 'llama-4-scout-17b-16e-instruct', 'Llama 4 Scout', 10, 2, 'Medium', 30, 1000, 6000, 1000000, '~30M', 131072],
// NVIDIA NIM β€” moved to credit-based model in 2025; no longer truly recurring monthly. Disabled by default.
['nvidia', 'meta/llama-3.1-70b-instruct', 'Llama 3.1 70B (NV)', 11, 6, 'Large', 40, null, null, null, 'credits-based', 131072],
// Cohere β€” trial tier is 1000 calls/mo total β†’ realistic budget 1-2M
['cohere', 'command-r-plus-08-2024', 'Command R+ (08-2024)', 12, 11, 'Large', 20, 33, null, null, '~1-2M', 131072],
['cloudflare', '@cf/meta/llama-3.1-70b-instruct', 'Llama 3.1 70B (CF)', 13, 11, 'Medium', null, null, null, null, '~18-45M', 131072],
// Hugging Face β€” free Inference credits are ~$0.10/mo β†’ budget closer to 1-3M on a 70B model
['huggingface', 'accounts/fireworks/models/llama-v3p3-70b-instruct', 'Llama 3.3 70B (HF)', 14, 11, 'Medium', null, null, null, null, '~1-3M', 131072],
// New providers β€” recurring monthly free tiers, no card required
['zhipu', 'glm-4.5-flash', 'GLM-4.5 Flash', 5, 4, 'Large', null, null, null, 1000000, '~30M', 131072],
['moonshot', 'kimi-latest', 'Kimi Latest', 4, 8, 'Large', 60, null, null, 500000, '~15M', 200000],
['minimax', 'MiniMax-M1', 'MiniMax M1', 5, 8, 'Large', 20, null, 1000000, null, '~30M', 200000],
];
const insertMany = db.transaction(() => {
for (const m of models) {
insert.run(...m);
}
});
insertMany();
// Seed default fallback config from models
const allModels = db.prepare('SELECT id, intelligence_rank FROM models ORDER BY intelligence_rank ASC').all() as { id: number; intelligence_rank: number }[];
const insertFallback = db.prepare('INSERT INTO fallback_config (model_db_id, priority, enabled) VALUES (?, ?, 1)');
const insertFallbacks = db.transaction(() => {
for (let i = 0; i < allModels.length; i++) {
insertFallback.run(allModels[i].id, i + 1);
}
});
insertFallbacks();
console.log(`Seeded ${models.length} models and fallback config`);
}
/**
* Idempotent migration to bring existing DBs up to the April 2026 pool.
* Covers: replaces outdated models (DeepSeek R1 β†’ V3.1, GPT-4o β†’ GPT-5),
* corrects stale rate-limits / monthly budgets, adds new smarter models
* and three new providers (Zhipu, Moonshot, MiniMax).
*/
function migrateModels(db: Database.Database) {
// 1) Replace outdated models in-place (preserves fallback_config & any references)
const renames: Array<[string, string, string, string, number, string, number | null, number | null, number]> = [
// platform, oldModelId, newModelId, newDisplayName, intelligenceRank, monthlyBudget, rpdLimit, contextWindow, sizeLabelPriority(unused)
];
const renameStmt = db.prepare(`
UPDATE models
SET model_id = ?, display_name = ?, intelligence_rank = ?,
monthly_token_budget = ?, rpd_limit = COALESCE(?, rpd_limit),
context_window = COALESCE(?, context_window),
size_label = COALESCE(?, size_label)
WHERE platform = ? AND model_id = ?
`);
// DeepSeek R1 (free) -> DeepSeek V3.1 (free)
renameStmt.run('deepseek/deepseek-v3.1:free', 'DeepSeek V3.1 (free)', 2, '~6M', 200, 131072, 'Frontier', 'openrouter', 'deepseek/deepseek-r1:free');
// GitHub GPT-4o -> GPT-5
renameStmt.run('openai/gpt-5', 'GPT-5 (GitHub)', 1, '~18M', null, 128000, 'Frontier', 'github', 'gpt-4o');
// 2) Correct stale limits / budgets on existing rows
db.prepare(`UPDATE models SET rpd_limit = 20, monthly_token_budget = '~3M' WHERE platform = 'google' AND model_id = 'gemini-2.5-flash'`).run();
db.prepare(`UPDATE models SET rpm_limit = 20 WHERE platform = 'sambanova' AND model_id = 'Meta-Llama-3.3-70B-Instruct'`).run();
db.prepare(`UPDATE models SET tpm_limit = 6000 WHERE platform = 'groq' AND model_id = 'llama-4-scout-17b-16e-instruct'`).run();
db.prepare(`UPDATE models SET monthly_token_budget = '~1-2M' WHERE platform = 'cohere' AND model_id = 'command-r-plus-08-2024'`).run();
db.prepare(`UPDATE models SET monthly_token_budget = '~1-3M' WHERE platform = 'huggingface' AND model_id = 'accounts/fireworks/models/llama-v3p3-70b-instruct'`).run();
// NVIDIA moved to credit model β€” disable and label accordingly
db.prepare(`UPDATE models SET monthly_token_budget = 'credits-based', enabled = 0 WHERE platform = 'nvidia' AND model_id = 'meta/llama-3.1-70b-instruct'`).run();
// 3) Insert new models (UNIQUE(platform, model_id) makes this idempotent)
const insert = db.prepare(`
INSERT OR IGNORE INTO models (platform, model_id, display_name, intelligence_rank, speed_rank, size_label, rpm_limit, rpd_limit, tpm_limit, tpd_limit, monthly_token_budget, context_window)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`);
const newModels: Array<[string, string, string, number, number, string, number | null, number | null, number | null, number | null, string, number | null]> = [
// Cerebras β€” same free pool as qwen3-235b
['cerebras', 'qwen-3-coder-480b', 'Qwen3-Coder 480B', 2, 1, 'Frontier', 30, null, 60000, 1000000, '~30M', 131072],
['cerebras', 'llama-4-maverick-17b-128e-instruct', 'Llama 4 Maverick', 3, 1, 'Frontier', 30, null, 60000, 1000000, '~30M', 131072],
['cerebras', 'gpt-oss-120b', 'GPT-OSS 120B', 3, 1, 'Large', 30, null, 60000, 1000000, '~30M', 131072],
// OpenRouter free tier
['openrouter', 'deepseek/deepseek-v3.1:free', 'DeepSeek V3.1 (free)', 2, 10, 'Frontier', 20, 200, null, null, '~6M', 131072],
['openrouter', 'moonshotai/kimi-k2:free', 'Kimi K2 (free)', 2, 9, 'Frontier', 20, 200, null, null, '~6M', 131072],
['openrouter', 'qwen/qwen3-coder:free', 'Qwen3 Coder (free)', 3, 9, 'Frontier', 20, 200, null, null, '~6M', 262144],
['openrouter', 'z-ai/glm-4.5-air:free', 'GLM-4.5 Air (free)', 4, 9, 'Large', 20, 200, null, null, '~6M', 131072],
// Mistral Experiment pool β€” shared ~1B/mo across models
['mistral', 'magistral-medium-latest', 'Magistral Medium', 4, 8, 'Large', 2, null, 500000, null, '~50-100M', 40000],
['mistral', 'codestral-latest', 'Codestral', 6, 6, 'Medium', 2, null, 500000, null, '~50-100M', 32000],
// New providers
['zhipu', 'glm-4.5-flash', 'GLM-4.5 Flash', 5, 4, 'Large', null, null, null, 1000000, '~30M', 131072],
['moonshot', 'kimi-latest', 'Kimi Latest', 4, 8, 'Large', 60, null, null, 500000, '~15M', 200000],
['minimax', 'MiniMax-M1', 'MiniMax M1', 5, 8, 'Large', 20, null, 1000000, null, '~30M', 200000],
];
const apply = db.transaction(() => {
for (const m of newModels) insert.run(...m);
// Ensure every model has a fallback_config row (new inserts + any orphans)
const missing = db.prepare(`
SELECT m.id FROM models m
LEFT JOIN fallback_config f ON m.id = f.model_db_id
WHERE f.id IS NULL
ORDER BY m.intelligence_rank ASC
`).all() as { id: number }[];
if (missing.length > 0) {
const maxPriority = (db.prepare('SELECT COALESCE(MAX(priority), 0) AS mx FROM fallback_config').get() as { mx: number }).mx;
const addFallback = db.prepare('INSERT INTO fallback_config (model_db_id, priority, enabled) VALUES (?, ?, 1)');
for (let i = 0; i < missing.length; i++) {
addFallback.run(missing[i].id, maxPriority + i + 1);
}
}
});
apply();
}
/**
* Second-pass migration after live-testing every model against its provider.
* Corrects model IDs verified wrong, removes models not actually available on
* the current free tier, and adds real :free OpenRouter models found in the
* live catalog (April 2026).
*/
function migrateModelsV2(db: Database.Database) {
// Helper: delete a model and its fallback_config entry (FK is RESTRICT-by-default)
const deleteModel = db.prepare(`DELETE FROM models WHERE platform = ? AND model_id = ?`);
const deleteFallback = db.prepare(`
DELETE FROM fallback_config WHERE model_db_id IN (
SELECT id FROM models WHERE platform = ? AND model_id = ?
)
`);
const removals: Array<[string, string]> = [
// GitHub free tier does NOT include GPT-5 (only catalog-listed). Revert handled below.
// Cerebras: qwen-3-coder-480b and llama-4-maverick not on free tier; gpt-oss-120b is listed
// but requires special access β€” our key gets 404. Remove all three.
['cerebras', 'qwen-3-coder-480b'],
['cerebras', 'llama-4-maverick-17b-128e-instruct'],
['cerebras', 'gpt-oss-120b'],
// These OpenRouter :free variants do not exist in the live catalog (April 2026)
['openrouter', 'deepseek/deepseek-v3.1:free'],
['openrouter', 'moonshotai/kimi-k2:free'],
];
const applyRemovals = db.transaction(() => {
for (const [p, m] of removals) {
deleteFallback.run(p, m);
deleteModel.run(p, m);
}
});
applyRemovals();
// GitHub: gpt-5 is in the model catalog but returns "unavailable_model" on free tier
// inference. Revert to gpt-4o which works. This only runs if the gpt-5 row exists.
db.prepare(`
UPDATE models
SET model_id = 'gpt-4o', display_name = 'GPT-4o', intelligence_rank = 5,
size_label = 'Large', context_window = 8000, monthly_token_budget = '~18M'
WHERE platform = 'github' AND model_id = 'openai/gpt-5'
`).run();
// Groq: scout requires the meta-llama/ publisher prefix
db.prepare(`
UPDATE models SET model_id = 'meta-llama/llama-4-scout-17b-16e-instruct'
WHERE platform = 'groq' AND model_id = 'llama-4-scout-17b-16e-instruct'
`).run();
// Add real OpenRouter :free models that exist in the live catalog
const insert = db.prepare(`
INSERT OR IGNORE INTO models (platform, model_id, display_name, intelligence_rank, speed_rank, size_label, rpm_limit, rpd_limit, tpm_limit, tpd_limit, monthly_token_budget, context_window)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`);
const additions: Array<[string, string, string, number, number, string, number | null, number | null, number | null, number | null, string, number | null]> = [
// Frontier-tier free models verified in OR catalog 2026-04
['openrouter', 'nvidia/nemotron-3-super-120b-a12b:free', 'Nemotron 3 Super 120B (free)', 2, 9, 'Frontier', 20, 200, null, null, '~6M', 262144],
['openrouter', 'qwen/qwen3-next-80b-a3b-instruct:free', 'Qwen3-Next 80B (free)', 3, 9, 'Large', 20, 200, null, null, '~6M', 262144],
['openrouter', 'minimax/minimax-m2.5:free', 'MiniMax M2.5 (free)', 3, 9, 'Large', 20, 200, null, null, '~6M', 196608],
['openrouter', 'google/gemma-4-31b-it:free', 'Gemma 4 31B (free)', 5, 9, 'Medium', 20, 200, null, null, '~6M', 262144],
];
const applyAdditions = db.transaction(() => {
for (const a of additions) insert.run(...a);
// Fallback entries for new models
const missing = db.prepare(`
SELECT m.id FROM models m
LEFT JOIN fallback_config f ON m.id = f.model_db_id
WHERE f.id IS NULL ORDER BY m.intelligence_rank ASC
`).all() as { id: number }[];
if (missing.length > 0) {
const maxPriority = (db.prepare('SELECT COALESCE(MAX(priority), 0) AS mx FROM fallback_config').get() as { mx: number }).mx;
const addFb = db.prepare('INSERT INTO fallback_config (model_db_id, priority, enabled) VALUES (?, ?, 1)');
for (let i = 0; i < missing.length; i++) addFb.run(missing[i].id, maxPriority + i + 1);
}
});
applyAdditions();
}
/**
* Re-rank intelligence based on April 2026 coding + agentic tool-use benchmarks:
* SWE-bench Verified, Terminal-Bench 2, TAU-Bench, Aider Polyglot.
* Higher rank = weaker. Ties are allowed (same weights across providers).
*/
function migrateModelsV3Ranks(db: Database.Database) {
const setRank = db.prepare(`UPDATE models SET intelligence_rank = ? WHERE platform = ? AND model_id = ?`);
const ranks: Array<[number, string, string]> = [
// #1-10 frontier coders / agents
[1, 'openrouter', 'minimax/minimax-m2.5:free'], // SWE-V ~80%, TB2 ~57%
[2, 'openrouter', 'qwen/qwen3-coder:free'], // SWE-V ~70%
[3, 'openrouter', 'qwen/qwen3-next-80b-a3b-instruct:free'], // SWE-V ~70.6%
[4, 'moonshot', 'kimi-latest'], // K2: SWE-V ~71%
[5, 'cerebras', 'qwen-3-235b-a22b-instruct-2507'], // SWE-V ~65-72%
[6, 'google', 'gemini-2.5-pro'], // SWE-V 63.8%, Aider 83%
[7, 'openrouter', 'z-ai/glm-4.5-air:free'], // ~58% SWE-V (distill of 4.5)
[8, 'openrouter', 'openai/gpt-oss-120b:free'], // SWE-V 62.4%
[9, 'openrouter', 'nvidia/nemotron-3-super-120b-a12b:free'], // SWE-V 53.7%
[10, 'minimax', 'MiniMax-M1'], // M1 predecessor, ~45-55%
// #11-15 mid-tier specialists
[11, 'mistral', 'codestral-latest'], // HumanEval 86.6%
[12, 'mistral', 'mistral-large-latest'],
[13, 'mistral', 'magistral-medium-latest'], // reasoning, not code-tuned
[14, 'google', 'gemini-2.5-flash'],
[15, 'zhipu', 'glm-4.5-flash'],
// #16 Llama 3.3 70B β€” identical weights across providers (tie)
[16, 'groq', 'llama-3.3-70b-versatile'],
[16, 'sambanova', 'Meta-Llama-3.3-70B-Instruct'],
[16, 'openrouter', 'meta-llama/llama-3.3-70b-instruct:free'],
[16, 'huggingface', 'accounts/fireworks/models/llama-v3p3-70b-instruct'],
// #17-23 weaker
[17, 'openrouter', 'nousresearch/hermes-3-llama-3.1-405b:free'], // L3.1 base with tool-use tune
[18, 'groq', 'meta-llama/llama-4-scout-17b-16e-instruct'], // multimodal focus
[19, 'openrouter', 'google/gemma-4-31b-it:free'],
[20, 'google', 'gemini-2.5-flash-lite'],
[21, 'github', 'gpt-4o'], // Aug 2024, SWE-V ~33%
[22, 'nvidia', 'meta/llama-3.1-70b-instruct'], // older Llama 3.1 tune
[22, 'cloudflare', '@cf/meta/llama-3.1-70b-instruct'], // same base weights
[23, 'cohere', 'command-r-plus-08-2024'], // RAG-focused, weakest on code
];
const apply = db.transaction(() => {
for (const [rank, platform, modelId] of ranks) {
setRank.run(rank, platform, modelId);
}
});
apply();
}
/**
* V4: Agentic-tool-use focus. Live-probed every candidate against real free-tier
* keys (April 2026) with a weather-tool function-calling test. Keeps only models
* that return a structured tool_calls response and are reachable on the free tier.
*
* Adds SambaNova DeepSeek/Llama-4/gpt-oss, Groq gpt-oss & qwen3-32b, OpenRouter
* ling-2.6-flash + nemotron-nano + gpt-oss + trinity, Mistral devstral/medium,
* GitHub gpt-4.1, Cohere command-a, Cloudflare llama-4/gpt-oss/glm-4.7. Removes
* moonshot/kimi (paid-only now), minimax/M1 (superseded), HF/Fireworks route
* (no structured tools), OR/gemma-4 (weak at tools). Renames CF llama-3.1 β†’ 3.3
* fp8-fast. Corrects stale limits.
*/
function migrateModelsV4(db: Database.Database) {
// 1) Remove entries that are unavailable or fail agentic tool use
const deleteModel = db.prepare(`DELETE FROM models WHERE platform = ? AND model_id = ?`);
const deleteFallback = db.prepare(`
DELETE FROM fallback_config WHERE model_db_id IN (
SELECT id FROM models WHERE platform = ? AND model_id = ?
)
`);
const removals: Array<[string, string]> = [
['moonshot', 'kimi-latest'], // paid-only now ($1 min deposit)
['minimax', 'MiniMax-M1'], // superseded; use OR minimax-m2.5:free
['openrouter', 'google/gemma-4-31b-it:free'], // weak at tool use
['huggingface', 'accounts/fireworks/models/llama-v3p3-70b-instruct'], // emits tool call as text content, not structured
];
const applyRemovals = db.transaction(() => {
for (const [p, m] of removals) {
deleteFallback.run(p, m);
deleteModel.run(p, m);
}
});
applyRemovals();
// 2) Cloudflare: replace Llama 3.1 70B with the current-gen 3.3 70B fp8-fast
db.prepare(`
UPDATE models
SET model_id = '@cf/meta/llama-3.3-70b-instruct-fp8-fast',
display_name = 'Llama 3.3 70B fp8-fast (CF)',
context_window = 131072
WHERE platform = 'cloudflare' AND model_id = '@cf/meta/llama-3.1-70b-instruct'
`).run();
// 3) Field corrections verified via primary sources + live probe
db.prepare(`UPDATE models SET tpm_limit = 12000 WHERE platform = 'groq' AND model_id = 'llama-3.3-70b-versatile'`).run();
db.prepare(`UPDATE models SET rpd_limit = 20 WHERE platform = 'sambanova' AND model_id = 'Meta-Llama-3.3-70B-Instruct'`).run();
db.prepare(`UPDATE models SET rpd_limit = 14400 WHERE platform = 'cerebras' AND model_id = 'qwen-3-235b-a22b-instruct-2507'`).run();
db.prepare(`UPDATE models SET rpd_limit = 250, monthly_token_budget = '~25M' WHERE platform = 'google' AND model_id = 'gemini-2.5-flash'`).run();
// gemini-2.5-pro is at-risk: April 2026 Google moved Pro-class off free tier in practice.
// Our live probe hit "quota exceeded" immediately. Cut rpd in half to reduce 429 blast radius.
db.prepare(`UPDATE models SET rpd_limit = 50, monthly_token_budget = '~6M' WHERE platform = 'google' AND model_id = 'gemini-2.5-pro'`).run();
// 4) Add live-probed, tool-capable models
const insert = db.prepare(`
INSERT OR IGNORE INTO models (platform, model_id, display_name, intelligence_rank, speed_rank, size_label, rpm_limit, rpd_limit, tpm_limit, tpd_limit, monthly_token_budget, context_window)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`);
const additions: Array<[string, string, string, number, number, string, number | null, number | null, number | null, number | null, string, number | null]> = [
// OpenRouter :free β€” shared 20 RPM / 200 RPD / ~6M tokens across :free pool
['openrouter', 'inclusionai/ling-2.6-flash:free', 'Ling 2.6 Flash (free)', 7, 9, 'Large', 20, 200, null, null, '~6M', 262144],
['openrouter', 'arcee-ai/trinity-large-preview:free', 'Trinity Large Preview (free)', 13, 9, 'Frontier', 20, 200, null, null, '~6M', 131072],
['openrouter', 'nvidia/nemotron-3-nano-30b-a3b:free', 'Nemotron 3 Nano 30B (free)', 22, 9, 'Medium', 20, 200, null, null, '~6M', 262144],
['openrouter', 'openai/gpt-oss-120b:free', 'GPT-OSS 120B (free)', 6, 9, 'Large', 20, 200, null, null, '~6M', 131072],
['openrouter', 'openai/gpt-oss-20b:free', 'GPT-OSS 20B (free)', 18, 9, 'Medium', 20, 200, null, null, '~6M', 131072],
['openrouter', 'meta-llama/llama-3.3-70b-instruct:free', 'Llama 3.3 70B (free)', 17, 9, 'Medium', 20, 200, null, null, '~6M', 131072],
// SambaNova β€” 20 RPM / 20 RPD / 200K TPD shared free Developer tier
['sambanova', 'DeepSeek-V3.1', 'DeepSeek V3.1', 5, 9, 'Frontier', 20, 20, null, 200000, '~3M', 131072],
['sambanova', 'DeepSeek-V3.2', 'DeepSeek V3.2', 4, 9, 'Frontier', 20, 20, null, 200000, '~3M', 131072],
['sambanova', 'Llama-4-Maverick-17B-128E-Instruct', 'Llama 4 Maverick', 11, 9, 'Large', 20, 20, null, 200000, '~3M', 8192],
['sambanova', 'gpt-oss-120b', 'GPT-OSS 120B (SambaNova)', 6, 9, 'Large', 20, 20, null, 200000, '~3M', 131072],
// Groq β€” very fast; 30 RPM per model, 1000 RPD on most, 14.4k on the 8B
['groq', 'openai/gpt-oss-120b', 'GPT-OSS 120B (Groq)', 6, 2, 'Large', 30, 1000, 8000, 200000, '~6M', 131072],
['groq', 'openai/gpt-oss-20b', 'GPT-OSS 20B (Groq)', 18, 2, 'Medium', 30, 1000, 8000, 200000, '~6M', 131072],
['groq', 'qwen/qwen3-32b', 'Qwen3 32B (Groq)', 19, 2, 'Medium', 60, 1000, 6000, 500000, '~15M', 131072],
['groq', 'llama-3.1-8b-instant', 'Llama 3.1 8B Instant', 28, 2, 'Small', 30, 14400, 6000, 500000, '~15M', 131072],
// Mistral Experiment tier β€” shared 2 RPM / 500k TPM / 1B tokens/mo across all models
['mistral', 'devstral-latest', 'Devstral', 16, 8, 'Medium', 2, null, 500000, null, '~50-100M', 131072],
['mistral', 'mistral-medium-latest', 'Mistral Medium 3.5', 14, 8, 'Large', 2, null, 500000, null, '~50-100M', 131072],
// GitHub Models β€” Low-tier category (15 RPM / 150 RPD, 8K in / 4K out per call)
['github', 'openai/gpt-4.1', 'GPT-4.1 (GitHub)', 20, 7, 'Large', 10, 50, null, null, '~9M', 128000],
// Cohere β€” shared 1000 calls/mo trial pool, 20 RPM Chat
['cohere', 'command-a-03-2025', 'Command-A (03-2025)', 27, 11, 'Large', 20, 33, null, null, '~1-2M', 131072],
// Cloudflare Workers AI β€” shared 10K Neurons/day free pool across all @cf/* models
['cloudflare', '@cf/openai/gpt-oss-120b', 'GPT-OSS 120B (CF)', 6, 11, 'Large', null, null, null, null, '~18-45M', 131072],
['cloudflare', '@cf/zai-org/glm-4.7-flash', 'GLM-4.7 Flash (CF)', 10, 11, 'Large', null, null, null, null, '~18-45M', 131072],
['cloudflare', '@cf/meta/llama-4-scout-17b-16e-instruct', 'Llama 4 Scout (CF)', 12, 11, 'Large', null, null, null, null, '~18-45M', 131072],
];
const apply = db.transaction(() => {
for (const a of additions) insert.run(...a);
const missing = db.prepare(`
SELECT m.id FROM models m
LEFT JOIN fallback_config f ON m.id = f.model_db_id
WHERE f.id IS NULL ORDER BY m.intelligence_rank ASC
`).all() as { id: number }[];
if (missing.length > 0) {
const maxPriority = (db.prepare('SELECT COALESCE(MAX(priority), 0) AS mx FROM fallback_config').get() as { mx: number }).mx;
const addFb = db.prepare('INSERT INTO fallback_config (model_db_id, priority, enabled) VALUES (?, ?, 1)');
for (let i = 0; i < missing.length; i++) addFb.run(missing[i].id, maxPriority + i + 1);
}
});
apply();
// 5) Re-rank the live catalog by agentic tool-use capability (lower = smarter).
// Grounded in April 2026 SWE-Bench Verified + BFCL v3 + Tau-Bench numbers.
const setRank = db.prepare(`UPDATE models SET intelligence_rank = ? WHERE platform = ? AND model_id = ?`);
const ranks: Array<[number, string, string]> = [
[1, 'openrouter', 'minimax/minimax-m2.5:free'],
[2, 'openrouter', 'qwen/qwen3-coder:free'],
[3, 'openrouter', 'qwen/qwen3-next-80b-a3b-instruct:free'],
[4, 'sambanova', 'DeepSeek-V3.2'],
[5, 'sambanova', 'DeepSeek-V3.1'],
[6, 'cerebras', 'qwen-3-235b-a22b-instruct-2507'],
[6, 'openrouter', 'openai/gpt-oss-120b:free'],
[6, 'groq', 'openai/gpt-oss-120b'],
[6, 'sambanova', 'gpt-oss-120b'],
[6, 'cloudflare', '@cf/openai/gpt-oss-120b'],
[7, 'openrouter', 'inclusionai/ling-2.6-flash:free'],
[8, 'openrouter', 'z-ai/glm-4.5-air:free'],
[10, 'cloudflare', '@cf/zai-org/glm-4.7-flash'],
[11, 'sambanova', 'Llama-4-Maverick-17B-128E-Instruct'],
[12, 'groq', 'meta-llama/llama-4-scout-17b-16e-instruct'],
[12, 'cloudflare', '@cf/meta/llama-4-scout-17b-16e-instruct'],
[13, 'openrouter', 'arcee-ai/trinity-large-preview:free'],
[14, 'google', 'gemini-2.5-pro'],
[14, 'mistral', 'mistral-large-latest'],
[14, 'mistral', 'mistral-medium-latest'],
[16, 'mistral', 'devstral-latest'],
[16, 'mistral', 'codestral-latest'],
[17, 'groq', 'llama-3.3-70b-versatile'],
[17, 'sambanova', 'Meta-Llama-3.3-70B-Instruct'],
[17, 'cloudflare', '@cf/meta/llama-3.3-70b-instruct-fp8-fast'],
[17, 'openrouter', 'meta-llama/llama-3.3-70b-instruct:free'],
[17, 'nvidia', 'meta/llama-3.1-70b-instruct'],
[18, 'openrouter', 'openai/gpt-oss-20b:free'],
[18, 'groq', 'openai/gpt-oss-20b'],
[19, 'groq', 'qwen/qwen3-32b'],
[20, 'google', 'gemini-2.5-flash'],
[20, 'github', 'openai/gpt-4.1'],
[21, 'mistral', 'magistral-medium-latest'],
[22, 'openrouter', 'nvidia/nemotron-3-super-120b-a12b:free'],
[23, 'openrouter', 'nvidia/nemotron-3-nano-30b-a3b:free'],
[24, 'zhipu', 'glm-4.5-flash'],
[25, 'github', 'gpt-4o'],
[26, 'google', 'gemini-2.5-flash-lite'],
[27, 'cohere', 'command-a-03-2025'],
[27, 'cohere', 'command-r-plus-08-2024'],
[28, 'groq', 'llama-3.1-8b-instant'],
];
const applyRanks = db.transaction(() => {
for (const [r, p, m] of ranks) setRank.run(r, p, m);
});
applyRanks();
}
/**
* V5: Google moved all Pro-tier Gemini off the free tier on 2026-04-01 β€” disable
* gemini-2.5-pro. Add Cerebras `zai-glm-4.7` (355B z.ai GLM preview, newly on
* free tier but throttled to 10 RPM / 100 RPD due to high demand; context capped
* at 8192 on free tier).
*/
function migrateModelsV5(db: Database.Database) {
db.prepare(`UPDATE models SET enabled = 0 WHERE platform = 'google' AND model_id = 'gemini-2.5-pro'`).run();
const insert = db.prepare(`
INSERT OR IGNORE INTO models (platform, model_id, display_name, intelligence_rank, speed_rank, size_label, rpm_limit, rpd_limit, tpm_limit, tpd_limit, monthly_token_budget, context_window)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`);
const apply = db.transaction(() => {
insert.run('cerebras', 'zai-glm-4.7', 'GLM-4.7 (Cerebras)', 7, 1, 'Frontier', 10, 100, null, null, '~3M', 8192);
const missing = db.prepare(`
SELECT m.id FROM models m
LEFT JOIN fallback_config f ON m.id = f.model_db_id
WHERE f.id IS NULL ORDER BY m.intelligence_rank ASC
`).all() as { id: number }[];
if (missing.length > 0) {
const maxPriority = (db.prepare('SELECT COALESCE(MAX(priority), 0) AS mx FROM fallback_config').get() as { mx: number }).mx;
const addFb = db.prepare('INSERT INTO fallback_config (model_db_id, priority, enabled) VALUES (?, ?, 1)');
for (let i = 0; i < missing.length; i++) addFb.run(missing[i].id, maxPriority + i + 1);
}
});
apply();
}
/**
* V6: Live-probed against real free-tier keys on 2026-04-25.
*
* Corrections (Google free-tier RPD): the documented "250" / "1000" RPD numbers
* for gemini-2.5-flash and gemini-2.5-flash-lite are stale β€” both share a 20
* RPD per-model-per-project free pool now. Confirmed by the
* `generate_content_free_tier_requests` quota error, limit 20.
*
* Removals: arcee-ai/trinity-large-preview:free returns 404 "No endpoints found"
* β€” pulled from OpenRouter's free pool. (Other previously-suspected dead OR :free
* IDs are still live in /api/v1/models, so they stay.)
*
* Additions (all probe-verified to return 200 with content on the user's keys):
* - 3 Cloudflare Workers AI reasoning models
* - 3 Google preview models, including Pro (which returned a free-tier 429
* against the same 20 RPD pool, confirming free-tier eligibility)
* - 2 OpenRouter :free models with no expiration_date
*/
function migrateModelsV6(db: Database.Database) {
// 1) Remove confirmed-dead OR route
const deleteModel = db.prepare(`DELETE FROM models WHERE platform = ? AND model_id = ?`);
const deleteFallback = db.prepare(`
DELETE FROM fallback_config WHERE model_db_id IN (
SELECT id FROM models WHERE platform = ? AND model_id = ?
)
`);
const removals: Array<[string, string]> = [
['openrouter', 'arcee-ai/trinity-large-preview:free'],
];
const applyRemovals = db.transaction(() => {
for (const [p, m] of removals) {
deleteFallback.run(p, m);
deleteModel.run(p, m);
}
});
applyRemovals();
// 2) Correct stale Google free-tier RPD numbers
db.prepare(`
UPDATE models SET rpd_limit = 20, monthly_token_budget = '~3M'
WHERE platform = 'google' AND model_id = 'gemini-2.5-flash'
`).run();
db.prepare(`
UPDATE models SET rpd_limit = 20, monthly_token_budget = '~3M'
WHERE platform = 'google' AND model_id = 'gemini-2.5-flash-lite'
`).run();
// 3) Add live-probed models
const insert = db.prepare(`
INSERT OR IGNORE INTO models (platform, model_id, display_name, intelligence_rank, speed_rank, size_label, rpm_limit, rpd_limit, tpm_limit, tpd_limit, monthly_token_budget, context_window)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`);
const additions: Array<[string, string, string, number, number, string, number | null, number | null, number | null, number | null, string, number | null]> = [
// Cloudflare Workers AI β€” 10K Neurons/day shared free pool. Reasoning traces
// burn output tokens fast, so per-call effective budget is small. Estimates
// assume 1K-in/500-out typical: kimi-k2.5 β‰ˆ 50/day, qwen3-30b β‰ˆ 200/day,
// r1-distill β‰ˆ 5/day on the reasoning-heavy path.
['cloudflare', '@cf/moonshotai/kimi-k2.5', 'Kimi K2.5 (CF)', 3, 11, 'Frontier', null, null, null, null, '~10-20M', 262144],
['cloudflare', '@cf/qwen/qwen3-30b-a3b-fp8', 'Qwen3 30B-A3B fp8 (CF)', 7, 11, 'Large', null, null, null, null, '~18-45M', 131072],
['cloudflare', '@cf/deepseek-ai/deepseek-r1-distill-qwen-32b', 'DeepSeek R1 Distill Qwen 32B (CF)', 9, 11, 'Large', null, null, null, null, '~3-5M', 131072],
// Google preview tier β€” shares the 20 RPD per-model free pool. Pro confirmed
// free-tier-eligible by the `free_tier_requests` quota metric in 429 errors.
['google', 'gemini-3.1-flash-lite-preview', 'Gemini 3.1 Flash-Lite Preview', 18, 3, 'Medium', 15, 20, 250000, null, '~3M', 1048576],
['google', 'gemini-3-flash-preview', 'Gemini 3 Flash Preview', 11, 5, 'Large', 10, 20, 250000, null, '~3M', 1048576],
['google', 'gemini-3.1-pro-preview', 'Gemini 3.1 Pro Preview', 1, 8, 'Frontier', 5, 20, 250000, null, '~3M', 1048576],
// OpenRouter :free pool β€” 20 RPM / 50 RPD (1000 once $10 credits bought).
['openrouter', 'google/gemma-4-31b-it:free', 'Gemma 4 31B (free)', 19, 9, 'Medium', 20, 200, null, null, '~6M', 262144],
['openrouter', 'liquid/lfm-2.5-1.2b-instruct:free', 'Liquid LFM 2.5 1.2B (free)', 30, 10, 'Small', 20, 200, null, null, '~6M', 32768],
];
const apply = db.transaction(() => {
for (const a of additions) insert.run(...a);
const missing = db.prepare(`
SELECT m.id FROM models m
LEFT JOIN fallback_config f ON m.id = f.model_db_id
WHERE f.id IS NULL ORDER BY m.intelligence_rank ASC
`).all() as { id: number }[];
if (missing.length > 0) {
const maxPriority = (db.prepare('SELECT COALESCE(MAX(priority), 0) AS mx FROM fallback_config').get() as { mx: number }).mx;
const addFb = db.prepare('INSERT INTO fallback_config (model_db_id, priority, enabled) VALUES (?, ?, 1)');
for (let i = 0; i < missing.length; i++) addFb.run(missing[i].id, maxPriority + i + 1);
}
});
apply();
}
/**
* V7 (April 2026): live-probed delta against OpenRouter's free pool + Z.ai.
* - Removes inclusionai/ling-2.6-flash:free (transitioned to paid, 404 on chat).
* - Adds 8 new :free routes confirmed via /v1/models + chat-completion probe.
* - Adds zhipu/glm-4.7-flash (probe: 429 "overloaded" β€” free-pool throttle, not
* "insufficient balance" which paid models return). Same baseUrl works for both
* api.z.ai and open.bigmodel.cn keys.
* HF and NVIDIA left as-is: HF still serves chat with current key; NVIDIA already disabled.
*/
function migrateModelsV7(db: Database.Database) {
const deleteModel = db.prepare(`DELETE FROM models WHERE platform = ? AND model_id = ?`);
const deleteFallback = db.prepare(`
DELETE FROM fallback_config WHERE model_db_id IN (
SELECT id FROM models WHERE platform = ? AND model_id = ?
)
`);
const removals: Array<[string, string]> = [
['openrouter', 'inclusionai/ling-2.6-flash:free'],
];
const applyRemovals = db.transaction(() => {
for (const [p, m] of removals) {
deleteFallback.run(p, m);
deleteModel.run(p, m);
}
});
applyRemovals();
const insert = db.prepare(`
INSERT OR IGNORE INTO models (platform, model_id, display_name, intelligence_rank, speed_rank, size_label, rpm_limit, rpd_limit, tpm_limit, tpd_limit, monthly_token_budget, context_window)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`);
// OpenRouter :free quotas: 20 RPM / 50 RPD without credits, 1000 RPD with $10 lifetime topup.
// Catalog convention is rpd=200 (matches existing rows).
const additions: Array<[string, string, string, number, number, string, number | null, number | null, number | null, number | null, string, number | null]> = [
['openrouter', 'inclusionai/ling-2.6-1t:free', 'Ling 2.6 1T (free)', 4, 9, 'Frontier', 20, 200, null, null, '~6M', 262144],
['openrouter', 'tencent/hy3-preview:free', 'Tencent HY3 Preview (free)', 7, 9, 'Frontier', 20, 200, null, null, '~6M', 262144],
['openrouter', 'poolside/laguna-m.1:free', 'Poolside Laguna M.1 (free)', 13, 9, 'Large', 20, 200, null, null, '~6M', 131072],
['openrouter', 'google/gemma-4-26b-a4b-it:free', 'Gemma 4 26B-A4B (free)', 22, 9, 'Medium', 20, 200, null, null, '~6M', 262144],
['openrouter', 'nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free', 'Nemotron 3 Nano 30B Reasoning (free)', 23, 9, 'Medium', 20, 200, null, null, '~6M', 262144],
['openrouter', 'poolside/laguna-xs.2:free', 'Poolside Laguna XS.2 (free)', 26, 10, 'Medium', 20, 200, null, null, '~6M', 131072],
['openrouter', 'nvidia/nemotron-nano-9b-v2:free', 'Nemotron Nano 9B v2 (free)', 28, 10, 'Medium', 20, 200, null, null, '~6M', 128000],
['openrouter', 'liquid/lfm-2.5-1.2b-thinking:free', 'Liquid LFM 2.5 1.2B Thinking (free)', 30, 10, 'Small', 20, 200, null, null, '~6M', 32768],
// Zhipu (Z.ai) β€” free pool. glm-4.7-flash quotas unpublished; mirror glm-4.5-flash row shape.
['zhipu', 'glm-4.7-flash', 'GLM-4.7 Flash', 18, 4, 'Large', null, null, null, 1000000, '~30M', 131072],
];
const apply = db.transaction(() => {
for (const a of additions) insert.run(...a);
const missing = db.prepare(`
SELECT m.id FROM models m
LEFT JOIN fallback_config f ON m.id = f.model_db_id
WHERE f.id IS NULL ORDER BY m.intelligence_rank ASC
`).all() as { id: number }[];
if (missing.length > 0) {
const maxPriority = (db.prepare('SELECT COALESCE(MAX(priority), 0) AS mx FROM fallback_config').get() as { mx: number }).mx;
const addFb = db.prepare('INSERT INTO fallback_config (model_db_id, priority, enabled) VALUES (?, ?, 1)');
for (let i = 0; i < missing.length; i++) addFb.run(missing[i].id, maxPriority + i + 1);
}
});
apply();
}
/**
* V8 (May 2026): 3-day delta. SambaNova's /v1/models added two free-tier models;
* Cloudflare's @cf catalog added two new text models. All four probe-verified 200
* with the user's keys. SambaNova's paid-only MiniMax-M2.5 explicitly returns 422
* "Couldn't find valid service tier", so the 200s on these rows confirm free-tier
* access. Cloudflare's @cf/* models share the 10K Neurons/day free pool.
*/
function migrateModelsV8(db: Database.Database) {
const insert = db.prepare(`
INSERT OR IGNORE INTO models (platform, model_id, display_name, intelligence_rank, speed_rank, size_label, rpm_limit, rpd_limit, tpm_limit, tpd_limit, monthly_token_budget, context_window)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`);
const additions: Array<[string, string, string, number, number, string, number | null, number | null, number | null, number | null, string, number | null]> = [
// SambaNova free pool: 20 RPM / 20 RPD / 200K TPD shared across all free models.
['sambanova', 'DeepSeek-V3.1-cb', 'DeepSeek V3.1 (CB)', 5, 9, 'Frontier', 20, 20, null, 200000, '~3M', 131072],
['sambanova', 'gemma-3-12b-it', 'Gemma 3 12B (SambaNova)', 22, 9, 'Medium', 20, 20, null, 200000, '~3M', 131072],
// Cloudflare @cf β€” 10K Neurons/day shared pool.
['cloudflare', '@cf/moonshotai/kimi-k2.6', 'Kimi K2.6 (CF)', 2, 11, 'Frontier', null, null, null, null, '~10-20M', 262144],
['cloudflare', '@cf/ibm-granite/granite-4.0-h-micro', 'Granite 4.0 H Micro (CF)', 29, 11, 'Small', null, null, null, null, '~5-10M', 131072],
];
const apply = db.transaction(() => {
for (const a of additions) insert.run(...a);
const missing = db.prepare(`
SELECT m.id FROM models m
LEFT JOIN fallback_config f ON m.id = f.model_db_id
WHERE f.id IS NULL ORDER BY m.intelligence_rank ASC
`).all() as { id: number }[];
if (missing.length > 0) {
const maxPriority = (db.prepare('SELECT COALESCE(MAX(priority), 0) AS mx FROM fallback_config').get() as { mx: number }).mx;
const addFb = db.prepare('INSERT INTO fallback_config (model_db_id, priority, enabled) VALUES (?, ?, 1)');
for (let i = 0; i < missing.length; i++) addFb.run(missing[i].id, maxPriority + i + 1);
}
});
apply();
}
/**
* V9 (May 2026): disable cerebras/zai-glm-4.7. The model still appears in
* Cerebras's /v1/models listing but the chat-completions endpoint returns
* 404 "Model does not exist or you do not have access" for free-tier keys β€”
* matches their docs note about temporarily reducing free-tier access on
* zai-glm-4.7 due to high demand. Row kept (not deleted) so it can be
* re-enabled later without losing fallback_config history.
*/
function migrateModelsV9(db: Database.Database) {
db.prepare(
"UPDATE models SET enabled = 0 WHERE platform = 'cerebras' AND model_id = 'zai-glm-4.7'"
).run();
}
/**
* V10 (May 2026): Ollama Cloud β€” first new platform since Z.ai/Zhipu in V7.
* Free plan: GPU-time-based quota (not per-token), 1 concurrent model,
* 5h session caps, no card required. /v1/models lists 39 SKUs but only 28
* respond on the Free tier β€” paid models return 403 with an explicit
* "this model requires a subscription" message.
*
* Curated to ~10 representative free models that either (a) aren't reachable
* elsewhere in the catalog or (b) provide a useful alternate route through
* Ollama's independent rate-limit pool. Probe-verified May 2 2026.
*
* Quota shape: GPU-time, not tokens. monthly_token_budget reflects rough
* Free-tier "session" capacity rather than a hard token cap.
*/
function migrateModelsV10(db: Database.Database) {
const insert = db.prepare(`
INSERT OR IGNORE INTO models (platform, model_id, display_name, intelligence_rank, speed_rank, size_label, rpm_limit, rpd_limit, tpm_limit, tpd_limit, monthly_token_budget, context_window)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`);
const additions: Array<[string, string, string, number, number, string, number | null, number | null, number | null, number | null, string, number | null]> = [
// Budget strings are estimates: Ollama publishes no token cap (quota is GPU-time +
// 7-day rolling). Frontier ~5-10M, Large ~10-20M, Medium ~20-30M reflect that
// heavier models burn quota faster. Numeric limits stay null β€” real provider
// throttling is the source of truth, not these display strings.
['ollama', 'qwen3-coder:480b', 'Qwen3-Coder 480B (Ollama)', 2, 9, 'Frontier', null, null, null, null, '~5-10M', 262144],
['ollama', 'mistral-large-3:675b', 'Mistral Large 3 675B (Ollama)', 3, 9, 'Frontier', null, null, null, null, '~5-10M', 131072],
['ollama', 'deepseek-v3.2', 'DeepSeek V3.2 (Ollama)', 4, 9, 'Frontier', null, null, null, null, '~5-10M', 131072],
['ollama', 'cogito-2.1:671b', 'Cogito 2.1 671B (Ollama)', 4, 9, 'Frontier', null, null, null, null, '~5-10M', 131072],
['ollama', 'kimi-k2-thinking', 'Kimi K2 Thinking (Ollama)', 5, 9, 'Frontier', null, null, null, null, '~5-10M', 131072],
['ollama', 'glm-4.7', 'GLM-4.7 (Ollama)', 6, 9, 'Frontier', null, null, null, null, '~5-10M', 131072],
['ollama', 'gpt-oss:120b', 'GPT-OSS 120B (Ollama)', 6, 9, 'Large', null, null, null, null, '~10-20M', 131072],
['ollama', 'devstral-2:123b', 'Devstral 2 123B (Ollama)', 8, 10, 'Large', null, null, null, null, '~10-20M', 131072],
['ollama', 'gpt-oss:20b', 'GPT-OSS 20B (Ollama)', 18, 10, 'Medium', null, null, null, null, '~20-30M', 131072],
['ollama', 'gemma4:31b', 'Gemma 4 31B (Ollama)', 22, 10, 'Medium', null, null, null, null, '~20-30M', 131072],
];
const apply = db.transaction(() => {
for (const a of additions) insert.run(...a);
const missing = db.prepare(`
SELECT m.id FROM models m
LEFT JOIN fallback_config f ON m.id = f.model_db_id
WHERE f.id IS NULL ORDER BY m.intelligence_rank ASC
`).all() as { id: number }[];
if (missing.length > 0) {
const maxPriority = (db.prepare('SELECT COALESCE(MAX(priority), 0) AS mx FROM fallback_config').get() as { mx: number }).mx;
const addFb = db.prepare('INSERT INTO fallback_config (model_db_id, priority, enabled) VALUES (?, ?, 1)');
for (let i = 0; i < missing.length; i++) addFb.run(missing[i].id, maxPriority + i + 1);
}
});
apply();
}
/**
* V11 (May 2026):
* 1. Fix long-standing bug: Cerebras `qwen3-235b` was inserted with the
* wrong model_id in the original seed (real id is
* `qwen-3-235b-a22b-instruct-2507`). Subsequent rank/limit updates that
* target the correct id have been silent no-ops since V0 on fresh deploys.
* 2. Re-enable NVIDIA NIM β€” `meta/llama-3.1-70b-instruct` was disabled in V2
* when NIM moved to credits. Per May 2026 audit it's free again (~1,000
* starter credits never expire, 40 RPM/model).
* 3. Add three new aggregator/anon-friendly platforms confirmed live May 2026:
* Kilo Gateway, Pollinations, LLM7.io β€” all three accept anonymous
* requests on at least one model.
* - The user still needs a placeholder key entry (any non-empty string
* works) because the router filters on `keys.length === 0` to decide
* whether a platform is routable.
* Chutes was evaluated and dropped: probe with a free-tier key returned
* 402 on every model β€” "Quota exceeded and account balance is $0.0,
* please pay with fiat or send tao". The "free" tier requires a paid
* balance, which conflicts with the no-card criterion.
*/
function migrateModelsV11(db: Database.Database) {
// 1) Rename cerebras qwen3-235b β†’ qwen-3-235b-a22b-instruct-2507 if the
// old id still exists on this DB. Safe to re-run because of the WHERE.
db.prepare(`
UPDATE models SET model_id = 'qwen-3-235b-a22b-instruct-2507'
WHERE platform = 'cerebras' AND model_id = 'qwen3-235b'
`).run();
// 2) Re-enable NVIDIA NIM (still has 1,000+ starter credits free-tier).
db.prepare(`
UPDATE models SET enabled = 1, monthly_token_budget = '~3M (1k credits)'
WHERE platform = 'nvidia' AND model_id = 'meta/llama-3.1-70b-instruct'
`).run();
// 3) Add catalog rows for the four new platforms. Numeric limits are
// conservative β€” provider docs publish best-effort bounds that fluctuate.
const insert = db.prepare(`
INSERT OR IGNORE INTO models (platform, model_id, display_name, intelligence_rank, speed_rank, size_label, rpm_limit, rpd_limit, tpm_limit, tpd_limit, monthly_token_budget, context_window)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`);
const additions: Array<[string, string, string, number, number, string, number | null, number | null, number | null, number | null, string, number | null]> = [
// NVIDIA NIM β€” live-probed May 2026 with a free-tier key. All 8 returned
// 200 + content. Limits are per-model: 40 RPM, shared 1k starter credits
// (never-expire) used for the rough budget estimate. The existing
// meta/llama-3.1-70b-instruct row stays (re-enabled above).
['nvidia', 'meta/llama-3.3-70b-instruct', 'Llama 3.3 70B (NV)', 17, 6, 'Large', 40, null, null, null, '~3M (credits)', 131072],
['nvidia', 'meta/llama-4-maverick-17b-128e-instruct', 'Llama 4 Maverick (NV)', 11, 6, 'Large', 40, null, null, null, '~3M (credits)', 131072],
['nvidia', 'deepseek-ai/deepseek-v4-pro', 'DeepSeek V4 Pro (NV)', 3, 9, 'Frontier', 40, null, null, null, '~2M (credits)', 131072],
['nvidia', 'mistralai/mistral-large-3-675b-instruct-2512', 'Mistral Large 3 675B (NV)', 3, 9, 'Frontier', 40, null, null, null, '~2M (credits)', 131072],
['nvidia', 'minimaxai/minimax-m2.7', 'MiniMax M2.7 (NV)', 3, 9, 'Frontier', 40, null, null, null, '~2M (credits)', 196608],
['nvidia', 'nvidia/nemotron-3-super-120b-a12b', 'Nemotron 3 Super 120B (NV)', 22, 9, 'Frontier', 40, null, null, null, '~2M (credits)', 262144],
['nvidia', 'nvidia/nemotron-3-nano-30b-a3b', 'Nemotron 3 Nano 30B (NV)', 22, 9, 'Medium', 40, null, null, null, '~3M (credits)', 262144],
['nvidia', 'google/gemma-4-31b-it', 'Gemma 4 31B (NV)', 19, 9, 'Medium', 40, null, null, null, '~3M (credits)', 262144],
['nvidia', 'moonshotai/kimi-k2.6', 'Kimi K2.6 (NV)', 3, 9, 'Frontier', 40, null, null, null, '~2M (credits)', 131072],
// Cerebras β€” live-probed May 2026 with a free-tier key. Both 200 + content.
// gpt-oss-120b was removed in V2 ("requires special access, 404 on our
// key") but is reachable on the current free tier β€” re-add. llama3.1-8b
// is the fast small-model alternative (no hyphen, distinct from Groq's
// llama-3.1-8b-instant id). Free-pool limits match qwen-3-235b row.
['cerebras', 'gpt-oss-120b', 'GPT-OSS 120B (Cerebras)', 6, 1, 'Large', 30, 1000, 60000, 1000000, '~30M', 131072],
['cerebras', 'llama3.1-8b', 'Llama 3.1 8B (Cerebras)', 28, 1, 'Small', 30, 1000, 60000, 1000000, '~30M', 131072],
// Groq compound β€” agent system that internally routes through gpt-oss
// models and exposes the trace in usage metadata. Standard chat-completions
// shape works (200 + content). Same free-tier limits as other Groq rows.
['groq', 'groq/compound', 'Compound (Groq)', 6, 2, 'Large', 30, 1000, 8000, 200000, '~6M', 131072],
['groq', 'groq/compound-mini', 'Compound Mini (Groq)', 18, 2, 'Medium', 30, 1000, 8000, 200000, '~6M', 131072],
// Kilo Gateway β€” 200 req/hr per IP anon. Most named :free routes have
// transitioned to paid ("free period ended"); probe-confirmed live:
['kilo', 'nvidia/nemotron-3-super-120b-a12b:free', 'Nemotron 3 Super 120B (Kilo)', 22, 9, 'Frontier', null, null, null, null, '~2-3M (200/hr)', 262144],
// Pollinations β€” anonymous /openai endpoint. Public model list returns
// just one anonymous-tier entry. Tool calls supported per their metadata.
['pollinations', 'openai-fast', 'GPT-OSS 20B (Pollinations)', 18, 10, 'Medium', null, null, null, null, '~? (anon)', 131072],
// LLM7.io β€” 100 req/hr free (anonymous works). Probe-confirmed list:
['llm7', 'gpt-oss-20b', 'GPT-OSS 20B (LLM7)', 18, 10, 'Medium', 100, null, null, null, '~2-3M (100/hr)', 131072],
['llm7', 'meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo', 'Llama 3.1 8B Turbo (LLM7)', 28, 10, 'Small', 100, null, null, null, '~2-3M (100/hr)', 131072],
['llm7', 'codestral-latest', 'Codestral (LLM7)', 16, 8, 'Medium', 100, null, null, null, '~2-3M (100/hr)', 32000],
['llm7', 'ministral-8b-2512', 'Ministral 8B (LLM7)', 28, 10, 'Small', 100, null, null, null, '~2-3M (100/hr)', 131072],
['llm7', 'GLM-4.6V-Flash', 'GLM-4.6V Flash (LLM7)', 15, 9, 'Large', 100, null, null, null, '~2-3M (100/hr)', 131072],
];
const apply = db.transaction(() => {
for (const a of additions) insert.run(...a);
const missing = db.prepare(`
SELECT m.id FROM models m
LEFT JOIN fallback_config f ON m.id = f.model_db_id
WHERE f.id IS NULL ORDER BY m.intelligence_rank ASC
`).all() as { id: number }[];
if (missing.length > 0) {
const maxPriority = (db.prepare('SELECT COALESCE(MAX(priority), 0) AS mx FROM fallback_config').get() as { mx: number }).mx;
const addFb = db.prepare('INSERT INTO fallback_config (model_db_id, priority, enabled) VALUES (?, ?, 1)');
for (let i = 0; i < missing.length; i++) addFb.run(missing[i].id, maxPriority + i + 1);
}
});
apply();
}
/**
* V12 (May 2026): live-probed delta against OpenRouter's free pool.
*
* Removals (both confirmed 404 "no longer available as a free model" β€” moved
* to paid SKUs at the same id without the :free suffix):
* - inclusionai/ling-2.6-1t:free
* - tencent/hy3-preview:free
*
* Additions (probe-verified 200 + tool_calls with tool_choice=auto on the
* user's OR key; tool_choice=required is rejected by all three providers,
* but the router's tool requests use the OpenAI default of auto):
* - arcee-ai/trinity-large-thinking:free β€” Arcee's Trinity *Thinking*
* successor to the trinity-large-preview:free row pulled in V6 (404 then).
* - baidu/cobuddy:free β€” Baidu Qianfan coding/agent
* model with native tool use + reasoning.
* - openrouter/owl-alpha β€” OR-house agentic foundation
* model. Zero-priced but no :free suffix, so it sits on a different rate
* pool than the shared :free 20 RPM / 200 RPD bucket. 1M ctx.
* - nousresearch/hermes-3-llama-3.1-405b:free β€” 405B route ranked in V3
* but never inserted. Currently 429 on probe (upstream throttle), not
* gone. No tools support listed; router falls past it on tool requests.
*
* Dropped from the add list:
* - deepseek/deepseek-v4-flash:free β€” listed at $0 in /v1/models but the
* Crucible provider returns 402 "Out of credits" β€” not actually free.
*
* Context-window corrections β€” OR raised these to match upstream provider
* caps; our seeded values were stale:
* - nvidia/nemotron-3-super-120b-a12b:free 262144 β†’ 1000000
* - qwen/qwen3-coder:free 262144 β†’ 1048576
*/
function migrateModelsV12(db: Database.Database) {
const deleteModel = db.prepare(`DELETE FROM models WHERE platform = ? AND model_id = ?`);
const deleteFallback = db.prepare(`
DELETE FROM fallback_config WHERE model_db_id IN (
SELECT id FROM models WHERE platform = ? AND model_id = ?
)
`);
const removals: Array<[string, string]> = [
['openrouter', 'inclusionai/ling-2.6-1t:free'],
['openrouter', 'tencent/hy3-preview:free'],
];
const applyRemovals = db.transaction(() => {
for (const [p, m] of removals) {
deleteFallback.run(p, m);
deleteModel.run(p, m);
}
});
applyRemovals();
// Context-window upgrades for existing rows.
db.prepare(`
UPDATE models SET context_window = 1000000
WHERE platform = 'openrouter' AND model_id = 'nvidia/nemotron-3-super-120b-a12b:free'
`).run();
db.prepare(`
UPDATE models SET context_window = 1048576
WHERE platform = 'openrouter' AND model_id = 'qwen/qwen3-coder:free'
`).run();
const insert = db.prepare(`
INSERT OR IGNORE INTO models (platform, model_id, display_name, intelligence_rank, speed_rank, size_label, rpm_limit, rpd_limit, tpm_limit, tpd_limit, monthly_token_budget, context_window)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`);
// :free pool quotas as elsewhere in catalog: 20 RPM / 200 RPD / ~6M tokens.
// openrouter/owl-alpha sits on the non-:free zero-priced pool β€” quotas
// unpublished; mirror :free numbers conservatively so it cools down on 429.
const additions: Array<[string, string, string, number, number, string, number | null, number | null, number | null, number | null, string, number | null]> = [
['openrouter', 'arcee-ai/trinity-large-thinking:free', 'Trinity Large Thinking (free)', 5, 9, 'Frontier', 20, 200, null, null, '~6M', 262144],
['openrouter', 'baidu/cobuddy:free', 'CoBuddy (free)', 6, 9, 'Large', 20, 200, null, null, '~6M', 131072],
['openrouter', 'openrouter/owl-alpha', 'Owl Alpha (OR-house)', 5, 9, 'Frontier', 20, 200, null, null, '~6M', 1048576],
['openrouter', 'nousresearch/hermes-3-llama-3.1-405b:free', 'Hermes 3 405B (free)', 17, 9, 'Large', 20, 200, null, null, '~6M', 131072],
];
const apply = db.transaction(() => {
for (const a of additions) insert.run(...a);
const missing = db.prepare(`
SELECT m.id FROM models m
LEFT JOIN fallback_config f ON m.id = f.model_db_id
WHERE f.id IS NULL ORDER BY m.intelligence_rank ASC
`).all() as { id: number }[];
if (missing.length > 0) {
const maxPriority = (db.prepare('SELECT COALESCE(MAX(priority), 0) AS mx FROM fallback_config').get() as { mx: number }).mx;
const addFb = db.prepare('INSERT INTO fallback_config (model_db_id, priority, enabled) VALUES (?, ?, 1)');
for (let i = 0; i < missing.length; i++) addFb.run(missing[i].id, maxPriority + i + 1);
}
});
apply();
}
/**
* V13 (May 2026): cross-provider catalog refresh β€” live-probed against the
* user's keys for Cerebras, Groq, SambaNova, Cloudflare, Google, NVIDIA,
* Mistral, Cohere, Ollama, HuggingFace, Z.ai, Chutes. Z.ai/Chutes confirmed
* no-change (non-flash Z.ai SKUs require recharge; Chutes still gated on
* $0 balance).
*
* DISABLES (row kept for re-enable history; pattern follows V5/V9):
* - google/gemini-3.1-pro-preview β€” 429 with quotaMetric `free_tier_requests`
* limit=0; moved off free tier. (V6 inferred Pro was free; lapsed since.)
* - ollama/kimi-k2-thinking, ollama/mistral-large-3:675b, ollama/deepseek-v3.2
* β€” all return 403 "this model requires a subscription" on the Free plan
* now. (V10 catalogued them as Free; Ollama paywalled them since.)
*
* HARD REMOVALS (model is gone, not paywalled):
* - sambanova/DeepSeek-V3.1-cb β€” absent from live /v1/models; V8 was over-eager.
* - cloudflare/@cf/moonshotai/kimi-k2.5 β€” CF changelog: aliases to k2.6 on
* 2026-05-30. Two rows for one backend wastes a fallback slot.
*
* UPDATES (numbers verified from live response headers or /v1/models):
* - cerebras: all 3 enabled rows β†’ 5 RPM / 30K TPM / 2400 RPD / 1M TPD
* (V4's 30/14400/60K/1M was stale; current free pool is 5 RPM shared).
* - groq/llama-3.3-70b-versatile: TPD 500K β†’ 100K (docs).
* - groq/llama-4-scout: TPM 6K β†’ 30K (live header).
* - groq/compound + groq/compound-mini: RPD 1000→250, TPM 8K→70K, TPD→null
* (compound systems run on a separate budget per docs + live header).
* - sambanova/DeepSeek-V3.2: ctx 131K β†’ 32K (/v1/models reports 32768).
* - cloudflare/@cf/meta/llama-3.3-70b-instruct-fp8-fast: ctx 131K β†’ 24K
* (CF model page: fp8-fast variant capped at 24K).
* - mistral context windows (all stale vs /v1/models):
* codestral-latest 32K β†’ 256K
* devstral-latest 131K β†’ 262K
* magistral-medium 40K β†’ 131K
* mistral-large-latest 131K β†’ 262K
*
* ADDITIONS (all chat-probed; tools verified with tool_choice=auto):
* - groq: openai/gpt-oss-safeguard-20b (tool-tuned 20B; same pool as gpt-oss-20b)
* - cloudflare: @cf/nvidia/nemotron-3-120b-a12b, @cf/google/gemma-4-26b-a4b-it
* (both 256K ctx, function_calling=true; 429'd on probe due to daily
* neuron exhaustion, not model-level β€” same Free pool as other CF rows)
* - google: gemini-3.5-flash (2026-05 release; 1M ctx; tool calls verified)
* - nvidia: deepseek-ai/deepseek-v4-flash, z-ai/glm-5.1 (~2min cold start),
* qwen/qwen3-coder-480b-a35b-instruct (full id β€” discovered via /v1/models)
* - mistral: mistral-small-latest (Small 4), ministral-8b-latest (edge 8B)
* - cohere: command-a-reasoning-08-2025, command-r-08-2024. README ToS
* table marks Cohere "❌ Avoid for personal use" β€” these rows expand
* fallback coverage but inherit the same caveat.
* - ollama: qwen3-coder-next (~80B-A3B coder)
*
* NEW PLATFORM (huggingface):
* - V4 removed HF for "tool-call format issues" on the legacy serverless
* endpoint that emitted tool calls as text. The new router.huggingface.co
* meta-router uses each backend's native protocol and normalizes the
* response β€” chat-probed clean tool_calls on DeepSeek-V4-Flash, Kimi-K2.6,
* Qwen3-Coder-Next, GLM-4.7, Qwen3-235B. Recurring $0.10/mo router credit
* on the free tier (no card, no expiry). Budget ~1-3M tokens/mo depending
* on backend. Seeded with 3 frontier rows.
* - Provider registration lives in server/src/providers/index.ts.
*
* NO-CHANGE PROVIDERS (probed, nothing to update):
* - openrouter (V12 just shipped)
* - z.ai/zhipu β€” non-flash SKUs all 1113 "insufficient balance"; flash-only
* pool is correctly catalogued.
* - chutes β€” every probe 402 on $0 balance; V11 drop decision stands.
*
* DEFERRED:
* - cerebras/qwen-3-235b-a22b-instruct-2507 + llama3.1-8b: docs flag
* hard-deprecation 2026-05-27 (4d from migration), but both still 200
* today. Disable in V14 on/after that date.
* - sambanova: every chat probe returned 402 PAYMENT_METHOD_REQUIRED on the
* user's account. /v1/models still lists rows so REMOVE/UPDATE above is
* valid, but the SambaNova free tier may have lapsed account-wide.
* Investigate before adding new SambaNova rows.
* - github: gpt-5 family + xai/grok-3 both 400 "unavailable_model" on free
* tier β€” keep gpt-4o (V2 verdict still holds).
*/
function migrateModelsV13(db: Database.Database) {
// 1) Disables (row kept; can be re-enabled without losing fallback history).
const disable = db.prepare(`UPDATE models SET enabled = 0 WHERE platform = ? AND model_id = ?`);
const disables: Array<[string, string]> = [
['google', 'gemini-3.1-pro-preview'],
['ollama', 'kimi-k2-thinking'],
['ollama', 'mistral-large-3:675b'],
['ollama', 'deepseek-v3.2'],
];
for (const [p, m] of disables) disable.run(p, m);
// 2) Hard removals.
const deleteModel = db.prepare(`DELETE FROM models WHERE platform = ? AND model_id = ?`);
const deleteFallback = db.prepare(`
DELETE FROM fallback_config WHERE model_db_id IN (
SELECT id FROM models WHERE platform = ? AND model_id = ?
)
`);
const removals: Array<[string, string]> = [
['sambanova', 'DeepSeek-V3.1-cb'],
['cloudflare', '@cf/moonshotai/kimi-k2.5'],
];
const applyRemovals = db.transaction(() => {
for (const [p, m] of removals) {
deleteFallback.run(p, m);
deleteModel.run(p, m);
}
});
applyRemovals();
// 3) Cerebras free-pool limit correction (3 enabled rows; zai-glm-4.7 stays
// on its V5-set per-model 10/100 cap since it's gated separately).
db.prepare(`
UPDATE models
SET rpm_limit = 5, rpd_limit = 2400, tpm_limit = 30000, tpd_limit = 1000000
WHERE platform = 'cerebras'
AND model_id IN ('qwen-3-235b-a22b-instruct-2507', 'gpt-oss-120b', 'llama3.1-8b')
`).run();
// 4) Groq limit corrections.
db.prepare(`UPDATE models SET tpd_limit = 100000 WHERE platform = 'groq' AND model_id = 'llama-3.3-70b-versatile'`).run();
db.prepare(`UPDATE models SET tpm_limit = 30000 WHERE platform = 'groq' AND model_id = 'meta-llama/llama-4-scout-17b-16e-instruct'`).run();
db.prepare(`
UPDATE models SET rpd_limit = 250, tpm_limit = 70000, tpd_limit = NULL
WHERE platform = 'groq' AND model_id IN ('groq/compound', 'groq/compound-mini')
`).run();
// 5) Single-row context-window corrections.
db.prepare(`UPDATE models SET context_window = 32768 WHERE platform = 'sambanova' AND model_id = 'DeepSeek-V3.2'`).run();
db.prepare(`UPDATE models SET context_window = 24000 WHERE platform = 'cloudflare' AND model_id = '@cf/meta/llama-3.3-70b-instruct-fp8-fast'`).run();
// 6) Mistral context-window corrections.
db.prepare(`UPDATE models SET context_window = 256000 WHERE platform = 'mistral' AND model_id = 'codestral-latest'`).run();
db.prepare(`UPDATE models SET context_window = 262144 WHERE platform = 'mistral' AND model_id = 'devstral-latest'`).run();
db.prepare(`UPDATE models SET context_window = 131072 WHERE platform = 'mistral' AND model_id = 'magistral-medium-latest'`).run();
db.prepare(`UPDATE models SET context_window = 262144 WHERE platform = 'mistral' AND model_id = 'mistral-large-latest'`).run();
// 7) Additions across providers (chat-probed; tools verified where claimed).
const insert = db.prepare(`
INSERT OR IGNORE INTO models (platform, model_id, display_name, intelligence_rank, speed_rank, size_label, rpm_limit, rpd_limit, tpm_limit, tpd_limit, monthly_token_budget, context_window)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`);
const additions: Array<[string, string, string, number, number, string, number | null, number | null, number | null, number | null, string, number | null]> = [
// Groq β€” shared 30 RPM / 1K RPD / 8K TPM / 200K TPD per-model pool.
['groq', 'openai/gpt-oss-safeguard-20b', 'GPT-OSS Safeguard 20B (Groq)', 18, 2, 'Medium', 30, 1000, 8000, 200000, '~6M', 131072],
// Cloudflare β€” 10K Neurons/day shared free pool.
['cloudflare', '@cf/nvidia/nemotron-3-120b-a12b', 'Nemotron 3 120B (CF)', 9, 11, 'Frontier', null, null, null, null, '~5-10M', 262144],
['cloudflare', '@cf/google/gemma-4-26b-a4b-it', 'Gemma 4 26B-A4B it (CF)', 22, 11, 'Medium', null, null, null, null, '~10-20M', 262144],
// Google β€” same 20 RPD per-model free pool. 3.5 Flash is the current Flash flagship.
['google', 'gemini-3.5-flash', 'Gemini 3.5 Flash', 3, 5, 'Large', 10, 20, 250000, null, '~3M', 1048576],
// NVIDIA NIM β€” credits-based; per-model 40 RPM.
['nvidia', 'deepseek-ai/deepseek-v4-flash', 'DeepSeek V4 Flash (NV)', 4, 9, 'Frontier', 40, null, null, null, '~3M (credits)', 131072],
['nvidia', 'z-ai/glm-5.1', 'GLM-5.1 (NV, slow cold-start)', 5, 9, 'Frontier', 40, null, null, null, '~3M (credits)', 200000],
['nvidia', 'qwen/qwen3-coder-480b-a35b-instruct', 'Qwen3-Coder 480B (NV)', 2, 9, 'Frontier', 40, null, null, null, '~3M (credits)', 262144],
// Mistral β€” Experiment plan 2 RPM / 500K TPM / shared ~1B/mo.
['mistral', 'mistral-small-latest', 'Mistral Small 4', 14, 8, 'Medium', 2, null, 500000, null, '~50-100M', 262144],
['mistral', 'ministral-8b-latest', 'Ministral 3 8B', 28, 8, 'Small', 2, null, 500000, null, '~50-100M', 262144],
// Cohere β€” trial 20 RPM / 1000 RPM total. ToS table marks ❌ Avoid for personal use.
['cohere', 'command-a-reasoning-08-2025', 'Command A Reasoning (08-2025)', 13, 11, 'Large', 20, 33, null, null, '~1-2M', 256000],
['cohere', 'command-r-08-2024', 'Command R (08-2024)', 25, 11, 'Medium', 20, 33, null, null, '~1-2M', 131072],
// Ollama Cloud β€” GPU-time quota.
['ollama', 'qwen3-coder-next', 'Qwen3-Coder Next (Ollama)', 3, 9, 'Large', null, null, null, null, '~10-20M', 262144],
// HuggingFace router (new platform) β€” recurring $0.10/mo credit, no card.
['huggingface', 'deepseek-ai/DeepSeek-V4-Flash', 'DeepSeek V4 Flash (HF)', 4, 9, 'Frontier', null, null, null, null, '~1-3M', 131072],
['huggingface', 'moonshotai/Kimi-K2.6', 'Kimi K2.6 (HF)', 3, 9, 'Frontier', null, null, null, null, '~1-3M', 262144],
['huggingface', 'Qwen/Qwen3-Coder-Next', 'Qwen3-Coder Next (HF)', 3, 9, 'Large', null, null, null, null, '~1-3M', 262144],
];
const apply = db.transaction(() => {
for (const a of additions) insert.run(...a);
const missing = db.prepare(`
SELECT m.id FROM models m
LEFT JOIN fallback_config f ON m.id = f.model_db_id
WHERE f.id IS NULL ORDER BY m.intelligence_rank ASC
`).all() as { id: number }[];
if (missing.length > 0) {
const maxPriority = (db.prepare('SELECT COALESCE(MAX(priority), 0) AS mx FROM fallback_config').get() as { mx: number }).mx;
const addFb = db.prepare('INSERT INTO fallback_config (model_db_id, priority, enabled) VALUES (?, ?, 1)');
for (let i = 0; i < missing.length; i++) addFb.run(missing[i].id, maxPriority + i + 1);
}
});
apply();
}
/**
* V14 (May 2026): Cerebras hard-deprecation 2026-05-27.
*
* Per inference-docs.cerebras.ai/models/overview, both
* `qwen-3-235b-a22b-instruct-2507` and `llama3.1-8b` hit a hard deprecation
* on 2026-05-27 with no announced free-tier replacement at the same
* parameter class. Disable both ahead of the cutover so the router stops
* sending traffic to them. Row kept (not deleted) so it can be re-enabled
* if Cerebras restores or renames either model β€” same pattern as V9's
* disable of `zai-glm-4.7`.
*
* Cerebras `gpt-oss-120b` is NOT in the deprecation list and stays enabled
* as the sole free-tier Cerebras route.
*/
function migrateModelsV14(db: Database.Database) {
db.prepare(`
UPDATE models SET enabled = 0
WHERE platform = 'cerebras'
AND model_id IN ('qwen-3-235b-a22b-instruct-2507', 'llama3.1-8b')
`).run();
}
function ensureUnifiedKey(db: Database.Database) {
const existing = db.prepare("SELECT value FROM settings WHERE key = 'unified_api_key'").get() as { value: string } | undefined;
if (!existing) {
const key = `freellmapi-${crypto.randomBytes(24).toString('hex')}`;
db.prepare("INSERT INTO settings (key, value) VALUES ('unified_api_key', ?)").run(key);
console.log(`\n Your unified API key: ${key}\n`);
}
}
export function getUnifiedApiKey(): string {
const db = getDb();
const row = db.prepare("SELECT value FROM settings WHERE key = 'unified_api_key'").get() as { value: string };
return row.value;
}
export function regenerateUnifiedKey(): string {
const db = getDb();
const key = `freellmapi-${crypto.randomBytes(24).toString('hex')}`;
db.prepare("UPDATE settings SET value = ? WHERE key = 'unified_api_key'").run(key);
return key;
}