modelmix 4.2.2 → 4.2.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/README.md CHANGED
@@ -41,7 +41,7 @@ import { ModelMix } from 'modelmix';
41
41
 
42
42
  // Get structured JSON responses
43
43
  const model = ModelMix.new()
44
- .sonnet4() // Anthropic claude-sonnet-4-20250514
44
+ .sonnet45() // Anthropic claude-sonnet-4-20250514
45
45
  .addText("Name and capital of 3 South American countries.");
46
46
 
47
47
  const outputExample = { countries: [{ name: "", capital: "" }] };
@@ -59,10 +59,9 @@ const setup = {
59
59
  };
60
60
 
61
61
  const model = await ModelMix.new(setup)
62
- .sonnet4() // (main model) Anthropic claude-sonnet-4-20250514
63
- .gpt5mini() // (fallback 1) OpenAI gpt-5-mini
64
- .gemini25flash({ config: { temperature: 0 } }) // (fallback 2) Google gemini-2.5-flash
65
- .gpt5nano() // (fallback 3) OpenAI gpt-5-nano
62
+ .sonnet45() // (main model) Anthropic claude-sonnet-4-5-20250929
63
+ .gpt5mini() // (fallback 2) OpenAI gpt-5-mini
64
+ .gemini3flash({ config: { temperature: 0 } }) // (fallback 3) Google gemini-3-flash
66
65
  .grok3mini() // (fallback 4) Grok grok-3-mini
67
66
  .addText("What's your name?");
68
67
 
package/demo/demo.js CHANGED
@@ -27,8 +27,8 @@ const pplxSettings = {
27
27
  mmix.replace({ '{name}': 'ALF' });
28
28
 
29
29
  console.log("\n" + '--------| gpt51() |--------');
30
- const opt = { reasoning_effort: 'none', verbosity: 'low' };
31
- const gpt = mmix.gpt51(opt).addText("Have you ever eaten a {animal}?");
30
+ const gptArgs = { options: { reasoning_effort: "none", verbosity: "low" } };
31
+ const gpt = mmix.gpt51(gptArgs).addText("Have you ever eaten a {animal}?");
32
32
  gpt.replace({ '{animal}': 'cat' });
33
33
  await gpt.json({ time: '24:00:00', message: 'Hello' }, { time: 'Time in format HH:MM:SS' });
34
34
 
package/demo/free.js CHANGED
@@ -1,9 +1,4 @@
1
- import { fileURLToPath } from 'url';
2
- import { dirname, join } from 'path';
3
-
4
- const __filename = fileURLToPath(import.meta.url);
5
- const __dirname = dirname(__filename);
6
- process.loadEnvFile(join(__dirname, '../.env'));
1
+ process.loadEnvFile();
7
2
  import { ModelMix } from '../index.js';
8
3
 
9
4
  const ai = ModelMix.new({ config: { debug: true } })
package/demo/gpt51.js CHANGED
@@ -8,14 +8,9 @@ const mmix = new ModelMix({
8
8
  });
9
9
 
10
10
  console.log("\n" + '--------| gpt51() |--------');
11
- const gpt = mmix.gpt51({
12
- config: {
13
- reasoning: {
14
- effort: "low",
15
- summary: "auto"
16
- }
17
- }
18
- });
11
+
12
+ const gptArgs = { options: { reasoning_effort: "none", verbosity: "low" } };
13
+ const gpt = mmix.gpt51(gptArgs);
19
14
 
20
15
  gpt.addText("Explain quantum entanglement in simple terms.");
21
16
  const response = await gpt.message();
@@ -93,7 +93,7 @@ async function contentGenerator() {
93
93
  console.log('\n=== Content Generator ===');
94
94
 
95
95
  const mmix = ModelMix.new({ config: { debug: true, max_history: 1 } })
96
- .gemini25flash()
96
+ .gemini3flash()
97
97
  .setSystem('You are a creative assistant that can generate different types of content.');
98
98
 
99
99
  // Tool for generating passwords
@@ -10,6 +10,8 @@
10
10
  "license": "ISC",
11
11
  "dependencies": {
12
12
  "@anthropic-ai/sdk": "^0.20.9",
13
+ "dotenv": "^17.2.3",
14
+ "isolated-vm": "^6.0.2",
13
15
  "lemonlog": "^1.1.4"
14
16
  }
15
17
  },
@@ -82,6 +84,67 @@
82
84
  "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
83
85
  "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
84
86
  },
87
+ "node_modules/base64-js": {
88
+ "version": "1.5.1",
89
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
90
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
91
+ "funding": [
92
+ {
93
+ "type": "github",
94
+ "url": "https://github.com/sponsors/feross"
95
+ },
96
+ {
97
+ "type": "patreon",
98
+ "url": "https://www.patreon.com/feross"
99
+ },
100
+ {
101
+ "type": "consulting",
102
+ "url": "https://feross.org/support"
103
+ }
104
+ ],
105
+ "license": "MIT"
106
+ },
107
+ "node_modules/bl": {
108
+ "version": "4.1.0",
109
+ "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
110
+ "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
111
+ "license": "MIT",
112
+ "dependencies": {
113
+ "buffer": "^5.5.0",
114
+ "inherits": "^2.0.4",
115
+ "readable-stream": "^3.4.0"
116
+ }
117
+ },
118
+ "node_modules/buffer": {
119
+ "version": "5.7.1",
120
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
121
+ "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
122
+ "funding": [
123
+ {
124
+ "type": "github",
125
+ "url": "https://github.com/sponsors/feross"
126
+ },
127
+ {
128
+ "type": "patreon",
129
+ "url": "https://www.patreon.com/feross"
130
+ },
131
+ {
132
+ "type": "consulting",
133
+ "url": "https://feross.org/support"
134
+ }
135
+ ],
136
+ "license": "MIT",
137
+ "dependencies": {
138
+ "base64-js": "^1.3.1",
139
+ "ieee754": "^1.1.13"
140
+ }
141
+ },
142
+ "node_modules/chownr": {
143
+ "version": "1.1.4",
144
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
145
+ "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
146
+ "license": "ISC"
147
+ },
85
148
  "node_modules/combined-stream": {
86
149
  "version": "1.0.8",
87
150
  "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
@@ -110,6 +173,30 @@
110
173
  }
