UNPKG

manifest

Version:

Self-hosted Manifest LLM router with embedded server, SQLite database, and dashboard

238 lines 9.73 kB
"use strict"; 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