UNPKG

manifest

Version:

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

160 lines 7.39 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 LimitCheckService_1; Object.defineProperty(exports, "__esModule", { value: true }); exports.LimitCheckService = void 0; const common_1 = require("@nestjs/common"); const notification_rules_service_1 = require("./notification-rules.service"); const notification_email_service_1 = require("./notification-email.service"); const email_provider_config_service_1 = require("./email-provider-config.service"); const notification_log_service_1 = require("./notification-log.service"); const ingest_event_bus_service_1 = require("../../common/services/ingest-event-bus.service"); const manifest_runtime_service_1 = require("../../common/services/manifest-runtime.service"); const period_util_1 = require("../../common/utils/period.util"); const CACHE_TTL_MS = 60_000; const MAX_CACHE_SIZE = 10_000; let LimitCheckService = LimitCheckService_1 = class LimitCheckService { rulesService; emailService; emailProviderConfig; ingestBus; runtime; notificationLog; logger = new common_1.Logger(LimitCheckService_1.name); rulesCache = new Map(); consumptionCache = new Map(); ingestSub; constructor(rulesService, emailService, emailProviderConfig, ingestBus, runtime, notificationLog) { this.rulesService = rulesService; this.emailService = emailService; this.emailProviderConfig = emailProviderConfig; this.ingestBus = ingestBus; this.runtime = runtime; this.notificationLog = notificationLog; } onModuleInit() { this.ingestSub = this.ingestBus.all().subscribe(() => { this.consumptionCache.clear(); }); } onModuleDestroy() { this.ingestSub?.unsubscribe(); } async checkLimits(tenantId, agentName) { const rules = await this.getCachedRules(tenantId, agentName); if (rules.length === 0) return null; for (const rule of rules) { const { periodStart, periodEnd } = (0, period_util_1.computePeriodBoundaries)(rule.period); const actual = await this.getCachedConsumption(tenantId, agentName, rule.metric_type, periodStart, periodEnd); if (actual >= rule.threshold) { this.notifyLimitExceeded(rule, actual, periodStart, periodEnd).catch((err) => { this.logger.error(`Failed to send block notification for rule ${rule.id}: ${err}`); }); return { ruleId: rule.id, metricType: rule.metric_type, threshold: rule.threshold, actual, period: rule.period, }; } } return null; } invalidateCache(tenantId, agentName) { const key = `${tenantId}:${agentName}`; this.rulesCache.delete(key); for (const k of this.consumptionCache.keys()) { if (k.startsWith(key + ':')) { this.consumptionCache.delete(k); } } } async notifyLimitExceeded(rule, actual, periodStart, periodEnd) { if (await this.notificationLog.hasAlreadySent(rule.id, periodStart)) return; const now = (0, notification_log_service_1.formatNotificationTimestamp)(); const providerConfig = await this.emailProviderConfig.getFullConfig(rule.user_id); const email = await this.notificationLog.resolveUserEmail(rule.user_id, providerConfig?.notificationEmail); await this.notificationLog.insertLog({ ruleId: rule.id, periodStart, periodEnd, actualValue: actual, thresholdValue: rule.threshold, metricType: rule.metric_type, agentName: rule.agent_name, sentAt: now, }); if (email) { await this.emailService.sendThresholdAlert(email, { agentName: rule.agent_name, metricType: rule.metric_type, threshold: rule.threshold, actualValue: actual, period: rule.period, timestamp: now, agentUrl: `${this.runtime.getAuthBaseUrl()}/agents/${encodeURIComponent(rule.agent_name)}`, alertType: 'hard', periodResetDate: (0, period_util_1.computePeriodResetDate)(rule.period), }, providerConfig ?? undefined); } } async getCachedRules(tenantId, agentName) { const key = `${tenantId}:${agentName}`; const now = Date.now(); const cached = this.rulesCache.get(key); if (cached && cached.expiresAt > now) return cached.data; this.evictExpired(this.rulesCache, now); this.evictOldestIfFull(this.rulesCache); const rules = await this.rulesService.getActiveBlockRules(tenantId, agentName); this.rulesCache.set(key, { data: rules, expiresAt: now + CACHE_TTL_MS }); return rules; } async getCachedConsumption(tenantId, agentName, metricType, periodStart, periodEnd) { const key = `${tenantId}:${agentName}:${metricType}:${periodStart}`; const now = Date.now(); const cached = this.consumptionCache.get(key); if (cached && cached.expiresAt > now) return cached.data; this.evictExpired(this.consumptionCache, now); this.evictOldestIfFull(this.consumptionCache); const actual = await this.rulesService.getConsumption(tenantId, agentName, metricType, periodStart, periodEnd); this.consumptionCache.set(key, { data: actual, expiresAt: now + CACHE_TTL_MS }); return actual; } evictExpired(cache, now) { for (const [k, v] of cache) { if (v.expiresAt <= now) cache.delete(k); } } evictOldestIfFull(cache) { if (cache.size >= MAX_CACHE_SIZE) { const firstKey = cache.keys().next().value; if (firstKey) cache.delete(firstKey); } } }; exports.LimitCheckService = LimitCheckService; exports.LimitCheckService = LimitCheckService = LimitCheckService_1 = __decorate([ (0, common_1.Injectable)(), __metadata("design:paramtypes", [notification_rules_service_1.NotificationRulesService, notification_email_service_1.NotificationEmailService, email_provider_config_service_1.EmailProviderConfigService, ingest_event_bus_service_1.IngestEventBusService, manifest_runtime_service_1.ManifestRuntimeService, notification_log_service_1.NotificationLogService]) ], LimitCheckService); //# sourceMappingURL=limit-check.service.js.map