111
174
  }
112
175
  },
176
+ "node_modules/decompress-response": {
177
+ "version": "6.0.0",
178
+ "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
179
+ "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
180
+ "license": "MIT",
181
+ "dependencies": {
182
+ "mimic-response": "^3.1.0"
183
+ },
184
+ "engines": {
185
+ "node": ">=10"
186
+ },
187
+ "funding": {
188
+ "url": "https://github.com/sponsors/sindresorhus"
189
+ }
190
+ },
191
+ "node_modules/deep-extend": {
192
+ "version": "0.6.0",
193
+ "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
194
+ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
195
+ "license": "MIT",
196
+ "engines": {
197
+ "node": ">=4.0.0"
198
+ }
199
+ },
113
200
  "node_modules/delayed-stream": {
114
201
  "version": "1.0.0",
115
202
  "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@@ -118,6 +205,36 @@
118
205
  "node": ">=0.4.0"
119
206
  }
120
207
  },
208
+ "node_modules/detect-libc": {
209
+ "version": "2.1.2",
210
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
211
+ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
212
+ "license": "Apache-2.0",
213
+ "engines": {
214
+ "node": ">=8"
215
+ }
216
+ },
217
+ "node_modules/dotenv": {
218
+ "version": "17.2.3",
219
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz",
220
+ "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==",
221
+ "license": "BSD-2-Clause",
222
+ "engines": {
223
+ "node": ">=12"
224
+ },
225
+ "funding": {
226
+ "url": "https://dotenvx.com"
227
+ }
228
+ },
229
+ "node_modules/end-of-stream": {
230
+ "version": "1.4.5",
231
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
232
+ "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
233
+ "license": "MIT",
234
+ "dependencies": {
235
+ "once": "^1.4.0"
236
+ }
237
+ },
121
238
  "node_modules/event-target-shim": {
122
239
  "version": "5.0.1",
123
240
  "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
@@ -126,6 +243,15 @@
126
243
  "node": ">=6"
127
244
  }
128
245
  },
246
+ "node_modules/expand-template": {
247
+ "version": "2.0.3",
248
+ "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
249
+ "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==",
250
+ "license": "(MIT OR WTFPL)",
251
+ "engines": {
252
+ "node": ">=6"
253
+ }
254
+ },
129
255
  "node_modules/form-data": {
130
256
  "version": "4.0.0",
131
257
  "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
@@ -164,6 +290,18 @@
164
290
  "node": ">= 14"
165
291
  }
166
292
  },
293
+ "node_modules/fs-constants": {
294
+ "version": "1.0.0",
295
+ "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
296
+ "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
297
+ "license": "MIT"
298
+ },
299
+ "node_modules/github-from-package": {
300
+ "version": "0.0.0",
301
+ "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
302
+ "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==",
303
+ "license": "MIT"
304
+ },
167
305
  "node_modules/humanize-ms": {
168
306
  "version": "1.2.1",
169
307
  "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
@@ -172,6 +310,51 @@
172
310
  "ms": "^2.0.0"
173
311
  }
174
312
  },
313
+ "node_modules/ieee754": {
314
+ "version": "1.2.1",
315
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
316
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
317
+ "funding": [
318
+ {
319
+ "type": "github",
320
+ "url": "https://github.com/sponsors/feross"
321
+ },
322
+ {
323
+ "type": "patreon",
324
+ "url": "https://www.patreon.com/feross"
325
+ },
326
+ {
327
+ "type": "consulting",
328
+ "url": "https://feross.org/support"
329
+ }
330
+ ],
331
+ "license": "BSD-3-Clause"
332
+ },
333
+ "node_modules/inherits": {
334
+ "version": "2.0.4",
335
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
336
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
337
+ "license": "ISC"
338
+ },
339
+ "node_modules/ini": {
340
+ "version": "1.3.8",
341
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
342
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
343
+ "license": "ISC"
344
+ },
345
+ "node_modules/isolated-vm": {
346
+ "version": "6.0.2",
347
+ "resolved": "https://registry.npmjs.org/isolated-vm/-/isolated-vm-6.0.2.tgz",
348
+ "integrity": "sha512-Qw6AJuagG/VJuh2AIcSWmQPsAArti/L+lKhjXU+lyhYkbt3J57XZr+ZjgfTnOr4NJcY1r3f8f0eePS7MRGp+pg==",
349
+ "hasInstallScript": true,
350
+ "license": "ISC",
351
+ "dependencies": {
352
+ "prebuild-install": "^7.1.3"
353
+ },
354
+ "engines": {
355
+ "node": ">=22.0.0"
356
+ }
357
+ },
175
358
  "node_modules/lemonlog": {
176
359
  "version": "1.1.4",
177
360
  "resolved": "https://registry.npmjs.org/lemonlog/-/lemonlog-1.1.4.tgz",
@@ -200,11 +383,56 @@
200
383
  "node": ">= 0.6"
201
384
  }
202
385
  },
386
+ "node_modules/mimic-response": {
387
+ "version": "3.1.0",
388
+ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
389
+ "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
390
+ "license": "MIT",
391
+ "engines": {
392
+ "node": ">=10"
393
+ },
394
+ "funding": {
395
+ "url": "https://github.com/sponsors/sindresorhus"
396
+ }
397
+ },
398
+ "node_modules/minimist": {
399
+ "version": "1.2.8",
400
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
401
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
402
+ "license": "MIT",
403
+ "funding": {
404
+ "url": "https://github.com/sponsors/ljharb"
405
+ }
406
+ },
407
+ "node_modules/mkdirp-classic": {
408
+ "version": "0.5.3",
409
+ "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
410
+ "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==",
411
+ "license": "MIT"
412
+ },
203
413
  "node_modules/ms": {
204
414
  "version": "2.1.3",
205
415
  "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
206
416
  "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
207
417
  },
