backend-manager 4.0.4 → 4.0.6
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "backend-manager",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.6",
|
|
4
4
|
"description": "Quick tools for developing Firebase functions",
|
|
5
5
|
"main": "src/manager/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -51,8 +51,8 @@
|
|
|
51
51
|
"chalk": "^4.1.2",
|
|
52
52
|
"cors": "^2.8.5",
|
|
53
53
|
"dotenv": "^16.4.5",
|
|
54
|
-
"express": "^4.21.
|
|
55
|
-
"firebase-admin": "^12.
|
|
54
|
+
"express": "^4.21.1",
|
|
55
|
+
"firebase-admin": "^12.6.0",
|
|
56
56
|
"firebase-functions": "^6.0.1",
|
|
57
57
|
"fs-jetpack": "^5.1.0",
|
|
58
58
|
"glob": "^11.0.0",
|
|
@@ -13,7 +13,10 @@ Module.prototype.main = function () {
|
|
|
13
13
|
|
|
14
14
|
return new Promise(async function(resolve, reject) {
|
|
15
15
|
// Load libraries
|
|
16
|
-
_ = Manager.require('lodash')
|
|
16
|
+
_ = Manager.require('lodash');
|
|
17
|
+
|
|
18
|
+
// Set defaults
|
|
19
|
+
payload.data.payload.update = payload.data.payload.update || false;
|
|
17
20
|
|
|
18
21
|
// Perform checks
|
|
19
22
|
if (!payload.user.roles.admin) {
|
|
@@ -29,7 +32,7 @@ Module.prototype.main = function () {
|
|
|
29
32
|
|
|
30
33
|
// Only update if requested
|
|
31
34
|
if (payload.data.payload.update) {
|
|
32
|
-
await self.updateStats(data)
|
|
35
|
+
await self.updateStats(data, payload.data.payload.update)
|
|
33
36
|
.catch(e => data = e)
|
|
34
37
|
}
|
|
35
38
|
|
|
@@ -52,7 +55,7 @@ Module.prototype.main = function () {
|
|
|
52
55
|
|
|
53
56
|
return resolve({data: data})
|
|
54
57
|
})
|
|
55
|
-
.catch(
|
|
58
|
+
.catch((e) => {
|
|
56
59
|
return reject(assistant.errorify(`Failed to get: ${e}`, {code: 500}));
|
|
57
60
|
})
|
|
58
61
|
});
|
|
@@ -72,7 +75,7 @@ Module.prototype.fixStats = function (data) {
|
|
|
72
75
|
}
|
|
73
76
|
|
|
74
77
|
// TODO: ADD https://firebase.google.com/docs/firestore/query-data/aggregation-queries#pricing
|
|
75
|
-
Module.prototype.updateStats = function (existingData) {
|
|
78
|
+
Module.prototype.updateStats = function (existingData, update) {
|
|
76
79
|
const self = this;
|
|
77
80
|
|
|
78
81
|
return new Promise(async function(resolve, reject) {
|
|
@@ -82,43 +85,57 @@ Module.prototype.updateStats = function (existingData) {
|
|
|
82
85
|
const sessionsOnline = self.libraries.admin.database().ref(`sessions/online`);
|
|
83
86
|
|
|
84
87
|
let error = null;
|
|
85
|
-
let
|
|
86
|
-
app:
|
|
88
|
+
let newData = {
|
|
89
|
+
app: self.Manager.config?.app?.id || null,
|
|
87
90
|
};
|
|
88
91
|
|
|
89
|
-
// Fix
|
|
90
|
-
if (
|
|
92
|
+
// Fix user stats
|
|
93
|
+
if (
|
|
94
|
+
!existingData?.users?.total
|
|
95
|
+
|| update === true
|
|
96
|
+
|| update?.users
|
|
97
|
+
) {
|
|
91
98
|
await self.getAllUsers()
|
|
92
99
|
.then(r => {
|
|
93
|
-
_.set(
|
|
100
|
+
_.set(newData, 'users.total', r.length)
|
|
94
101
|
})
|
|
95
102
|
.catch(e => {
|
|
96
103
|
error = new Error(`Failed fixing stats: ${e}`);
|
|
97
104
|
})
|
|
98
105
|
}
|
|
99
106
|
|
|
107
|
+
// Reject if error
|
|
100
108
|
if (error) {
|
|
101
109
|
return reject(error);
|
|
102
110
|
}
|
|
103
111
|
|
|
104
112
|
// Fetch new notification stats
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
113
|
+
if (
|
|
114
|
+
update === true || update?.notifications
|
|
115
|
+
) {
|
|
116
|
+
await self.getAllNotifications()
|
|
117
|
+
.then(r => {
|
|
118
|
+
_.set(newData, 'notifications.total', r)
|
|
119
|
+
})
|
|
120
|
+
.catch(e => {
|
|
121
|
+
error = new Error(`Failed getting notifications: ${e}`);
|
|
122
|
+
})
|
|
123
|
+
}
|
|
112
124
|
|
|
113
125
|
// Fetch new subscription stats
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
126
|
+
if (
|
|
127
|
+
update === true || update?.subscriptions
|
|
128
|
+
) {
|
|
129
|
+
await self.getAllSubscriptions()
|
|
130
|
+
.then(r => {
|
|
131
|
+
_.set(newData, 'subscriptions', r)
|
|
132
|
+
})
|
|
133
|
+
.catch(e => {
|
|
134
|
+
error = new Error(`Failed getting subscriptions: ${e}`);
|
|
135
|
+
})
|
|
136
|
+
}
|
|
121
137
|
|
|
138
|
+
// Reject if error
|
|
122
139
|
if (error) {
|
|
123
140
|
return reject(error);
|
|
124
141
|
}
|
|
@@ -129,37 +146,46 @@ Module.prototype.updateStats = function (existingData) {
|
|
|
129
146
|
.then((snap) => {
|
|
130
147
|
const data = snap.val() || {};
|
|
131
148
|
const keys = Object.keys(data);
|
|
132
|
-
const existing =
|
|
133
|
-
|
|
149
|
+
const existing = newData?.users?.online || 0;
|
|
150
|
+
|
|
151
|
+
// Set new value
|
|
152
|
+
_.set(newData, 'users.online', existing + keys.length)
|
|
134
153
|
})
|
|
135
154
|
.catch(e => {
|
|
136
155
|
error = new Error(`Failed getting online users: ${e}`);
|
|
137
156
|
})
|
|
138
157
|
}
|
|
139
158
|
|
|
140
|
-
//
|
|
141
|
-
|
|
159
|
+
// Fetch new user stats
|
|
160
|
+
if (
|
|
161
|
+
update === true || update?.online
|
|
162
|
+
) {
|
|
163
|
+
// Count users online (in old gathering)
|
|
164
|
+
await _countUsersOnline(gatheringOnline);
|
|
142
165
|
|
|
143
|
-
|
|
144
|
-
|
|
166
|
+
// Count users online (in new session)
|
|
167
|
+
await _countUsersOnline(sessionsApp);
|
|
145
168
|
|
|
146
|
-
|
|
147
|
-
|
|
169
|
+
// Count users online (in new session)
|
|
170
|
+
await _countUsersOnline(sessionsOnline);
|
|
171
|
+
}
|
|
148
172
|
|
|
173
|
+
// Reject if error
|
|
149
174
|
if (error) {
|
|
150
175
|
return reject(error);
|
|
151
176
|
}
|
|
152
177
|
|
|
153
178
|
// Set metadata
|
|
154
|
-
|
|
179
|
+
newData.metadata = self.Manager.Metadata().set({tag: 'admin:get-stats'})
|
|
155
180
|
|
|
156
|
-
//
|
|
181
|
+
// newData stats
|
|
157
182
|
await stats
|
|
158
|
-
.set(
|
|
159
|
-
.catch(
|
|
183
|
+
.set(newData, { merge: true })
|
|
184
|
+
.catch((e) => {
|
|
160
185
|
return reject(new Error(`Failed getting stats: ${e}`));
|
|
161
186
|
})
|
|
162
187
|
|
|
188
|
+
// Return
|
|
163
189
|
return resolve();
|
|
164
190
|
});
|
|
165
191
|
}
|
|
@@ -208,10 +234,10 @@ Module.prototype.getAllSubscriptions = function () {
|
|
|
208
234
|
snapshot
|
|
209
235
|
.forEach((doc, i) => {
|
|
210
236
|
const data = doc.data();
|
|
211
|
-
const planId =
|
|
212
|
-
const frequency =
|
|
213
|
-
const isAdmin =
|
|
214
|
-
const isVip =
|
|
237
|
+
const planId = data?.plan?.id || 'basic';
|
|
238
|
+
const frequency = data?.plan?.payment?.frequency || 'unknown';
|
|
239
|
+
const isAdmin = data?.roles?.admin || false;
|
|
240
|
+
const isVip = data?.roles?.vip || false;
|
|
215
241
|
|
|
216
242
|
if (!stats.plans[planId]) {
|
|
217
243
|
stats.plans[planId] = {
|
|
@@ -20,7 +20,7 @@ Module.prototype.main = function () {
|
|
|
20
20
|
return reject(assistant.errorify(`Admin required.`, {code: 401}));
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
const productId =
|
|
23
|
+
const productId = payload?.data?.payload?.payload?.details?.productIdGlobal;
|
|
24
24
|
if (!productId) {
|
|
25
25
|
return reject(assistant.errorify(`No productId`, {code: 400}));
|
|
26
26
|
}
|
|
@@ -224,36 +224,46 @@ OpenAI.prototype.request = function (options) {
|
|
|
224
224
|
const assistant = self.assistant;
|
|
225
225
|
|
|
226
226
|
return new Promise(async function(resolve, reject) {
|
|
227
|
+
// Deep merge options
|
|
227
228
|
options = _.merge({}, options);
|
|
228
229
|
|
|
230
|
+
// Set defaults
|
|
229
231
|
options.model = typeof options.model === 'undefined' ? DEFAULT_MODEL : options.model;
|
|
232
|
+
options.response = typeof options.response === 'undefined' ? undefined : options.response;
|
|
230
233
|
options.timeout = typeof options.timeout === 'undefined' ? 120000 : options.timeout;
|
|
231
234
|
options.moderate = typeof options.moderate === 'undefined' ? true : options.moderate;
|
|
232
235
|
options.log = typeof options.log === 'undefined' ? false : options.log;
|
|
233
236
|
options.user = options.user || assistant.getUser();
|
|
234
237
|
|
|
238
|
+
// Format retries
|
|
235
239
|
options.retries = typeof options.retries === 'undefined' ? 0 : options.retries;
|
|
236
240
|
options.retryTriggers = typeof options.retryTriggers === 'undefined' ? ['network', 'parse'] : options.retryTriggers;
|
|
237
241
|
|
|
242
|
+
// Format other options
|
|
243
|
+
options.temperature = typeof options.temperature === 'undefined' ? 0.7 : options.temperature;
|
|
244
|
+
options.maxTokens = typeof options.maxTokens === 'undefined' ? 512 : options.maxTokens;
|
|
245
|
+
|
|
246
|
+
// Custom options
|
|
247
|
+
options.dedupeConsecutiveRoles = typeof options.dedupeConsecutiveRoles === 'undefined' ? true : options.dedupeConsecutiveRoles;
|
|
248
|
+
|
|
249
|
+
// Format prompt
|
|
238
250
|
options.prompt = options.prompt || {};
|
|
239
251
|
options.prompt.path = options.prompt.path || '';
|
|
240
|
-
options.prompt.
|
|
252
|
+
options.prompt.content = options.prompt.content || options.prompt.content || '';
|
|
241
253
|
options.prompt.settings = options.prompt.settings || {};
|
|
242
254
|
|
|
255
|
+
// Format message
|
|
243
256
|
options.message = options.message || {};
|
|
244
257
|
options.message.path = options.message.path || '';
|
|
245
|
-
options.message.
|
|
258
|
+
options.message.content = options.message.content || options.message.content || '';
|
|
246
259
|
options.message.settings = options.message.settings || {};
|
|
247
260
|
options.message.images = options.message.images || [];
|
|
248
261
|
|
|
262
|
+
// Format history
|
|
249
263
|
options.history = options.history || {};
|
|
250
264
|
options.history.messages = options.history.messages || [];
|
|
251
265
|
options.history.limit = typeof options.history.limit === 'undefined' ? 5 : options.history.limit;
|
|
252
266
|
|
|
253
|
-
options.response = typeof options.response === 'undefined' ? undefined : options.response;
|
|
254
|
-
options.temperature = typeof options.temperature === 'undefined' ? 0.7 : options.temperature;
|
|
255
|
-
options.maxTokens = typeof options.maxTokens === 'undefined' ? 512 : options.maxTokens;
|
|
256
|
-
|
|
257
267
|
let attempt = 0;
|
|
258
268
|
|
|
259
269
|
function _log() {
|
|
@@ -269,9 +279,9 @@ OpenAI.prototype.request = function (options) {
|
|
|
269
279
|
// console.log('*** input.content', input.content.slice(0, 50));
|
|
270
280
|
// console.log('*** input.path', input.path);
|
|
271
281
|
|
|
272
|
-
let
|
|
282
|
+
let content = '';
|
|
273
283
|
|
|
274
|
-
// Load
|
|
284
|
+
// Load content
|
|
275
285
|
if (input.path) {
|
|
276
286
|
const exists = jetpack.exists(input.path);
|
|
277
287
|
|
|
@@ -284,15 +294,15 @@ OpenAI.prototype.request = function (options) {
|
|
|
284
294
|
}
|
|
285
295
|
|
|
286
296
|
try {
|
|
287
|
-
|
|
297
|
+
content = jetpack.read(input.path);
|
|
288
298
|
} catch (e) {
|
|
289
299
|
return new Error(`Error reading file ${input.path}: ${e}`);
|
|
290
300
|
}
|
|
291
301
|
} else {
|
|
292
|
-
|
|
302
|
+
content = input.content;
|
|
293
303
|
}
|
|
294
304
|
|
|
295
|
-
return powertools.template(
|
|
305
|
+
return powertools.template(content, input.settings).trim();
|
|
296
306
|
}
|
|
297
307
|
|
|
298
308
|
// Log
|
|
@@ -345,51 +355,63 @@ OpenAI.prototype.request = function (options) {
|
|
|
345
355
|
body: {},
|
|
346
356
|
}
|
|
347
357
|
|
|
358
|
+
// Format depending on mode
|
|
348
359
|
if (mode === 'chatgpt') {
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
// Get history
|
|
360
|
+
// Get history with respect to the message limit
|
|
352
361
|
const history = options.history.messages.slice(-options.history.limit);
|
|
353
362
|
|
|
354
|
-
// Add prompt to history
|
|
363
|
+
// Add prompt to beginning of history
|
|
355
364
|
history.unshift({
|
|
356
365
|
role: 'system',
|
|
357
|
-
|
|
366
|
+
content: prompt,
|
|
358
367
|
images: [],
|
|
359
368
|
});
|
|
360
369
|
|
|
361
|
-
//
|
|
370
|
+
// Get last history item
|
|
362
371
|
const lastHistory = history[history.length - 1];
|
|
363
372
|
|
|
364
|
-
//
|
|
365
|
-
if (
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
});
|
|
373
|
+
// Remove last message from history
|
|
374
|
+
if (
|
|
375
|
+
options.dedupeConsecutiveRoles
|
|
376
|
+
&& lastHistory?.role === 'user'
|
|
377
|
+
) {
|
|
378
|
+
history.pop();
|
|
371
379
|
}
|
|
372
380
|
|
|
381
|
+
// Add message to history
|
|
382
|
+
history.push({
|
|
383
|
+
role: 'user',
|
|
384
|
+
content: message,
|
|
385
|
+
images: options.message.images,
|
|
386
|
+
});
|
|
387
|
+
|
|
373
388
|
// Format history
|
|
374
389
|
history.map((m) => {
|
|
375
|
-
|
|
390
|
+
const originalContent = m.content;
|
|
391
|
+
const originalImages = m.images;
|
|
376
392
|
|
|
393
|
+
// Set properties
|
|
394
|
+
m.role = m.role || 'system';
|
|
377
395
|
m.content = [];
|
|
396
|
+
m.images = [];
|
|
378
397
|
|
|
379
|
-
//
|
|
380
|
-
if (
|
|
398
|
+
// Format content
|
|
399
|
+
if (originalContent) {
|
|
381
400
|
m.content.push({
|
|
382
401
|
type: 'text',
|
|
383
|
-
text:
|
|
402
|
+
text: originalContent,
|
|
384
403
|
})
|
|
385
404
|
}
|
|
386
405
|
|
|
387
|
-
//
|
|
388
|
-
|
|
406
|
+
// Format images
|
|
407
|
+
if (originalImages) {
|
|
408
|
+
originalImages.forEach((i) => {
|
|
409
|
+
// Skip if no URL
|
|
410
|
+
if (!i.url) {
|
|
411
|
+
return
|
|
412
|
+
}
|
|
389
413
|
|
|
390
|
-
|
|
391
|
-
m.images.forEach((i) => {
|
|
392
|
-
if (i.url) {
|
|
414
|
+
// Add image
|
|
393
415
|
m.content.push({
|
|
394
416
|
type: 'image_url',
|
|
395
417
|
image_url: {
|
|
@@ -397,12 +419,15 @@ OpenAI.prototype.request = function (options) {
|
|
|
397
419
|
detail: i.detail || 'low',
|
|
398
420
|
}
|
|
399
421
|
});
|
|
400
|
-
}
|
|
401
|
-
}
|
|
422
|
+
});
|
|
423
|
+
}
|
|
402
424
|
|
|
403
|
-
// Delete
|
|
404
|
-
|
|
405
|
-
|
|
425
|
+
// Delete any field except for role, content, images
|
|
426
|
+
Object.keys(m).forEach((key) => {
|
|
427
|
+
if (!['role', 'content', 'images'].includes(key)) {
|
|
428
|
+
delete m[key];
|
|
429
|
+
}
|
|
430
|
+
});
|
|
406
431
|
})
|
|
407
432
|
|
|
408
433
|
// Log message
|
|
@@ -410,16 +435,20 @@ OpenAI.prototype.request = function (options) {
|
|
|
410
435
|
_log('Message', m.role, m.content);
|
|
411
436
|
});
|
|
412
437
|
|
|
438
|
+
// Set request
|
|
439
|
+
request.url = 'https://api.openai.com/v1/chat/completions';
|
|
413
440
|
request.body = {
|
|
414
441
|
model: options.model,
|
|
415
442
|
response_format: responseFormat,
|
|
416
443
|
messages: history,
|
|
417
444
|
temperature: options.temperature,
|
|
418
|
-
max_tokens: options.maxTokens,
|
|
445
|
+
// max_tokens: options.maxTokens,
|
|
446
|
+
max_completion_tokens: options.maxTokens,
|
|
419
447
|
user: user,
|
|
420
448
|
}
|
|
421
449
|
resultPath = 'choices[0].message.content';
|
|
422
450
|
} else if (mode === 'moderation') {
|
|
451
|
+
// Set request
|
|
423
452
|
request.url = 'https://api.openai.com/v1/moderations';
|
|
424
453
|
request.body = {
|
|
425
454
|
input: message,
|
|
@@ -431,8 +460,40 @@ OpenAI.prototype.request = function (options) {
|
|
|
431
460
|
// Request
|
|
432
461
|
await fetch(request.url, request)
|
|
433
462
|
.then(async (r) => {
|
|
463
|
+
// Log
|
|
464
|
+
// _log('Response RAW', JSON.stringify(r));
|
|
465
|
+
// {
|
|
466
|
+
// "id": "chatcmpl-AGKe03mwx644T6db3QRoXFz0aFuil",
|
|
467
|
+
// "object": "chat.completion",
|
|
468
|
+
// "created": 1728455968,
|
|
469
|
+
// "model": "gpt-4o-mini-2024-07-18",
|
|
470
|
+
// "choices": [{
|
|
471
|
+
// "index": 0,
|
|
472
|
+
// "message": {
|
|
473
|
+
// "role": "assistant",
|
|
474
|
+
// "content": "{\n \"message\": \"We offer several pricing plans:\\n\\n1. **Basic Plan**: Free\\n - Chatsy branding on chat\\n - 1 chatbot\\n - 5 knowledge base FAQs per chatbot\\n - English only\\n\\n2. **Premium Plan**: $19/month\\n - Chatsy branding removed\\n - 1 chatbot\\n - 10 knowledge base FAQs per chatbot\\n - English only\\n\\n3. **Pro Plan**: $29/month\\n - Chatsy branding removed\\n - 3 chatbots\\n - 10 knowledge base FAQs per chatbot\\n - Automatically chats in the language of your customers\\n\\n4. **Pro Plan**: $49/month\\n - Chatsy branding removed\\n - 10 chatbots\\n - 10 knowledge base FAQs per chatbot\\n - Automatically chats in the language of your customers\\n\\nLet me know if you need more details or assistance with anything else!\",\n \"user\": {\n \"name\": \"\"\n },\n \"scores\": {\n \"questionRelevancy\": 1\n }\n}",
|
|
475
|
+
// "refusal": null
|
|
476
|
+
// },
|
|
477
|
+
// "logprobs": null,
|
|
478
|
+
// "finish_reason": "stop"
|
|
479
|
+
// }],
|
|
480
|
+
// "usage": {
|
|
481
|
+
// "prompt_tokens": 1306,
|
|
482
|
+
// "completion_tokens": 231,
|
|
483
|
+
// "total_tokens": 1537,
|
|
484
|
+
// "prompt_tokens_details": {
|
|
485
|
+
// "cached_tokens": 1024
|
|
486
|
+
// },
|
|
487
|
+
// "completion_tokens_details": {
|
|
488
|
+
// "reasoning_tokens": 0
|
|
489
|
+
// }
|
|
490
|
+
// },
|
|
491
|
+
// "system_fingerprint": "fp_e2bde53e6e"
|
|
492
|
+
// }
|
|
493
|
+
|
|
434
494
|
// Set token counts
|
|
435
|
-
self.tokens.input.count += r?.usage?.prompt_tokens || 0
|
|
495
|
+
self.tokens.input.count += (r?.usage?.prompt_tokens || 0)
|
|
496
|
+
- (r?.usage?.prompt_tokens_details?.cached_tokens || 0);
|
|
436
497
|
self.tokens.output.count += r?.usage?.completion_tokens || 0;
|
|
437
498
|
self.tokens.total.count = self.tokens.input.count + self.tokens.output.count;
|
|
438
499
|
|