manifest
Version:
Self-hosted Manifest LLM router with embedded server, SQLite database, and dashboard
238 lines • 9.73 kB
JavaScript
;
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var ModelsDevSyncService_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ModelsDevSyncService = void 0;
const common_1 = require("@nestjs/common");
const schedule_1 = require("@nestjs/schedule");
const providers_1 = require("../common/constants/providers");
const PROVIDER_ID_MAP = {
anthropic: 'anthropic',
openai: 'openai',
gemini: 'google',
deepseek: 'deepseek',
mistral: 'mistral',
xai: 'xai',
minimax: 'minimax',
moonshot: 'moonshotai',
qwen: 'alibaba',
zai: 'zai',
copilot: 'github-copilot',
};
const SUPPORTED_PROVIDERS = new Set(Object.keys(PROVIDER_ID_MAP));
function resolveProviderId(providerId) {
const lower = providerId.toLowerCase();
if (SUPPORTED_PROVIDERS.has(lower))
return lower;
const entry = providers_1.PROVIDER_BY_ID_OR_ALIAS.get(lower);
return entry?.id ?? lower;
}
const MODELS_DEV_API = 'https://models.dev/api.json';
const FETCH_TIMEOUT_MS = 10000;
const VERSION_SUFFIX_RE = /-\d{3}$/;
const DATE_SUFFIX_RE = /-\d{4}-?\d{2}-?\d{2}$/;
const SHORT_DATE_SUFFIX_RE = /-\d{4}$/;
const LATEST_SUFFIX = '-latest';
const GOOGLE_VARIANT_RE = /-(?:preview(?:-\d{2,4}){1,3}|exp-\d{4}|latest)$/;
const REASONING_SUFFIX_RE = /-(reasoning|non-reasoning)$/;
const LEGACY_NAME_ALIASES = new Map([
['open-mistral-nemo', 'mistral-nemo'],
['mistral-tiny', 'open-mistral-7b'],
]);
let ModelsDevSyncService = ModelsDevSyncService_1 = class ModelsDevSyncService {
logger = new common_1.Logger(ModelsDevSyncService_1.name);
cache = new Map();
lastFetchedAt = null;
async onModuleInit() {
try {
await this.refreshCache();
}
catch (err) {
this.logger.error(`Startup models.dev cache refresh failed: ${err}`);
}
}
async refreshCache() {
this.logger.log('Refreshing models.dev cache...');
const raw = await this.fetchModelsDevData();
if (!raw)
return 0;
const newCache = new Map();
let totalModels = 0;
for (const [ourId, modelsDevId] of Object.entries(PROVIDER_ID_MAP)) {
const provider = raw[modelsDevId];
if (!provider?.models)
continue;
const modelMap = new Map();
for (const [modelId, model] of Object.entries(provider.models)) {
if (!this.isChatCompatible(model))
continue;
const entry = this.parseModel(modelId, model);
modelMap.set(modelId, entry);
totalModels++;
}
if (modelMap.size > 0) {
newCache.set(ourId, modelMap);
}
}
this.cache = newCache;
this.lastFetchedAt = new Date();
this.logger.log(`models.dev cache loaded: ${newCache.size} providers, ${totalModels} models`);
return totalModels;
}
lookupModel(providerId, modelId) {
const providerModels = this.cache.get(resolveProviderId(providerId));
if (!providerModels)
return null;
const exact = providerModels.get(modelId);
if (exact)
return exact;
const aliasBase = modelId
.replace(DATE_SUFFIX_RE, '')
.replace(SHORT_DATE_SUFFIX_RE, '')
.replace(/-latest$/, '');
const aliasTarget = LEGACY_NAME_ALIASES.get(aliasBase) ??
LEGACY_NAME_ALIASES.get(modelId) ??
LEGACY_NAME_ALIASES.get(modelId.replace(/-latest$/, ''));
if (aliasTarget) {
const found = providerModels.get(aliasTarget);
if (found)
return found;
}
const noVersion = modelId.replace(VERSION_SUFFIX_RE, '');
if (noVersion !== modelId) {
const found = providerModels.get(noVersion);
if (found)
return found;
}
const noDate = modelId.replace(DATE_SUFFIX_RE, '');
if (noDate !== modelId) {
const found = providerModels.get(noDate);
if (found)
return found;
}
if (!modelId.endsWith(LATEST_SUFFIX)) {
const withLatest = providerModels.get(modelId + LATEST_SUFFIX);
if (withLatest)
return withLatest;
}
if (noDate !== modelId && !noDate.endsWith(LATEST_SUFFIX)) {
const found = providerModels.get(noDate + LATEST_SUFFIX);
if (found)
return found;
}
const noGoogleVariant = modelId.replace(GOOGLE_VARIANT_RE, '');
if (noGoogleVariant !== modelId) {
const found = providerModels.get(noGoogleVariant);
if (found)
return found;
}
const noReasoning = modelId.replace(REASONING_SUFFIX_RE, '');
if (noReasoning !== modelId) {
const found = providerModels.get(noReasoning);
if (found)
return found;
}
const noShortDate = modelId.replace(SHORT_DATE_SUFFIX_RE, '');
if (noShortDate !== modelId) {
const found = providerModels.get(noShortDate);
if (found)
return found;
if (!noShortDate.endsWith(LATEST_SUFFIX)) {
const withLatest = providerModels.get(noShortDate + LATEST_SUFFIX);
if (withLatest)
return withLatest;
}
}
if (modelId.endsWith(LATEST_SUFFIX)) {
const base = modelId.slice(0, -LATEST_SUFFIX.length);
const prefix = `${base}-`;
for (const [key, entry] of providerModels) {
if (key.startsWith(prefix) && key !== modelId)
return entry;
}
const baseMatch = providerModels.get(base);
if (baseMatch)
return baseMatch;
}
return null;
}
getModelsForProvider(providerId) {
const providerModels = this.cache.get(resolveProviderId(providerId));
if (!providerModels)
return [];
return [...providerModels.values()];
}
isProviderSupported(providerId) {
return SUPPORTED_PROVIDERS.has(resolveProviderId(providerId));
}
getLastFetchedAt() {
return this.lastFetchedAt;
}
parseModel(modelId, raw) {
const inputPerMillion = raw.cost?.input ?? null;
const outputPerMillion = raw.cost?.output ?? null;
const cacheReadPerMillion = raw.cost?.cache_read ?? null;
const cacheWritePerMillion = raw.cost?.cache_write ?? null;
return {
id: modelId,
name: raw.name || modelId,
family: raw.family,
reasoning: raw.reasoning ?? false,
toolCall: raw.tool_call ?? false,
structuredOutput: raw.structured_output ?? false,
contextWindow: raw.limit?.context,
maxOutputTokens: raw.limit?.output,
inputPricePerToken: inputPerMillion !== null ? inputPerMillion / 1_000_000 : null,
outputPricePerToken: outputPerMillion !== null ? outputPerMillion / 1_000_000 : null,
cacheReadPricePerToken: cacheReadPerMillion !== null ? cacheReadPerMillion / 1_000_000 : null,
cacheWritePricePerToken: cacheWritePerMillion !== null ? cacheWritePerMillion / 1_000_000 : null,
};
}
isChatCompatible(model) {
const inputMods = model.modalities?.input?.map((m) => m.toLowerCase());
if (inputMods && inputMods.length > 0 && !inputMods.includes('text')) {
return false;
}
const outputMods = model.modalities?.output?.map((m) => m.toLowerCase());
if (outputMods && outputMods.length > 0) {
return outputMods.includes('text');
}
return true;
}
async fetchModelsDevData() {
try {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
const res = await fetch(MODELS_DEV_API, { signal: controller.signal });
clearTimeout(timeout);
if (!res.ok) {
this.logger.error(`models.dev API returned ${res.status}`);
return null;
}
return (await res.json());
}
catch (err) {
this.logger.error(`Failed to fetch models.dev data: ${err}`);
return null;
}
}
};
exports.ModelsDevSyncService = ModelsDevSyncService;
__decorate([
(0, schedule_1.Cron)(schedule_1.CronExpression.EVERY_DAY_AT_4AM),
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", Promise)
], ModelsDevSyncService.prototype, "refreshCache", null);
exports.ModelsDevSyncService = ModelsDevSyncService = ModelsDevSyncService_1 = __decorate([
(0, common_1.Injectable)()
], ModelsDevSyncService);
//# sourceMappingURL=models-dev-sync.service.js.map