418
+ "node_modules/napi-build-utils": {
419
+ "version": "2.0.0",
420
+ "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz",
421
+ "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==",
422
+ "license": "MIT"
423
+ },
424
+ "node_modules/node-abi": {
425
+ "version": "3.85.0",
426
+ "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.85.0.tgz",
427
+ "integrity": "sha512-zsFhmbkAzwhTft6nd3VxcG0cvJsT70rL+BIGHWVq5fi6MwGrHwzqKaxXE+Hl2GmnGItnDKPPkO5/LQqjVkIdFg==",
428
+ "license": "MIT",
429
+ "dependencies": {
430
+ "semver": "^7.3.5"
431
+ },
432
+ "engines": {
433
+ "node": ">=10"
434
+ }
435
+ },
208
436
  "node_modules/node-domexception": {
209
437
  "version": "1.0.0",
210
438
  "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
@@ -242,16 +470,231 @@
242
470
  }
243
471
  }
244
472
  },
473
+ "node_modules/once": {
474
+ "version": "1.4.0",
475
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
476
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
477
+ "license": "ISC",
478
+ "dependencies": {
479
+ "wrappy": "1"
480
+ }
481
+ },
482
+ "node_modules/prebuild-install": {
483
+ "version": "7.1.3",
484
+ "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz",
485
+ "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==",
486
+ "license": "MIT",
487
+ "dependencies": {
488
+ "detect-libc": "^2.0.0",
489
+ "expand-template": "^2.0.3",
490
+ "github-from-package": "0.0.0",
491
+ "minimist": "^1.2.3",
492
+ "mkdirp-classic": "^0.5.3",
493
+ "napi-build-utils": "^2.0.0",
494
+ "node-abi": "^3.3.0",
495
+ "pump": "^3.0.0",
496
+ "rc": "^1.2.7",
497
+ "simple-get": "^4.0.0",
498
+ "tar-fs": "^2.0.0",
499
+ "tunnel-agent": "^0.6.0"
500
+ },
501
+ "bin": {
502
+ "prebuild-install": "bin.js"
503
+ },
504
+ "engines": {
505
+ "node": ">=10"
506
+ }
507
+ },
508
+ "node_modules/pump": {
509
+ "version": "3.0.3",
510
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz",
511
+ "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==",
512
+ "license": "MIT",
513
+ "dependencies": {
514
+ "end-of-stream": "^1.1.0",
515
+ "once": "^1.3.1"
516
+ }
517
+ },
518
+ "node_modules/rc": {
519
+ "version": "1.2.8",
520
+ "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
521
+ "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
522
+ "license": "(BSD-2-Clause OR MIT OR Apache-2.0)",
523
+ "dependencies": {
524
+ "deep-extend": "^0.6.0",
525
+ "ini": "~1.3.0",
526
+ "minimist": "^1.2.0",
527
+ "strip-json-comments": "~2.0.1"
528
+ },
529
+ "bin": {
530
+ "rc": "cli.js"
531
+ }
532
+ },
533
+ "node_modules/readable-stream": {
534
+ "version": "3.6.2",
535
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
536
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
537
+ "license": "MIT",
538
+ "dependencies": {
539
+ "inherits": "^2.0.3",
540
+ "string_decoder": "^1.1.1",
541
+ "util-deprecate": "^1.0.1"
542
+ },
543
+ "engines": {
544
+ "node": ">= 6"
545
+ }
546
+ },
547
+ "node_modules/safe-buffer": {
548
+ "version": "5.2.1",
549
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
550
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
551
+ "funding": [
552
+ {
553
+ "type": "github",
554
+ "url": "https://github.com/sponsors/feross"
555
+ },
556
+ {
557
+ "type": "patreon",
558
+ "url": "https://www.patreon.com/feross"
559
+ },
560
+ {
561
+ "type": "consulting",
562
+ "url": "https://feross.org/support"
563
+ }
564
+ ],
565
+ "license": "MIT"
566
+ },
567
+ "node_modules/semver": {
568
+ "version": "7.7.3",
569
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
570
+ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
571
+ "license": "ISC",
572
+ "bin": {
573
+ "semver": "bin/semver.js"
574
+ },
575
+ "engines": {
576
+ "node": ">=10"
577
+ }
578
+ },
579
+ "node_modules/simple-concat": {
580
+ "version": "1.0.1",
581
+ "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
582
+ "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
583
+ "funding": [
584
+ {
585
+ "type": "github",
586
+ "url": "https://github.com/sponsors/feross"
587
+ },
588
+ {
589
+ "type": "patreon",
590
+ "url": "https://www.patreon.com/feross"
591
+ },
592
+ {
593
+ "type": "consulting",
594
+ "url": "https://feross.org/support"
595
+ }
596
+ ],
597
+ "license": "MIT"
598
+ },
599
+ "node_modules/simple-get": {
600
+ "version": "4.0.1",
601
+ "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz",
602
+ "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
603
+ "funding": [
604
+ {
605
+ "type": "github",
606
+ "url": "https://github.com/sponsors/feross"
607
+ },
608
+ {
609
+ "type": "patreon",
610
+ "url": "https://www.patreon.com/feross"
611
+ },
612
+ {
613
+ "type": "consulting",
614
+ "url": "https://feross.org/support"
615
+ }
616
+ ],
617
+ "license": "MIT",
618
+ "dependencies": {
619
+ "decompress-response": "^6.0.0",
620
+ "once": "^1.3.1",
621
+ "simple-concat": "^1.0.0"
622
+ }
623
+ },
624
+ "node_modules/string_decoder": {
625
+ "version": "1.3.0",
626
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
627
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
628
+ "license": "MIT",
629
+ "dependencies": {
630
+ "safe-buffer": "~5.2.0"
631
+ }
632
+ },
633
+ "node_modules/strip-json-comments": {
634
+ "version": "2.0.1",
635
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
636
+ "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
637
+ "license": "MIT",
638
+ "engines": {
639
+ "node": ">=0.10.0"
640
+ }
641
+ },
642
+ "node_modules/tar-fs": {
643
+ "version": "2.1.4",
644
+ "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz",
645
+ "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==",
646
+ "license": "MIT",
647
+ "dependencies": {
648
+ "chownr": "^1.1.1",
649
+ "mkdirp-classic": "^0.5.2",
650
+ "pump": "^3.0.0",
651
+ "tar-stream": "^2.1.4"
652
+ }
653
+ },
654
+ "node_modules/tar-stream": {
655
+ "version": "2.2.0",
656
+ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
657
+ "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
658
+ "license": "MIT",
659
+ "dependencies": {
660
+ "bl": "^4.0.3",
661
+ "end-of-stream": "^1.4.1",
662
+ "fs-constants": "^1.0.0",
663
+ "inherits": "^2.0.3",
664
+ "readable-stream": "^3.1.1"
665
+ },
666
+ "engines": {
667
+ "node": ">=6"
668
+ }
669
+ },
245
670
  "node_modules/tr46": {
246
671
  "version": "0.0.3",
247
672
  "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
248
673
  "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
249
674
  },
