CrispStrobe commited on
Commit
03b99dd
·
1 Parent(s): 88466b5

feat: implement private model caching for HF Hub and add manual mappings for Qwen/IONOS models

Browse files
Files changed (2) hide show
  1. data/providers.json +0 -0
  2. scripts/fetch-providers.js +55 -22
data/providers.json CHANGED
The diff for this file is too large to render. See raw diff
 
scripts/fetch-providers.js CHANGED
@@ -53,7 +53,7 @@ function updateProviderModels(providers, providerName, models) {
53
  return false;
54
  }
55
 
56
- // Smart merge: preserve existing metadata (size_b, hf_id, capabilities) if missing in new data
57
  const existingMap = new Map((provider.models || []).map(m => [m.name, m]));
58
 
59
  provider.models = models.map(newModel => {
@@ -66,6 +66,7 @@ function updateProviderModels(providers, providerName, models) {
66
  // But preserve these if newModel doesn't have them
67
  size_b: newModel.size_b || existing.size_b,
68
  hf_id: newModel.hf_id || existing.hf_id,
 
69
  capabilities: (newModel.capabilities && newModel.capabilities.length > 0)
70
  ? newModel.capabilities
71
  : existing.capabilities,
@@ -112,7 +113,7 @@ function findOrMatch(modelName, orIndex) {
112
  for (const entry of orIndex) {
113
  if (entry.norm === n) return entry;
114
  }
115
- // 2. Provider model name starts with OR model part (e.g. "claude-3-5-sonnet-20241022" starts with "claude-3-5-sonnet")
116
  let best = null;
117
  let bestLen = 0;
118
  for (const entry of orIndex) {
@@ -122,12 +123,11 @@ function findOrMatch(modelName, orIndex) {
122
  }
123
  }
124
  if (best) return best;
125
- // 3. OR model part starts with provider name (e.g. "claude-haiku-4-5" → "claude-haiku-4-5-20251001")
126
  for (const entry of orIndex) {
127
  if (entry.norm.startsWith(n + ' ')) return entry;
128
  }
129
  // 4. OR model norm contains provider name as a contiguous word sequence.
130
- // Handles short display names like "Sonnet 4.6" matching inside "claude sonnet 4 6".
131
  if (n.length >= 5) {
132
  let bestC = null, bestCLen = Infinity;
133
  for (const entry of orIndex) {
@@ -139,8 +139,7 @@ function findOrMatch(modelName, orIndex) {
139
  }
140
  if (bestC) return bestC;
141
  }
142
- // 5. All tokens of provider name appear in OR norm (handles word-order differences).
143
- // e.g. "Sonnet 3.7" → tokens ["sonnet","3","7"] match inside "claude 3 7 sonnet 20250219".
144
  const tokens = n.split(' ');
145
  if (tokens.length >= 2 && n.length >= 7) {
146
  let bestT = null, bestTLen = Infinity;
@@ -157,23 +156,30 @@ function findOrMatch(modelName, orIndex) {
157
 
158
  // Fetch total_parameters from Hugging Face Hub API (Metadata)
159
  async function fetchHFSize(hfId) {
160
- if (!hfId || hfId.includes(' ') || !hfId.includes('/')) return null;
161
  const token = process.env.HF_TOKEN;
162
  const headers = token ? { Authorization: `Bearer ${token}` } : {};
163
  try {
 
164
  const data = await getJson(`https://huggingface.co/api/models/${hfId}`, { headers, retries: 1 });
 
165
  // Check various common metadata locations for total parameters
166
  let params = data.safetensors?.total || data.config?.total_parameters || data.config?.model_type_params;
167
  if (!params && data.cardData?.model_details?.parameters) {
168
  const match = data.cardData.model_details.parameters.match(/([\d.]+)\s*[Bb]/);
169
  if (match) params = parseFloat(match[1]) * 1_000_000_000;
170
  }
171
- if (!params) return null;
 
 
172
  const b = params / 1_000_000_000;
173
  // Keep 2 decimals for small models (<1B), 1 decimal for others
174
- return b < 1 ? Math.round(b * 100) / 100 : Math.round(b * 10) / 10;
 
175
  } catch (e) {
176
- return null; // Silently skip failures for individual models
 
 
177
  }
178
  }
179
 
@@ -186,6 +192,19 @@ const MANUAL_HF_ID_MAP = {
186
  'whisper large v3': 'openai/whisper-large-v3',
187
  'step 3 5 flash': 'stepfun-ai/Step-3.5-Flash',
188
  'bge m3': 'BAAI/bge-m3',
 
 
 
 
 
 
 
 
 
 
 
 
 
189
  };
190
 
191
  const MANUAL_SIZE_MAP = {
@@ -282,6 +301,7 @@ async function propagateExtraData(data) {
282
  }
283
  // Crucial: inherit hf_id to enable Hub API fallback below
284
  if (!model.hf_id && match.hf_id) model.hf_id = match.hf_id;
 
285
  }
286
  }
287
 
@@ -293,7 +313,8 @@ async function propagateExtraData(data) {
293
  }
294
 
295
  // 6. QUEUE: Still missing size? Try Hub API metadata lookup
296
- if (!model.size_b && (model.name.includes('/') || model.hf_id)) {
 
297
  hfLookupQueue.push(model);
298
  }
299
  }
@@ -303,18 +324,26 @@ async function propagateExtraData(data) {
303
  const uniqueIds = [...new Set(hfLookupQueue.map(m => m.hf_id || m.name).filter(id => id.includes('/')))].slice(0, 200);
304
  if (uniqueIds.length > 0) {
305
  console.log(`\n HF Hub: technical metadata inspection for ${uniqueIds.length} models...`);
306
- const idToSize = new Map();
307
 
308
  // Process sequentially with small delay to avoid 429 rate limits
309
  for (let i = 0; i < uniqueIds.length; i++) {
310
  const id = uniqueIds[i];
311
  process.stdout.write(` [${i + 1}/${uniqueIds.length}] ${id.padEnd(50)} `);
312
- const size = await fetchHFSize(id);
313
- if (size) {
314
- idToSize.set(id, size);
315
- process.stdout.write(`✓ ${size}B\n`);
 
316
  } else {
317
- process.stdout.write(`✗\n`);
 
 
 
 
 
 
 
318
  }
319
  await new Promise(r => setTimeout(r, 50)); // Tiny delay
320
  }
@@ -322,17 +351,21 @@ async function propagateExtraData(data) {
322
  for (const model of hfLookupQueue) {
323
  if (!model.size_b) {
324
  const id = model.hf_id || model.name;
325
- const size = idToSize.get(id);
326
- if (size) {
327
- model.size_b = size;
328
- hfSizeFetched++;
 
 
 
 
 
329
  }
330
  }
331
  }
332
  console.log(` ✓ Total ${hfSizeFetched} new sizes from HF metadata`);
333
  }
334
 
335
-
336
  if (autoTagged > 0) console.log(`Auto-tagged ${autoTagged} image-gen/embedding models.`);
337
  if (propagatedCaps > 0) console.log(`Propagated capabilities to ${propagatedCaps} models.`);
338
  if (propagatedSize + hfSizeFetched > 0) console.log(`Enriched size data for ${propagatedSize + hfSizeFetched} models.`);
 
53
  return false;
54
  }
55
 
56
+ // Smart merge: preserve existing metadata (size_b, hf_id, capabilities, hf_private) if missing in new data
57
  const existingMap = new Map((provider.models || []).map(m => [m.name, m]));
58
 
59
  provider.models = models.map(newModel => {
 
66
  // But preserve these if newModel doesn't have them
67
  size_b: newModel.size_b || existing.size_b,
68
  hf_id: newModel.hf_id || existing.hf_id,
69
+ hf_private: newModel.hf_private ?? existing.hf_private,
70
  capabilities: (newModel.capabilities && newModel.capabilities.length > 0)
71
  ? newModel.capabilities
72
  : existing.capabilities,
 
113
  for (const entry of orIndex) {
114
  if (entry.norm === n) return entry;
115
  }
116
+ // 2. Provider model name starts with OR model part
117
  let best = null;
118
  let bestLen = 0;
119
  for (const entry of orIndex) {
 
123
  }
124
  }
125
  if (best) return best;
126
+ // 3. OR model part starts with provider name
127
  for (const entry of orIndex) {
128
  if (entry.norm.startsWith(n + ' ')) return entry;
129
  }
130
  // 4. OR model norm contains provider name as a contiguous word sequence.
 
131
  if (n.length >= 5) {
132
  let bestC = null, bestCLen = Infinity;
133
  for (const entry of orIndex) {
 
139
  }
140
  if (bestC) return bestC;
141
  }
142
+ // 5. All tokens of provider name appear in OR norm.
 
143
  const tokens = n.split(' ');
144
  if (tokens.length >= 2 && n.length >= 7) {
145
  let bestT = null, bestTLen = Infinity;
 
156
 
157
  // Fetch total_parameters from Hugging Face Hub API (Metadata)
158
  async function fetchHFSize(hfId) {
159
+ if (!hfId || hfId.includes(' ') || !hfId.includes('/')) return { error: 'Invalid ID' };
160
  const token = process.env.HF_TOKEN;
161
  const headers = token ? { Authorization: `Bearer ${token}` } : {};
162
  try {
163
+ // Limit to 1 retry for technical metadata lookups
164
  const data = await getJson(`https://huggingface.co/api/models/${hfId}`, { headers, retries: 1 });
165
+
166
  // Check various common metadata locations for total parameters
167
  let params = data.safetensors?.total || data.config?.total_parameters || data.config?.model_type_params;
168
  if (!params && data.cardData?.model_details?.parameters) {
169
  const match = data.cardData.model_details.parameters.match(/([\d.]+)\s*[Bb]/);
170
  if (match) params = parseFloat(match[1]) * 1_000_000_000;
171
  }
172
+
173
+ if (!params) return { error: 'No parameter data in Hub metadata' };
174
+
175
  const b = params / 1_000_000_000;
176
  // Keep 2 decimals for small models (<1B), 1 decimal for others
177
+ const size = b < 1 ? Math.round(b * 100) / 100 : Math.round(b * 10) / 10;
178
+ return { size };
179
  } catch (e) {
180
+ // Flag as private if we get 401 (unauthorized) or 404 (not found - often private/aliased)
181
+ const isPrivate = e.message.includes('401') || e.message.includes('404');
182
+ return { error: e.message, private: isPrivate };
183
  }
184
  }
185
 
 
192
  'whisper large v3': 'openai/whisper-large-v3',
193
  'step 3 5 flash': 'stepfun-ai/Step-3.5-Flash',
194
  'bge m3': 'BAAI/bge-m3',
195
+ 'lightonocr 2': 'lightonai/LightOnOCR-2-1B',
196
+ 'flux 1 schnell': 'black-forest-labs/FLUX.1-schnell',
197
+ 'paraphrase multilingual mpnet base v2': 'sentence-transformers/paraphrase-multilingual-mpnet-base-v2',
198
+ 'bge large en v1 5': 'BAAI/bge-large-en-v1.5',
199
+ 'bge multilingual gemma2': 'BAAI/bge-multilingual-gemma2',
200
+ 'photomaker v2': 'TencentARC/PhotoMaker-V2',
201
+ 'flux schnell': 'black-forest-labs/FLUX.1-schnell',
202
+ // Qwen mappings
203
+ 'qwen3 coder flash': 'Qwen/Qwen2.5-Coder-7B-Instruct', // Counterpart mapping
204
+ 'qwen3 coder plus': 'Qwen/Qwen2.5-Coder-32B-Instruct',
205
+ 'qwen 3 5 flash': 'Qwen/Qwen2.5-7B-Instruct',
206
+ 'qwen vl plus': 'Qwen/Qwen2-VL-7B-Instruct',
207
+ 'qwen vl max': 'Qwen/Qwen2-VL-72B-Instruct',
208
  };
209
 
210
  const MANUAL_SIZE_MAP = {
 
301
  }
302
  // Crucial: inherit hf_id to enable Hub API fallback below
303
  if (!model.hf_id && match.hf_id) model.hf_id = match.hf_id;
304
+ if (model.hf_private === undefined && match.hf_private !== undefined) model.hf_private = match.hf_private;
305
  }
306
  }
307
 
 
313
  }
314
 
315
  // 6. QUEUE: Still missing size? Try Hub API metadata lookup
316
+ // Skip models that we've previously marked as private/unauthorized
317
+ if (!model.size_b && !model.hf_private && (model.name.includes('/') || model.hf_id)) {
318
  hfLookupQueue.push(model);
319
  }
320
  }
 
324
  const uniqueIds = [...new Set(hfLookupQueue.map(m => m.hf_id || m.name).filter(id => id.includes('/')))].slice(0, 200);
325
  if (uniqueIds.length > 0) {
326
  console.log(`\n HF Hub: technical metadata inspection for ${uniqueIds.length} models...`);
327
+ const idToResult = new Map();
328
 
329
  // Process sequentially with small delay to avoid 429 rate limits
330
  for (let i = 0; i < uniqueIds.length; i++) {
331
  const id = uniqueIds[i];
332
  process.stdout.write(` [${i + 1}/${uniqueIds.length}] ${id.padEnd(50)} `);
333
+ const result = await fetchHFSize(id);
334
+
335
+ if (result.size) {
336
+ idToResult.set(id, result);
337
+ process.stdout.write(`✓ ${result.size}B\n`);
338
  } else {
339
+ idToResult.set(id, result);
340
+ process.stdout.write(`✗ ${result.error || 'Unknown Error'}\n`);
341
+
342
+ // CIRCUIT BREAKER: Stop if we hit a rate limit (429)
343
+ if (result.error && result.error.includes('429')) {
344
+ console.warn('\n ⚠ HIT RATE LIMIT (429) - Stopping further HF lookups for this run.');
345
+ break;
346
+ }
347
  }
348
  await new Promise(r => setTimeout(r, 50)); // Tiny delay
349
  }
 
351
  for (const model of hfLookupQueue) {
352
  if (!model.size_b) {
353
  const id = model.hf_id || model.name;
354
+ const result = idToResult.get(id);
355
+ if (result) {
356
+ if (result.size) {
357
+ model.size_b = result.size;
358
+ hfSizeFetched++;
359
+ }
360
+ if (result.private) {
361
+ model.hf_private = true;
362
+ }
363
  }
364
  }
365
  }
366
  console.log(` ✓ Total ${hfSizeFetched} new sizes from HF metadata`);
367
  }
368
 
 
369
  if (autoTagged > 0) console.log(`Auto-tagged ${autoTagged} image-gen/embedding models.`);
370
  if (propagatedCaps > 0) console.log(`Propagated capabilities to ${propagatedCaps} models.`);
371
  if (propagatedSize + hfSizeFetched > 0) console.log(`Enriched size data for ${propagatedSize + hfSizeFetched} models.`);