Spaces:
Paused
Paused
File size: 5,376 Bytes
ffba252 49e63bd ffba252 43d89fe ffba252 43d89fe 49e63bd 43d89fe 49e63bd 43d89fe ffba252 43d89fe 49e63bd 43d89fe ffba252 43d89fe ffba252 43d89fe ffba252 49e63bd 43d89fe ffba252 49e63bd 43d89fe 49e63bd 43d89fe 49e63bd ffba252 43d89fe 49e63bd ffba252 43d89fe 49e63bd ffba252 43d89fe ffba252 49e63bd ffba252 43d89fe ffba252 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | '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);
});
}
|