manifest
Version:
Self-hosted Manifest LLM router with embedded server, SQLite database, and dashboard
151 lines • 7.18 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 NotificationCronService_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.NotificationCronService = void 0;
const common_1 = require("@nestjs/common");
const schedule_1 = require("@nestjs/schedule");
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 manifest_runtime_service_1 = require("../../common/services/manifest-runtime.service");
const period_util_1 = require("../../common/utils/period.util");
let NotificationCronService = NotificationCronService_1 = class NotificationCronService {
rulesService;
emailService;
emailProviderConfigService;
runtime;
notificationLog;
logger = new common_1.Logger(NotificationCronService_1.name);
constructor(rulesService, emailService, emailProviderConfigService, runtime, notificationLog) {
this.rulesService = rulesService;
this.emailService = emailService;
this.emailProviderConfigService = emailProviderConfigService;
this.runtime = runtime;
this.notificationLog = notificationLog;
}
async onModuleInit() {
try {
const triggered = await this.checkThresholds();
if (triggered > 0) {
this.logger.log(`Startup catch-up: ${triggered} notification(s) triggered`);
}
}
catch (err) {
this.logger.error(`Startup catch-up failed: ${err}`);
}
}
async checkThresholds(userId) {
const rules = userId
? await this.rulesService.getActiveRulesForUser(userId)
: await this.rulesService.getAllActiveRules();
if (!rules.length)
return 0;
this.logger.log(`Checking ${rules.length} notification rules...`);
const groups = new Map();
for (const rule of rules) {
const key = `${rule.tenant_id}|${rule.agent_name}|${rule.period}`;
if (!groups.has(key))
groups.set(key, { rules: [], consumption: new Map() });
groups.get(key).rules.push(rule);
}
for (const [, group] of groups) {
const sample = group.rules[0];
const { periodStart, periodEnd } = (0, period_util_1.computePeriodBoundaries)(sample.period);
const metrics = new Set(group.rules.map((r) => r.metric_type));
for (const metric of metrics) {
try {
const value = await this.rulesService.getConsumption(sample.tenant_id, sample.agent_name, metric, periodStart, periodEnd);
group.consumption.set(metric, value);
}
catch (err) {
this.logger.error(`Error fetching consumption for ${sample.agent_name}/${metric}: ${err}`);
}
}
}
let triggered = 0;
for (const [, group] of groups) {
for (const rule of group.rules) {
try {
const actual = group.consumption.get(rule.metric_type);
if (actual === undefined)
continue;
const sent = await this.evaluateRule(rule, actual);
if (sent)
triggered++;
}
catch (err) {
this.logger.error(`Error evaluating rule ${rule.id}: ${err}`);
}
}
}
if (triggered > 0) {
this.logger.log(`${triggered} notification(s) triggered`);
}
return triggered;
}
async evaluateRule(rule, actual) {
const { periodStart, periodEnd } = (0, period_util_1.computePeriodBoundaries)(rule.period);
if (await this.notificationLog.hasAlreadySent(rule.id, periodStart))
return false;
if (actual < rule.threshold)
return false;
const now = (0, notification_log_service_1.formatNotificationTimestamp)();
const fullConfig = await this.emailProviderConfigService.getFullConfig(rule.user_id);
const email = await this.notificationLog.resolveUserEmail(rule.user_id, fullConfig?.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) {
const emailSent = 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: 'soft',
}, fullConfig ?? undefined);
if (!emailSent) {
this.logger.warn(`Failed to send alert for rule ${rule.id}, will retry next cron run`);
}
}
else {
this.logger.warn(`No email found for user ${rule.user_id}, skipping alert for rule ${rule.id}`);
}
return true;
}
};
exports.NotificationCronService = NotificationCronService;
__decorate([
(0, schedule_1.Cron)(schedule_1.CronExpression.EVERY_HOUR),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String]),
__metadata("design:returntype", Promise)
], NotificationCronService.prototype, "checkThresholds", null);
exports.NotificationCronService = NotificationCronService = NotificationCronService_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,
manifest_runtime_service_1.ManifestRuntimeService,
notification_log_service_1.NotificationLogService])
], NotificationCronService);
//# sourceMappingURL=notification-cron.service.js.map