backend-manager 3.2.86 → 3.2.87
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.
|
|
3
|
+
"version": "3.2.87",
|
|
4
4
|
"description": "Quick tools for developing Firebase functions",
|
|
5
5
|
"main": "src/manager/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"homepage": "https://itwcreativeworks.com",
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@firebase/rules-unit-testing": "^2.0.7",
|
|
38
|
-
"@google-cloud/storage": "^7.
|
|
38
|
+
"@google-cloud/storage": "^7.9.0",
|
|
39
39
|
"@sendgrid/mail": "^7.7.0",
|
|
40
40
|
"@sentry/node": "^6.19.7",
|
|
41
41
|
"busboy": "^1.6.0",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"cors": "^2.8.5",
|
|
44
44
|
"dotenv": "^16.4.5",
|
|
45
45
|
"firebase-admin": "^11.11.1",
|
|
46
|
-
"firebase-functions": "^4.8.
|
|
46
|
+
"firebase-functions": "^4.8.2",
|
|
47
47
|
"fs-jetpack": "^5.1.0",
|
|
48
48
|
"hcaptcha": "^0.1.1",
|
|
49
49
|
"inquirer": "^8.2.5",
|
|
@@ -56,11 +56,11 @@
|
|
|
56
56
|
"moment": "^2.30.1",
|
|
57
57
|
"nanoid": "^3.3.7",
|
|
58
58
|
"node-fetch": "^2.7.0",
|
|
59
|
-
"node-powertools": "^1.4.
|
|
59
|
+
"node-powertools": "^1.4.1",
|
|
60
60
|
"npm-api": "^1.0.1",
|
|
61
61
|
"paypal-server-api": "^2.0.0",
|
|
62
62
|
"pushid": "^1.0.0",
|
|
63
|
-
"resolve-account": "^1.0.
|
|
63
|
+
"resolve-account": "^1.0.18",
|
|
64
64
|
"semver": "^7.6.0",
|
|
65
65
|
"shortid": "^2.2.16",
|
|
66
66
|
"sizeitup": "^1.0.7",
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
const fetch = require('wonderful-fetch');
|
|
2
|
+
const jetpack = require('fs-jetpack');
|
|
3
|
+
const powertools = require('node-powertools');
|
|
4
|
+
const _ = require('lodash');
|
|
5
|
+
const JSON5 = require('json5');
|
|
6
|
+
|
|
7
|
+
const TOKEN_COST_TABLE = {
|
|
8
|
+
// Mar 6th, 2024
|
|
9
|
+
'gpt-4-turbo-preview': {
|
|
10
|
+
input: 0.0100,
|
|
11
|
+
output: 0.0300,
|
|
12
|
+
},
|
|
13
|
+
'gpt-4-1106-preview': {
|
|
14
|
+
input: 0.0100,
|
|
15
|
+
output: 0.0300,
|
|
16
|
+
},
|
|
17
|
+
'gpt-4': {
|
|
18
|
+
input: 0.0300,
|
|
19
|
+
output: 0.0600,
|
|
20
|
+
},
|
|
21
|
+
'gpt-3.5-turbo': {
|
|
22
|
+
input: 0.0005,
|
|
23
|
+
output: 0.0015,
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
// Nov 6th, 2023
|
|
27
|
+
// 'gpt-4-turbo-preview': {
|
|
28
|
+
// input: 0.0100,
|
|
29
|
+
// output: 0.0300,
|
|
30
|
+
// },
|
|
31
|
+
// 'gpt-4-1106-preview': {
|
|
32
|
+
// input: 0.0100,
|
|
33
|
+
// output: 0.0300,
|
|
34
|
+
// },
|
|
35
|
+
// 'gpt-4': {
|
|
36
|
+
// input: 0.0300,
|
|
37
|
+
// output: 0.0600,
|
|
38
|
+
// },
|
|
39
|
+
// 'gpt-3.5-turbo': {
|
|
40
|
+
// input: 0.0010,
|
|
41
|
+
// output: 0.0020,
|
|
42
|
+
// },
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function OpenAI(assistant, key) {
|
|
46
|
+
const self = this;
|
|
47
|
+
|
|
48
|
+
self.assistant = assistant;
|
|
49
|
+
self.Manager = assistant.Manager;
|
|
50
|
+
self.user = assistant.user;
|
|
51
|
+
self.key = key;
|
|
52
|
+
|
|
53
|
+
self.tokens = {
|
|
54
|
+
total: {
|
|
55
|
+
count: 0,
|
|
56
|
+
price: 0,
|
|
57
|
+
},
|
|
58
|
+
input: {
|
|
59
|
+
count: 0,
|
|
60
|
+
price: 0,
|
|
61
|
+
},
|
|
62
|
+
output: {
|
|
63
|
+
count: 0,
|
|
64
|
+
price: 0,
|
|
65
|
+
},
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return self;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
OpenAI.prototype.request = function (options) {
|
|
72
|
+
const self = this;
|
|
73
|
+
const Manager = self.Manager;
|
|
74
|
+
const assistant = self.assistant;
|
|
75
|
+
|
|
76
|
+
return new Promise(async function(resolve, reject) {
|
|
77
|
+
options = _.merge({}, options);
|
|
78
|
+
|
|
79
|
+
options.model = typeof options.model === 'undefined' ? 'gpt-3.5-turbo' : options.model;
|
|
80
|
+
options.timeout = typeof options.timeout === 'undefined' ? 120000 : options.timeout;
|
|
81
|
+
options.moderate = typeof options.moderate === 'undefined' ? true : options.moderate;
|
|
82
|
+
options.log = typeof options.log === 'undefined' ? false : options.log;
|
|
83
|
+
options.user = options.user || assistant.getUser();
|
|
84
|
+
|
|
85
|
+
options.retries = typeof options.retries === 'undefined' ? 0 : options.retries;
|
|
86
|
+
options.retryTriggers = typeof options.retryTriggers === 'undefined' ? ['network', 'parse'] : options.retryTriggers;
|
|
87
|
+
|
|
88
|
+
options.prompt = options.prompt || {};
|
|
89
|
+
options.prompt.path = options.prompt.path || '';
|
|
90
|
+
options.prompt.content = options.prompt.content || '';
|
|
91
|
+
options.prompt.settings = options.prompt.settings || {};
|
|
92
|
+
|
|
93
|
+
options.message = options.message || {};
|
|
94
|
+
options.message.path = options.message.path || '';
|
|
95
|
+
options.message.content = options.message.content || '';
|
|
96
|
+
options.message.settings = options.message.settings || {};
|
|
97
|
+
|
|
98
|
+
options.history = options.history || {};
|
|
99
|
+
options.history.messages = options.history.messages || [];
|
|
100
|
+
options.history.limit = typeof options.history.limit === 'undefined' ? 5 : options.history.limit;
|
|
101
|
+
|
|
102
|
+
options.response = typeof options.response === 'undefined' ? undefined : options.response;
|
|
103
|
+
options.temperature = typeof options.temperature === 'undefined' ? 0.7 : options.temperature;
|
|
104
|
+
options.maxTokens = typeof options.maxTokens === 'undefined' ? 512 : options.maxTokens;
|
|
105
|
+
|
|
106
|
+
let attempt = 0;
|
|
107
|
+
|
|
108
|
+
function _log() {
|
|
109
|
+
if (!options.log) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
assistant.log('callOpenAI():', ...arguments);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function _load(input) {
|
|
117
|
+
// console.log('*** input!!!', input.content.slice(0, 50), input.path);
|
|
118
|
+
// console.log('*** input.content', input.content.slice(0, 50));
|
|
119
|
+
// console.log('*** input.path', input.path);
|
|
120
|
+
// _log('Loading', input);
|
|
121
|
+
|
|
122
|
+
let content = '';
|
|
123
|
+
|
|
124
|
+
// Load content
|
|
125
|
+
if (input.path) {
|
|
126
|
+
const exists = jetpack.exists(input.path);
|
|
127
|
+
if (!exists) {
|
|
128
|
+
return new Error(`Path ${input.path} not found`);
|
|
129
|
+
} else if (exists === 'dir') {
|
|
130
|
+
return new Error(`Path ${input.path} is a directory`);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
content = jetpack.read(input.path);
|
|
135
|
+
} catch (e) {
|
|
136
|
+
return new Error(`Error reading file ${input.path}: ${e}`);
|
|
137
|
+
}
|
|
138
|
+
} else {
|
|
139
|
+
content = input.content;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return powertools.template(content, input.settings).trim();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Log
|
|
146
|
+
_log('Starting', self.key, options);
|
|
147
|
+
|
|
148
|
+
// Load prompt
|
|
149
|
+
const prompt = _load(options.prompt);
|
|
150
|
+
const message = _load(options.message);
|
|
151
|
+
const user = options.user?.auth?.uid || assistant.request.geolocation.ip;
|
|
152
|
+
const responseFormat = options.response === 'json' && !options.model.includes('gpt-3.5')
|
|
153
|
+
? { type: 'json_object' }
|
|
154
|
+
: undefined;
|
|
155
|
+
|
|
156
|
+
// Log
|
|
157
|
+
_log('Prompt', prompt);
|
|
158
|
+
_log('Message', message);
|
|
159
|
+
_log('User', user);
|
|
160
|
+
|
|
161
|
+
// Check for errors
|
|
162
|
+
if (prompt instanceof Error) {
|
|
163
|
+
return reject(assistant.errorify(`Error loading prompt: ${prompt}`, {code: 400}));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (message instanceof Error) {
|
|
167
|
+
return reject(assistant.errorify(`Error loading message: ${message}`, {code: 400}));
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Format history
|
|
171
|
+
options.history.messages.forEach((m) => {
|
|
172
|
+
m.role = m.role || 'system';
|
|
173
|
+
m.content = (m.content || '').trim();
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// Request
|
|
177
|
+
function _request(mode, options) {
|
|
178
|
+
return new Promise(async function(resolve, reject) {
|
|
179
|
+
let resultPath = '';
|
|
180
|
+
const request = {
|
|
181
|
+
url: '',
|
|
182
|
+
method: 'post',
|
|
183
|
+
response: 'json',
|
|
184
|
+
// log: true,
|
|
185
|
+
tries: 1,
|
|
186
|
+
timeout: options.timeout,
|
|
187
|
+
headers: {
|
|
188
|
+
'Authorization': `Bearer ${Manager.config.openai.key}`,
|
|
189
|
+
},
|
|
190
|
+
body: {},
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (mode === 'chatgpt') {
|
|
194
|
+
request.url = 'https://api.openai.com/v1/chat/completions';
|
|
195
|
+
options.history.messages = options.history.messages.slice(-options.history.limit);
|
|
196
|
+
options.history.messages.unshift({
|
|
197
|
+
role: 'system',
|
|
198
|
+
content: prompt,
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
// Set last history item
|
|
202
|
+
const lastHistory = options.history.messages[options.history.messages.length - 1];
|
|
203
|
+
|
|
204
|
+
// If message is different than last message in history, add it
|
|
205
|
+
if (lastHistory?.content !== message) {
|
|
206
|
+
options.history.messages.push({
|
|
207
|
+
role: 'user',
|
|
208
|
+
content: message,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Log message
|
|
213
|
+
_log('Messages', options.history.messages);
|
|
214
|
+
|
|
215
|
+
request.body = {
|
|
216
|
+
model: options.model,
|
|
217
|
+
response_format: responseFormat,
|
|
218
|
+
messages: options.history.messages,
|
|
219
|
+
temperature: options.temperature,
|
|
220
|
+
max_tokens: options.maxTokens,
|
|
221
|
+
user: user,
|
|
222
|
+
}
|
|
223
|
+
resultPath = 'choices[0].message.content';
|
|
224
|
+
} else if (mode === 'moderation') {
|
|
225
|
+
request.url = 'https://api.openai.com/v1/moderations';
|
|
226
|
+
request.body = {
|
|
227
|
+
input: message,
|
|
228
|
+
user: user,
|
|
229
|
+
}
|
|
230
|
+
resultPath = 'results[0]';
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Request
|
|
234
|
+
await fetch(request.url, request)
|
|
235
|
+
.then((r) => {
|
|
236
|
+
// Set token counts
|
|
237
|
+
self.tokens.total.count += r?.usage?.total_tokens || 0;
|
|
238
|
+
self.tokens.input.count += r?.usage?.prompt_tokens || 0;
|
|
239
|
+
self.tokens.output.count += r?.usage?.completion_tokens || 0;
|
|
240
|
+
|
|
241
|
+
// Set token prices
|
|
242
|
+
self.tokens.total.price = (self.tokens.total.count / 1000) * TOKEN_COST_TABLE[options.model].input;
|
|
243
|
+
self.tokens.input.price = (self.tokens.input.count / 1000) * TOKEN_COST_TABLE[options.model].input;
|
|
244
|
+
self.tokens.output.price = (self.tokens.output.count / 1000) * TOKEN_COST_TABLE[options.model].output;
|
|
245
|
+
|
|
246
|
+
return resolve(_.get(r, resultPath));
|
|
247
|
+
})
|
|
248
|
+
.catch((e) => {
|
|
249
|
+
return reject(e);
|
|
250
|
+
})
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Moderate if needed
|
|
255
|
+
let moderation = null;
|
|
256
|
+
if (options.moderate) {
|
|
257
|
+
moderation = await _request('moderation', options)
|
|
258
|
+
.then(async (r) => {
|
|
259
|
+
_log('Moderated', r);
|
|
260
|
+
|
|
261
|
+
return r;
|
|
262
|
+
})
|
|
263
|
+
.catch((e) => e);
|
|
264
|
+
|
|
265
|
+
// Check for moderation flag
|
|
266
|
+
if (moderation.flagged) {
|
|
267
|
+
return reject(assistant.errorify(`This request is inappropriate`, {code: 451}));
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function _attempt() {
|
|
272
|
+
const retries = options.retries;
|
|
273
|
+
const triggers = options.retryTriggers;
|
|
274
|
+
|
|
275
|
+
// Increment attempt
|
|
276
|
+
attempt++;
|
|
277
|
+
|
|
278
|
+
// Log
|
|
279
|
+
_log(`Request ${attempt}/${retries}`);
|
|
280
|
+
|
|
281
|
+
// Request
|
|
282
|
+
_request('chatgpt', options)
|
|
283
|
+
.then((r) => {
|
|
284
|
+
_log('Response', r);
|
|
285
|
+
|
|
286
|
+
// Try to parse JSON response if needed
|
|
287
|
+
try {
|
|
288
|
+
const content = options.response === 'json' ? JSON5.parse(r) : r;
|
|
289
|
+
|
|
290
|
+
// Return
|
|
291
|
+
return resolve({
|
|
292
|
+
content: content,
|
|
293
|
+
tokens: self.tokens,
|
|
294
|
+
moderation: moderation,
|
|
295
|
+
})
|
|
296
|
+
} catch (e) {
|
|
297
|
+
assistant.error('Error parsing response', r, e);
|
|
298
|
+
|
|
299
|
+
// Retry
|
|
300
|
+
if (attempt < retries && triggers.includes('parse')) {
|
|
301
|
+
return _attempt();
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Return
|
|
305
|
+
return reject(e);
|
|
306
|
+
}
|
|
307
|
+
})
|
|
308
|
+
.catch((e) => {
|
|
309
|
+
assistant.error('Error requesting', e);
|
|
310
|
+
|
|
311
|
+
// Retry
|
|
312
|
+
if (attempt < retries && triggers.includes('network')) {
|
|
313
|
+
return _attempt();
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Return
|
|
317
|
+
return reject(e);
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Make attempt
|
|
322
|
+
_attempt();
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
module.exports = OpenAI;
|
|
@@ -10,6 +10,10 @@ const TOKEN_COST_TABLE = {
|
|
|
10
10
|
input: 0.0100,
|
|
11
11
|
output: 0.0300,
|
|
12
12
|
},
|
|
13
|
+
'gpt-4-vision-preview': {
|
|
14
|
+
input: 0.0100,
|
|
15
|
+
output: 0.0300,
|
|
16
|
+
},
|
|
13
17
|
'gpt-4-1106-preview': {
|
|
14
18
|
input: 0.0100,
|
|
15
19
|
output: 0.0300,
|
|
@@ -42,6 +46,12 @@ const TOKEN_COST_TABLE = {
|
|
|
42
46
|
// },
|
|
43
47
|
}
|
|
44
48
|
|
|
49
|
+
const UNSUPPORTED_JSON = [
|
|
50
|
+
/gpt-3.5/,
|
|
51
|
+
/gpt-4-vision/,
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
|
|
45
55
|
function OpenAI(assistant, key) {
|
|
46
56
|
const self = this;
|
|
47
57
|
|
|
@@ -87,13 +97,14 @@ OpenAI.prototype.request = function (options) {
|
|
|
87
97
|
|
|
88
98
|
options.prompt = options.prompt || {};
|
|
89
99
|
options.prompt.path = options.prompt.path || '';
|
|
90
|
-
options.prompt.
|
|
100
|
+
options.prompt.text = options.prompt.text || options.prompt.content || '';
|
|
91
101
|
options.prompt.settings = options.prompt.settings || {};
|
|
92
102
|
|
|
93
103
|
options.message = options.message || {};
|
|
94
104
|
options.message.path = options.message.path || '';
|
|
95
|
-
options.message.
|
|
105
|
+
options.message.text = options.message.text || options.message.content || '';
|
|
96
106
|
options.message.settings = options.message.settings || {};
|
|
107
|
+
options.message.images = options.message.images || [];
|
|
97
108
|
|
|
98
109
|
options.history = options.history || {};
|
|
99
110
|
options.history.messages = options.history.messages || [];
|
|
@@ -119,9 +130,9 @@ OpenAI.prototype.request = function (options) {
|
|
|
119
130
|
// console.log('*** input.path', input.path);
|
|
120
131
|
// _log('Loading', input);
|
|
121
132
|
|
|
122
|
-
let
|
|
133
|
+
let text = '';
|
|
123
134
|
|
|
124
|
-
// Load
|
|
135
|
+
// Load text
|
|
125
136
|
if (input.path) {
|
|
126
137
|
const exists = jetpack.exists(input.path);
|
|
127
138
|
if (!exists) {
|
|
@@ -131,27 +142,33 @@ OpenAI.prototype.request = function (options) {
|
|
|
131
142
|
}
|
|
132
143
|
|
|
133
144
|
try {
|
|
134
|
-
|
|
145
|
+
text = jetpack.read(input.path);
|
|
135
146
|
} catch (e) {
|
|
136
147
|
return new Error(`Error reading file ${input.path}: ${e}`);
|
|
137
148
|
}
|
|
138
149
|
} else {
|
|
139
|
-
|
|
150
|
+
text = input.text;
|
|
140
151
|
}
|
|
141
152
|
|
|
142
|
-
return powertools.template(
|
|
153
|
+
return powertools.template(text, input.settings).trim();
|
|
143
154
|
}
|
|
144
155
|
|
|
145
156
|
// Log
|
|
146
157
|
_log('Starting', self.key, options);
|
|
147
158
|
|
|
159
|
+
// Determine response format
|
|
160
|
+
let responseFormat = options.response === 'json' ? { type: 'json_object' } : undefined;
|
|
161
|
+
if (UNSUPPORTED_JSON.some((model) => options.model.match(model))) {
|
|
162
|
+
responseFormat = undefined;
|
|
163
|
+
assistant.warn(`Model ${options.model} does not support JSON response format`);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
_log('responseFormat', responseFormat);
|
|
167
|
+
|
|
148
168
|
// Load prompt
|
|
149
169
|
const prompt = _load(options.prompt);
|
|
150
170
|
const message = _load(options.message);
|
|
151
171
|
const user = options.user?.auth?.uid || assistant.request.geolocation.ip;
|
|
152
|
-
const responseFormat = options.response === 'json' && !options.model.includes('gpt-3.5')
|
|
153
|
-
? { type: 'json_object' }
|
|
154
|
-
: undefined;
|
|
155
172
|
|
|
156
173
|
// Log
|
|
157
174
|
_log('Prompt', prompt);
|
|
@@ -167,12 +184,6 @@ OpenAI.prototype.request = function (options) {
|
|
|
167
184
|
return reject(assistant.errorify(`Error loading message: ${message}`, {code: 400}));
|
|
168
185
|
}
|
|
169
186
|
|
|
170
|
-
// Format history
|
|
171
|
-
options.history.messages.forEach((m) => {
|
|
172
|
-
m.role = m.role || 'system';
|
|
173
|
-
m.content = (m.content || '').trim();
|
|
174
|
-
});
|
|
175
|
-
|
|
176
187
|
// Request
|
|
177
188
|
function _request(mode, options) {
|
|
178
189
|
return new Promise(async function(resolve, reject) {
|
|
@@ -192,30 +203,57 @@ OpenAI.prototype.request = function (options) {
|
|
|
192
203
|
|
|
193
204
|
if (mode === 'chatgpt') {
|
|
194
205
|
request.url = 'https://api.openai.com/v1/chat/completions';
|
|
195
|
-
|
|
196
|
-
|
|
206
|
+
const history = options.history.messages.slice(-options.history.limit);
|
|
207
|
+
history.unshift({
|
|
197
208
|
role: 'system',
|
|
198
|
-
|
|
209
|
+
text: prompt,
|
|
210
|
+
images: [],
|
|
199
211
|
});
|
|
200
212
|
|
|
201
213
|
// Set last history item
|
|
202
|
-
const lastHistory =
|
|
214
|
+
const lastHistory = history[history.length - 1];
|
|
203
215
|
|
|
204
216
|
// If message is different than last message in history, add it
|
|
205
|
-
if (lastHistory?.
|
|
206
|
-
|
|
217
|
+
if (lastHistory?.text !== message) {
|
|
218
|
+
history.push({
|
|
207
219
|
role: 'user',
|
|
208
|
-
|
|
220
|
+
text: message,
|
|
221
|
+
images: options.message.images,
|
|
209
222
|
});
|
|
210
223
|
}
|
|
211
224
|
|
|
225
|
+
// Format history
|
|
226
|
+
history.map((m) => {
|
|
227
|
+
m.role = m.role || 'system';
|
|
228
|
+
m.content = [
|
|
229
|
+
{ type: 'text', text: m.text },
|
|
230
|
+
]
|
|
231
|
+
|
|
232
|
+
// Loop through and add
|
|
233
|
+
m.images.forEach((i) => {
|
|
234
|
+
if (i.url) {
|
|
235
|
+
m.content.push({
|
|
236
|
+
type: 'image_url',
|
|
237
|
+
image_url: {
|
|
238
|
+
url: i.url,
|
|
239
|
+
detail: i.detail || 'low',
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
}),
|
|
244
|
+
|
|
245
|
+
// Delete text and images
|
|
246
|
+
delete m.text;
|
|
247
|
+
delete m.images;
|
|
248
|
+
})
|
|
249
|
+
|
|
212
250
|
// Log message
|
|
213
|
-
_log('Messages',
|
|
251
|
+
_log('Messages', history);
|
|
214
252
|
|
|
215
253
|
request.body = {
|
|
216
254
|
model: options.model,
|
|
217
255
|
response_format: responseFormat,
|
|
218
|
-
messages:
|
|
256
|
+
messages: history,
|
|
219
257
|
temperature: options.temperature,
|
|
220
258
|
max_tokens: options.maxTokens,
|
|
221
259
|
user: user,
|
|
@@ -306,7 +344,17 @@ OpenAI.prototype.request = function (options) {
|
|
|
306
344
|
}
|
|
307
345
|
})
|
|
308
346
|
.catch((e) => {
|
|
309
|
-
|
|
347
|
+
const parsed = tryParse(e.message)?.error || {};
|
|
348
|
+
const type = parsed?.type || '';
|
|
349
|
+
const message = parsed?.message || e.message;
|
|
350
|
+
|
|
351
|
+
// Log
|
|
352
|
+
assistant.error(`Error requesting (type=${type}, message=${message})`, e);
|
|
353
|
+
|
|
354
|
+
// Check for invalid request error
|
|
355
|
+
if (type === 'invalid_request_error') {
|
|
356
|
+
return reject(assistant.errorify(message, {code: 400}));
|
|
357
|
+
}
|
|
310
358
|
|
|
311
359
|
// Retry
|
|
312
360
|
if (attempt < retries && triggers.includes('network')) {
|
|
@@ -323,4 +371,12 @@ OpenAI.prototype.request = function (options) {
|
|
|
323
371
|
});
|
|
324
372
|
}
|
|
325
373
|
|
|
374
|
+
function tryParse(content) {
|
|
375
|
+
try {
|
|
376
|
+
return JSON5.parse(content);
|
|
377
|
+
} catch (e) {
|
|
378
|
+
return content;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
326
382
|
module.exports = OpenAI;
|