mirror of
https://github.com/elder-plinius/P4RS3LT0NGV3.git
synced 2026-07-12 23:46:33 +02:00
242 lines
7.3 KiB
JavaScript
242 lines
7.3 KiB
JavaScript
/**
|
|
* Fetch, cache, and normalize OpenRouter model lists for UI dropdowns.
|
|
*/
|
|
window.OpenRouterModels = {
|
|
CACHE_KEY: 'openrouter-models-cache-v1',
|
|
CACHE_TTL_MS: 60 * 60 * 1000,
|
|
|
|
ENABLED_STORAGE_KEY: 'openrouter-models-disabled-v1',
|
|
|
|
VIRTUAL: [
|
|
{
|
|
id: 'openrouter/free',
|
|
name: 'Free router',
|
|
summary: 'Zero cost — random free model matched to your request',
|
|
provider: 'OpenRouter',
|
|
virtual: true,
|
|
routerKind: 'free'
|
|
},
|
|
{
|
|
id: 'openrouter/auto',
|
|
name: 'Auto router',
|
|
summary: 'Smart routing — billed at whichever model is picked',
|
|
provider: 'OpenRouter',
|
|
virtual: true,
|
|
routerKind: 'auto'
|
|
}
|
|
],
|
|
|
|
getApiKey: function() {
|
|
try {
|
|
return (
|
|
localStorage.getItem('openrouter-api-key') ||
|
|
localStorage.getItem('plinyos-api-key') ||
|
|
localStorage.getItem('openrouter_api_key') ||
|
|
''
|
|
).trim();
|
|
} catch (e) {
|
|
return '';
|
|
}
|
|
},
|
|
|
|
getStaticFallback: function() {
|
|
if (window.OPENROUTER_MODELS_FALLBACK && window.OPENROUTER_MODELS_FALLBACK.length) {
|
|
return window.OPENROUTER_MODELS_FALLBACK.slice();
|
|
}
|
|
if (window.OPENROUTER_MODELS && window.OPENROUTER_MODELS.length) {
|
|
return window.OPENROUTER_MODELS.slice();
|
|
}
|
|
return this.VIRTUAL.slice();
|
|
},
|
|
|
|
isFreePricing: function(pricing) {
|
|
if (!pricing) return false;
|
|
return String(pricing.prompt) === '0' && String(pricing.completion) === '0';
|
|
},
|
|
|
|
normalize: function(raw) {
|
|
var id = raw && raw.id ? raw.id : '';
|
|
var provider = id.indexOf('/') !== -1 ? id.split('/')[0] : '';
|
|
return {
|
|
id: id,
|
|
name: (raw && raw.name) || id,
|
|
provider: provider,
|
|
free: this.isFreePricing(raw && raw.pricing),
|
|
contextLength: raw && raw.context_length ? raw.context_length : null,
|
|
pricing: raw && raw.pricing ? raw.pricing : null
|
|
};
|
|
},
|
|
|
|
formatLabel: function(model) {
|
|
if (!model) return '';
|
|
if (model.virtual) {
|
|
return model.name + ' — ' + (model.summary || model.provider);
|
|
}
|
|
var label = model.name || model.id;
|
|
if (model.provider) {
|
|
label += ' (' + model.provider + ')';
|
|
}
|
|
if (model.free) {
|
|
label += ' · free';
|
|
}
|
|
return label;
|
|
},
|
|
|
|
getRouterHint: function(modelId) {
|
|
var match = this.VIRTUAL.find(function(model) {
|
|
return model.id === modelId;
|
|
});
|
|
return match ? match.summary : '';
|
|
},
|
|
|
|
loadDisabledIds: function() {
|
|
try {
|
|
var raw = localStorage.getItem(this.ENABLED_STORAGE_KEY);
|
|
if (!raw) return [];
|
|
var parsed = JSON.parse(raw);
|
|
return Array.isArray(parsed) ? parsed : [];
|
|
} catch (e) {
|
|
return [];
|
|
}
|
|
},
|
|
|
|
saveDisabledIds: function(ids) {
|
|
try {
|
|
localStorage.setItem(this.ENABLED_STORAGE_KEY, JSON.stringify(ids || []));
|
|
} catch (e) {
|
|
console.warn('Failed to save OpenRouter model preferences:', e);
|
|
}
|
|
},
|
|
|
|
isModelEnabled: function(modelId, disabledIds) {
|
|
if (!modelId) return true;
|
|
disabledIds = disabledIds || [];
|
|
return disabledIds.indexOf(modelId) === -1;
|
|
},
|
|
|
|
filterForDropdown: function(catalog, disabledIds, pinnedIds) {
|
|
catalog = catalog || [];
|
|
disabledIds = disabledIds || [];
|
|
pinnedIds = pinnedIds || [];
|
|
|
|
return catalog.filter(function(model) {
|
|
if (!model || !model.id) return false;
|
|
if (pinnedIds.indexOf(model.id) !== -1) return true;
|
|
return disabledIds.indexOf(model.id) === -1;
|
|
});
|
|
},
|
|
|
|
getPinnedModelIds: function(context) {
|
|
context = context || {};
|
|
var ids = [];
|
|
['pcModel', 'acModel', 'saModel', 'translateModel'].forEach(function(field) {
|
|
if (context[field]) ids.push(context[field]);
|
|
});
|
|
return ids;
|
|
},
|
|
|
|
loadCache: function(apiKey) {
|
|
try {
|
|
var raw = localStorage.getItem(this.CACHE_KEY);
|
|
if (!raw) return null;
|
|
var parsed = JSON.parse(raw);
|
|
if (parsed.apiKey !== (apiKey || '')) return null;
|
|
if (Date.now() - parsed.fetchedAt > this.CACHE_TTL_MS) return null;
|
|
return Array.isArray(parsed.models) ? parsed.models : null;
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
},
|
|
|
|
saveCache: function(apiKey, models) {
|
|
try {
|
|
localStorage.setItem(this.CACHE_KEY, JSON.stringify({
|
|
apiKey: apiKey || '',
|
|
fetchedAt: Date.now(),
|
|
models: models
|
|
}));
|
|
} catch (e) {
|
|
console.warn('Failed to cache OpenRouter models:', e);
|
|
}
|
|
},
|
|
|
|
mergeWithVirtual: function(models) {
|
|
var seen = {};
|
|
var merged = [];
|
|
|
|
this.VIRTUAL.forEach(function(model) {
|
|
seen[model.id] = true;
|
|
merged.push(Object.assign({}, model));
|
|
});
|
|
|
|
(models || []).forEach(function(model) {
|
|
if (!model || !model.id || seen[model.id]) return;
|
|
seen[model.id] = true;
|
|
merged.push(model);
|
|
});
|
|
|
|
return merged;
|
|
},
|
|
|
|
fetch: async function(apiKey, options) {
|
|
options = options || {};
|
|
var key = (apiKey || '').trim();
|
|
|
|
if (!options.force) {
|
|
var cached = this.loadCache(key);
|
|
if (cached && cached.length) {
|
|
return cached;
|
|
}
|
|
}
|
|
|
|
var url = key
|
|
? 'https://openrouter.ai/api/v1/models/user'
|
|
: 'https://openrouter.ai/api/v1/models?sort=most-popular';
|
|
|
|
var headers = {};
|
|
if (key) {
|
|
headers.Authorization = 'Bearer ' + key;
|
|
}
|
|
|
|
var resp = await fetch(url, { headers: headers });
|
|
if (!resp.ok) {
|
|
var error = new Error('Failed to load models (HTTP ' + resp.status + ')');
|
|
error.status = resp.status;
|
|
throw error;
|
|
}
|
|
|
|
var json = await resp.json();
|
|
var models = (json.data || []).map(this.normalize.bind(this));
|
|
models = this.mergeWithVirtual(models);
|
|
this.saveCache(key, models);
|
|
return models;
|
|
},
|
|
|
|
fetchKeyInfo: async function(apiKey) {
|
|
var key = (apiKey || '').trim();
|
|
if (!key) return null;
|
|
|
|
try {
|
|
var resp = await fetch('https://openrouter.ai/api/v1/key', {
|
|
headers: { Authorization: 'Bearer ' + key }
|
|
});
|
|
if (!resp.ok) return null;
|
|
var json = await resp.json();
|
|
return json.data || null;
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
},
|
|
|
|
ensureValidSelection: function(currentId, models, preferredId) {
|
|
if (!models || !models.length) return currentId || preferredId || '';
|
|
if (currentId && models.some(function(model) { return model.id === currentId; })) {
|
|
return currentId;
|
|
}
|
|
if (preferredId && models.some(function(model) { return model.id === preferredId; })) {
|
|
return preferredId;
|
|
}
|
|
return models[0].id;
|
|
}
|
|
};
|