675
+ "node_modules/tunnel-agent": {
676
+ "version": "0.6.0",
677
+ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
678
+ "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
679
+ "license": "Apache-2.0",
680
+ "dependencies": {
681
+ "safe-buffer": "^5.0.1"
682
+ },
683
+ "engines": {
684
+ "node": "*"
685
+ }
686
+ },
250
687
  "node_modules/undici-types": {
251
688
  "version": "5.26.5",
252
689
  "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
253
690
  "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
254
691
  },
692
+ "node_modules/util-deprecate": {
693
+ "version": "1.0.2",
694
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
695
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
696
+ "license": "MIT"
697
+ },
255
698
  "node_modules/web-streams-polyfill": {
256
699
  "version": "3.3.3",
257
700
  "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
@@ -273,6 +716,12 @@
273
716
  "tr46": "~0.0.3",
274
717
  "webidl-conversions": "^3.0.0"
275
718
  }
719
+ },
720
+ "node_modules/wrappy": {
721
+ "version": "1.0.2",
722
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
723
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
724
+ "license": "ISC"
276
725
  }
277
726
  }
278
727
  }
package/demo/package.json CHANGED
@@ -12,6 +12,7 @@
12
12
  "dependencies": {
13
13
  "@anthropic-ai/sdk": "^0.20.9",
14
14
  "dotenv": "^17.2.3",
15
+ "isolated-vm": "^6.0.2",
15
16
  "lemonlog": "^1.1.4"
16
17
  }
17
18
  }
