CrispStrobe
fix(providers): make Nebius & Requesty keyless (no API key needed)
43d89fe
Raw
History Blame Contribute Delete
5.38 kB
'use strict';
/**
* Nebius Token Factory (formerly Nebius AI Studio) pricing fetcher.
*
* The public "Model endpoints" page (tokenfactory.nebius.com/endpoints) lists
* every shared model with pricing to anonymous users. It loads that data from
* a CSRF-protected proxy endpoint — no API key required:
*
* GET https://tokenfactory.nebius.com/proxy/inference/private/v1/models_info
*
* To call it we first load the app once to obtain an anonymous CSRF cookie
* (__Host-psifi.x-csrf-token), then replay it as both the Cookie and the
* x-csrf-token header. The response is richer than the authenticated
* /v1/models API: each model carries huggingface_url, size_b, vendor, tags and
* `flavors` with per-million-token prices.
*
* { models: [ { type, name, huggingface_url, size_b, flavors: [
* { model_id, model_type, label, input_price_per_million_tokens,
* output_price_per_million_tokens, use_cases, tags, context_window_k } ] } ] }
*/
const { fetchRobust } = require('../fetch-utils');
const BOOTSTRAP_URL = 'https://tokenfactory.nebius.com/endpoints';
const MODELS_URL = 'https://tokenfactory.nebius.com/proxy/inference/private/v1/models_info';
const UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36';
// Load the app once to mint an anonymous CSRF token (returned as a cookie).
async function getCsrf() {
const res = await fetchRobust(BOOTSTRAP_URL, { headers: { 'User-Agent': UA } });
const setCookie = res.headers.get('set-cookie') || '';
const m = setCookie.match(/__Host-psifi\.x-csrf-token=([^;,]+)/);
if (!m) throw new Error('could not obtain CSRF cookie from Token Factory');
const cookieVal = m[1];
const headerVal = decodeURIComponent(cookieVal).split('|')[0];
return { cookieVal, headerVal };
}
const getType = (modelType) => {
const t = (modelType || '').toLowerCase();
if (t.includes('embedding')) return 'embedding';
if (t === 'image2text' || t.includes('vision')) return 'vision';
if (t === 'text2image' || t.includes('image_generation')) return 'image';
return 'chat';
};
const hfIdFrom = (model, flavor) => {
const url = model.huggingface_url || '';
if (url.includes('huggingface.co/')) return url.split('huggingface.co/')[1].replace(/\/+$/, '');
const id = flavor.model_id || '';
return /^[^/\s]+\/[^/\s]+$/.test(id) ? id : undefined;
};
function buildEntry(model, flavor, labelSuffix) {
const type = getType(flavor.model_type || model.type);
const input = Number(flavor.input_price_per_million_tokens);
const output = Number(flavor.output_price_per_million_tokens);
const caps = [];
const uses = (flavor.use_cases || []).map((u) => u.toLowerCase());
const tags = (flavor.tags || model.tags || []).map((t) => t.toLowerCase());
if (type === 'vision') caps.push('vision');
if (uses.includes('function_calling') || tags.includes('tool use')) caps.push('tools');
if (uses.includes('reasoning') || tags.includes('reasoning')) caps.push('reasoning');
const entry = {
name: model.name + (labelSuffix ? ` (${labelSuffix})` : ''),
type,
input_price_per_1m: isFinite(input) ? input : 0,
output_price_per_1m: isFinite(output) ? output : 0,
currency: 'USD',
};
if (caps.length) entry.capabilities = caps;
const hf_id = hfIdFrom(model, flavor);
if (hf_id) entry.hf_id = hf_id;
const size_b = Number(model.size_b) || undefined;
if (size_b) entry.size_b = size_b;
const ctx = Number(flavor.context_window_k || model.context_window_k);
if (ctx) entry.context_window = ctx * 1000;
return entry;
}
async function fetchNebius() {
const { cookieVal, headerVal } = await getCsrf();
const res = await fetchRobust(MODELS_URL, {
headers: {
'User-Agent': UA,
Accept: 'application/json',
'x-csrf-token': headerVal,
Cookie: `__Host-psifi.x-csrf-token=${cookieVal}`,
},
});
const data = await res.json();
const list = data.models || data.data || (Array.isArray(data) ? data : []);
const models = [];
for (const model of list) {
const flavors = Array.isArray(model.flavors) && model.flavors.length ? model.flavors : [{}];
const multi = flavors.length > 1;
for (const flavor of flavors) {
// Only label-suffix when a model exposes several priced variants.
const suffix = multi ? (flavor.label || flavor.quantization) : '';
const entry = buildEntry(model, flavor, suffix);
if (entry.input_price_per_1m > 0 || entry.output_price_per_1m > 0) models.push(entry);
}
}
models.sort((a, b) => a.input_price_per_1m - b.input_price_per_1m);
return models;
}
module.exports = { fetchNebius, providerName: 'Nebius' };
// Run standalone: node scripts/providers/nebius.js
if (require.main === module) {
fetchNebius()
.then((models) => {
console.log(`Fetched ${models.length} models from Nebius (public models_info):\n`);
const byType = {};
models.forEach((m) => { (byType[m.type] = byType[m.type] || []).push(m); });
for (const [type, ms] of Object.entries(byType)) {
console.log(` [${type}]`);
ms.forEach((m) =>
console.log(` ${m.name.padEnd(45)} $${m.input_price_per_1m} / $${m.output_price_per_1m}`)
);
}
})
.catch((err) => {
console.error('Error:', err.message);
process.exit(1);
});
}