manifest
Version:
Self-hosted Manifest LLM router with embedded server, SQLite database, and dashboard
160 lines • 7.39 kB
JavaScript
"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