manifest
Version:
Self-hosted Manifest LLM router with embedded server, SQLite database, and dashboard
226 lines • 10 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 __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