@@ -0,0 +1,77 @@
1
+ process.loadEnvFile();
2
+ import { ModelMix } from '../index.js';
3
+ import ivm from 'isolated-vm';
4
+
5
+ console.log('🧬 ModelMix - JavaScript REPL Tool Demo');
6
+
7
+ // Crear isolate una sola vez (reutilizable)
8
+ const isolate = new ivm.Isolate({ memoryLimit: 128 }); // 128MB máximo
9
+
10
+ // Ejemplo simple: REPL de JavaScript para calcular potencias de 2
11
+ async function replPowersExample() {
12
+ console.log('\n=== JavaScript REPL - Potencias de 2 ===\n');
13
+ const gptArgs = { options: { reasoning_effort: "none", verbosity: null } };
14
+ const mmix = ModelMix.new({ config: { debug: true, max_history: 10 } })
15
+ .gpt41nano()
16
+ .gpt52(gptArgs)
17
+ .gemini3flash()
18
+ .setSystem('You are a helpful assistant with access to a JavaScript REPL. When you use the REPL and get results, always show them to the user in your response.');
19
+
20
+ // Variable para capturar el resultado de la herramienta
21
+ let toolResult = null;
22
+
23
+ // Agregar herramienta REPL personalizada
24
+ mmix.addTool({
25
+ name: "javascript_repl",
26
+ description: "Execute JavaScript code in a REPL environment. You can run any valid JavaScript code and get the result.",
27
+ inputSchema: {
28
+ type: "object",
29
+ properties: {
30
+ code: {
31
+ type: "string",
32
+ description: "JavaScript code to execute"
33
+ }
34
+ },
35
+ required: ["code"]
36
+ }
37
+ }, async ({ code }) => {
38
+ console.log('🔧 Ejecutando código JavaScript:');
39
+ console.log('─'.repeat(50));
40
+ console.log(code);
41
+ console.log('─'.repeat(50));
42
+
43
+ try {
44
+ const context = await isolate.createContext();
45
+ const result = await context.eval(`JSON.stringify(eval(${JSON.stringify(code)}))`, { timeout: 10000 });
46
+ toolResult = JSON.parse(result);
47
+ console.log('\n✅ Resultado:', toolResult);
48
+ return result;
49
+ } catch (error) {
50
+ console.log('\n❌ Error:', error.message);
51
+ return `Error: ${error.message}`;
52
+ }
53
+ });
54
+
55
+ // Pedir al modelo que calcule 100 potencias de 2
56
+ mmix.addText('Calcular las primeras 100 potencias de 2 (2^0 hasta 2^99). Después de ejecutar el código, menciona algunos valores del resultado como las primeras 5 y las últimas 5 potencias.');
57
+
58
+ const result = await mmix.message();
59
+ console.log('\n💬 Respuesta del modelo:');
60
+ console.log(result);
61
+
62
+ // Mostrar muestra del resultado si está disponible
63
+ if (toolResult && Array.isArray(toolResult)) {
64
+ console.log('\n📊 Muestra de resultados (primeros 10 y últimos 10):');
65
+ console.log('Primeros 10:', toolResult.slice(0, 10));
66
+ console.log('Últimos 10:', toolResult.slice(-10));
67
+ console.log(`\nTotal: ${toolResult.length} potencias calculadas`);
68
+ }
69
+ }
70
+
71
+ try {
72
+ await replPowersExample();
73
+ console.log('\n✅ Ejemplo completado');
74
+ } catch (error) {
75
+ console.error('❌ Error:', error);
76
+ }
77
+
package/index.js CHANGED
@@ -20,7 +20,7 @@ class ModelMix {
20
20
  this.mcp = {};
21
21
  this.mcpToolsManager = new MCPToolsManager();
22
22
  this.options = {
23
- max_tokens: 5000,
23
+ max_tokens: 8192,
24
24
  temperature: 1, // 1 --> More creative, 0 --> More deterministic.
25
25
  ...options
26
26
  };
@@ -35,6 +35,7 @@ class ModelMix {
35
35
  system: 'You are an assistant.',
36
36
  max_history: 1, // Default max history
37
37
  debug: false,
38
+ verbose: 2, // 0=silent, 1=minimal, 2=readable summary, 3=full details
38
39
  bottleneck: defaultBottleneckConfig,
39
40
  ...config
40
41
  }
@@ -55,8 +56,8 @@ class ModelMix {
55
56
  return new ModelMix({ options, config, mix });
56
57
  }
57
58
 
58
- new() {
59
- return new ModelMix({ options: this.options, config: this.config });
59
+ new({ options = {}, config = {}, mix = {} } = {}) {
60
+ return new ModelMix({ options: { ...this.options, ...options }, config: { ...this.config, ...config }, mix: { ...this.mix, ...mix } });
60
61
  }
61
62
 
62
63
  static formatJSON(obj) {
@@ -79,6 +80,57 @@ class ModelMix {
79
80
  }
80
81
  }
81
82
 
83
+ // Verbose logging helpers
84
+ static truncate(str, maxLen = 100) {
85
+ if (!str || typeof str !== 'string') return str;
86
+ return str.length > maxLen ? str.substring(0, maxLen) + '...' : str;
87
+ }
88
+
89
+ static getVerboseLevel(config) {
90
+ // debug=true acts as verbose level 3
91
+ return config.verbose || 0;
92
+ }
93
+
94
+ static verboseLog(level, config, ...args) {
95
+ const verboseLevel = ModelMix.getVerboseLevel(config);
96
+ if (verboseLevel >= level) {
97
+ console.log(...args);
98
+ }
99
+ }
100
+
101
+ static formatInputSummary(messages, system) {
102
+ const lastMessage = messages[messages.length - 1];
103
+ let inputText = '';
104
+
105
+ if (lastMessage && Array.isArray(lastMessage.content)) {
106
+ const textContent = lastMessage.content.find(c => c.type === 'text');
107
+ if (textContent) inputText = textContent.text;
108
+ } else if (lastMessage && typeof lastMessage.content === 'string') {
109
+ inputText = lastMessage.content;
110
+ }
111
+
112
+ const lines = [];
113
+ lines.push(` 📝 System: ${ModelMix.truncate(system, 60)}`);
114
+ lines.push(` 💬 Input: ${ModelMix.truncate(inputText, 150)}`);
115
+ lines.push(` 📊 Messages: ${messages.length}`);
116
+ return lines.join('\n');
117
+ }
118
+
119
+ static formatOutputSummary(result) {
120
+ const lines = [];
121
+ if (result.message) {
122
+ lines.push(` 📤 Output: ${ModelMix.truncate(result.message, 200)}`);
123
+ }
124
+ if (result.think) {
125
+ lines.push(` 🧠 Thinking: ${ModelMix.truncate(result.think, 100)}`);
126
+ }
127
+ if (result.toolCalls && result.toolCalls.length > 0) {
128
+ const toolNames = result.toolCalls.map(t => t.function?.name || t.name).join(', ');
129
+ lines.push(` 🔧 Tools: ${toolNames}`);
130
+ }
131
+ return lines.join('\n');
132
+ }
133
+
82
134
  attach(key, provider) {
83
135
 
84
136
  if (this.models.some(model => model.key === key)) {
@@ -102,9 +154,6 @@ class ModelMix {
102
154
  gpt41nano({ options = {}, config = {} } = {}) {
103
155
  return this.attach('gpt-4.1-nano', new MixOpenAI({ options, config }));
104
156
  }
105
- gpt4o({ options = {}, config = {} } = {}) {
106
- return this.attach('gpt-4o', new MixOpenAI({ options, config }));
107
- }
108
157
  o4mini({ options = {}, config = {} } = {}) {
109
158
  return this.attach('o4-mini', new MixOpenAI({ options, config }));
110
159
  }
@@ -276,8 +325,11 @@ class ModelMix {
276
325
  return this.attach('MiniMax-M2', new MixMiniMax({ options, config }));
277
326
  }
278
327
 
279
- minimaxM21({ options = {}, config = {} } = {}) {
280
- return this.attach('MiniMax-M2.1', new MixMiniMax({ options, config }));
328
+ minimaxM21({ options = {}, config = {}, mix = { minimax: true } } = {}) {
329
+ mix = { ...this.mix, ...mix };
330
+ if (mix.minimax) this.attach('MiniMax-M2.1', new MixMiniMax({ options, config }));
331
+ if (mix.cerebras) this.attach('MiniMax-M2.1', new MixCerebras({ options, config }));
332
+ return this;
281
333
  }
282
334
 
283
335
  minimaxM2Stable({ options = {}, config = {} } = {}) {
@@ -292,8 +344,10 @@ class ModelMix {
292
344
  }
293
345
 
294
346
  GLM47({ options = {}, config = {}, mix = { fireworks: true } } = {}) {
347
+ mix = { ...this.mix, ...mix };
295
348
  if (mix.fireworks) this.attach('accounts/fireworks/models/glm-4p7', new MixFireworks({ options, config }));
296
349
  if (mix.openrouter) this.attach('z-ai/glm-4.7', new MixOpenRouter({ options, config }));
350
+ if (mix.cerebras) this.attach('zai-glm-4.7', new MixCerebras({ options, config }));
297
351
  return this;
298
352
  }
299
353
 
@@ -656,9 +710,16 @@ class ModelMix {
656
710
  ...config,
657
711
  };
658
712
 
659
- if (currentConfig.debug) {
713
+ const verboseLevel = ModelMix.getVerboseLevel(currentConfig);
714
+
715
+ if (verboseLevel >= 1) {
660
716
  const isPrimary = i === 0;
661
- log.debug(`[${currentModelKey}] Attempt #${i + 1}` + (isPrimary ? ' (Primary)' : ' (Fallback)'));
717
+ const tag = isPrimary ? '🚀' : '🔄';
718
+ console.log(`\n${tag} [${currentModelKey}] Attempt #${i + 1}` + (isPrimary ? '' : ' (Fallback)'));
719
+ }
720
+
721
+ if (verboseLevel >= 2) {
722
+ console.log(ModelMix.formatInputSummary(this.messages, currentConfig.system));
662
723
  }
663
724
 
664
725
  try {
@@ -692,27 +753,36 @@ class ModelMix {
692
753
  return this.execute();
693
754
  }
694
755
 
695
- if (currentConfig.debug) {
696
- console.log(`\nRequest successful: ${currentModelKey}`);
756
+ // Verbose level 1: Just success indicator
757
+ if (verboseLevel >= 1) {
758
+ console.log(` ✅ Success`);
759
+ }
760
+
761
+ // Verbose level 2: Readable summary of output
762
+ if (verboseLevel >= 2) {
763
+ console.log(ModelMix.formatOutputSummary(result));
764
+ }
697
765
 
766
+ // Verbose level 3 (debug): Full response details
767
+ if (verboseLevel >= 3) {
698
768
  if (result.response) {
699
- console.log('\nRAW RESPONSE:');
769
+ console.log('\n 📦 RAW RESPONSE:');
700
770
  console.log(ModelMix.formatJSON(result.response));
701
771
  }
702
772
 
703
773
  if (result.message) {
704
- console.log('\nMESSAGE:');
774
+ console.log('\n 💬 FULL MESSAGE:');
705
775
  console.log(ModelMix.formatMessage(result.message));
706
776
  }
707
777
 
708
778
  if (result.think) {
709
- console.log('\nTHINKING:');
779
+ console.log('\n 🧠 FULL THINKING:');
710
780
  console.log(result.think);
711
781
  }
712
-
713
- console.log('');
714
782
  }
715
783
 
784
+ if (verboseLevel >= 1) console.log('');
785
+
716
786
  return result;
717
787
 
718
788
  } catch (error) {
@@ -940,15 +1010,19 @@ class MixCustom {
940
1010
 
941
1011
  options.messages = this.convertMessages(options.messages, config);
942
1012
 
943
- if (config.debug) {
944
- console.log('\nREQUEST:');
1013
+ const verboseLevel = ModelMix.getVerboseLevel(config);
945
1014
 
946
- console.log('\nCONFIG:');
1015
+ // Verbose level 3 (debug): Full request details
1016
+ if (verboseLevel >= 3) {
1017
+ console.log('\n 📡 REQUEST DETAILS:');
1018
+
1019
+ console.log('\n ⚙️ CONFIG:');
947
1020
  const configToLog = { ...config };
948
1021
  delete configToLog.debug;
1022
+ delete configToLog.verbose;
949
1023
  console.log(ModelMix.formatJSON(configToLog));
950
1024
 
951
- console.log('\nOPTIONS:');
1025
+ console.log('\n 📋 OPTIONS:');
952
1026
  console.log(ModelMix.formatJSON(options));
953
1027
  }
954
1028
 
@@ -1680,7 +1754,8 @@ class MixGoogle extends MixCustom {
1680
1754
  functionCall: {
1681
1755
  name: toolCall.function.name,
1682
1756
  args: JSON.parse(toolCall.function.arguments)
1683
- }
1757
+ },
1758
+ thought_signature: toolCall.thought_signature || ""
1684
1759
  }))
1685
1760
  }
1686
1761
  }
@@ -1766,15 +1841,19 @@ class MixGoogle extends MixCustom {
1766
1841
  };
1767
1842
 
1768
1843
  try {
1769
- if (config.debug) {
1770
- console.log('\nREQUEST (GOOGLE):');
1844
+ const verboseLevel = ModelMix.getVerboseLevel(config);
1771
1845
 
1772
- console.log('\nCONFIG:');
1846
+ // Verbose level 3 (debug): Full request details
1847
+ if (verboseLevel >= 3) {
1848
+ console.log('\n 📡 REQUEST DETAILS (GOOGLE):');
1849
+
1850
+ console.log('\n ⚙️ CONFIG:');
1773
1851
  const configToLog = { ...config };
1774
1852
  delete configToLog.debug;
1853
+ delete configToLog.verbose;
1775
1854
  console.log(ModelMix.formatJSON(configToLog));
1776
1855
 
1777
- console.log('\nPAYLOAD:');
1856
+ console.log('\n 📋 PAYLOAD:');
1778
1857
  console.log(ModelMix.formatJSON(payload));
1779
1858
  }
1780
1859
 
@@ -1808,7 +1887,8 @@ class MixGoogle extends MixCustom {
1808
1887
  function: {
1809
1888
  name: part.functionCall.name,
1810
1889
  arguments: JSON.stringify(part.functionCall.args)
1811
- }
1890
+ },
1891
+ thought_signature: part.thoughtSignature || ""
1812
1892
  };
1813
1893
  }
1814
1894
  return null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "modelmix",
3
- "version": "4.2.2",
3
+ "version": "4.2.4",
4
4
  "description": "🧬 Reliable interface with automatic fallback for AI LLMs.",
5
5
  "main": "index.js",
6
6
  "repository": {
@@ -46,7 +46,7 @@
46
46
  },
47
47
  "homepage": "https://github.com/clasen/ModelMix#readme",
48
48
  "dependencies": {
49
- "@modelcontextprotocol/sdk": "^1.23.0",
49
+ "@modelcontextprotocol/sdk": "^1.25.2",
50
50
  "axios": "^1.12.2",
51
51
  "bottleneck": "^2.19.5",
52
52
  "file-type": "^16.5.4",
@@ -72,7 +72,7 @@ describe('Rate Limiting with Bottleneck Tests', () => {
72
72
  it('should enforce minimum time between requests', async () => {
73
73
  const startTimes = [];
74
74
 
75
- model.gpt4o();
75
+ model.gpt41();
76
76
 
77
77
  // Mock API responses
78
78
  nock('https://api.openai.com')
@@ -122,7 +122,7 @@ describe('Rate Limiting with Bottleneck Tests', () => {
122
122
  }
123
123
  });
124
124
 
125
- model.gpt4o();
125
+ model.gpt41();
126
126
 
127
127
  // Mock API with delay to simulate concurrent requests
128
128
  nock('https://api.openai.com')
@@ -184,7 +184,7 @@ describe('Rate Limiting with Bottleneck Tests', () => {
184
184
  it('should apply rate limiting to OpenAI requests', async () => {
185
185
  const requestTimes = [];
186
186
 
187
- model.gpt4o();
187
+ model.gpt41();
188
188
 
189
189
  nock('https://api.openai.com')
190
190
  .post('/v1/chat/completions')
@@ -267,7 +267,7 @@ describe('Rate Limiting with Bottleneck Tests', () => {
267
267
  });
268
268
 
269
269
  it('should handle rate limiting with API errors', async () => {
270
- model.gpt4o();
270
+ model.gpt41();
271
271
 
272
272
  nock('https://api.openai.com')
273
273
  .post('/v1/chat/completions')
@@ -289,7 +289,7 @@ describe('Rate Limiting with Bottleneck Tests', () => {
289
289
  it('should continue rate limiting after errors', async () => {
290
290
  const requestTimes = [];
291
291
 
292
- model.gpt4o();
292
+ model.gpt41();
293
293
 
294
294
  // First request fails
295
295
  nock('https://api.openai.com')
@@ -345,7 +345,7 @@ describe('Rate Limiting with Bottleneck Tests', () => {
345
345
  }
346
346
  });
347
347
 
348
- model.gpt4o();
348
+ model.gpt41();
349
349
 
350
350
  let requestCount = 0;
351
351
 
@@ -392,7 +392,7 @@ describe('Rate Limiting with Bottleneck Tests', () => {
392
392
  }
393
393
  });
394
394
 
395
- model.gpt4o();
395
+ model.gpt41();
396
396
 
397
397
  const results = [];
398
398
 
@@ -440,7 +440,7 @@ describe('Rate Limiting with Bottleneck Tests', () => {
440
440
  }
441
441
  });
442
442
 
443
- model.gpt4o();
443
+ model.gpt41();
444
444
 
445
445
  nock('https://api.openai.com')
446
446
  .post('/v1/chat/completions')
@@ -489,7 +489,7 @@ describe('Rate Limiting with Bottleneck Tests', () => {
489
489
  done();
490
490
  });
491
491
 
492
- model.gpt4o();
492
+ model.gpt41();
493
493
 
494
494
  nock('https://api.openai.com')
495
495
  .post('/v1/chat/completions')
@@ -25,7 +25,7 @@ describe('Image Processing and Multimodal Support Tests', () => {
25
25
  it('should handle base64 image data correctly', async () => {
26
26
  const base64Image = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAFUlEQVR42mP8z8BQz0AEYBxVSF+FABJADveWkH6oAAAAAElFTkSuQmCC';
27
27
 
28
- model.gpt4o()
28
+ model.gpt41()
29
29
  .addText('What do you see in this image?')
30
30
  .addImageFromUrl(base64Image);
31
31
 
package/test/json.test.js CHANGED
@@ -198,7 +198,7 @@ describe('JSON Schema and Structured Output Tests', () => {
198
198
  }]
199
199
  };
200
200
 
201
- model.gpt4o().addText('List 3 countries');
201
+ model.gpt41().addText('List 3 countries');
202
202
 
203
203
  // Mock the API response
204
204
  nock('https://api.openai.com')
@@ -270,7 +270,7 @@ describe('JSON Schema and Structured Output Tests', () => {
270
270
  });
271
271
 
272
272
  it('should handle JSON parsing errors gracefully', async () => {
273
- model.gpt4o().addText('Generate invalid JSON');
273
+ model.gpt41().addText('Generate invalid JSON');
274
274
 
275
275
  // Mock invalid JSON response
276
276
  nock('https://api.openai.com')
package/test/live.mcp.js CHANGED
@@ -110,8 +110,8 @@ describe('Live MCP Integration Tests', function () {
110
110
  }
111
111
  });
112
112
 
113
- it('should use custom MCP tools with Gemini 2.5 Flash', async function () {
114
- const model = ModelMix.new(setup).gemini25flash();
113
+ it('should use custom MCP tools with Gemini 3 Flash', async function () {
114
+ const model = ModelMix.new(setup).gemini3flash();
115
115
 
116
116
  // Add password generator tool
117
117
  model.addTool({
@@ -149,11 +149,13 @@ describe('Live MCP Integration Tests', function () {
149
149
  model.addText('Generate a secure password of 16 characters with symbols.');
150
150
 
151
151
  const response = await model.message();
152
- console.log(`Gemini 2.5 Flash with MCP tools: ${response}`);
152
+ console.log(`Gemini 3 Flash with MCP tools: ${response}`);
153
153
 
154
154
  expect(response).to.be.a('string');
155
- expect(response).to.include('password');
156
- expect(response).to.include('16');
155
+ // Check password is mentioned and a generated password string is present
156
+ expect(response.toLowerCase()).to.include('password');
157
+ // Verify a generated password is in the response (at least 12 chars with mix of alphanumeric/symbols)
158
+ expect(response).to.match(/[a-zA-Z0-9!@#$%^&*()_+\-=\[\]{}|;:,.<>?]{12,}/);
157
159
  });
158
160
 
159
161
  });
@@ -374,8 +376,8 @@ describe('Live MCP Integration Tests', function () {
374
376
  expect(result.factorial_result).to.equal(120);
375
377
  });
376
378
 
377
- it('should use MCP tools with JSON output using Gemini 2.5 Flash', async function () {
378
- const model = ModelMix.new(setup).gemini25flash();
379
+ it('should use MCP tools with JSON output using Gemini 3 Flash', async function () {
380
+ const model = ModelMix.new(setup).gemini3flash();
379
381
 
380
382
  // Add system info tool
381
383
  model.addTool({
@@ -414,7 +416,7 @@ describe('Live MCP Integration Tests', function () {
414
416
  generated_at: ""
415
417
  });
416
418
 
417
- console.log(`Gemini 2.5 Flash with MCP tools JSON result:`, result);
419
+ console.log(`Gemini 3 Flash with MCP tools JSON result:`, result);
418
420
 
419
421
  expect(result).to.be.an('object');
420
422
  expect(result.timestamp).to.be.a('number');
@@ -27,7 +27,7 @@ describe('Template and File Operations Tests', () => {
27
27
  });
28
28
 
29
29
  it('should replace simple template variables', async () => {
30
- model.gpt4o()
30
+ model.gpt41()
31
31
  .replace({
32
32
  '{{name}}': 'Alice',
33
33
  '{{age}}': '30',
@@ -56,7 +56,7 @@ describe('Template and File Operations Tests', () => {
56
56
  });
57
57
 
58
58
  it('should handle multiple template replacements', async () => {
59
- model.gpt4o()
59
+ model.gpt41()
60
60
  .replace({ '{{greeting}}': 'Hello' })
61
61
  .replace({ '{{name}}': 'Bob' })
62
62
  .replace({ '{{action}}': 'welcome' })
@@ -82,7 +82,7 @@ describe('Template and File Operations Tests', () => {
82
82
  });
83
83
 
84
84
  it('should handle nested template objects', async () => {
85
- model.gpt4o()
85
+ model.gpt41()
86
86
  .replace({
87
87
  '{{user_name}}': 'Charlie',
88
88
  '{{user_role}}': 'admin',
@@ -111,7 +111,7 @@ describe('Template and File Operations Tests', () => {
111
111
  });
112
112
 
113
113
  it('should preserve unreplaced templates', async () => {
114
- model.gpt4o()
114
+ model.gpt41()
115
115
  .replace({ '{{name}}': 'David' })
116
116
  .addText('Hello {{name}}, your ID is {{user_id}} and status is {{status}}');
117
117
 
@@ -135,7 +135,7 @@ describe('Template and File Operations Tests', () => {
135
135
  });
136
136
 
137
137
  it('should handle empty and special character replacements', async () => {
138
- model.gpt4o()
138
+ model.gpt41()
139
139
  .replace({
140
140
  '{{empty}}': '',
141
141
  '{{special}}': 'Hello & "World" <test>',
@@ -175,7 +175,7 @@ describe('Template and File Operations Tests', () => {
175
175
  });
176
176
 
177
177
  it('should load and replace from template file', async () => {
178
- model.gpt4o()
178
+ model.gpt41()
179
179
  .replaceKeyFromFile('{{template}}', path.join(fixturesPath, 'template.txt'))
180
180
  .replace({
181
181
  '{{name}}': 'Eve',
@@ -214,7 +214,7 @@ describe('Template and File Operations Tests', () => {
214
214
  });
215
215
 
216
216
  it('should load and process JSON data file', async () => {
217
- model.gpt4o()
217
+ model.gpt41()
218
218
  .replaceKeyFromFile('{{data}}', path.join(fixturesPath, 'data.json'))
219
219
  .addText('Process this data: {{data}}');
220
220
 
@@ -246,7 +246,7 @@ describe('Template and File Operations Tests', () => {
246
246
  });
247
247
 
248
248
  it('should handle file loading errors gracefully', async () => {
249
- model.gpt4o()
249
+ model.gpt41()
250
250
  .replaceKeyFromFile('{{missing}}', path.join(fixturesPath, 'nonexistent.txt'))
251
251
  .addText('This should contain: {{missing}}');
252
252
 
@@ -271,7 +271,7 @@ describe('Template and File Operations Tests', () => {
271
271
  });
272
272
 
273
273
  it('should handle multiple file replacements', async () => {
274
- model.gpt4o()
274
+ model.gpt41()
275
275
  .replaceKeyFromFile('{{template}}', path.join(fixturesPath, 'template.txt'))
276
276
  .replaceKeyFromFile('{{data}}', path.join(fixturesPath, 'data.json'))
277
277
  .replace({
@@ -315,7 +315,7 @@ describe('Template and File Operations Tests', () => {
315
315
  it('should handle relative and absolute paths', async () => {
316
316
  const absolutePath = path.resolve(fixturesPath, 'template.txt');
317
317
 
318
- model.gpt4o()
318
+ model.gpt41()
319
319
  .replaceKeyFromFile('{{absolute}}', absolutePath)
320
320
  .replace({
321
321
  '{{name}}': 'Grace',
@@ -362,7 +362,7 @@ describe('Template and File Operations Tests', () => {
362
362
  });
363
363
 
364
364
  it('should combine file loading with template replacement in complex scenarios', async () => {
365
- model.gpt4o()
365
+ model.gpt41()
366
366
  .replaceKeyFromFile('{{user_data}}', path.join(fixturesPath, 'data.json'))
367
367
  .replace({
368
368
  '{{action}}': 'analyze',
@@ -402,7 +402,7 @@ describe('Template and File Operations Tests', () => {
402
402
  roles: ['admin', 'user']
403
403
  };
404
404
 
405
- model.gpt4o()
405
+ model.gpt41()
406
406
  .replaceKeyFromFile('{{data}}', path.join(fixturesPath, 'data.json'))
407
407
  .replace({ '{{instruction}}': 'Count active users by role' })
408
408
  .addText('{{instruction}} from this data: {{data}}');
@@ -447,16 +447,16 @@ describe('Template and File Operations Tests', () => {
447
447
 
448
448
  it('should handle template replacement errors gracefully', () => {
449
449
  expect(() => {
450
- model.gpt4o().replace(null);
450
+ model.gpt41().replace(null);
451
451
  }).to.not.throw();
452
452
 
453
453
  expect(() => {
454
- model.gpt4o().replace(undefined);
454
+ model.gpt41().replace(undefined);
455
455
  }).to.not.throw();
456
456
  });
457
457
 
458
458
  it('should handle file reading errors without crashing', async () => {
459
- model.gpt4o()
459
+ model.gpt41()
460
460
  .replaceKeyFromFile('{{bad_file}}', '/path/that/does/not/exist.txt')
461
461
  .addText('Content: {{bad_file}}');
462
462