modelmix 3.6.2 → 3.6.4
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/index.js +106 -89
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -13,7 +13,6 @@ class ModelMix {
|
|
|
13
13
|
constructor({ options = {}, config = {} } = {}) {
|
|
14
14
|
this.models = [];
|
|
15
15
|
this.messages = [];
|
|
16
|
-
this.imagesToProcess = [];
|
|
17
16
|
this.tools = {};
|
|
18
17
|
this.toolClient = {};
|
|
19
18
|
this.mcp = {};
|
|
@@ -32,7 +31,6 @@ class ModelMix {
|
|
|
32
31
|
|
|
33
32
|
this.config = {
|
|
34
33
|
system: 'You are an assistant.',
|
|
35
|
-
systemExtra: '',
|
|
36
34
|
max_history: 1, // Default max history
|
|
37
35
|
debug: false,
|
|
38
36
|
bottleneck: defaultBottleneckConfig,
|
|
@@ -204,113 +202,133 @@ class ModelMix {
|
|
|
204
202
|
}
|
|
205
203
|
|
|
206
204
|
addImageFromBuffer(buffer, { role = "user" } = {}) {
|
|
207
|
-
this.
|
|
205
|
+
this.messages.push({
|
|
206
|
+
role,
|
|
207
|
+
content: [{
|
|
208
|
+
type: "image",
|
|
209
|
+
source: {
|
|
210
|
+
type: "buffer",
|
|
211
|
+
data: buffer
|
|
212
|
+
}
|
|
213
|
+
}]
|
|
214
|
+
});
|
|
208
215
|
return this;
|
|
209
216
|
}
|
|
210
217
|
|
|
211
218
|
addImage(filePath, { role = "user" } = {}) {
|
|
212
|
-
this.
|
|
219
|
+
this.messages.push({
|
|
220
|
+
role,
|
|
221
|
+
content: [{
|
|
222
|
+
type: "image",
|
|
223
|
+
source: {
|
|
224
|
+
type: "file",
|
|
225
|
+
data: filePath
|
|
226
|
+
}
|
|
227
|
+
}]
|
|
228
|
+
});
|
|
213
229
|
return this;
|
|
214
230
|
}
|
|
215
231
|
|
|
216
|
-
addImageFromUrl(url,
|
|
217
|
-
this.
|
|
232
|
+
addImageFromUrl(url, { role = "user" } = {}) {
|
|
233
|
+
this.messages.push({
|
|
234
|
+
role,
|
|
235
|
+
content: [{
|
|
236
|
+
type: "image",
|
|
237
|
+
source: {
|
|
238
|
+
type: "url",
|
|
239
|
+
data: url
|
|
240
|
+
}
|
|
241
|
+
}]
|
|
242
|
+
});
|
|
218
243
|
return this;
|
|
219
244
|
}
|
|
220
245
|
|
|
221
246
|
async processImages() {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
},
|
|
232
|
-
file: (image) => {
|
|
233
|
-
const buffer = this.readFile(image.filePath, { encoding: null });
|
|
234
|
-
return { buffer, source: `file: ${image.filePath}` };
|
|
235
|
-
},
|
|
236
|
-
buffer: (image) => ({
|
|
237
|
-
buffer: image.buffer,
|
|
238
|
-
source: 'buffer'
|
|
239
|
-
})
|
|
240
|
-
};
|
|
241
|
-
|
|
242
|
-
const imageContents = await Promise.all(
|
|
243
|
-
this.imagesToProcess.map(async (image) => {
|
|
247
|
+
// Process images that are in messages
|
|
248
|
+
for (let i = 0; i < this.messages.length; i++) {
|
|
249
|
+
const message = this.messages[i];
|
|
250
|
+
if (!message.content) continue;
|
|
251
|
+
|
|
252
|
+
for (let j = 0; j < message.content.length; j++) {
|
|
253
|
+
const content = message.content[j];
|
|
254
|
+
if (content.type !== 'image' || content.source.type === 'base64') continue;
|
|
255
|
+
|
|
244
256
|
try {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
257
|
+
let buffer, mimeType;
|
|
258
|
+
|
|
259
|
+
switch (content.source.type) {
|
|
260
|
+
case 'url':
|
|
261
|
+
const response = await axios.get(content.source.data, { responseType: 'arraybuffer' });
|
|
262
|
+
buffer = Buffer.from(response.data);
|
|
263
|
+
mimeType = response.headers['content-type'];
|
|
264
|
+
break;
|
|
265
|
+
|
|
266
|
+
case 'file':
|
|
267
|
+
buffer = this.readFile(content.source.data, { encoding: null });
|
|
268
|
+
break;
|
|
269
|
+
|
|
270
|
+
case 'buffer':
|
|
271
|
+
buffer = content.source.data;
|
|
272
|
+
break;
|
|
248
273
|
}
|
|
249
|
-
|
|
250
|
-
const { buffer, mimeType: providedMimeType, source } = await processor(image);
|
|
251
274
|
|
|
252
|
-
// Detect mimeType
|
|
253
|
-
let mimeType = providedMimeType;
|
|
275
|
+
// Detect mimeType if not provided
|
|
254
276
|
if (!mimeType) {
|
|
255
277
|
const fileType = await fromBuffer(buffer);
|
|
256
278
|
if (!fileType || !fileType.mime.startsWith('image/')) {
|
|
257
|
-
throw new Error(`Invalid image
|
|
279
|
+
throw new Error(`Invalid image - unable to detect valid image format`);
|
|
258
280
|
}
|
|
259
281
|
mimeType = fileType.mime;
|
|
260
282
|
}
|
|
261
|
-
|
|
262
|
-
return {
|
|
263
|
-
base64: buffer.toString('base64'),
|
|
264
|
-
mimeType,
|
|
265
|
-
config: image.config
|
|
266
|
-
};
|
|
267
283
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
return null;
|
|
271
|
-
}
|
|
272
|
-
})
|
|
273
|
-
);
|
|
274
|
-
|
|
275
|
-
imageContents
|
|
276
|
-
.filter(Boolean)
|
|
277
|
-
.forEach((image) => {
|
|
278
|
-
this.messages.push({
|
|
279
|
-
...image.config,
|
|
280
|
-
content: [{
|
|
284
|
+
// Update the content with processed image
|
|
285
|
+
message.content[j] = {
|
|
281
286
|
type: "image",
|
|
282
287
|
source: {
|
|
283
288
|
type: "base64",
|
|
284
|
-
media_type:
|
|
285
|
-
data:
|
|
289
|
+
media_type: mimeType,
|
|
290
|
+
data: buffer.toString('base64')
|
|
286
291
|
}
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
} catch (error) {
|
|
295
|
+
console.error(`Error processing image:`, error);
|
|
296
|
+
// Remove failed image from content
|
|
297
|
+
message.content.splice(j, 1);
|
|
298
|
+
j--;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
290
302
|
}
|
|
291
303
|
|
|
292
304
|
async message() {
|
|
293
|
-
this.options
|
|
294
|
-
let raw = await this.execute();
|
|
305
|
+
let raw = await this.execute({ options: { stream: false } });
|
|
295
306
|
return raw.message;
|
|
296
307
|
}
|
|
297
308
|
|
|
298
309
|
async json(schemaExample = null, schemaDescription = {}, { type = 'json_object', addExample = false, addSchema = true } = {}) {
|
|
299
|
-
|
|
310
|
+
|
|
311
|
+
let options = {
|
|
312
|
+
response_format: { type },
|
|
313
|
+
stream: false,
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
let config = {
|
|
317
|
+
system: this.config.system,
|
|
318
|
+
}
|
|
300
319
|
|
|
301
320
|
if (schemaExample) {
|
|
302
|
-
|
|
321
|
+
config.schema = generateJsonSchema(schemaExample, schemaDescription);
|
|
303
322
|
|
|
304
323
|
if (addSchema) {
|
|
305
|
-
|
|
324
|
+
config.system += "\nOutput JSON Schema: \n```\n" + JSON.stringify(this.config.schema) + "\n```";
|
|
306
325
|
}
|
|
307
326
|
if (addExample) {
|
|
308
|
-
|
|
327
|
+
config.system += "\nOutput JSON Example: \n```\n" + JSON.stringify(schemaExample) + "\n```";
|
|
309
328
|
}
|
|
310
329
|
}
|
|
311
|
-
const
|
|
312
|
-
this.
|
|
313
|
-
return JSON.parse(this._extractBlock(response));
|
|
330
|
+
const { message } = await this.execute({ options, config });
|
|
331
|
+
return JSON.parse(this._extractBlock(message));
|
|
314
332
|
}
|
|
315
333
|
|
|
316
334
|
_extractBlock(response) {
|
|
@@ -319,23 +337,24 @@ class ModelMix {
|
|
|
319
337
|
}
|
|
320
338
|
|
|
321
339
|
async block({ addSystemExtra = true } = {}) {
|
|
340
|
+
let config = {
|
|
341
|
+
system: this.config.system,
|
|
342
|
+
}
|
|
343
|
+
|
|
322
344
|
if (addSystemExtra) {
|
|
323
|
-
|
|
345
|
+
config.system += "\nReturn the result of the task between triple backtick block code tags ```";
|
|
324
346
|
}
|
|
325
|
-
const
|
|
326
|
-
this.
|
|
327
|
-
return this._extractBlock(response);
|
|
347
|
+
const { message } = await this.execute({ options: { stream: false }, config });
|
|
348
|
+
return this._extractBlock(message);
|
|
328
349
|
}
|
|
329
350
|
|
|
330
351
|
async raw() {
|
|
331
|
-
this.options
|
|
332
|
-
return this.execute();
|
|
352
|
+
return this.execute({ options: { stream: false } });
|
|
333
353
|
}
|
|
334
354
|
|
|
335
355
|
async stream(callback) {
|
|
336
|
-
this.options.stream = true;
|
|
337
356
|
this.streamCallback = callback;
|
|
338
|
-
return this.execute();
|
|
357
|
+
return this.execute({ options: { stream: true } });
|
|
339
358
|
}
|
|
340
359
|
|
|
341
360
|
replaceKeyFromFile(key, filePath) {
|
|
@@ -408,9 +427,9 @@ class ModelMix {
|
|
|
408
427
|
}
|
|
409
428
|
}
|
|
410
429
|
|
|
411
|
-
async execute() {
|
|
430
|
+
async execute({ config = {}, options = {} } = {}) {
|
|
412
431
|
if (!this.models || this.models.length === 0) {
|
|
413
|
-
throw new Error("No models specified. Use methods like .
|
|
432
|
+
throw new Error("No models specified. Use methods like .gpt41mini(), .sonnet4() first.");
|
|
414
433
|
}
|
|
415
434
|
|
|
416
435
|
return this.limiter.schedule(async () => {
|
|
@@ -429,16 +448,18 @@ class ModelMix {
|
|
|
429
448
|
const providerInstance = currentModel.provider;
|
|
430
449
|
const optionsTools = providerInstance.getOptionsTools(this.tools);
|
|
431
450
|
|
|
432
|
-
|
|
451
|
+
options = {
|
|
433
452
|
...this.options,
|
|
434
453
|
...providerInstance.options,
|
|
435
454
|
...optionsTools,
|
|
455
|
+
...options,
|
|
436
456
|
model: currentModelKey
|
|
437
457
|
};
|
|
438
458
|
|
|
439
|
-
|
|
459
|
+
config = {
|
|
440
460
|
...this.config,
|
|
441
461
|
...providerInstance.config,
|
|
462
|
+
...config,
|
|
442
463
|
};
|
|
443
464
|
|
|
444
465
|
if (config.debug) {
|
|
@@ -771,7 +792,7 @@ class MixOpenAI extends MixCustom {
|
|
|
771
792
|
|
|
772
793
|
static convertMessages(messages, config) {
|
|
773
794
|
|
|
774
|
-
const content = config.system
|
|
795
|
+
const content = config.system;
|
|
775
796
|
messages = [{ role: 'system', content }, ...messages || []];
|
|
776
797
|
|
|
777
798
|
const results = []
|
|
@@ -861,7 +882,7 @@ class MixAnthropic extends MixCustom {
|
|
|
861
882
|
|
|
862
883
|
delete options.response_format;
|
|
863
884
|
|
|
864
|
-
options.system = config.system
|
|
885
|
+
options.system = config.system;
|
|
865
886
|
return super.create({ config, options });
|
|
866
887
|
}
|
|
867
888
|
|
|
@@ -992,16 +1013,12 @@ class MixPerplexity extends MixCustom {
|
|
|
992
1013
|
async create({ config = {}, options = {} } = {}) {
|
|
993
1014
|
|
|
994
1015
|
if (config.schema) {
|
|
995
|
-
config.systemExtra = '';
|
|
996
|
-
|
|
997
1016
|
options.response_format = {
|
|
998
1017
|
type: 'json_schema',
|
|
999
1018
|
json_schema: { schema: config.schema }
|
|
1000
1019
|
};
|
|
1001
1020
|
}
|
|
1002
1021
|
|
|
1003
|
-
const content = config.system + config.systemExtra;
|
|
1004
|
-
options.messages = [{ role: 'system', content }, ...options.messages || []];
|
|
1005
1022
|
return super.create({ config, options });
|
|
1006
1023
|
}
|
|
1007
1024
|
}
|
|
@@ -1035,7 +1052,7 @@ class MixOllama extends MixCustom {
|
|
|
1035
1052
|
}
|
|
1036
1053
|
|
|
1037
1054
|
static convertMessages(messages, config) {
|
|
1038
|
-
const content = config.system
|
|
1055
|
+
const content = config.system;
|
|
1039
1056
|
messages = [{ role: 'system', content }, ...messages || []];
|
|
1040
1057
|
|
|
1041
1058
|
return messages.map(entry => {
|
|
@@ -1226,7 +1243,7 @@ class MixGoogle extends MixCustom {
|
|
|
1226
1243
|
const fullUrl = `${this.config.url}/${options.model}:${generateContentApi}?key=${this.config.apiKey}`;
|
|
1227
1244
|
|
|
1228
1245
|
|
|
1229
|
-
const content = config.system
|
|
1246
|
+
const content = config.system;
|
|
1230
1247
|
const systemInstruction = { parts: [{ text: content }] };
|
|
1231
1248
|
|
|
1232
1249
|
options.messages = MixGoogle.convertMessages(options.messages);
|