backend-manager 3.2.13 → 3.2.15

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": "3.2.13",
3
+ "version": "3.2.15",
4
4
  "description": "Quick tools for developing Firebase functions",
5
5
  "main": "src/manager/index.js",
6
6
  "bin": {
@@ -14,9 +14,7 @@ module.exports = function (payload, config) {
14
14
  subject: `Free ${config.brand.name} download link for ${payload.name || 'you'}!`,
15
15
  template: 'd-1d730ac8cc544b7cbccc8fa4a4b3f9ce',
16
16
  group: 24077,
17
- options: {
18
- copy: false,
19
- },
17
+ copy: false,
20
18
  data: {},
21
19
  }
22
20
  }
@@ -330,6 +330,12 @@ BackendAssistant.prototype._log = function () {
330
330
  }
331
331
  }
332
332
 
333
+ BackendAssistant.prototype.getUser = function () {
334
+ const self = this;
335
+
336
+ return self?.usage?.user || self.request.user;
337
+ }
338
+
333
339
  BackendAssistant.prototype.errorify = function (e, options) {
334
340
  const self = this;
335
341
  const res = self.ref.res;
@@ -27,6 +27,7 @@ Middleware.prototype.run = function (library, options) {
27
27
  const data = assistant.request.data;
28
28
  const geolocation = assistant.request.geolocation;
29
29
  const client = assistant.request.client;
30
+ const isProduction = assistant.meta.environment === 'production';
30
31
 
31
32
  // Set options
32
33
  options = options || {};
@@ -56,11 +57,11 @@ Middleware.prototype.run = function (library, options) {
56
57
 
57
58
  // Setup usage
58
59
  if (options.setupUsage) {
59
- assistant.usage = await Manager.Usage().init(assistant);
60
+ assistant.usage = await Manager.Usage().init(assistant, {log: isProduction});
60
61
  }
61
62
 
62
63
  // Log working user
63
- const workingUser = assistant?.usage?.user || assistant.request.user;
64
+ const workingUser = assistant.getUser();
64
65
  assistant.log(`Middleware.process(): User (${workingUser.auth.uid}, ${workingUser.auth.email}, ${workingUser.plan.id}=${workingUser.plan.status}):`, JSON.stringify(workingUser));
65
66
 
66
67
  // Setup analytics
@@ -1,5 +1,23 @@
1
1
  const fetch = require('wonderful-fetch');
2
+ const jetpack = require('fs-jetpack');
2
3
  const powertools = require('node-powertools');
4
+ const _ = require('lodash');
5
+
6
+ const TOKEN_COST_TABLE = {
7
+ // Nov 6th, 2023
8
+ 'gpt-4-1106-preview': {
9
+ input: 0.0100,
10
+ output: 0.0300,
11
+ },
12
+ 'gpt-4': {
13
+ input: 0.0300,
14
+ output: 0.0600,
15
+ },
16
+ 'gpt-3.5-turbo': {
17
+ input: 0.0010,
18
+ output: 0.0020,
19
+ },
20
+ }
3
21
 
4
22
  function OpenAI(assistant, key) {
5
23
  const self = this;
@@ -9,37 +27,71 @@ function OpenAI(assistant, key) {
9
27
  self.user = assistant.user;
10
28
  self.key = key;
11
29
 
30
+ self.tokens = {
31
+ total: {
32
+ count: 0,
33
+ price: 0,
34
+ },
35
+ input: {
36
+ count: 0,
37
+ price: 0,
38
+ },
39
+ output: {
40
+ count: 0,
41
+ price: 0,
42
+ },
43
+ }
44
+
12
45
  return self;
13
46
  }
14
47
 
15
- OpenAI.prototype.request = function (options, messages, template) {
48
+ OpenAI.prototype.request = function (options) {
16
49
  const self = this;
17
50
  const Manager = self.Manager;
18
51
  const assistant = self.assistant;
19
52
 
20
- assistant.log('callOpenAI(): Starting', self.key);
21
- assistant.log('callOpenAI(): Starting', options, messages, template);
22
-
23
53
  return new Promise(async function(resolve, reject) {
24
- options = options || {};
25
- options.prompt = options.prompt || '';
26
- options.timeout = options.timeout || 120000;
27
- options.model = options.model || (
28
- self.user.plan.id === 'basic'
29
- ? BASIC_MODEL
30
- : PREMIUM_MODEL
31
- );
32
- options.user = options.user || assistant.request.geolocation.ip;
54
+ options = _.merge({}, options);
33
55
 
34
- // Load prompt
35
- options.prompt = powertools.template(
36
- jetpack.read(`${__dirname}/prompts/${options.prompt}.md`),
37
- template,
38
- );
56
+ options.model = typeof options.model === 'undefined' ? 'gpt-3.5-turbo' : options.model;
57
+ options.timeout = typeof options.timeout === 'undefined' ? 120000 : options.timeout;
58
+ options.moderate = typeof options.moderate === 'undefined' ? true : options.moderate;
59
+ options.user = options.user || assistant.getUser();
60
+
61
+ options.prompt = options.prompt || {};
62
+ options.prompt.path = options.prompt.path || '';
63
+ options.prompt.settings = options.prompt.settings || {};
64
+
65
+ options.message = options.message || {};
66
+ options.message.path = options.message.path || '';
67
+ options.message.settings = options.message.settings || {};
39
68
 
40
- // assistant.log('callOpenAI():', options);
69
+ options.history = options.history || {};
70
+ options.history.messages = options.history.messages || [];
71
+ options.history.limit = typeof options.history.limit === 'undefined' ? 5 : options.history.limit;
72
+
73
+ options.response = typeof options.response === 'undefined' ? undefined : options.response;
74
+ options.temperature = typeof options.temperature === 'undefined' ? 0.7 : options.temperature;
75
+ options.maxTokens = typeof options.maxTokens === 'undefined' ? 512 : options.maxTokens;
76
+
77
+ assistant.log('callOpenAI(): Starting', self.key);
41
78
  assistant.log('callOpenAI(): Starting', options);
42
79
 
80
+ // Load prompt
81
+ const prompt = powertools.template(
82
+ jetpack.read(options.prompt.path),
83
+ options.prompt.settings,
84
+ ).trim();
85
+ const message = powertools.template(
86
+ jetpack.read(options.message.path),
87
+ options.message.settings,
88
+ ).trim();
89
+ const user = options.user?.auth?.uid || assistant.request.geolocation.ip;
90
+
91
+ assistant.log('callOpenAI(): Prompt', prompt);
92
+ assistant.log('callOpenAI(): Message', message);
93
+ assistant.log('callOpenAI(): User', user);
94
+
43
95
  function _request(mode, options) {
44
96
  return new Promise(async function(resolve, reject) {
45
97
  let resultPath = '';
@@ -58,25 +110,33 @@ OpenAI.prototype.request = function (options, messages, template) {
58
110
 
59
111
  if (mode === 'chatgpt') {
60
112
  request.url = 'https://api.openai.com/v1/chat/completions';
61
- messages = messages.slice(-MAX_MESSAGE_LENGTH);
62
- messages.unshift({
113
+ options.history.messages = options.history.messages.slice(-options.history.limit);
114
+ options.history.messages.unshift({
63
115
  role: 'system',
64
- content: options.prompt,
116
+ content: prompt,
117
+ });
118
+ options.history.messages.push({
119
+ role: 'user',
120
+ content: message,
65
121
  });
122
+
123
+ // Log message
124
+ assistant.log('callOpenAI(): Messages', options.history.messages);
125
+
66
126
  request.body = {
67
127
  model: options.model,
68
- response_format: { type: 'json_object' },
69
- messages: messages,
70
- temperature: 0.7, // default: 0.7
71
- max_tokens: 512, // default: infinity
72
- user: options.user,
128
+ response_format: options.response === 'json' ? { type: 'json_object' } : undefined,
129
+ messages: options.history.messages,
130
+ temperature: options.temperature,
131
+ max_tokens: options.maxTokens,
132
+ user: user,
73
133
  }
74
134
  resultPath = 'choices[0].message.content';
75
135
  } else if (mode === 'moderation') {
76
136
  request.url = 'https://api.openai.com/v1/moderations';
77
137
  request.body = {
78
- input: messages.map(obj => obj.content).join('\n\nMessage:\n'),
79
- user: options.user,
138
+ input: message,
139
+ user: user,
80
140
  }
81
141
  resultPath = 'results[0]';
82
142
  }
@@ -102,25 +162,39 @@ OpenAI.prototype.request = function (options, messages, template) {
102
162
  });
103
163
  }
104
164
 
105
- await _request('moderation', options)
106
- .then(async (r) => {
107
- // assistant.log('callOpenAI(): moderation', r);
108
- assistant.log('callOpenAI(): Moderated');
165
+ // Moderate if needed
166
+ let moderation = null;
167
+ if (options.moderate) {
168
+ moderation = await _request('moderation', options)
169
+ .then(async (r) => {
170
+ assistant.log('callOpenAI(): Moderated', r);
109
171
 
110
- // If the moderation is not approved, return
111
- if (r.flagged) {
112
- return reject(new Error('Moderation not approved'));
113
- }
172
+ // Save moderation
173
+ self.moderation = r;
114
174
 
115
- await _request('chatgpt', options)
116
- .then((r) => {
117
- try {
118
- return resolve(JSON.parse(r));
119
- } catch (e) {
120
- return reject(e);
121
- }
175
+ return r;
122
176
  })
123
- .catch((e) => reject(e));
177
+ .catch((e) => e);
178
+
179
+ if (moderation.flagged) {
180
+ return reject(assistant.errorify(`This request is inappropriate`, {code: 400, sentry: false, send: false, log: false}));
181
+ }
182
+ }
183
+
184
+ // Request
185
+ await _request('chatgpt', options)
186
+ .then((r) => {
187
+ try {
188
+ const content = options.response === 'json' ? JSON.parse(r) : r;
189
+
190
+ return resolve({
191
+ content: content,
192
+ tokens: self.tokens,
193
+ moderation: moderation,
194
+ })
195
+ } catch (e) {
196
+ return reject(e);
197
+ }
124
198
  })
125
199
  .catch((e) => reject(e));
126
200
  });