Spaces:
Running
Running
CrispStrobe commited on
Commit ·
e9acff4
1
Parent(s): d699da8
feat: include eu-endpoint tagging in OpenRouter data
Browse files- data/providers.json +0 -0
- scripts/providers/openrouter.js +33 -10
data/providers.json
CHANGED
|
The diff for this file is too large to render.
See raw diff
|
|
|
scripts/providers/openrouter.js
CHANGED
|
@@ -12,6 +12,7 @@ loadEnv();
|
|
| 12 |
const { getJson } = require('../fetch-utils');
|
| 13 |
|
| 14 |
const API_URL = 'https://openrouter.ai/api/v1/models';
|
|
|
|
| 15 |
|
| 16 |
// OpenRouter stores per-token prices; multiply by 1e6 to get per-1M price.
|
| 17 |
const toPerMillion = (val) => (val ? parseFloat(val) * 1_000_000 : 0);
|
|
@@ -65,10 +66,30 @@ async function fetchOpenRouter() {
|
|
| 65 |
};
|
| 66 |
if (apiKey) headers['Authorization'] = `Bearer ${apiKey}`;
|
| 67 |
|
| 68 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 69 |
const models = [];
|
| 70 |
|
| 71 |
-
for (const model of
|
| 72 |
const pricing = model.pricing || {};
|
| 73 |
const inputPrice = toPerMillion(pricing.prompt);
|
| 74 |
const outputPrice = toPerMillion(pricing.completion);
|
|
@@ -86,6 +107,11 @@ async function fetchOpenRouter() {
|
|
| 86 |
|
| 87 |
const type = getModelType(model.architecture);
|
| 88 |
const capabilities = getCapabilities(model.architecture, model.supported_parameters);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 89 |
|
| 90 |
const modelEntry = {
|
| 91 |
name: model.id,
|
|
@@ -145,15 +171,12 @@ if (require.main === module) {
|
|
| 145 |
const free = models.filter(m => m.input_price_per_1m === 0 && !m.price_per_image);
|
| 146 |
const vision = models.filter(m => m.type === 'vision');
|
| 147 |
const imageGen = models.filter(m => m.type === 'image');
|
|
|
|
| 148 |
console.log(`Fetched ${models.length} models from OpenRouter API ${apiKey ? '(authenticated)' : '(public – set OPENROUTER_API_KEY for more models)'}`);
|
| 149 |
-
console.log(` Free: ${free.length}, Vision: ${vision.length}, Image-gen: ${imageGen.length}`);
|
| 150 |
-
console.log('\nFirst 5
|
| 151 |
-
|
| 152 |
-
console.log(` ${m.name.padEnd(55)} $${m.input_price_per_1m} / $${m.output_price_per_1m} [${m.type}]
|
| 153 |
-
);
|
| 154 |
-
console.log('\nFirst 5 free:');
|
| 155 |
-
free.slice(0, 5).forEach((m) =>
|
| 156 |
-
console.log(` ${m.name.padEnd(55)} [${m.type}] ${(m.capabilities||[]).join(',')}`)
|
| 157 |
);
|
| 158 |
})
|
| 159 |
.catch((err) => {
|
|
|
|
| 12 |
const { getJson } = require('../fetch-utils');
|
| 13 |
|
| 14 |
const API_URL = 'https://openrouter.ai/api/v1/models';
|
| 15 |
+
const EU_API_URL = 'https://eu.openrouter.ai/api/v1/models';
|
| 16 |
|
| 17 |
// OpenRouter stores per-token prices; multiply by 1e6 to get per-1M price.
|
| 18 |
const toPerMillion = (val) => (val ? parseFloat(val) * 1_000_000 : 0);
|
|
|
|
| 66 |
};
|
| 67 |
if (apiKey) headers['Authorization'] = `Bearer ${apiKey}`;
|
| 68 |
|
| 69 |
+
// If we have an API key, use the /user endpoint to get EU-filtered models correctly.
|
| 70 |
+
// Standard /models endpoint doesn't filter by subdomain.
|
| 71 |
+
const globalUrl = apiKey ? `${API_URL}/user` : API_URL;
|
| 72 |
+
const euUrl = apiKey ? `${EU_API_URL}/user` : EU_API_URL;
|
| 73 |
+
|
| 74 |
+
process.stdout.write('OpenRouter: fetching Global... ');
|
| 75 |
+
const globalData = await getJson(globalUrl, { headers });
|
| 76 |
+
|
| 77 |
+
let euModelIds = new Set();
|
| 78 |
+
if (apiKey) {
|
| 79 |
+
process.stdout.write('EU... ');
|
| 80 |
+
try {
|
| 81 |
+
const euData = await getJson(euUrl, { headers });
|
| 82 |
+
if (euData?.data) {
|
| 83 |
+
euModelIds = new Set(euData.data.map(m => m.id));
|
| 84 |
+
}
|
| 85 |
+
} catch (e) {
|
| 86 |
+
console.warn(`\n ⚠ Failed to fetch EU models: ${e.message}`);
|
| 87 |
+
}
|
| 88 |
+
}
|
| 89 |
+
|
| 90 |
const models = [];
|
| 91 |
|
| 92 |
+
for (const model of globalData.data || []) {
|
| 93 |
const pricing = model.pricing || {};
|
| 94 |
const inputPrice = toPerMillion(pricing.prompt);
|
| 95 |
const outputPrice = toPerMillion(pricing.completion);
|
|
|
|
| 107 |
|
| 108 |
const type = getModelType(model.architecture);
|
| 109 |
const capabilities = getCapabilities(model.architecture, model.supported_parameters);
|
| 110 |
+
|
| 111 |
+
// Tag with eu-endpoint if model is available via EU subdomain
|
| 112 |
+
if (euModelIds.has(model.id)) {
|
| 113 |
+
capabilities.push('eu-endpoint');
|
| 114 |
+
}
|
| 115 |
|
| 116 |
const modelEntry = {
|
| 117 |
name: model.id,
|
|
|
|
| 171 |
const free = models.filter(m => m.input_price_per_1m === 0 && !m.price_per_image);
|
| 172 |
const vision = models.filter(m => m.type === 'vision');
|
| 173 |
const imageGen = models.filter(m => m.type === 'image');
|
| 174 |
+
const eu = models.filter(m => m.capabilities?.includes('eu-endpoint'));
|
| 175 |
console.log(`Fetched ${models.length} models from OpenRouter API ${apiKey ? '(authenticated)' : '(public – set OPENROUTER_API_KEY for more models)'}`);
|
| 176 |
+
console.log(` Free: ${free.length}, Vision: ${vision.length}, Image-gen: ${imageGen.length}, EU-Endpoint: ${eu.length}`);
|
| 177 |
+
console.log('\nFirst 5 EU-available:');
|
| 178 |
+
eu.slice(0, 5).forEach((m) =>
|
| 179 |
+
console.log(` ${m.name.padEnd(55)} $${m.input_price_per_1m} / $${m.output_price_per_1m} [${m.type}]`)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 180 |
);
|
| 181 |
})
|
| 182 |
.catch((err) => {
|