Spaces:
Paused
Paused
CrispStrobe commited on
Commit ·
c5e4684
1
Parent(s): e2e9c3d
feat: resolve MiniMax-M1 (366B), Mistral Small variants, and Qwen Plus via deep config.json parsing and global technical sync
Browse files- data/providers.json +0 -0
- scripts/fetch-providers.js +97 -302
data/providers.json
CHANGED
|
The diff for this file is too large to render.
See raw diff
|
|
|
scripts/fetch-providers.js
CHANGED
|
@@ -2,11 +2,6 @@
|
|
| 2 |
|
| 3 |
/**
|
| 4 |
* Fetch live pricing data from all supported providers and update data/providers.json.
|
| 5 |
-
*
|
| 6 |
-
* Usage:
|
| 7 |
-
* node scripts/fetch-providers.js # fetch all providers
|
| 8 |
-
* node scripts/fetch-providers.js scaleway # fetch only Scaleway
|
| 9 |
-
* node scripts/fetch-providers.js openrouter # fetch only OpenRouter
|
| 10 |
*/
|
| 11 |
|
| 12 |
const fs = require('fs');
|
|
@@ -45,12 +40,8 @@ function saveData(data) {
|
|
| 45 |
|
| 46 |
function updateProviderModels(providers, providerName, models) {
|
| 47 |
const provider = providers.find((p) => p.name === providerName);
|
| 48 |
-
if (!provider)
|
| 49 |
-
console.warn(` ⚠ Provider "${providerName}" not found in providers.json – skipping.`);
|
| 50 |
-
return false;
|
| 51 |
-
}
|
| 52 |
|
| 53 |
-
// Smart merge: preserve existing metadata if missing in new data
|
| 54 |
const existingMap = new Map((provider.models || []).map(m => [m.name, m]));
|
| 55 |
|
| 56 |
provider.models = models.map(newModel => {
|
|
@@ -58,8 +49,8 @@ function updateProviderModels(providers, providerName, models) {
|
|
| 58 |
if (!existing) return newModel;
|
| 59 |
|
| 60 |
return {
|
| 61 |
-
...existing,
|
| 62 |
-
...newModel,
|
| 63 |
size_b: newModel.size_b || existing.size_b,
|
| 64 |
size_source: newModel.size_source || existing.size_source,
|
| 65 |
hf_id: newModel.hf_id || existing.hf_id,
|
|
@@ -77,94 +68,32 @@ function updateProviderModels(providers, providerName, models) {
|
|
| 77 |
const normName = (s) =>
|
| 78 |
s.toLowerCase().replace(/[-_.:]/g, ' ').replace(/[^a-z0-9 ]/g, '').replace(/\s+/g, ' ').trim();
|
| 79 |
|
| 80 |
-
function buildOrIndex(orProvider) {
|
| 81 |
-
if (!orProvider) return [];
|
| 82 |
-
const index = [];
|
| 83 |
-
for (const m of orProvider.models || []) {
|
| 84 |
-
if (!m.capabilities || m.capabilities.length === 0) continue;
|
| 85 |
-
const modelPart = m.name.replace(/:free$/, '').split('/').pop();
|
| 86 |
-
index.push({
|
| 87 |
-
norm: normName(modelPart),
|
| 88 |
-
capabilities: m.capabilities,
|
| 89 |
-
type: m.type,
|
| 90 |
-
size_b: m.size_b,
|
| 91 |
-
size_source: m.size_source,
|
| 92 |
-
hf_id: m.hf_id,
|
| 93 |
-
ollama_id: m.ollama_id,
|
| 94 |
-
hf_private: m.hf_private,
|
| 95 |
-
});
|
| 96 |
-
}
|
| 97 |
-
return index;
|
| 98 |
-
}
|
| 99 |
-
|
| 100 |
-
function findOrMatch(modelName, orIndex) {
|
| 101 |
-
const raw = modelName.replace(/@[^/]+$/, '').replace(/:[^/]+$/, '');
|
| 102 |
-
const modelPart = raw.includes('/') ? raw.split('/').pop() : raw;
|
| 103 |
-
const n = normName(modelPart).replace(/ (?:reasoning|thinking|extended|nothinking)$/, '');
|
| 104 |
-
|
| 105 |
-
for (const entry of orIndex) if (entry.norm === n) return entry;
|
| 106 |
-
let best = null, bestLen = 0;
|
| 107 |
-
for (const entry of orIndex) {
|
| 108 |
-
if (n.startsWith(entry.norm) && entry.norm.length > bestLen) {
|
| 109 |
-
best = entry; bestLen = entry.norm.length;
|
| 110 |
-
}
|
| 111 |
-
}
|
| 112 |
-
if (best) return best;
|
| 113 |
-
for (const entry of orIndex) if (entry.norm.startsWith(n + ' ')) return entry;
|
| 114 |
-
if (n.length >= 5) {
|
| 115 |
-
let bestC = null, bestCLen = Infinity;
|
| 116 |
-
for (const entry of orIndex) {
|
| 117 |
-
const e = entry.norm;
|
| 118 |
-
if ((e === n || e.includes(' ' + n + ' ') || e.startsWith(n + ' ') || e.endsWith(' ' + n)) && e.length < bestCLen) {
|
| 119 |
-
bestC = entry; bestCLen = e.length;
|
| 120 |
-
}
|
| 121 |
-
}
|
| 122 |
-
if (bestC) return bestC;
|
| 123 |
-
}
|
| 124 |
-
const tokens = n.split(' ');
|
| 125 |
-
if (tokens.length >= 2 && n.length >= 7) {
|
| 126 |
-
let bestT = null, bestTLen = Infinity;
|
| 127 |
-
for (const entry of orIndex) {
|
| 128 |
-
const eTokens = entry.norm.split(' ');
|
| 129 |
-
if (tokens.every((t) => eTokens.includes(t)) && entry.norm.length < bestTLen) {
|
| 130 |
-
bestT = entry; bestTLen = entry.norm.length;
|
| 131 |
-
}
|
| 132 |
-
}
|
| 133 |
-
if (bestT) return bestT;
|
| 134 |
-
}
|
| 135 |
-
return null;
|
| 136 |
-
}
|
| 137 |
-
|
| 138 |
// Estimate parameters from config.json (vLLM style fallback)
|
| 139 |
-
function estimateParams(config) {
|
| 140 |
if (!config) return null;
|
| 141 |
const h = config.hidden_size || config.d_model || config.n_embd;
|
| 142 |
const l = config.num_hidden_layers || config.n_layer;
|
| 143 |
const v = config.vocab_size;
|
| 144 |
const i = config.intermediate_size || config.d_ff;
|
| 145 |
-
|
| 146 |
-
// MoE support
|
| 147 |
const numExperts = config.num_local_experts || config.n_experts || config.num_experts || 1;
|
| 148 |
const modelType = (config.model_type || '').toLowerCase();
|
| 149 |
|
| 150 |
if (h && l && v) {
|
| 151 |
const intermediate = i || (4 * h);
|
| 152 |
-
|
| 153 |
-
// Embedding parameters
|
| 154 |
const vocabParams = v * h;
|
| 155 |
const posParams = (config.max_position_embeddings || 512) * h;
|
| 156 |
const typeParams = (config.type_vocab_size || 0) * h;
|
| 157 |
const embedParams = vocabParams + posParams + typeParams;
|
| 158 |
-
|
| 159 |
-
// Layer parameters (Attention + MLP)
|
| 160 |
const attentionParams = 4 * (h * h);
|
| 161 |
|
| 162 |
-
//
|
| 163 |
-
const hasGlu = ['llama', 'mistral', 'phi3', 'qwen2', 'gemma', 'gemma2', 'minimax'].includes(modelType)
|
| 164 |
-
|
|
|
|
| 165 |
|
| 166 |
-
const
|
| 167 |
-
|
|
|
|
| 168 |
}
|
| 169 |
return null;
|
| 170 |
}
|
|
@@ -175,18 +104,15 @@ async function fetchHFSize(hfId) {
|
|
| 175 |
const token = process.env.HF_TOKEN;
|
| 176 |
const headers = token ? { Authorization: `Bearer ${token}` } : {};
|
| 177 |
let isPrivate = false;
|
| 178 |
-
|
| 179 |
try {
|
| 180 |
-
let params = null;
|
| 181 |
-
let source = 'hf-total';
|
| 182 |
-
let data = {};
|
| 183 |
|
| 184 |
-
// 1.
|
| 185 |
try {
|
| 186 |
data = await getJson(`https://huggingface.co/api/models/${hfId}`, { headers, retries: 1 });
|
| 187 |
params = data.safetensors?.total || data.config?.total_parameters || data.config?.model_type_params;
|
| 188 |
|
| 189 |
-
// Fallback: cardData
|
| 190 |
if (!params && data.cardData?.model_details?.parameters) {
|
| 191 |
const match = data.cardData.model_details.parameters.match(/([\d.]+)\s*[Bb]/);
|
| 192 |
if (match) { params = parseFloat(match[1]) * 1_000_000_000; source = 'hf-card'; }
|
|
@@ -195,14 +121,14 @@ async function fetchHFSize(hfId) {
|
|
| 195 |
if (e.message.includes('401') || e.message.includes('404')) isPrivate = true;
|
| 196 |
}
|
| 197 |
|
| 198 |
-
// 2.
|
| 199 |
if (!params && !isPrivate) {
|
| 200 |
-
try {
|
| 201 |
-
const config = await getJson(`https://huggingface.co/${hfId}/raw/main/config.json`, { headers, retries: 1 });
|
| 202 |
-
params = config.total_parameters || estimateParams(config);
|
| 203 |
-
source = config.total_parameters ? 'hf-total' : 'hf-config-estimate';
|
| 204 |
} catch (e) {
|
| 205 |
-
if (e.message.includes('401') || e.message.includes('404')) isPrivate = true;
|
| 206 |
}
|
| 207 |
}
|
| 208 |
|
|
@@ -210,12 +136,9 @@ async function fetchHFSize(hfId) {
|
|
| 210 |
if (!params) return { error: 'No parameter data found' };
|
| 211 |
|
| 212 |
const b = params / 1_000_000_000;
|
| 213 |
-
// Keep 2 decimals for small models (<1B), 1 decimal for others
|
| 214 |
const size = b < 1 ? Math.round(b * 100) / 100 : Math.round(b * 10) / 10;
|
| 215 |
return { size, source };
|
| 216 |
-
} catch (e) {
|
| 217 |
-
return { error: e.message };
|
| 218 |
-
}
|
| 219 |
}
|
| 220 |
|
| 221 |
async function fetchOllamaMetadata(ollamaId) {
|
|
@@ -237,116 +160,40 @@ async function fetchOllamaMetadata(ollamaId) {
|
|
| 237 |
|
| 238 |
const EMBEDDER_KEYWORDS = ['embed', 'bge', 'gte', 'e5', 'stella', 'minilm', 'multilingual-mpnet'];
|
| 239 |
|
| 240 |
-
// Manual mappings for models with non-standard naming.
|
| 241 |
const MANUAL_HF_ID_MAP = {
|
| 242 |
-
'
|
| 243 |
-
'
|
| 244 |
-
'
|
| 245 |
-
'
|
| 246 |
-
'
|
| 247 |
-
'
|
| 248 |
-
'bge m3': 'BAAI/bge-m3',
|
| 249 |
-
'bge en icl': 'BAAI/bge-en-icl',
|
| 250 |
-
'lightonocr 2': 'lightonai/LightOnOCR-2-1B',
|
| 251 |
-
'sdxl': 'stabilityai/stable-diffusion-xl-base-1.0',
|
| 252 |
-
'flux 1 schnell': 'black-forest-labs/FLUX.1-schnell',
|
| 253 |
-
'flux schnell': 'black-forest-labs/FLUX.1-schnell',
|
| 254 |
-
'paraphrase multilingual mpnet base v2': 'sentence-transformers/paraphrase-multilingual-mpnet-base-v2',
|
| 255 |
-
'bge large en v1 5': 'BAAI/bge-large-en-v1.5',
|
| 256 |
-
'bge multilingual gemma2': 'BAAI/bge-multilingual-gemma2',
|
| 257 |
-
'photomaker v2': 'TencentARC/PhotoMaker-V2',
|
| 258 |
-
'canopy labs orpheus english': 'canopy-labs/orpheus-medium',
|
| 259 |
-
'canopy labs orpheus arabic saudi': 'canopy-labs/orpheus-medium',
|
| 260 |
-
'qwen turbo': 'Alibaba/Qwen-Turbo',
|
| 261 |
-
'alibaba qwen turbo': 'Alibaba/Qwen-Turbo',
|
| 262 |
-
'qwen qwen turbo': 'Alibaba/Qwen-Turbo',
|
| 263 |
-
'qwen plus': 'Alibaba/Qwen-Plus',
|
| 264 |
-
'alibaba qwen plus': 'Alibaba/Qwen-Plus',
|
| 265 |
-
'qwen qwen plus': 'Alibaba/Qwen-Plus',
|
| 266 |
-
'qwen max': 'Alibaba/Qwen-Max',
|
| 267 |
-
'alibaba qwen max': 'Alibaba/Qwen-Max',
|
| 268 |
-
'qwen qwen max': 'Alibaba/Qwen-Max',
|
| 269 |
-
'qwen 3 coder flash': 'Qwen/Qwen2.5-Coder-7B-Instruct',
|
| 270 |
-
'qwen3 coder flash': 'Qwen/Qwen2.5-Coder-7B-Instruct',
|
| 271 |
-
'qwen 3 coder plus': 'Qwen/Qwen2.5-Coder-32B-Instruct',
|
| 272 |
-
'qwen3 coder plus': 'Qwen/Qwen2.5-Coder-32B-Instruct',
|
| 273 |
-
'qwen 3 5 flash': 'Qwen/Qwen2.5-7B-Instruct',
|
| 274 |
-
'qwen3 5 flash 02 23': 'Qwen/Qwen2.5-7B-Instruct',
|
| 275 |
-
'qwen3 5 plus 02 15': 'Qwen/Qwen2.5-32B-Instruct',
|
| 276 |
-
'qwen vl plus': 'Qwen/Qwen2-VL-7B-Instruct',
|
| 277 |
-
'qwen vl max': 'Qwen/Qwen2-VL-72B-Instruct',
|
| 278 |
-
'deepseek chat': 'deepseek-ai/DeepSeek-V3',
|
| 279 |
-
'deepseek reasoner': 'deepseek-ai/DeepSeek-R1',
|
| 280 |
-
'deepseek v3 turbo': 'deepseek-ai/DeepSeek-V3',
|
| 281 |
-
'deepseek v3 0324 fast': 'deepseek-ai/DeepSeek-V3',
|
| 282 |
-
'deepseek r1t2 chimera': 'deepseek-ai/DeepSeek-R1',
|
| 283 |
-
'deepseek v3 2 exp': 'deepseek-ai/DeepSeek-V3.2',
|
| 284 |
-
'deepseek v3 2 speciale': 'deepseek-ai/DeepSeek-V3.2',
|
| 285 |
-
'deepseek v3 base': 'deepseek-ai/DeepSeek-V3',
|
| 286 |
-
'deepseek v3 0324 base': 'deepseek-ai/DeepSeek-V3',
|
| 287 |
-
'grok 4 1 fast': 'xai-org/grok-fast',
|
| 288 |
-
'grok 4 fast': 'xai-org/grok-fast',
|
| 289 |
-
'grok code fast 1': 'xai-org/grok-code',
|
| 290 |
-
'grok 3 mini': 'xai-org/grok-mini',
|
| 291 |
-
'grok 3 mini beta': 'xai-org/grok-mini',
|
| 292 |
-
'grok 4 20 multi agent beta': 'xai-org/grok-4',
|
| 293 |
-
'grok 4 20 beta': 'xai-org/grok-4',
|
| 294 |
-
'grok 4': 'xai-org/grok-4',
|
| 295 |
-
'grok 3': 'xai-org/grok-3',
|
| 296 |
-
'grok 3 beta': 'xai-org/grok-3',
|
| 297 |
-
'grok 2 1212': 'xai-org/grok-2',
|
| 298 |
-
'glm 4 6v': 'THUDM/glm-4v-9b',
|
| 299 |
-
'glm 5 turbo': 'THUDM/glm-5-turbo',
|
| 300 |
-
'minimax m2 7': 'MiniMaxAI/MiniMax-M2.7',
|
| 301 |
-
'minimax m2 7 highspeed': 'MiniMaxAI/MiniMax-M2.7',
|
| 302 |
-
'minimax 01': 'MiniMaxAI/MiniMax-Text-01',
|
| 303 |
-
'minimax m2 her': 'MiniMaxAI/MiniMax-M2.7',
|
| 304 |
'phi 4': 'microsoft/phi-4',
|
| 305 |
-
'
|
| 306 |
-
'
|
| 307 |
-
'
|
| 308 |
-
'
|
| 309 |
-
'
|
| 310 |
-
'
|
| 311 |
-
'flux 1 pro': 'black-forest-labs/FLUX.1-pro',
|
| 312 |
-
'flux 2 flex': 'black-forest-labs/FLUX.2-flex',
|
| 313 |
-
'flux 2 max': 'black-forest-labs/FLUX.2-max',
|
| 314 |
-
'flux kontext pro': 'black-forest-labs/FLUX.1-pro',
|
| 315 |
-
'flux pro 1 1': 'black-forest-labs/FLUX.1-pro',
|
| 316 |
-
'flux pro': 'black-forest-labs/FLUX.1-pro',
|
| 317 |
-
'flux pro 1 0 fill': 'black-forest-labs/FLUX.1-pro',
|
| 318 |
-
'flux pro 1 1 ultra': 'black-forest-labs/FLUX.1-pro',
|
| 319 |
-
'flux kontext max': 'black-forest-labs/FLUX.1-pro',
|
| 320 |
-
'mistral large 3': 'mistralai/Mistral-Large-Instruct-2411',
|
| 321 |
-
'mistral large 2411': 'mistralai/Mistral-Large-Instruct-2411',
|
| 322 |
-
'mistral large 2407': 'mistralai/Mistral-Large-Instruct-2407',
|
| 323 |
-
'mistral small 4': 'mistralai/Mistral-Small-Instruct-2409',
|
| 324 |
-
'mistral medium 3': 'mistralai/Mistral-Medium-Instruct-2407',
|
| 325 |
-
'codestral latest': 'mistralai/Codestral-22B-v0.1',
|
| 326 |
-
'devstral 2': 'mistralai/Mistral-7B-v0.1',
|
| 327 |
};
|
| 328 |
|
| 329 |
const MANUAL_OLLAMA_ID_MAP = {
|
| 330 |
'phi 4': 'phi4',
|
| 331 |
'deepseek chat': 'deepseek-v3',
|
| 332 |
'deepseek reasoner': 'deepseek-r1',
|
| 333 |
-
'codestral': 'codestral',
|
| 334 |
'mistral small 24b': 'mistral-small',
|
| 335 |
-
|
| 336 |
-
|
| 337 |
-
|
| 338 |
-
'
|
| 339 |
-
'
|
| 340 |
-
'
|
| 341 |
-
'
|
| 342 |
-
'
|
| 343 |
-
'
|
| 344 |
-
'
|
| 345 |
-
'
|
| 346 |
-
'
|
| 347 |
-
'
|
| 348 |
-
'mixtral 8x7b': 'mixtral',
|
| 349 |
-
'mixtral 8x22b': 'mixtral-8x22b',
|
| 350 |
};
|
| 351 |
|
| 352 |
const PROPRIETARY_KEYWORDS = [
|
|
@@ -355,151 +202,99 @@ const PROPRIETARY_KEYWORDS = [
|
|
| 355 |
];
|
| 356 |
|
| 357 |
async function propagateExtraData(data) {
|
| 358 |
-
const orProvider = data.providers.find((p) => p.name === 'OpenRouter');
|
| 359 |
-
const orIndex = buildOrIndex(orProvider);
|
| 360 |
let benchmarks = [];
|
| 361 |
try { benchmarks = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'data', 'benchmarks.json'), 'utf8')); } catch (e) {}
|
| 362 |
const hfIdToSize = new Map();
|
| 363 |
-
benchmarks.forEach((b) => {
|
| 364 |
-
if (b.params_b && b.hf_id) hfIdToSize.set(b.hf_id.toLowerCase(), b.params_b);
|
| 365 |
-
});
|
| 366 |
|
| 367 |
-
// Multi-pass Enrichment Sweep
|
| 368 |
// 1. Initial manual and fuzzy mapping
|
| 369 |
data.providers.forEach(p => p.models.forEach(model => {
|
| 370 |
const n = normName(model.name);
|
| 371 |
-
|
| 372 |
-
|
| 373 |
-
|
| 374 |
-
if (n === key || n.endsWith(' ' + key) || n.endsWith('/' + key)) { model.hf_id = val; break; }
|
| 375 |
}
|
| 376 |
}
|
| 377 |
-
if (!model.
|
| 378 |
-
|
| 379 |
-
|
| 380 |
-
}
|
| 381 |
}
|
| 382 |
-
|
| 383 |
-
|
|
|
|
| 384 |
const size = hfIdToSize.get(model.hf_id.toLowerCase());
|
| 385 |
if (size) { model.size_b = size; model.size_source = 'benchmark'; }
|
| 386 |
}
|
| 387 |
}));
|
| 388 |
|
| 389 |
-
// 2.
|
| 390 |
-
const
|
| 391 |
data.providers.forEach(p => p.models.forEach(m => {
|
| 392 |
-
if (m.size_b
|
| 393 |
-
const baseName = m.name.split('/').pop().replace(/:free$/, '').toLowerCase();
|
| 394 |
-
const existing = globalMeta.get(baseName) || {};
|
| 395 |
-
globalMeta.set(baseName, {
|
| 396 |
-
size_b: m.size_b || existing.size_b,
|
| 397 |
-
size_source: m.size_source || existing.size_source,
|
| 398 |
-
hf_id: m.hf_id || existing.hf_id,
|
| 399 |
-
ollama_id: m.ollama_id || existing.ollama_id,
|
| 400 |
-
hf_private: m.hf_private || existing.hf_private,
|
| 401 |
-
});
|
| 402 |
-
}
|
| 403 |
}));
|
| 404 |
|
| 405 |
-
|
| 406 |
-
const baseName = m.name.split('/').pop().replace(/:free$/, '').toLowerCase();
|
| 407 |
-
const meta = globalMeta.get(baseName);
|
| 408 |
-
if (meta) {
|
| 409 |
-
m.size_b = m.size_b || meta.size_b;
|
| 410 |
-
m.size_source = m.size_source || meta.size_source;
|
| 411 |
-
m.hf_id = m.hf_id || meta.hf_id;
|
| 412 |
-
m.ollama_id = m.ollama_id || meta.ollama_id;
|
| 413 |
-
m.hf_private = m.hf_private || meta.hf_private;
|
| 414 |
-
}
|
| 415 |
-
}));
|
| 416 |
-
|
| 417 |
-
// 3. Technical Lookups (Final fallback for remaining gaps)
|
| 418 |
-
let hfSizeFetched = 0, ollamaFetched = 0;
|
| 419 |
-
const hfLookupQueue = [], ollamaLookupQueue = [];
|
| 420 |
-
|
| 421 |
-
data.providers.forEach(provider => {
|
| 422 |
-
provider.models.forEach(model => {
|
| 423 |
-
const n = normName(model.name);
|
| 424 |
-
if (model.type === 'image' && (!model.capabilities || !model.capabilities.length)) { model.capabilities = ['image-gen']; }
|
| 425 |
-
if (model.type === 'chat' && EMBEDDER_KEYWORDS.some(k => n.includes(k))) { model.type = 'embedding'; }
|
| 426 |
-
|
| 427 |
-
if (!model.size_b) {
|
| 428 |
-
// Force lookup if we have a clear repo ID, even if previously marked private
|
| 429 |
-
if (model.hf_id || (model.name.includes('/') && !model.hf_private)) hfLookupQueue.push(model);
|
| 430 |
-
else if (!model.hf_private && model.ollama_id) ollamaLookupQueue.push(model);
|
| 431 |
-
}
|
| 432 |
-
});
|
| 433 |
-
});
|
| 434 |
-
|
| 435 |
-
const uniqueIds = [...new Set(hfLookupQueue.map(m => m.hf_id || m.name).filter(id => id.includes('/')))].slice(0, 300);
|
| 436 |
if (uniqueIds.length > 0) {
|
| 437 |
console.log(`\n HF Hub: technical metadata inspection for ${uniqueIds.length} models...`);
|
| 438 |
const idToResult = new Map();
|
| 439 |
-
for (
|
| 440 |
-
|
| 441 |
-
process.stdout.write(` [${i + 1}/${uniqueIds.length}] ${id.padEnd(50)} `);
|
| 442 |
const result = await fetchHFSize(id);
|
| 443 |
idToResult.set(id, result);
|
| 444 |
if (result.size) process.stdout.write(`✓ ${result.size}B (${result.source})\n`);
|
| 445 |
-
else
|
| 446 |
await new Promise(r => setTimeout(r, 50));
|
| 447 |
}
|
| 448 |
for (const model of hfLookupQueue) {
|
| 449 |
if (!model.size_b) {
|
| 450 |
-
const id = model.hf_id
|
| 451 |
const result = idToResult.get(id);
|
| 452 |
-
if (result) {
|
| 453 |
-
|
| 454 |
-
|
| 455 |
-
|
| 456 |
}
|
| 457 |
}
|
| 458 |
}
|
| 459 |
}
|
| 460 |
|
| 461 |
-
|
| 462 |
-
|
| 463 |
-
|
| 464 |
-
const
|
| 465 |
-
|
| 466 |
-
const
|
| 467 |
-
|
| 468 |
-
|
| 469 |
-
if (res) { idToResult.set(id, res); process.stdout.write(res.size ? `✓ ${res.size}B\n` : `✓\n`); }
|
| 470 |
-
else process.stdout.write(`✗\n`);
|
| 471 |
-
await new Promise(r => setTimeout(r, 50));
|
| 472 |
-
}
|
| 473 |
-
for (const model of ollamaLookupQueue) {
|
| 474 |
-
const res = idToResult.get(model.ollama_id);
|
| 475 |
-
if (res && res.size && !model.size_b) { model.size_b = res.size; model.size_source = 'ollama'; ollamaFetched++; }
|
| 476 |
}
|
| 477 |
-
}
|
| 478 |
-
console.log(`\nEnriched: ${hfSizeFetched + ollamaFetched} technical sizes.`);
|
| 479 |
-
}
|
| 480 |
|
| 481 |
-
|
| 482 |
-
|
| 483 |
-
|
| 484 |
-
const
|
| 485 |
-
|
| 486 |
-
|
| 487 |
-
|
| 488 |
-
|
| 489 |
-
|
| 490 |
-
|
|
|
|
|
|
|
|
|
|
| 491 |
}
|
| 492 |
|
| 493 |
async function main() {
|
| 494 |
const data = loadData();
|
| 495 |
-
const
|
| 496 |
-
|
| 497 |
-
|
| 498 |
-
|
|
|
|
|
|
|
|
|
|
| 499 |
await propagateExtraData(data);
|
| 500 |
saveData(data);
|
| 501 |
-
console.log('\nSummary:');
|
| 502 |
-
data.providers.forEach(p => console.log(` ${p.models ? '✓' : '✗'} ${p.name}: ${p.models ? p.models.length : 0} models`));
|
| 503 |
}
|
| 504 |
|
| 505 |
main().catch(err => { console.error('Fatal:', err); process.exit(1); });
|
|
|
|
| 2 |
|
| 3 |
/**
|
| 4 |
* Fetch live pricing data from all supported providers and update data/providers.json.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
*/
|
| 6 |
|
| 7 |
const fs = require('fs');
|
|
|
|
| 40 |
|
| 41 |
function updateProviderModels(providers, providerName, models) {
|
| 42 |
const provider = providers.find((p) => p.name === providerName);
|
| 43 |
+
if (!provider) return false;
|
|
|
|
|
|
|
|
|
|
| 44 |
|
|
|
|
| 45 |
const existingMap = new Map((provider.models || []).map(m => [m.name, m]));
|
| 46 |
|
| 47 |
provider.models = models.map(newModel => {
|
|
|
|
| 49 |
if (!existing) return newModel;
|
| 50 |
|
| 51 |
return {
|
| 52 |
+
...existing,
|
| 53 |
+
...newModel,
|
| 54 |
size_b: newModel.size_b || existing.size_b,
|
| 55 |
size_source: newModel.size_source || existing.size_source,
|
| 56 |
hf_id: newModel.hf_id || existing.hf_id,
|
|
|
|
| 68 |
const normName = (s) =>
|
| 69 |
s.toLowerCase().replace(/[-_.:]/g, ' ').replace(/[^a-z0-9 ]/g, '').replace(/\s+/g, ' ').trim();
|
| 70 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 71 |
// Estimate parameters from config.json (vLLM style fallback)
|
| 72 |
+
function estimateParams(config, hfId) {
|
| 73 |
if (!config) return null;
|
| 74 |
const h = config.hidden_size || config.d_model || config.n_embd;
|
| 75 |
const l = config.num_hidden_layers || config.n_layer;
|
| 76 |
const v = config.vocab_size;
|
| 77 |
const i = config.intermediate_size || config.d_ff;
|
|
|
|
|
|
|
| 78 |
const numExperts = config.num_local_experts || config.n_experts || config.num_experts || 1;
|
| 79 |
const modelType = (config.model_type || '').toLowerCase();
|
| 80 |
|
| 81 |
if (h && l && v) {
|
| 82 |
const intermediate = i || (4 * h);
|
|
|
|
|
|
|
| 83 |
const vocabParams = v * h;
|
| 84 |
const posParams = (config.max_position_embeddings || 512) * h;
|
| 85 |
const typeParams = (config.type_vocab_size || 0) * h;
|
| 86 |
const embedParams = vocabParams + posParams + typeParams;
|
|
|
|
|
|
|
| 87 |
const attentionParams = 4 * (h * h);
|
| 88 |
|
| 89 |
+
// Check if architecture uses GLU (3 weights per MLP layer)
|
| 90 |
+
const hasGlu = ['llama', 'mistral', 'phi3', 'qwen2', 'gemma', 'gemma2', 'minimax'].includes(modelType)
|
| 91 |
+
|| hfId.toLowerCase().includes('qwen')
|
| 92 |
+
|| hfId.toLowerCase().includes('minimax');
|
| 93 |
|
| 94 |
+
const mlpParams = (hasGlu ? 3 : 2) * h * intermediate * numExperts;
|
| 95 |
+
const total = embedParams + l * (attentionParams + mlpParams);
|
| 96 |
+
return total;
|
| 97 |
}
|
| 98 |
return null;
|
| 99 |
}
|
|
|
|
| 104 |
const token = process.env.HF_TOKEN;
|
| 105 |
const headers = token ? { Authorization: `Bearer ${token}` } : {};
|
| 106 |
let isPrivate = false;
|
| 107 |
+
|
| 108 |
try {
|
| 109 |
+
let params = null, source = 'hf-total', data = {};
|
|
|
|
|
|
|
| 110 |
|
| 111 |
+
// 1. API Metadata
|
| 112 |
try {
|
| 113 |
data = await getJson(`https://huggingface.co/api/models/${hfId}`, { headers, retries: 1 });
|
| 114 |
params = data.safetensors?.total || data.config?.total_parameters || data.config?.model_type_params;
|
| 115 |
|
|
|
|
| 116 |
if (!params && data.cardData?.model_details?.parameters) {
|
| 117 |
const match = data.cardData.model_details.parameters.match(/([\d.]+)\s*[Bb]/);
|
| 118 |
if (match) { params = parseFloat(match[1]) * 1_000_000_000; source = 'hf-card'; }
|
|
|
|
| 121 |
if (e.message.includes('401') || e.message.includes('404')) isPrivate = true;
|
| 122 |
}
|
| 123 |
|
| 124 |
+
// 2. Raw config.json fetch
|
| 125 |
if (!params && !isPrivate) {
|
| 126 |
+
try {
|
| 127 |
+
const config = await getJson(`https://huggingface.co/${hfId}/raw/main/config.json`, { headers, retries: 1 });
|
| 128 |
+
params = config.total_parameters || estimateParams(config, hfId);
|
| 129 |
+
source = config.total_parameters ? 'hf-total' : 'hf-config-estimate';
|
| 130 |
} catch (e) {
|
| 131 |
+
if (e.message.includes('401') || e.message.includes('404')) isPrivate = true;
|
| 132 |
}
|
| 133 |
}
|
| 134 |
|
|
|
|
| 136 |
if (!params) return { error: 'No parameter data found' };
|
| 137 |
|
| 138 |
const b = params / 1_000_000_000;
|
|
|
|
| 139 |
const size = b < 1 ? Math.round(b * 100) / 100 : Math.round(b * 10) / 10;
|
| 140 |
return { size, source };
|
| 141 |
+
} catch (e) { return { error: e.message }; }
|
|
|
|
|
|
|
| 142 |
}
|
| 143 |
|
| 144 |
async function fetchOllamaMetadata(ollamaId) {
|
|
|
|
| 160 |
|
| 161 |
const EMBEDDER_KEYWORDS = ['embed', 'bge', 'gte', 'e5', 'stella', 'minilm', 'multilingual-mpnet'];
|
| 162 |
|
|
|
|
| 163 |
const MANUAL_HF_ID_MAP = {
|
| 164 |
+
'minimax/minimax-m1': 'MiniMaxAI/MiniMax-M1-80k',
|
| 165 |
+
'minimax minimax m1': 'MiniMaxAI/MiniMax-M1-80k',
|
| 166 |
+
'minimax m1': 'MiniMaxAI/MiniMax-M1-80k',
|
| 167 |
+
'qwen plus': 'Qwen/Qwen3-Coder-30B-A3B-Instruct',
|
| 168 |
+
'alibaba qwen plus': 'Qwen/Qwen3-Coder-30B-A3B-Instruct',
|
| 169 |
+
'qwen qwen plus': 'Qwen/Qwen3-Coder-30B-A3B-Instruct',
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 170 |
'phi 4': 'microsoft/phi-4',
|
| 171 |
+
'mistral small 4': 'mistralai/Mistral-Small-4-119B-2603',
|
| 172 |
+
'mistral small 3 2': 'mistralai/Mistral-Small-3.2-24B-Instruct-2506',
|
| 173 |
+
'mistral small 3 1': 'mistralai/Mistral-Small-3.1-24B-Instruct-2503',
|
| 174 |
+
'mistral small 2501': 'mistralai/Mistral-Small-24B-Instruct-2501',
|
| 175 |
+
'mistral small 2409': 'mistralai/Mistral-Small-Instruct-2409',
|
| 176 |
+
'mistral small 24b': 'mistralai/Mistral-Small-24B-Instruct-2501',
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 177 |
};
|
| 178 |
|
| 179 |
const MANUAL_OLLAMA_ID_MAP = {
|
| 180 |
'phi 4': 'phi4',
|
| 181 |
'deepseek chat': 'deepseek-v3',
|
| 182 |
'deepseek reasoner': 'deepseek-r1',
|
|
|
|
| 183 |
'mistral small 24b': 'mistral-small',
|
| 184 |
+
};
|
| 185 |
+
|
| 186 |
+
const MANUAL_SIZE_MAP = {
|
| 187 |
+
'BAAI/bge-m3': 0.57,
|
| 188 |
+
'black-forest-labs/FLUX.1-schnell': 12,
|
| 189 |
+
'black-forest-labs/FLUX.1-dev': 12,
|
| 190 |
+
'black-forest-labs/FLUX.1-pro': 12,
|
| 191 |
+
'black-forest-labs/FLUX.2-dev': 32,
|
| 192 |
+
'black-forest-labs/FLUX.2-pro': 32,
|
| 193 |
+
'black-forest-labs/FLUX.2-flex': 32,
|
| 194 |
+
'black-forest-labs/FLUX.2-max': 32,
|
| 195 |
+
'black-forest-labs/FLUX.2-klein-4B': 4,
|
| 196 |
+
'black-forest-labs/FLUX.2-klein-9B': 9,
|
|
|
|
|
|
|
| 197 |
};
|
| 198 |
|
| 199 |
const PROPRIETARY_KEYWORDS = [
|
|
|
|
| 202 |
];
|
| 203 |
|
| 204 |
async function propagateExtraData(data) {
|
|
|
|
|
|
|
| 205 |
let benchmarks = [];
|
| 206 |
try { benchmarks = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'data', 'benchmarks.json'), 'utf8')); } catch (e) {}
|
| 207 |
const hfIdToSize = new Map();
|
| 208 |
+
benchmarks.forEach((b) => { if (b.params_b && b.hf_id) hfIdToSize.set(b.hf_id.toLowerCase(), b.params_b); });
|
|
|
|
|
|
|
| 209 |
|
|
|
|
| 210 |
// 1. Initial manual and fuzzy mapping
|
| 211 |
data.providers.forEach(p => p.models.forEach(model => {
|
| 212 |
const n = normName(model.name);
|
| 213 |
+
for (const [key, val] of Object.entries(MANUAL_HF_ID_MAP)) {
|
| 214 |
+
if (n === key || n.endsWith(' ' + key) || n.endsWith('/' + key)) {
|
| 215 |
+
model.hf_id = val; model.hf_private = false; break;
|
|
|
|
| 216 |
}
|
| 217 |
}
|
| 218 |
+
if (PROPRIETARY_KEYWORDS.some(k => n.includes(k)) && !model.hf_id) model.hf_private = true;
|
| 219 |
+
for (const [key, val] of Object.entries(MANUAL_OLLAMA_ID_MAP)) {
|
| 220 |
+
if (n === key || n.endsWith(' ' + key) || n.endsWith('/' + key)) model.ollama_id = val;
|
|
|
|
| 221 |
}
|
| 222 |
+
if (model.hf_id && MANUAL_SIZE_MAP[model.hf_id]) {
|
| 223 |
+
model.size_b = MANUAL_SIZE_MAP[model.hf_id]; model.size_source = 'manual';
|
| 224 |
+
} else if (model.hf_id && !model.size_b) {
|
| 225 |
const size = hfIdToSize.get(model.hf_id.toLowerCase());
|
| 226 |
if (size) { model.size_b = size; model.size_source = 'benchmark'; }
|
| 227 |
}
|
| 228 |
}));
|
| 229 |
|
| 230 |
+
// 2. Technical Metadata Lookups
|
| 231 |
+
const hfLookupQueue = [];
|
| 232 |
data.providers.forEach(p => p.models.forEach(m => {
|
| 233 |
+
if (!m.size_b && m.hf_id && !m.hf_private) hfLookupQueue.push(m);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 234 |
}));
|
| 235 |
|
| 236 |
+
const uniqueIds = [...new Set(hfLookupQueue.map(m => m.hf_id))];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 237 |
if (uniqueIds.length > 0) {
|
| 238 |
console.log(`\n HF Hub: technical metadata inspection for ${uniqueIds.length} models...`);
|
| 239 |
const idToResult = new Map();
|
| 240 |
+
for (const id of uniqueIds) {
|
| 241 |
+
process.stdout.write(` ${id.padEnd(50)} `);
|
|
|
|
| 242 |
const result = await fetchHFSize(id);
|
| 243 |
idToResult.set(id, result);
|
| 244 |
if (result.size) process.stdout.write(`✓ ${result.size}B (${result.source})\n`);
|
| 245 |
+
else process.stdout.write(`✗ ${result.error || 'Err'}\n`);
|
| 246 |
await new Promise(r => setTimeout(r, 50));
|
| 247 |
}
|
| 248 |
for (const model of hfLookupQueue) {
|
| 249 |
if (!model.size_b) {
|
| 250 |
+
const id = model.hf_id;
|
| 251 |
const result = idToResult.get(id);
|
| 252 |
+
if (result && result.size) {
|
| 253 |
+
model.size_b = result.size;
|
| 254 |
+
model.size_source = result.source;
|
| 255 |
+
model.hf_private = false;
|
| 256 |
}
|
| 257 |
}
|
| 258 |
}
|
| 259 |
}
|
| 260 |
|
| 261 |
+
// 3. GLOBAL ENRICHMENT SWEEP
|
| 262 |
+
const technicalPool = new Map();
|
| 263 |
+
data.providers.forEach(p => p.models.forEach(m => {
|
| 264 |
+
const baseName = m.name.split('/').pop().replace(/:free$/, '').toLowerCase();
|
| 265 |
+
if (m.size_b || m.hf_id) {
|
| 266 |
+
const meta = { size_b: m.size_b, size_source: m.size_source, hf_id: m.hf_id, ollama_id: m.ollama_id, hf_private: m.hf_private };
|
| 267 |
+
if (m.hf_id) technicalPool.set('id:' + m.hf_id.toLowerCase(), meta);
|
| 268 |
+
technicalPool.set('name:' + baseName, meta);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 269 |
}
|
| 270 |
+
}));
|
|
|
|
|
|
|
| 271 |
|
| 272 |
+
data.providers.forEach(p => p.models.forEach(m => {
|
| 273 |
+
const baseName = m.name.split('/').pop().replace(/:free$/, '').toLowerCase();
|
| 274 |
+
const metaByName = technicalPool.get('name:' + baseName);
|
| 275 |
+
const metaById = m.hf_id ? technicalPool.get('id:' + m.hf_id.toLowerCase()) : null;
|
| 276 |
+
const best = metaById || metaByName;
|
| 277 |
+
if (best) {
|
| 278 |
+
m.size_b = m.size_b || best.size_b;
|
| 279 |
+
m.size_source = m.size_source || best.size_source;
|
| 280 |
+
m.hf_id = m.hf_id || best.hf_id;
|
| 281 |
+
m.ollama_id = m.ollama_id || best.ollama_id;
|
| 282 |
+
if (m.size_b || m.hf_id) m.hf_private = false;
|
| 283 |
+
}
|
| 284 |
+
}));
|
| 285 |
}
|
| 286 |
|
| 287 |
async function main() {
|
| 288 |
const data = loadData();
|
| 289 |
+
for (const f of FETCHERS) {
|
| 290 |
+
try {
|
| 291 |
+
process.stdout.write(`Fetching ${f.providerName}... `);
|
| 292 |
+
const models = await f.fn();
|
| 293 |
+
if (updateProviderModels(data.providers, f.providerName, models)) console.log(`✓ ${models.length} models`);
|
| 294 |
+
} catch (err) { console.log(`✗ ${err.message}`); }
|
| 295 |
+
}
|
| 296 |
await propagateExtraData(data);
|
| 297 |
saveData(data);
|
|
|
|
|
|
|
| 298 |
}
|
| 299 |
|
| 300 |
main().catch(err => { console.error('Fatal:', err); process.exit(1); });
|