manifest 5.33.20 → 5.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/backend/analytics/services/messages-query.service.js +14 -23
- package/dist/backend/common/utils/ttl-cache.js +59 -0
- package/dist/backend/main.js +3 -3
- package/dist/backend/model-discovery/provider-model-fetcher.service.js +64 -117
- package/dist/backend/notifications/notifications.module.js +2 -0
- package/dist/backend/notifications/services/limit-check.service.js +18 -44
- package/dist/backend/notifications/services/notification-cron.service.js +20 -47
- package/dist/backend/notifications/services/notification-log.service.js +75 -0
- package/dist/backend/otlp/guards/agent-key-auth.guard.js +72 -68
- package/dist/backend/routing/proxy/anthropic-adapter.js +9 -1
- package/dist/backend/routing/proxy/google-adapter.js +14 -9
- package/dist/backend/routing/proxy/proxy-message-recorder.js +6 -5
- package/dist/backend/routing/proxy/proxy-response-handler.js +24 -4
- package/dist/backend/routing/proxy/proxy.controller.js +19 -6
- package/dist/backend/routing/routing-core/provider.service.js +6 -5
- package/dist/index.js +1 -1
- package/dist/openclaw.plugin.json +2 -2
- package/openclaw.plugin.json +2 -2
- package/package.json +1 -1
- package/public/assets/Account-Bmij8ZrS.js +1 -0
- package/public/assets/Limits-CJay2wDy.js +1 -0
- package/public/assets/Login-B6PWrEGq.js +1 -0
- package/public/assets/MessageLog-Cb_ucG8H.js +1 -0
- package/public/assets/ModelPrices-CK2tG0zg.js +1 -0
- package/public/assets/Overview-DY7f_oAi.js +1 -0
- package/public/assets/{Register-DdOFjnan.js → Register-CbGDtQku.js} +1 -1
- package/public/assets/{ResetPassword-BeAVlNfR.js → ResetPassword-DFQ1lTig.js} +1 -1
- package/public/assets/Routing-Kn93Tqlz.js +6 -0
- package/public/assets/Settings-BZw5xW8c.js +1 -0
- package/public/assets/{SocialButtons-CeoNbddT.js → SocialButtons-DEIg2R3v.js} +1 -1
- package/public/assets/index-BCCvBxnZ.js +2 -0
- package/public/assets/model-display-C23X2Y_y.js +1 -0
- package/public/assets/overview-DGoLsOFJ.js +1 -0
- package/public/assets/routing-C26EmiVE.js +1 -0
- package/public/assets/routing-utils-DGg0JMgE.js +1 -0
- package/public/index.html +1 -1
- package/public/assets/Account-CYm-TXzW.js +0 -1
- package/public/assets/Limits-COjHmsgO.js +0 -1
- package/public/assets/Login-BKfeXNh1.js +0 -1
- package/public/assets/MessageLog-ByDZNXuf.js +0 -1
- package/public/assets/ModelPrices-CkDhB6td.js +0 -1
- package/public/assets/Overview-i14orjyX.js +0 -1
- package/public/assets/ProviderIcon-C4FJfEjA.js +0 -1
- package/public/assets/Routing-DuIyJZaX.js +0 -6
- package/public/assets/Settings-0NuwmyZQ.js +0 -1
- package/public/assets/index-gTrwEb5v.js +0 -2
- package/public/assets/model-display-BM7OhbwK.js +0 -1
- package/public/assets/overview-DqdTBNGG.js +0 -1
package/README.md
CHANGED
|
@@ -97,6 +97,7 @@ Works with 300+ models across these providers:
|
|
|
97
97
|
| [Z.ai (Zhipu)](https://z.ai/) | `glm-5`, `glm-4.7`, `glm-4.5` + 5 more |
|
|
98
98
|
| [OpenRouter](https://openrouter.ai/) | 300+ models from all providers |
|
|
99
99
|
| [Ollama](https://ollama.com/) | Run any model locally (Llama, Gemma, Mistral, ...) |
|
|
100
|
+
| Custom providers | Any provider with an OpenAI-compatible API endpoint |
|
|
100
101
|
|
|
101
102
|
## Contributing
|
|
102
103
|
|
|
@@ -22,6 +22,7 @@ const query_helpers_1 = require("./query-helpers");
|
|
|
22
22
|
const tenant_cache_service_1 = require("../../common/services/tenant-cache.service");
|
|
23
23
|
const sql_dialect_1 = require("../../common/utils/sql-dialect");
|
|
24
24
|
const provider_inference_1 = require("../../common/utils/provider-inference");
|
|
25
|
+
const ttl_cache_1 = require("../../common/utils/ttl-cache");
|
|
25
26
|
const MODELS_CACHE_TTL_MS = 60_000;
|
|
26
27
|
const COUNT_CACHE_TTL_MS = 30_000;
|
|
27
28
|
const MAX_CACHE_ENTRIES = 5_000;
|
|
@@ -30,8 +31,14 @@ let MessagesQueryService = class MessagesQueryService {
|
|
|
30
31
|
dataSource;
|
|
31
32
|
tenantCache;
|
|
32
33
|
dialect;
|
|
33
|
-
modelsCache = new
|
|
34
|
-
|
|
34
|
+
modelsCache = new ttl_cache_1.TtlCache({
|
|
35
|
+
maxSize: MAX_CACHE_ENTRIES,
|
|
36
|
+
ttlMs: MODELS_CACHE_TTL_MS,
|
|
37
|
+
});
|
|
38
|
+
countCache = new ttl_cache_1.TtlCache({
|
|
39
|
+
maxSize: MAX_CACHE_ENTRIES,
|
|
40
|
+
ttlMs: COUNT_CACHE_TTL_MS,
|
|
41
|
+
});
|
|
35
42
|
constructor(turnRepo, dataSource, tenantCache) {
|
|
36
43
|
this.turnRepo = turnRepo;
|
|
37
44
|
this.dataSource = dataSource;
|
|
@@ -104,7 +111,7 @@ let MessagesQueryService = class MessagesQueryService {
|
|
|
104
111
|
}
|
|
105
112
|
}
|
|
106
113
|
const cachedCount = params.cursor ? this.countCache.get(countCacheKey) : undefined;
|
|
107
|
-
const countHit = cachedCount
|
|
114
|
+
const countHit = cachedCount !== undefined;
|
|
108
115
|
const [countResult, rows, allModels] = await Promise.all([
|
|
109
116
|
countHit ? null : countQb.getRawOne(),
|
|
110
117
|
dataQb
|
|
@@ -116,11 +123,11 @@ let MessagesQueryService = class MessagesQueryService {
|
|
|
116
123
|
]);
|
|
117
124
|
let totalCount;
|
|
118
125
|
if (countHit) {
|
|
119
|
-
totalCount = cachedCount
|
|
126
|
+
totalCount = cachedCount;
|
|
120
127
|
}
|
|
121
128
|
else {
|
|
122
129
|
totalCount = Number(countResult?.total ?? 0);
|
|
123
|
-
this.
|
|
130
|
+
this.countCache.set(countCacheKey, totalCount);
|
|
124
131
|
}
|
|
125
132
|
const hasMore = rows.length > params.limit;
|
|
126
133
|
const items = rows.slice(0, params.limit);
|
|
@@ -149,11 +156,8 @@ let MessagesQueryService = class MessagesQueryService {
|
|
|
149
156
|
async getDistinctModels(userId, range, tenantId, agentName) {
|
|
150
157
|
const cacheKey = `${userId}:${agentName ?? ''}:${range ?? 'all'}`;
|
|
151
158
|
const cached = this.modelsCache.get(cacheKey);
|
|
152
|
-
if (cached && cached.expiresAt > Date.now()) {
|
|
153
|
-
return cached.models;
|
|
154
|
-
}
|
|
155
159
|
if (cached)
|
|
156
|
-
|
|
160
|
+
return cached;
|
|
157
161
|
const cutoff = range ? (0, sql_dialect_1.computeCutoff)((0, range_util_1.rangeToInterval)(range)) : undefined;
|
|
158
162
|
const modelsQb = this.turnRepo
|
|
159
163
|
.createQueryBuilder('at')
|
|
@@ -166,12 +170,7 @@ let MessagesQueryService = class MessagesQueryService {
|
|
|
166
170
|
(0, query_helpers_1.addTenantFilter)(modelsQb, userId, agentName, tenantId);
|
|
167
171
|
const modelsResult = await modelsQb.orderBy('at.model', 'ASC').getRawMany();
|
|
168
172
|
const models = modelsResult.map((r) => String(r['model']));
|
|
169
|
-
|
|
170
|
-
const firstKey = this.modelsCache.keys().next().value;
|
|
171
|
-
if (firstKey !== undefined)
|
|
172
|
-
this.modelsCache.delete(firstKey);
|
|
173
|
-
}
|
|
174
|
-
this.modelsCache.set(cacheKey, { models, expiresAt: Date.now() + MODELS_CACHE_TTL_MS });
|
|
173
|
+
this.modelsCache.set(cacheKey, models);
|
|
175
174
|
return models;
|
|
176
175
|
}
|
|
177
176
|
buildCountCacheKey(params) {
|
|
@@ -185,14 +184,6 @@ let MessagesQueryService = class MessagesQueryService {
|
|
|
185
184
|
params.cost_max ?? '',
|
|
186
185
|
].join(':');
|
|
187
186
|
}
|
|
188
|
-
setCountCache(key, count) {
|
|
189
|
-
if (this.countCache.size >= MAX_CACHE_ENTRIES && !this.countCache.has(key)) {
|
|
190
|
-
const firstKey = this.countCache.keys().next().value;
|
|
191
|
-
if (firstKey !== undefined)
|
|
192
|
-
this.countCache.delete(firstKey);
|
|
193
|
-
}
|
|
194
|
-
this.countCache.set(key, { count, expiresAt: Date.now() + COUNT_CACHE_TTL_MS });
|
|
195
|
-
}
|
|
196
187
|
};
|
|
197
188
|
exports.MessagesQueryService = MessagesQueryService;
|
|
198
189
|
exports.MessagesQueryService = MessagesQueryService = __decorate([
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TtlCache = void 0;
|
|
4
|
+
class TtlCache {
|
|
5
|
+
store = new Map();
|
|
6
|
+
maxSize;
|
|
7
|
+
ttlMs;
|
|
8
|
+
constructor(options) {
|
|
9
|
+
this.maxSize = options.maxSize;
|
|
10
|
+
this.ttlMs = options.ttlMs;
|
|
11
|
+
}
|
|
12
|
+
get(key) {
|
|
13
|
+
const entry = this.store.get(key);
|
|
14
|
+
if (!entry)
|
|
15
|
+
return undefined;
|
|
16
|
+
if (entry.expiresAt <= Date.now()) {
|
|
17
|
+
this.store.delete(key);
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
return entry.value;
|
|
21
|
+
}
|
|
22
|
+
set(key, value) {
|
|
23
|
+
if (this.store.size >= this.maxSize && !this.store.has(key)) {
|
|
24
|
+
const firstKey = this.store.keys().next().value;
|
|
25
|
+
if (firstKey !== undefined)
|
|
26
|
+
this.store.delete(firstKey);
|
|
27
|
+
}
|
|
28
|
+
this.store.set(key, { value, expiresAt: Date.now() + this.ttlMs });
|
|
29
|
+
}
|
|
30
|
+
delete(key) {
|
|
31
|
+
return this.store.delete(key);
|
|
32
|
+
}
|
|
33
|
+
has(key) {
|
|
34
|
+
const entry = this.store.get(key);
|
|
35
|
+
if (!entry)
|
|
36
|
+
return false;
|
|
37
|
+
if (entry.expiresAt <= Date.now()) {
|
|
38
|
+
this.store.delete(key);
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
clear() {
|
|
44
|
+
this.store.clear();
|
|
45
|
+
}
|
|
46
|
+
get size() {
|
|
47
|
+
return this.store.size;
|
|
48
|
+
}
|
|
49
|
+
evictExpired() {
|
|
50
|
+
const now = Date.now();
|
|
51
|
+
for (const [key, entry] of this.store) {
|
|
52
|
+
if (entry.expiresAt <= now) {
|
|
53
|
+
this.store.delete(key);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
exports.TtlCache = TtlCache;
|
|
59
|
+
//# sourceMappingURL=ttl-cache.js.map
|
package/dist/backend/main.js
CHANGED
|
@@ -102,8 +102,8 @@ async function bootstrap() {
|
|
|
102
102
|
});
|
|
103
103
|
}
|
|
104
104
|
else {
|
|
105
|
-
const rateLimit = require('express-rate-limit');
|
|
106
|
-
const loginLimiter = rateLimit
|
|
105
|
+
const { default: rateLimit } = await Promise.resolve().then(() => __importStar(require('express-rate-limit')));
|
|
106
|
+
const loginLimiter = rateLimit({
|
|
107
107
|
windowMs: 15 * 60 * 1000,
|
|
108
108
|
max: 20,
|
|
109
109
|
standardHeaders: true,
|
|
@@ -111,7 +111,7 @@ async function bootstrap() {
|
|
|
111
111
|
message: { error: 'Too many login attempts. Try again later.' },
|
|
112
112
|
});
|
|
113
113
|
expressApp.use('/api/auth/sign-in', loginLimiter);
|
|
114
|
-
const { toNodeHandler } = require('better-auth/node');
|
|
114
|
+
const { toNodeHandler } = await Promise.resolve().then(() => __importStar(require('better-auth/node')));
|
|
115
115
|
expressApp.all('/api/auth/*splat', toNodeHandler(auth_instance_1.auth));
|
|
116
116
|
}
|
|
117
117
|
expressApp.use(express.json({ limit: '1mb' }));
|
|
@@ -17,57 +17,47 @@ const DEFAULT_CONTEXT_WINDOW = 128000;
|
|
|
17
17
|
const ANTHROPIC_DEFAULT_CONTEXT = 200000;
|
|
18
18
|
const GEMINI_DEFAULT_CONTEXT = 1000000;
|
|
19
19
|
const MINIMAX_SUBSCRIPTION_MODELS_URL = 'https://api.minimax.io/anthropic/v1/models?limit=100';
|
|
20
|
-
function
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
20
|
+
function createModelParser(config) {
|
|
21
|
+
return (body, provider) => {
|
|
22
|
+
const arr = body?.[config.arrayKey];
|
|
23
|
+
if (!Array.isArray(arr))
|
|
24
|
+
return [];
|
|
25
|
+
return arr
|
|
26
|
+
.filter((m) => config.filter(m))
|
|
27
|
+
.map((m) => {
|
|
28
|
+
const entry = m;
|
|
29
|
+
const id = config.getId(entry);
|
|
30
|
+
const ctxVal = config.contextWindow ?? DEFAULT_CONTEXT_WINDOW;
|
|
31
|
+
return {
|
|
32
|
+
id,
|
|
33
|
+
displayName: config.getDisplayName(entry, id),
|
|
34
|
+
provider,
|
|
35
|
+
contextWindow: typeof ctxVal === 'function' ? ctxVal(entry) : ctxVal,
|
|
36
|
+
inputPricePerToken: config.inputPricePerToken ?? null,
|
|
37
|
+
outputPricePerToken: config.outputPricePerToken ?? null,
|
|
38
|
+
capabilityReasoning: false,
|
|
39
|
+
capabilityCode: config.capabilityCode ?? false,
|
|
40
|
+
qualityScore: config.qualityScore ?? 3,
|
|
41
|
+
};
|
|
42
|
+
});
|
|
43
|
+
};
|
|
43
44
|
}
|
|
45
|
+
const parseOpenAI = createModelParser({
|
|
46
|
+
arrayKey: 'data',
|
|
47
|
+
filter: (entry) => typeof entry.id === 'string' && entry.id.length > 0,
|
|
48
|
+
getId: (entry) => entry.id,
|
|
49
|
+
getDisplayName: (_entry, id) => id,
|
|
50
|
+
});
|
|
44
51
|
function bearerHeaders(key) {
|
|
45
52
|
return { Authorization: `Bearer ${key}` };
|
|
46
53
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
return typeof entry.id === 'string' && entry.type === 'model';
|
|
55
|
-
})
|
|
56
|
-
.map((m) => {
|
|
57
|
-
const entry = m;
|
|
58
|
-
return {
|
|
59
|
-
id: entry.id,
|
|
60
|
-
displayName: entry.display_name || entry.id,
|
|
61
|
-
provider,
|
|
62
|
-
contextWindow: ANTHROPIC_DEFAULT_CONTEXT,
|
|
63
|
-
inputPricePerToken: null,
|
|
64
|
-
outputPricePerToken: null,
|
|
65
|
-
capabilityReasoning: false,
|
|
66
|
-
capabilityCode: false,
|
|
67
|
-
qualityScore: 3,
|
|
68
|
-
};
|
|
69
|
-
});
|
|
70
|
-
}
|
|
54
|
+
const parseAnthropic = createModelParser({
|
|
55
|
+
arrayKey: 'data',
|
|
56
|
+
filter: (entry) => typeof entry.id === 'string' && entry.type === 'model',
|
|
57
|
+
getId: (entry) => entry.id,
|
|
58
|
+
getDisplayName: (entry, id) => entry.display_name || id,
|
|
59
|
+
contextWindow: ANTHROPIC_DEFAULT_CONTEXT,
|
|
60
|
+
});
|
|
71
61
|
function parseGemini(body, provider) {
|
|
72
62
|
const models = body?.models;
|
|
73
63
|
if (!Array.isArray(models))
|
|
@@ -128,76 +118,33 @@ function parseOpenRouter(body, provider) {
|
|
|
128
118
|
};
|
|
129
119
|
});
|
|
130
120
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
.filter((m) => {
|
|
159
|
-
const entry = m;
|
|
160
|
-
return typeof entry.slug === 'string' && entry.visibility === 'list';
|
|
161
|
-
})
|
|
162
|
-
.map((m) => {
|
|
163
|
-
const entry = m;
|
|
164
|
-
return {
|
|
165
|
-
id: entry.slug,
|
|
166
|
-
displayName: entry.display_name || entry.slug,
|
|
167
|
-
provider,
|
|
168
|
-
contextWindow: entry.context_window ?? 200000,
|
|
169
|
-
inputPricePerToken: 0,
|
|
170
|
-
outputPricePerToken: 0,
|
|
171
|
-
capabilityReasoning: false,
|
|
172
|
-
capabilityCode: true,
|
|
173
|
-
qualityScore: 3,
|
|
174
|
-
};
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
function parseCopilot(body, provider) {
|
|
178
|
-
const data = body?.data;
|
|
179
|
-
if (!Array.isArray(data))
|
|
180
|
-
return [];
|
|
181
|
-
return data
|
|
182
|
-
.filter((m) => {
|
|
183
|
-
const entry = m;
|
|
184
|
-
return typeof entry.id === 'string' && entry.id.length > 0;
|
|
185
|
-
})
|
|
186
|
-
.map((m) => {
|
|
187
|
-
const entry = m;
|
|
188
|
-
return {
|
|
189
|
-
id: `copilot/${entry.id}`,
|
|
190
|
-
displayName: entry.id,
|
|
191
|
-
provider,
|
|
192
|
-
contextWindow: DEFAULT_CONTEXT_WINDOW,
|
|
193
|
-
inputPricePerToken: 0,
|
|
194
|
-
outputPricePerToken: 0,
|
|
195
|
-
capabilityReasoning: false,
|
|
196
|
-
capabilityCode: false,
|
|
197
|
-
qualityScore: 3,
|
|
198
|
-
};
|
|
199
|
-
});
|
|
200
|
-
}
|
|
121
|
+
const parseOllama = createModelParser({
|
|
122
|
+
arrayKey: 'models',
|
|
123
|
+
filter: (entry) => typeof entry.name === 'string',
|
|
124
|
+
getId: (entry) => entry.name.replace(/:latest$/, ''),
|
|
125
|
+
getDisplayName: (_entry, id) => id,
|
|
126
|
+
inputPricePerToken: 0,
|
|
127
|
+
outputPricePerToken: 0,
|
|
128
|
+
qualityScore: 2,
|
|
129
|
+
});
|
|
130
|
+
const parseOpenaiSubscription = createModelParser({
|
|
131
|
+
arrayKey: 'models',
|
|
132
|
+
filter: (entry) => typeof entry.slug === 'string' && entry.visibility === 'list',
|
|
133
|
+
getId: (entry) => entry.slug,
|
|
134
|
+
getDisplayName: (entry, id) => entry.display_name || id,
|
|
135
|
+
contextWindow: (entry) => entry.context_window ?? 200000,
|
|
136
|
+
inputPricePerToken: 0,
|
|
137
|
+
outputPricePerToken: 0,
|
|
138
|
+
capabilityCode: true,
|
|
139
|
+
});
|
|
140
|
+
const parseCopilot = createModelParser({
|
|
141
|
+
arrayKey: 'data',
|
|
142
|
+
filter: (entry) => typeof entry.id === 'string' && entry.id.length > 0,
|
|
143
|
+
getId: (entry) => `copilot/${entry.id}`,
|
|
144
|
+
getDisplayName: (entry) => entry.id,
|
|
145
|
+
inputPricePerToken: 0,
|
|
146
|
+
outputPricePerToken: 0,
|
|
147
|
+
});
|
|
201
148
|
exports.PROVIDER_CONFIGS = {
|
|
202
149
|
openai: {
|
|
203
150
|
endpoint: 'https://api.openai.com/v1/models',
|
|
@@ -13,6 +13,7 @@ const notification_rules_service_1 = require("./services/notification-rules.serv
|
|
|
13
13
|
const notification_cron_service_1 = require("./services/notification-cron.service");
|
|
14
14
|
const notification_email_service_1 = require("./services/notification-email.service");
|
|
15
15
|
const email_provider_config_service_1 = require("./services/email-provider-config.service");
|
|
16
|
+
const notification_log_service_1 = require("./services/notification-log.service");
|
|
16
17
|
const limit_check_service_1 = require("./services/limit-check.service");
|
|
17
18
|
let NotificationsModule = class NotificationsModule {
|
|
18
19
|
};
|
|
@@ -25,6 +26,7 @@ exports.NotificationsModule = NotificationsModule = __decorate([
|
|
|
25
26
|
notification_cron_service_1.NotificationCronService,
|
|
26
27
|
notification_email_service_1.NotificationEmailService,
|
|
27
28
|
email_provider_config_service_1.EmailProviderConfigService,
|
|
29
|
+
notification_log_service_1.NotificationLogService,
|
|
28
30
|
limit_check_service_1.LimitCheckService,
|
|
29
31
|
],
|
|
30
32
|
exports: [limit_check_service_1.LimitCheckService],
|
|
@@ -12,38 +12,33 @@ var LimitCheckService_1;
|
|
|
12
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
13
|
exports.LimitCheckService = void 0;
|
|
14
14
|
const common_1 = require("@nestjs/common");
|
|
15
|
-
const typeorm_1 = require("typeorm");
|
|
16
|
-
const uuid_1 = require("uuid");
|
|
17
15
|
const notification_rules_service_1 = require("./notification-rules.service");
|
|
18
16
|
const notification_email_service_1 = require("./notification-email.service");
|
|
19
17
|
const email_provider_config_service_1 = require("./email-provider-config.service");
|
|
18
|
+
const notification_log_service_1 = require("./notification-log.service");
|
|
20
19
|
const ingest_event_bus_service_1 = require("../../common/services/ingest-event-bus.service");
|
|
21
20
|
const manifest_runtime_service_1 = require("../../common/services/manifest-runtime.service");
|
|
22
21
|
const period_util_1 = require("../../common/utils/period.util");
|
|
23
|
-
const sql_dialect_1 = require("../../common/utils/sql-dialect");
|
|
24
|
-
const local_mode_constants_1 = require("../../common/constants/local-mode.constants");
|
|
25
22
|
const CACHE_TTL_MS = 60_000;
|
|
26
23
|
const MAX_CACHE_SIZE = 10_000;
|
|
27
24
|
let LimitCheckService = LimitCheckService_1 = class LimitCheckService {
|
|
28
|
-
ds;
|
|
29
25
|
rulesService;
|
|
30
26
|
emailService;
|
|
31
27
|
emailProviderConfig;
|
|
32
28
|
ingestBus;
|
|
33
29
|
runtime;
|
|
30
|
+
notificationLog;
|
|
34
31
|
logger = new common_1.Logger(LimitCheckService_1.name);
|
|
35
|
-
dialect;
|
|
36
32
|
rulesCache = new Map();
|
|
37
33
|
consumptionCache = new Map();
|
|
38
34
|
ingestSub;
|
|
39
|
-
constructor(
|
|
40
|
-
this.ds = ds;
|
|
35
|
+
constructor(rulesService, emailService, emailProviderConfig, ingestBus, runtime, notificationLog) {
|
|
41
36
|
this.rulesService = rulesService;
|
|
42
37
|
this.emailService = emailService;
|
|
43
38
|
this.emailProviderConfig = emailProviderConfig;
|
|
44
39
|
this.ingestBus = ingestBus;
|
|
45
40
|
this.runtime = runtime;
|
|
46
|
-
this.
|
|
41
|
+
this.notificationLog = notificationLog;
|
|
47
42
|
}
|
|
48
43
|
onModuleInit() {
|
|
49
44
|
this.ingestSub = this.ingestBus.all().subscribe(() => {
|
|
@@ -53,9 +48,6 @@ let LimitCheckService = LimitCheckService_1 = class LimitCheckService {
|
|
|
53
48
|
onModuleDestroy() {
|
|
54
49
|
this.ingestSub?.unsubscribe();
|
|
55
50
|
}
|
|
56
|
-
sql(query) {
|
|
57
|
-
return (0, sql_dialect_1.portableSql)(query, this.dialect);
|
|
58
|
-
}
|
|
59
51
|
async checkLimits(tenantId, agentName) {
|
|
60
52
|
const rules = await this.getCachedRules(tenantId, agentName);
|
|
61
53
|
if (rules.length === 0)
|
|
@@ -88,12 +80,11 @@ let LimitCheckService = LimitCheckService_1 = class LimitCheckService {
|
|
|
88
80
|
}
|
|
89
81
|
}
|
|
90
82
|
async notifyLimitExceeded(rule, actual, periodStart, periodEnd) {
|
|
91
|
-
|
|
92
|
-
if (alreadySent.length > 0)
|
|
83
|
+
if (await this.notificationLog.hasAlreadySent(rule.id, periodStart))
|
|
93
84
|
return;
|
|
94
|
-
const now =
|
|
85
|
+
const now = (0, notification_log_service_1.formatNotificationTimestamp)();
|
|
95
86
|
const providerConfig = await this.emailProviderConfig.getFullConfig(rule.user_id);
|
|
96
|
-
const email = await this.resolveUserEmail(rule.user_id, providerConfig);
|
|
87
|
+
const email = await this.notificationLog.resolveUserEmail(rule.user_id, providerConfig?.notificationEmail);
|
|
97
88
|
let emailSent = false;
|
|
98
89
|
if (email) {
|
|
99
90
|
emailSent = await this.emailService.sendThresholdAlert(email, {
|
|
@@ -109,34 +100,17 @@ let LimitCheckService = LimitCheckService_1 = class LimitCheckService {
|
|
|
109
100
|
}, providerConfig ?? undefined);
|
|
110
101
|
}
|
|
111
102
|
if (emailSent || !email) {
|
|
112
|
-
await this.
|
|
113
|
-
|
|
114
|
-
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`), [
|
|
115
|
-
(0, uuid_1.v4)(),
|
|
116
|
-
rule.id,
|
|
103
|
+
await this.notificationLog.insertLog({
|
|
104
|
+
ruleId: rule.id,
|
|
117
105
|
periodStart,
|
|
118
106
|
periodEnd,
|
|
119
|
-
actual,
|
|
120
|
-
rule.threshold,
|
|
121
|
-
rule.metric_type,
|
|
122
|
-
rule.agent_name,
|
|
123
|
-
now,
|
|
124
|
-
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
async resolveUserEmail(userId, fullConfig) {
|
|
128
|
-
if (fullConfig?.notificationEmail)
|
|
129
|
-
return fullConfig.notificationEmail;
|
|
130
|
-
if (this.runtime.isLocalMode()) {
|
|
131
|
-
const configEmail = (0, local_mode_constants_1.readLocalNotificationEmail)();
|
|
132
|
-
if (configEmail)
|
|
133
|
-
return configEmail;
|
|
107
|
+
actualValue: actual,
|
|
108
|
+
thresholdValue: rule.threshold,
|
|
109
|
+
metricType: rule.metric_type,
|
|
110
|
+
agentName: rule.agent_name,
|
|
111
|
+
sentAt: now,
|
|
112
|
+
});
|
|
134
113
|
}
|
|
135
|
-
const rows = await this.ds.query(this.sql(`SELECT email FROM "user" WHERE id = $1`), [userId]);
|
|
136
|
-
const email = rows[0]?.email ?? null;
|
|
137
|
-
if (email === local_mode_constants_1.LOCAL_EMAIL)
|
|
138
|
-
return null;
|
|
139
|
-
return email;
|
|
140
114
|
}
|
|
141
115
|
async getCachedRules(tenantId, agentName) {
|
|
142
116
|
const key = `${tenantId}:${agentName}`;
|
|
@@ -179,11 +153,11 @@ let LimitCheckService = LimitCheckService_1 = class LimitCheckService {
|
|
|
179
153
|
exports.LimitCheckService = LimitCheckService;
|
|
180
154
|
exports.LimitCheckService = LimitCheckService = LimitCheckService_1 = __decorate([
|
|
181
155
|
(0, common_1.Injectable)(),
|
|
182
|
-
__metadata("design:paramtypes", [
|
|
183
|
-
notification_rules_service_1.NotificationRulesService,
|
|
156
|
+
__metadata("design:paramtypes", [notification_rules_service_1.NotificationRulesService,
|
|
184
157
|
notification_email_service_1.NotificationEmailService,
|
|
185
158
|
email_provider_config_service_1.EmailProviderConfigService,
|
|
186
159
|
ingest_event_bus_service_1.IngestEventBusService,
|
|
187
|
-
manifest_runtime_service_1.ManifestRuntimeService
|
|
160
|
+
manifest_runtime_service_1.ManifestRuntimeService,
|
|
161
|
+
notification_log_service_1.NotificationLogService])
|
|
188
162
|
], LimitCheckService);
|
|
189
163
|
//# sourceMappingURL=limit-check.service.js.map
|
|
@@ -13,30 +13,25 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
13
13
|
exports.NotificationCronService = void 0;
|
|
14
14
|
const common_1 = require("@nestjs/common");
|
|
15
15
|
const schedule_1 = require("@nestjs/schedule");
|
|
16
|
-
const typeorm_1 = require("typeorm");
|
|
17
|
-
const uuid_1 = require("uuid");
|
|
18
16
|
const notification_rules_service_1 = require("./notification-rules.service");
|
|
19
17
|
const notification_email_service_1 = require("./notification-email.service");
|
|
20
18
|
const email_provider_config_service_1 = require("./email-provider-config.service");
|
|
19
|
+
const notification_log_service_1 = require("./notification-log.service");
|
|
21
20
|
const manifest_runtime_service_1 = require("../../common/services/manifest-runtime.service");
|
|
22
|
-
const sql_dialect_1 = require("../../common/utils/sql-dialect");
|
|
23
21
|
const period_util_1 = require("../../common/utils/period.util");
|
|
24
|
-
const local_mode_constants_1 = require("../../common/constants/local-mode.constants");
|
|
25
22
|
let NotificationCronService = NotificationCronService_1 = class NotificationCronService {
|
|
26
|
-
ds;
|
|
27
23
|
rulesService;
|
|
28
24
|
emailService;
|
|
29
25
|
emailProviderConfigService;
|
|
30
26
|
runtime;
|
|
27
|
+
notificationLog;
|
|
31
28
|
logger = new common_1.Logger(NotificationCronService_1.name);
|
|
32
|
-
|
|
33
|
-
constructor(ds, rulesService, emailService, emailProviderConfigService, runtime) {
|
|
34
|
-
this.ds = ds;
|
|
29
|
+
constructor(rulesService, emailService, emailProviderConfigService, runtime, notificationLog) {
|
|
35
30
|
this.rulesService = rulesService;
|
|
36
31
|
this.emailService = emailService;
|
|
37
32
|
this.emailProviderConfigService = emailProviderConfigService;
|
|
38
33
|
this.runtime = runtime;
|
|
39
|
-
this.
|
|
34
|
+
this.notificationLog = notificationLog;
|
|
40
35
|
}
|
|
41
36
|
async onModuleInit() {
|
|
42
37
|
try {
|
|
@@ -49,9 +44,6 @@ let NotificationCronService = NotificationCronService_1 = class NotificationCron
|
|
|
49
44
|
this.logger.error(`Startup catch-up failed: ${err}`);
|
|
50
45
|
}
|
|
51
46
|
}
|
|
52
|
-
sql(query) {
|
|
53
|
-
return (0, sql_dialect_1.portableSql)(query, this.dialect);
|
|
54
|
-
}
|
|
55
47
|
async checkThresholds(userId) {
|
|
56
48
|
const rules = userId
|
|
57
49
|
? await this.rulesService.getActiveRulesForUser(userId)
|
|
@@ -103,16 +95,15 @@ let NotificationCronService = NotificationCronService_1 = class NotificationCron
|
|
|
103
95
|
}
|
|
104
96
|
async evaluateRule(rule, actual) {
|
|
105
97
|
const { periodStart, periodEnd } = (0, period_util_1.computePeriodBoundaries)(rule.period);
|
|
106
|
-
|
|
107
|
-
if (alreadySent.length > 0)
|
|
98
|
+
if (await this.notificationLog.hasAlreadySent(rule.id, periodStart))
|
|
108
99
|
return false;
|
|
109
100
|
if (actual < rule.threshold)
|
|
110
101
|
return false;
|
|
111
|
-
const now =
|
|
112
|
-
const
|
|
102
|
+
const now = (0, notification_log_service_1.formatNotificationTimestamp)();
|
|
103
|
+
const fullConfig = await this.emailProviderConfigService.getFullConfig(rule.user_id);
|
|
104
|
+
const email = await this.notificationLog.resolveUserEmail(rule.user_id, fullConfig?.notificationEmail);
|
|
113
105
|
let emailSent = false;
|
|
114
106
|
if (email) {
|
|
115
|
-
const providerConfig = await this.emailProviderConfigService.getFullConfig(rule.user_id);
|
|
116
107
|
emailSent = await this.emailService.sendThresholdAlert(email, {
|
|
117
108
|
agentName: rule.agent_name,
|
|
118
109
|
metricType: rule.metric_type,
|
|
@@ -122,46 +113,28 @@ let NotificationCronService = NotificationCronService_1 = class NotificationCron
|
|
|
122
113
|
timestamp: now,
|
|
123
114
|
agentUrl: `${this.runtime.getAuthBaseUrl()}/agents/${encodeURIComponent(rule.agent_name)}`,
|
|
124
115
|
alertType: 'soft',
|
|
125
|
-
},
|
|
116
|
+
}, fullConfig ?? undefined);
|
|
126
117
|
}
|
|
127
118
|
else {
|
|
128
119
|
this.logger.warn(`No email found for user ${rule.user_id}, skipping alert for rule ${rule.id}`);
|
|
129
120
|
}
|
|
130
121
|
if (emailSent || !email) {
|
|
131
|
-
await this.
|
|
132
|
-
|
|
133
|
-
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`), [
|
|
134
|
-
(0, uuid_1.v4)(),
|
|
135
|
-
rule.id,
|
|
122
|
+
await this.notificationLog.insertLog({
|
|
123
|
+
ruleId: rule.id,
|
|
136
124
|
periodStart,
|
|
137
125
|
periodEnd,
|
|
138
|
-
actual,
|
|
139
|
-
rule.threshold,
|
|
140
|
-
rule.metric_type,
|
|
141
|
-
rule.agent_name,
|
|
142
|
-
now,
|
|
143
|
-
|
|
126
|
+
actualValue: actual,
|
|
127
|
+
thresholdValue: rule.threshold,
|
|
128
|
+
metricType: rule.metric_type,
|
|
129
|
+
agentName: rule.agent_name,
|
|
130
|
+
sentAt: now,
|
|
131
|
+
});
|
|
144
132
|
}
|
|
145
133
|
else {
|
|
146
134
|
this.logger.warn(`Failed to send alert for rule ${rule.id}, will retry next cron run`);
|
|
147
135
|
}
|
|
148
136
|
return emailSent || !email;
|
|
149
137
|
}
|
|
150
|
-
async resolveUserEmail(userId) {
|
|
151
|
-
const fullConfig = await this.emailProviderConfigService.getFullConfig(userId);
|
|
152
|
-
if (fullConfig?.notificationEmail)
|
|
153
|
-
return fullConfig.notificationEmail;
|
|
154
|
-
if (this.runtime.isLocalMode()) {
|
|
155
|
-
const configEmail = (0, local_mode_constants_1.readLocalNotificationEmail)();
|
|
156
|
-
if (configEmail)
|
|
157
|
-
return configEmail;
|
|
158
|
-
}
|
|
159
|
-
const rows = await this.ds.query(this.sql(`SELECT email FROM "user" WHERE id = $1`), [userId]);
|
|
160
|
-
const email = rows[0]?.email ?? null;
|
|
161
|
-
if (email === local_mode_constants_1.LOCAL_EMAIL)
|
|
162
|
-
return null;
|
|
163
|
-
return email;
|
|
164
|
-
}
|
|
165
138
|
};
|
|
166
139
|
exports.NotificationCronService = NotificationCronService;
|
|
167
140
|
__decorate([
|
|
@@ -172,10 +145,10 @@ __decorate([
|
|
|
172
145
|
], NotificationCronService.prototype, "checkThresholds", null);
|
|
173
146
|
exports.NotificationCronService = NotificationCronService = NotificationCronService_1 = __decorate([
|
|
174
147
|
(0, common_1.Injectable)(),
|
|
175
|
-
__metadata("design:paramtypes", [
|
|
176
|
-
notification_rules_service_1.NotificationRulesService,
|
|
148
|
+
__metadata("design:paramtypes", [notification_rules_service_1.NotificationRulesService,
|
|
177
149
|
notification_email_service_1.NotificationEmailService,
|
|
178
150
|
email_provider_config_service_1.EmailProviderConfigService,
|
|
179
|
-
manifest_runtime_service_1.ManifestRuntimeService
|
|
151
|
+
manifest_runtime_service_1.ManifestRuntimeService,
|
|
152
|
+
notification_log_service_1.NotificationLogService])
|
|
180
153
|
], NotificationCronService);
|
|
181
154
|
//# sourceMappingURL=notification-cron.service.js.map
|