UNPKG

manifest

Version:

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

226 lines 10 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 __param = (this && this.__param) || function (paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); } }; Object.defineProperty(exports, "__esModule", { value: true }); exports.PublicStatsService = void 0; const common_1 = require("@nestjs/common"); const typeorm_1 = require("@nestjs/typeorm"); const typeorm_2 = require("typeorm"); const agent_message_entity_1 = require("../entities/agent-message.entity"); const model_pricing_cache_service_1 = require("../model-prices/model-pricing-cache.service"); const sql_dialect_1 = require("../common/utils/sql-dialect"); const MAX_RESULTS = 10; const EXCLUDED_PROVIDERS = new Set(['Unknown']); function isCustomModel(model) { return model.startsWith('custom:'); } let PublicStatsService = class PublicStatsService { messageRepo; pricingCache; dataSource; dialect; constructor(messageRepo, pricingCache, dataSource) { this.messageRepo = messageRepo; this.pricingCache = pricingCache; this.dataSource = dataSource; this.dialect = (0, sql_dialect_1.detectDialect)(this.dataSource.options.type); } async getUsageStats() { const cutoff7d = (0, sql_dialect_1.computeCutoff)('7 days'); const cutoff14d = (0, sql_dialect_1.computeCutoff)('14 days'); const cutoff30d = (0, sql_dialect_1.computeCutoff)('30 days'); const [countRow, topRows, tokenRows, tokenRowsPrev7d, tokenRows30d] = await Promise.all([ this.messageRepo.createQueryBuilder('at').select('COUNT(*)', 'total').getRawOne(), this.messageRepo .createQueryBuilder('at') .select('at.model', 'model') .addSelect('COUNT(*)', 'usage_count') .where('at.model IS NOT NULL') .groupBy('at.model') .orderBy('usage_count', 'DESC') .getRawMany(), this.messageRepo .createQueryBuilder('at') .select('at.model', 'model') .addSelect('SUM(at.input_tokens + at.output_tokens)', 'tokens') .where('at.model IS NOT NULL') .andWhere('at.timestamp >= :cutoff', { cutoff: cutoff7d }) .groupBy('at.model') .getRawMany(), this.messageRepo .createQueryBuilder('at') .select('at.model', 'model') .addSelect('SUM(at.input_tokens + at.output_tokens)', 'tokens') .where('at.model IS NOT NULL') .andWhere('at.timestamp >= :cutoff14d', { cutoff14d }) .andWhere('at.timestamp < :cutoff7d', { cutoff7d }) .groupBy('at.model') .getRawMany(), this.messageRepo .createQueryBuilder('at') .select('at.model', 'model') .addSelect('SUM(at.input_tokens + at.output_tokens)', 'tokens') .where('at.model IS NOT NULL') .andWhere('at.timestamp >= :cutoff30d', { cutoff30d }) .groupBy('at.model') .getRawMany(), ]); const tokenMap = new Map(); for (const r of tokenRows) { tokenMap.set(r.model, Number(r.tokens ?? 0)); } const tokenMapPrev7d = new Map(); for (const r of tokenRowsPrev7d) { tokenMapPrev7d.set(r.model, Number(r.tokens ?? 0)); } const tokenMap30d = new Map(); for (const r of tokenRows30d) { tokenMap30d.set(r.model, Number(r.tokens ?? 0)); } const eligible = []; for (const r of topRows) { const modelName = r.model; if (isCustomModel(modelName)) continue; const pricing = this.pricingCache.getByModel(modelName); const provider = pricing?.provider || 'Unknown'; if (EXCLUDED_PROVIDERS.has(provider)) continue; eligible.push({ model: modelName, provider, tokens_7d: tokenMap.get(modelName) ?? 0, tokens_previous_7d: tokenMapPrev7d.get(modelName) ?? 0, tokens_30d: tokenMap30d.get(modelName) ?? 0, input_price_per_million: pricing?.input_price_per_token != null ? Number(pricing.input_price_per_token) * 1_000_000 : null, output_price_per_million: pricing?.output_price_per_token != null ? Number(pricing.output_price_per_token) * 1_000_000 : null, usage_rank: 0, }); } eligible.sort((a, b) => b.tokens_7d - a.tokens_7d); const topModels = eligible.slice(0, MAX_RESULTS); topModels.forEach((m, i) => (m.usage_rank = i + 1)); return { total_messages: Number(countRow?.total ?? 0), top_models: topModels, token_map: tokenMap, }; } async getProviderDailyTokens() { const cutoff30d = (0, sql_dialect_1.computeCutoff)('30 days'); const dateBucket = (0, sql_dialect_1.sqlDateBucket)('at.timestamp', this.dialect); const rows = await this.messageRepo .createQueryBuilder('at') .select('at.model', 'model') .addSelect(dateBucket, 'date') .addSelect('at.auth_type', 'auth_type') .addSelect('SUM(at.input_tokens + at.output_tokens)', 'tokens') .addSelect('SUM(at.cost_usd)', 'cost') .where('at.model IS NOT NULL') .andWhere('at.timestamp >= :cutoff30d', { cutoff30d }) .groupBy('at.model') .addGroupBy('date') .addGroupBy('at.auth_type') .orderBy('date', 'ASC') .getRawMany(); const modelMap = new Map(); for (const r of rows) { const modelName = r.model; if (isCustomModel(modelName)) continue; const pricing = this.pricingCache.getByModel(modelName); const provider = pricing?.provider || 'Unknown'; if (EXCLUDED_PROVIDERS.has(provider)) continue; const key = `${modelName}:${r.auth_type ?? ''}`; let entry = modelMap.get(key); if (!entry) { entry = { modelName, provider, authType: r.auth_type ?? null, total: 0, cost: null, daily: new Map(), }; modelMap.set(key, entry); } const tokens = Number(r.tokens ?? 0); entry.total += tokens; const rowCost = r.cost != null ? Number(r.cost) : null; if (rowCost != null) { entry.cost = (entry.cost ?? 0) + rowCost; } entry.daily.set(r.date, (entry.daily.get(r.date) ?? 0) + tokens); } const providerMap = new Map(); for (const [, entry] of modelMap) { let prov = providerMap.get(entry.provider); if (!prov) { prov = { total: 0, models: [] }; providerMap.set(entry.provider, prov); } prov.total += entry.total; prov.models.push({ model: entry.modelName, auth_type: entry.authType, total_tokens: entry.total, total_cost: entry.cost, daily: Array.from(entry.daily.entries()) .sort(([a], [b]) => a.localeCompare(b)) .map(([date, tokens]) => ({ date, tokens })), }); } return Array.from(providerMap.entries()) .sort(([, a], [, b]) => b.total - a.total) .map(([provider, data]) => ({ provider, total_tokens: data.total, models: data.models.sort((a, b) => b.total_tokens - a.total_tokens), })); } getFreeModels(tokenMap) { return this.pricingCache .getAll() .filter((e) => { if ((e.input_price_per_token ?? 0) !== 0 || (e.output_price_per_token ?? 0) !== 0) return false; if (isCustomModel(e.model_name)) return false; const provider = e.provider || 'Unknown'; if (EXCLUDED_PROVIDERS.has(provider)) return false; return (tokenMap.get(e.model_name) ?? 0) > 0; }) .map((e) => ({ model_name: e.model_name, provider: e.provider || 'Unknown', tokens_7d: tokenMap.get(e.model_name) ?? 0, })) .sort((a, b) => b.tokens_7d - a.tokens_7d) .slice(0, MAX_RESULTS); } }; exports.PublicStatsService = PublicStatsService; exports.PublicStatsService = PublicStatsService = __decorate([ (0, common_1.Injectable)(), __param(0, (0, typeorm_1.InjectRepository)(agent_message_entity_1.AgentMessage)), __metadata("design:paramtypes", [typeorm_2.Repository, model_pricing_cache_service_1.ModelPricingCacheService, typeorm_2.DataSource]) ], PublicStatsService); //# sourceMappingURL=public-stats.service.js.map