modelmix 3.6.0 → 3.6.2

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.
Files changed (2) hide show
  1. package/index.js +64 -54
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  const axios = require('axios');
2
2
  const fs = require('fs');
3
- const { fileTypeFromBuffer } = require('file-type');
3
+ const { fromBuffer } = require('file-type');
4
4
  const log = require('lemonlog')('ModelMix');
5
5
  const Bottleneck = require('bottleneck');
6
6
  const path = require('path');
@@ -13,6 +13,7 @@ class ModelMix {
13
13
  constructor({ options = {}, config = {} } = {}) {
14
14
  this.models = [];
15
15
  this.messages = [];
16
+ this.imagesToProcess = [];
16
17
  this.tools = {};
17
18
  this.toolClient = {};
18
19
  this.mcp = {};
@@ -203,80 +204,89 @@ class ModelMix {
203
204
  }
204
205
 
205
206
  addImageFromBuffer(buffer, { role = "user" } = {}) {
206
-
207
- const fileType = fileTypeFromBuffer(buffer);
208
- if (!fileType || !fileType.mime.startsWith('image/')) {
209
- throw new Error('Invalid image buffer - unable to detect valid image format');
210
- }
211
-
212
- const data = buffer.toString('base64');
213
-
214
- const imageMessage = {
215
- ...{ role },
216
- content: [
217
- {
218
- type: "image",
219
- "source": {
220
- type: "base64",
221
- media_type: fileType.mime,
222
- data
223
- }
224
- }
225
- ]
226
- };
227
-
228
- this.messages.push(imageMessage);
207
+ this.imagesToProcess.push({ type: 'buffer', buffer, config: { role } });
229
208
  return this;
230
209
  }
231
210
 
232
211
  addImage(filePath, { role = "user" } = {}) {
233
- const imageBuffer = this.readFile(filePath, { encoding: null });
234
- return this.addImageFromBuffer(imageBuffer, { role });
212
+ this.imagesToProcess.push({ type: 'file', filePath, config: { role } });
213
+ return this;
235
214
  }
236
215
 
237
216
  addImageFromUrl(url, config = { role: "user" }) {
238
- if (!this.imagesToProcess) {
239
- this.imagesToProcess = [];
240
- }
241
- this.imagesToProcess.push({ url, config });
217
+ this.imagesToProcess.push({ type: 'url', url, config });
242
218
  return this;
243
219
  }
244
220
 
245
- async processImageUrls() {
246
- if (!this.imagesToProcess) return;
221
+ async processImages() {
222
+ if (this.imagesToProcess.length === 0) return;
223
+
224
+ const imageProcessors = {
225
+ url: async (image) => {
226
+ const response = await axios.get(image.url, { responseType: 'arraybuffer' });
227
+ return {
228
+ buffer: Buffer.from(response.data),
229
+ mimeType: response.headers['content-type']
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
+ };
247
241
 
248
242
  const imageContents = await Promise.all(
249
243
  this.imagesToProcess.map(async (image) => {
250
244
  try {
251
- const response = await axios.get(image.url, { responseType: 'arraybuffer' });
252
- const base64 = Buffer.from(response.data, 'binary').toString('base64');
253
- const mimeType = response.headers['content-type'];
254
- return { base64, mimeType, config: image.config };
245
+ const processor = imageProcessors[image.type];
246
+ if (!processor) {
247
+ throw new Error(`Unknown image type: ${image.type}`);
248
+ }
249
+
250
+ const { buffer, mimeType: providedMimeType, source } = await processor(image);
251
+
252
+ // Detect mimeType from buffer if not provided (for file and buffer types)
253
+ let mimeType = providedMimeType;
254
+ if (!mimeType) {
255
+ const fileType = await fromBuffer(buffer);
256
+ if (!fileType || !fileType.mime.startsWith('image/')) {
257
+ throw new Error(`Invalid image ${source || image.type} - unable to detect valid image format`);
258
+ }
259
+ mimeType = fileType.mime;
260
+ }
261
+
262
+ return {
263
+ base64: buffer.toString('base64'),
264
+ mimeType,
265
+ config: image.config
266
+ };
267
+
255
268
  } catch (error) {
256
- console.error(`Error downloading image from ${image.url}:`, error);
269
+ console.error(`Error processing image:`, error);
257
270
  return null;
258
271
  }
259
272
  })
260
273
  );
261
274
 
262
- imageContents.forEach((image) => {
263
- if (image) {
264
- const imageMessage = {
275
+ imageContents
276
+ .filter(Boolean)
277
+ .forEach((image) => {
278
+ this.messages.push({
265
279
  ...image.config,
266
- content: [
267
- {
268
- type: "image",
269
- "source": {
270
- type: "base64",
271
- media_type: image.mimeType,
272
- data: image.base64
273
- }
280
+ content: [{
281
+ type: "image",
282
+ source: {
283
+ type: "base64",
284
+ media_type: image.mimeType,
285
+ data: image.base64
274
286
  }
275
- ]
276
- };
277
- this.messages.push(imageMessage);
278
- }
279
- });
287
+ }]
288
+ });
289
+ });
280
290
  }
281
291
 
282
292
  async message() {
@@ -376,7 +386,7 @@ class ModelMix {
376
386
  }
377
387
 
378
388
  async prepareMessages() {
379
- await this.processImageUrls();
389
+ await this.processImages();
380
390
  this.applyTemplate();
381
391
  this.messages = this.messages.slice(-this.config.max_history);
382
392
  this.messages = this.groupByRoles(this.messages);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "modelmix",
3
- "version": "3.6.0",
3
+ "version": "3.6.2",
4
4
  "description": "🧬 ModelMix - Unified API for Diverse AI LLM.",
5
5
  "main": "index.js",
6
6
  "repository": {