Spaces:
Running
Running
CrispStrobe
fix: implement robust fetching with retries and exponential backoff for benchmarks and providers
d135f12 | ; | |
| const { loadEnv } = require('../load-env'); | |
| loadEnv(); | |
| const { getJson } = require('../fetch-utils'); | |
| const API_URL = 'https://router.requesty.ai/v1/models'; | |
| function loadApiKey() { | |
| return process.env.REQUESTY_API_KEY || null; | |
| } | |
| const toPerMillion = (val) => (val ? Math.round(parseFloat(val) * 1_000_000 * 10000) / 10000 : 0); | |
| const getSizeB = (id) => { | |
| const match = (id || '').match(/[^.\d](\d+)b/i) || (id || '').match(/^(\d+)b/i); | |
| return match ? parseInt(match[1]) : undefined; | |
| }; | |
| async function fetchRequesty() { | |
| const apiKey = loadApiKey(); | |
| if (!apiKey) { | |
| console.warn(' (no REQUESTY_API_KEY found – skipping Requesty)'); | |
| return []; | |
| } | |
| const data = await getJson(API_URL, { | |
| headers: { | |
| Authorization: `Bearer ${apiKey}`, | |
| Accept: 'application/json', | |
| }, | |
| }); | |
| const models = []; | |
| for (const model of data.data || []) { | |
| const inputPrice = toPerMillion(model.input_price); | |
| const outputPrice = toPerMillion(model.output_price); | |
| // Skip free/zero-priced entries | |
| if (inputPrice <= 0 && outputPrice <= 0) continue; | |
| const caps = []; | |
| if (model.supports_vision) caps.push('vision'); | |
| if (model.supports_reasoning) caps.push('reasoning'); | |
| if (model.supports_tool_calls) caps.push('tools'); | |
| const baseType = model.api === 'chat' ? 'chat' : model.api; | |
| const type = (baseType === 'chat' && model.supports_vision) ? 'vision' : baseType; | |
| const modelEntry = { | |
| name: model.id, | |
| type, | |
| input_price_per_1m: inputPrice, | |
| output_price_per_1m: outputPrice, | |
| currency: 'USD', | |
| }; | |
| if (caps.length) modelEntry.capabilities = caps; | |
| if (model.context_window) modelEntry.context_window = model.context_window; | |
| const size_b = getSizeB(model.id); | |
| if (size_b) modelEntry.size_b = size_b; | |
| models.push(modelEntry); | |
| } | |
| // Deduplicate @region and :effort variants — keep one entry per canonical base ID. | |
| // e.g. "anthropic/claude-3-7-sonnet@us-east-2" and "anthropic/claude-3-7-sonnet:high" | |
| // both collapse to "anthropic/claude-3-7-sonnet". | |
| const canonicalId = (id) => id.replace(/@[^/]+$/, '').replace(/:[^/]+$/, ''); | |
| const seen = new Map(); | |
| for (const model of models) { | |
| const base = canonicalId(model.name); | |
| if (!seen.has(base)) { | |
| // Store with canonical name | |
| seen.set(base, { ...model, name: base }); | |
| } else { | |
| // Prefer lower input price if already present | |
| const existing = seen.get(base); | |
| if (model.input_price_per_1m < existing.input_price_per_1m) { | |
| seen.set(base, { ...model, name: base }); | |
| } | |
| } | |
| } | |
| const deduped = [...seen.values()]; | |
| // Sort by input price | |
| deduped.sort((a, b) => a.input_price_per_1m - b.input_price_per_1m); | |
| return deduped; | |
| } | |
| module.exports = { fetchRequesty, providerName: 'Requesty' }; | |
| // Run standalone: node scripts/providers/requesty.js | |
| if (require.main === module) { | |
| fetchRequesty() | |
| .then((models) => { | |
| console.log(`Fetched ${models.length} models from Requesty API\n`); | |
| models.slice(0, 10).forEach((m) => | |
| console.log(` ${m.name.padEnd(55)} $${m.input_price_per_1m} / $${m.output_price_per_1m}`) | |
| ); | |
| if (models.length > 10) console.log(` ... and ${models.length - 10} more`); | |
| }) | |
| .catch((err) => { | |
| console.error('Error:', err.message); | |
| process.exit(1); | |
| }); | |
| } | |