modelmix 4.5.14 → 4.5.18

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/MODELS.md CHANGED
@@ -352,4 +352,4 @@ All providers inherit from `MixCustom` base class which provides common function
352
352
  - Uses OpenAI-compatible API interface
353
353
  - Requires `MINIMAX_API_KEY` environment variable
354
354
  - Inherits all OpenAI functionality including tool calling
355
- - Available models: `MiniMax-M2`
355
+ - Available models: `MiniMax-M2.5`, `MiniMax-M2.7`
package/README.md CHANGED
@@ -69,7 +69,7 @@ const model = await ModelMix.new(setup)
69
69
  .sonnet46() // (main model) Anthropic claude-sonnet-4-5-20250929
70
70
  .gpt5mini() // (fallback 2) OpenAI gpt-5-mini
71
71
  .gemini3flash({ config: { temperature: 0 } }) // (fallback 3) Google gemini-3-flash
72
- .grok3mini() // (fallback 4) Grok grok-3-mini
72
+ .grok43() // (fallback 4) Grok grok-4.3
73
73
  .addText("What's your name?");
74
74
 
75
75
  console.log(await model.message());
@@ -151,21 +151,20 @@ Here's a comprehensive list of available methods:
151
151
  | `opus47[think]()` | Anthropic | claude-opus-4-7 | [\$5.00 / \$25.00][2] |
152
152
  | `opus46[think]()` | Anthropic | claude-opus-4-6 | [\$5.00 / \$25.00][2] |
153
153
  | `sonnet46[think]()` | Anthropic | claude-sonnet-4-6 | [\$3.00 / \$15.00][2] |
154
- | `sonnet45[think]()` | Anthropic | claude-sonnet-4-5-20250929 | [\$3.00 / \$15.00][2] |
155
154
  | `haiku45[think]()` | Anthropic | claude-haiku-4-5-20251001 | [\$1.00 / \$5.00][2] |
156
155
  | `gemini31pro()` | Google | gemini-3.1-pro-preview | [\$2.00 / \$12.00][3] |
157
- | `gemini3pro()` | Google | gemini-3-pro-preview | [\$2.00 / \$12.00][3] |
158
- | `gemini3flash()` | Google | gemini-3-flash-preview | [\$0.50 / \$3.00][3] |
159
156
  | `gemini31flashLite()`| Google | gemini-3.1-flash-lite-preview | [\$0.25 / \$1.50][3] |
160
- | `grok4()` | Grok | grok-4-0709 | [\$3.00 / \$15.00][6] |
157
+ | `grok43()` | Grok | grok-4.3 | [\$1.25 / \$2.50][6] |
158
+ | `grok420multiAgent()`| Grok | grok-4.20-multi-agent-0309 | [\$1.25 / \$2.50][6] |
159
+ | `grok420[think]()` | Grok | grok-4.20-0309 | [\$1.25 / \$2.50][6] |
161
160
  | `grok41[think]()` | Grok | grok-4-1-fast | [\$0.20 / \$0.50][6] |
161
+ | `qwen36plus()` | Fireworks/Together | qwen3p6-plus / Qwen3.6-Plus | [\$0.50 / \$3.00][10] |
162
162
  | `deepseekV4Pro()` | Fireworks | models/deepseek-v4-pro | [\$1.74 / \$3.48][10] |
163
163
  | `GLM51()` | Fireworks | models/glm-5p1 | [\$1.05 / \$3.50][10] |
164
164
  | `minimaxM27()` | MiniMax | MiniMax-M2.7 | [\$0.30 / \$1.20][9] |
165
165
  | `sonar()` | Perplexity | sonar | [\$1.00 / \$1.00][4] |
166
166
  | `sonarPro()` | Perplexity | sonar-pro | [\$3.00 / \$15.00][4] |
167
167
  | `hermes3()` | Lambda | Hermes-3-Llama-3.1-405B-FP8 | [\$0.80 / \$0.80][8] |
168
- | `qwen3()` | Together | Qwen3-235B-A22B-fp8-tput | [\$0.20 / \$0.60][7] |
169
168
  | `kimiK25think()` | Together | Kimi-K2.5 | [\$0.50 / \$2.80][7] |
170
169
  | `kimiK26think()` | Fireworks | models/kimi-k2p6 | [\$0.95 / \$4.00][10] |
171
170
 
@@ -181,7 +180,8 @@ Here's a comprehensive list of available methods:
181
180
  [10]: https://fireworks.ai/pricing#serverless-pricing "Fireworks Pricing"
182
181
 
183
182
  Each method accepts optional `options`, `config`, and (for multi-provider methods) `mix` parameters to customize behavior.
184
- For NVIDIA on DeepSeek V4 Pro, use `deepseekV4Pro({ mix: { nvidia: true, fireworks: false } })`.
183
+ For NVIDIA on DeepSeek V4 Pro, use `deepseekV4Pro({ mix: { nvidia: true } })`.
184
+ For Together on Qwen 3.6 Plus, use `qwen36plus({ mix: { fireworks: false, together: true } })`.
185
185
 
186
186
  ```javascript
187
187
  const result = await ModelMix.new({
@@ -343,7 +343,7 @@ const result = await model.json(
343
343
 
344
344
  ### Enhanced descriptors
345
345
 
346
- Descriptions support **descriptor objects** with `description`, `required`, `enum`, and `default`:
346
+ Descriptions support **descriptor objects** with `description`, `required`, `enum`, `default`, and `nullable`:
347
347
 
348
348
  ```javascript
349
349
  const result = await model.json(
@@ -359,11 +359,97 @@ const result = await model.json(
359
359
  | Property | Type | Default | Description |
360
360
  | --- | --- | --- | --- |
361
361
  | `description` | `string` | — | Field description for the model |
362
- | `required` | `boolean` | `true` | If `false`, field is removed from `required` and type becomes nullable |
363
- | `enum` | `array` | — | Allowed values. If includes `null`, type auto-becomes nullable |
364
- | `default` | `any` | — | Default value for the field |
362
+ | `required` | `boolean` | `true` | If `false`, field is removed from `required` and its type becomes nullable |
363
+ | `enum` | `array` | — | Restricts the field to specific values. Including `null` in the array auto-makes the type nullable |
364
+ | `default` | `any` | — | Default value hint for the model |
365
+ | `nullable` | `boolean` | `false` | If `true`, makes the type nullable without removing from `required` |
365
366
 
366
- You can mix strings and descriptor objects freely in the same descriptions parameter.
367
+ You can mix plain strings and descriptor objects freely in the same descriptions parameter:
368
+
369
+ ```javascript
370
+ const result = await model.json(
371
+ { name: 'Martin', age: 22, status: 'active' },
372
+ {
373
+ name: 'Full name', // plain string
374
+ age: { description: 'Age in years', required: false }, // optional field
375
+ status: { description: 'Account status', enum: ['active', 'inactive', 'banned'], default: 'active' }
376
+ }
377
+ );
378
+ ```
379
+
380
+ ### Nested object descriptions
381
+
382
+ Pass a nested object as the description value to describe fields inside a nested object:
383
+
384
+ ```javascript
385
+ const result = await model.json(
386
+ { user: { name: 'Alice', age: 30 } },
387
+ {
388
+ user: { name: 'Full name of the user', age: 'Age in years' }
389
+ }
390
+ );
391
+ ```
392
+
393
+ To describe the object field itself (e.g. mark it optional) **and** its nested fields, use the `description` / `required` descriptor for the parent key, which applies only to the parent, while still passing nested descriptions as its own separate key:
394
+
395
+ ```javascript
396
+ // Mark the parent optional but don't describe its children
397
+ const result = await model.json(
398
+ { user: { name: 'Alice', age: 30 } },
399
+ { user: { description: 'User details', required: false } }
400
+ );
401
+ ```
402
+
403
+ ### Array item descriptions
404
+
405
+ Pass descriptions for the items of an array by wrapping the descriptions in an array:
406
+
407
+ ```javascript
408
+ const result = await model.json(
409
+ { countries: [{ name: 'France', capital: 'Paris' }] },
410
+ { countries: [{ name: 'Country name', capital: 'Capital city in uppercase' }] }
411
+ );
412
+ ```
413
+
414
+ To mark the array field itself optional while keeping item descriptions, use a descriptor on the key:
415
+
416
+ ```javascript
417
+ const result = await model.json(
418
+ { tags: ['admin'] },
419
+ { tags: { description: 'List of user roles', required: false } }
420
+ );
421
+ ```
422
+
423
+ ### Automatic type and format detection
424
+
425
+ `generateJsonSchema` infers types and formats automatically from the example values:
426
+
427
+ | Example value | Inferred schema |
428
+ | --- | --- |
429
+ | `42` | `{ type: 'integer' }` |
430
+ | `19.99` | `{ type: 'number' }` |
431
+ | `true` / `false` | `{ type: 'boolean' }` |
432
+ | `null` | `{ type: 'null' }` |
433
+ | `'hello'` | `{ type: 'string' }` |
434
+ | `'user@example.com'` | `{ type: 'string', format: 'email' }` |
435
+ | `'1990-01-01'` | `{ type: 'string', format: 'date', description: 'Date in format YYYY-MM-DD' }` |
436
+ | `'14:30'` | `{ type: 'string', format: 'time', description: 'Time in format HH:MM' }` |
437
+ | `'09:15:45'` | `{ type: 'string', format: 'time', description: 'Time in format HH:MM:SS' }` |
438
+ | `[{ … }]` | `{ type: 'array', items: { … } }` — schema inferred from the first element |
439
+ | `{ … }` | `{ type: 'object', properties: { … }, required: […] }` |
440
+
441
+ When a field carries an `enum` that includes `null`, or has `required: false` or `nullable: true`, its type is widened to `[type, 'null']`. For example:
442
+
443
+ ```javascript
444
+ // enum with null → type becomes ['string', 'null']
445
+ { description: 'Gender', enum: ['m', 'f', null] }
446
+
447
+ // required: false → removes from required[] and type becomes ['string', 'null']
448
+ { description: 'Nickname', required: false }
449
+
450
+ // nullable: true → type becomes ['string', 'null'] but stays in required[]
451
+ { description: 'Middle name', nullable: true }
452
+ ```
367
453
 
368
454
  ### Array auto-wrap
369
455
 
package/demo/grok.js CHANGED
@@ -13,7 +13,7 @@ const mmix = new ModelMix({
13
13
  });
14
14
 
15
15
 
16
- const r = await mmix.grok4()
16
+ const r = await mmix.grok43()
17
17
  .addText('hi there!')
18
18
  .addText('do you like cats?')
19
19
  .raw();
package/demo/short.js CHANGED
@@ -13,7 +13,7 @@ const mmix = await ModelMix.new(setup)
13
13
  .o4mini() // (fallback 1) OpenAI o4-mini
14
14
  .gemini25proExp({ config: { temperature: 0 } }) // (fallback 2) Google gemini-2.5-pro-exp-03-25
15
15
  .gpt41nano() // (fallback 3) OpenAI gpt-4.1-nano
16
- .grok3mini() // (fallback 4) Grok grok-3-mini-beta
16
+ .grok43() // (fallback 4) Grok grok-4.3
17
17
  .addText("What's your name?");
18
18
 
19
19
  console.log(await mmix.message());
package/index.js CHANGED
@@ -37,6 +37,8 @@ const MODEL_PRICING = {
37
37
  // OpenAI
38
38
  'gpt-realtime-mini': [0.60, 2.40],
39
39
  'gpt-realtime': [4.00, 16.00],
40
+ 'gpt-5.5-pro': [30.00, 180.00],
41
+ 'gpt-5.5': [5.00, 30.00],
40
42
  'gpt-5.4': [2.50, 15.00],
41
43
  'gpt-5.4-pro': [30, 180.00],
42
44
  'gpt-5.4-mini': [0.75, 4.50],
@@ -73,19 +75,23 @@ const MODEL_PRICING = {
73
75
  'gemini-2.5-flash': [0.30, 2.50],
74
76
  'gemini-3.1-flash-lite-preview': [0.25, 1.50],
75
77
  // Grok
76
- 'grok-4-0709': [3.00, 15.00],
78
+ 'grok-4.3': [1.25, 2.50],
79
+ 'grok-4.20-multi-agent-0309': [1.25, 2.50],
80
+ 'grok-4.20-0309-reasoning': [1.25, 2.50],
81
+ 'grok-4.20-0309-non-reasoning': [1.25, 2.50],
77
82
  'grok-4-1-fast-reasoning': [0.20, 0.50],
78
83
  'grok-4-1-fast-non-reasoning': [0.20, 0.50],
79
84
  // Fireworks
80
85
  'accounts/fireworks/models/deepseek-v3p2': [0.56, 1.68],
81
86
  'accounts/fireworks/models/deepseek-v4-pro': [1.74, 3.48],
87
+ 'deepseek-ai/DeepSeek-V4-Pro': [2.10, 4.40],
82
88
  'accounts/fireworks/models/glm-4p7': [0.55, 2.19],
83
89
  'accounts/fireworks/models/glm-5p1': [1.05, 3.50],
84
90
  'accounts/fireworks/models/kimi-k2p5': [0.50, 2.80],
85
91
  'accounts/fireworks/models/qwen3p6-plus': [0.50, 3.00],
92
+ 'Qwen/Qwen3.6-Plus': [0.50, 3.00],
86
93
  'fireworks/glm-5': [1.00, 3.20],
87
94
  // MiniMax
88
- 'MiniMax-M2.1': [0.30, 1.20],
89
95
  'MiniMax-M2.5': [0.30, 1.20],
90
96
  'MiniMax-M2.7': [0.30, 1.20],
91
97
  'fireworks/minimax-m2p5': [0.30, 1.20],
@@ -315,7 +321,13 @@ class ModelMix {
315
321
  }
316
322
  gpt54pro({ options = {}, config = {} } = {}) {
317
323
  return this.attach('gpt-5.4-pro', new MixOpenAIResponses({ options, config }));
318
- }
324
+ }
325
+ gpt55({ options = {}, config = {} } = {}) {
326
+ return this.attach('gpt-5.5', new MixOpenAIResponses({ options, config }));
327
+ }
328
+ gpt55pro({ options = {}, config = {} } = {}) {
329
+ return this.attach('gpt-5.5-pro', new MixOpenAIResponses({ options, config }));
330
+ }
319
331
  gptRealtime({ options = {}, config = {} } = {}) {
320
332
  return this.attach('gpt-realtime', new MixOpenAIWebSocket({ options, config }));
321
333
  }
@@ -421,14 +433,17 @@ class ModelMix {
421
433
  return this.attach('sonar', new MixPerplexity({ options, config }));
422
434
  }
423
435
 
424
- grok3({ options = {}, config = {} } = {}) {
425
- return this.attach('grok-3', new MixGrok({ options, config }));
436
+ grok43({ options = {}, config = {} } = {}) {
437
+ return this.attach('grok-4.3', new MixGrok({ options, config }));
426
438
  }
427
- grok3mini({ options = {}, config = {} } = {}) {
428
- return this.attach('grok-3-mini', new MixGrok({ options, config }));
439
+ grok420multiAgent({ options = {}, config = {} } = {}) {
440
+ return this.attach('grok-4.20-multi-agent-0309', new MixGrok({ options, config }));
429
441
  }
430
- grok4({ options = {}, config = {} } = {}) {
431
- return this.attach('grok-4-0709', new MixGrok({ options, config }));
442
+ grok420think({ options = {}, config = {} } = {}) {
443
+ return this.attach('grok-4.20-0309-reasoning', new MixGrok({ options, config }));
444
+ }
445
+ grok420({ options = {}, config = {} } = {}) {
446
+ return this.attach('grok-4.20-0309-non-reasoning', new MixGrok({ options, config }));
432
447
  }
433
448
  grok41think({ options = {}, config = {} } = {}) {
434
449
  return this.attach('grok-4-1-fast-reasoning', new MixGrok({ options, config }));
@@ -443,8 +458,11 @@ class ModelMix {
443
458
  return this;
444
459
  }
445
460
 
446
- qwen36plus({ options = {}, config = {}, mix = { } } = {}) {
447
- return this.attach('accounts/fireworks/models/qwen3p6-plus', new MixFireworks({ options, config }));
461
+ qwen36plus({ options = {}, config = {}, mix = { fireworks: true } } = {}) {
462
+ mix = { ...this.mix, ...mix };
463
+ if (mix.fireworks) this.attach('accounts/fireworks/models/qwen3p6-plus', new MixFireworks({ options, config }));
464
+ if (mix.together) this.attach('Qwen/Qwen3.6-Plus', new MixTogether({ options, config }));
465
+ return this;
448
466
  }
449
467
 
450
468
  scout({ options = {}, config = {}, mix = {} } = {}) {
@@ -497,16 +515,6 @@ class ModelMix {
497
515
  return this.attach(model, new MixLMStudio({ options, config }));
498
516
  }
499
517
 
500
- minimaxM2({ options = {}, config = {} } = {}) {
501
- return this.attach('MiniMax-M2', new MixMiniMax({ options, config }));
502
- }
503
-
504
- minimaxM21({ options = {}, config = {}, mix = { minimax: true } } = {}) {
505
- mix = { ...this.mix, ...mix };
506
- if (mix.minimax) this.attach('MiniMax-M2.1', new MixMiniMax({ options, config }));
507
- if (mix.cerebras) this.attach('MiniMax-M2.1', new MixCerebras({ options, config }));
508
- return this;
509
- }
510
518
 
511
519
  minimaxM25({ options = {}, config = {}, mix = { minimax: true } } = {}) {
512
520
  mix = { ...this.mix, ...mix };
@@ -515,10 +523,6 @@ class ModelMix {
515
523
  return this;
516
524
  }
517
525
 
518
- minimaxM2Stable({ options = {}, config = {} } = {}) {
519
- return this.attach('MiniMax-M2-Stable', new MixMiniMax({ options, config }));
520
- }
521
-
522
526
  minimaxM27({ options = {}, config = {}, mix = { openrouter: true, minimax: true } } = {}) {
523
527
  mix = { ...this.mix, ...mix };
524
528
  if (mix.nvidia) this.attach('minimaxai/minimax-m2.7', new MixNVIDIA({ options, config }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "modelmix",
3
- "version": "4.5.14",
3
+ "version": "4.5.18",
4
4
  "description": "🧬 Reliable interface with automatic fallback for AI LLMs.",
5
5
  "main": "index.js",
6
6
  "repository": {
@@ -104,7 +104,7 @@ Thinking variants: append `think` — e.g. `opus46think()` `sonnet46think()` `so
104
104
  `gemini3pro()` `gemini3flash()` `gemini25pro()` `gemini25flash()`
105
105
 
106
106
  ### Grok
107
- `grok4()` `grok41()` `grok41think()` `grok3()` `grok3mini()`
107
+ `grok43()` `grok420multiAgent()` `grok420()` `grok420think()` `grok41()` `grok41think()`
108
108
 
109
109
  ### Perplexity
110
110
  `sonar()` `sonarPro()`
@@ -113,10 +113,10 @@ Thinking variants: append `think` — e.g. `opus46think()` `sonnet46think()` `so
113
113
  `scout()` `maverick()`
114
114
 
115
115
  ### Together
116
- `qwen3()` `kimiK25think()` `gptOss()`
116
+ `qwen36plus()` `kimiK25think()` `gptOss()`
117
117
 
118
118
  ### MiniMax
119
- `minimaxM25()` `minimaxM21()` `minimaxM2()` `minimaxM2Stable()`
119
+ `minimaxM25()` `minimaxM27()`
120
120
 
121
121
  ### Fireworks
122
122
  `deepseekV32()` `GLM5()` `GLM47()`
@@ -176,7 +176,53 @@ const result = await model.json(
176
176
  );
177
177
  ```
178
178
 
179
- Descriptor properties: `description` (string), `required` (boolean, default true — if false, field becomes nullable), `enum` (array — if includes null, type auto-becomes nullable), `default` (any).
179
+ Descriptor properties:
180
+
181
+ | Property | Type | Notes |
182
+ | --- | --- | --- |
183
+ | `description` | string | Field description for the model |
184
+ | `required` | boolean (default `true`) | `false` → removes from `required[]` **and** makes type nullable |
185
+ | `enum` | array | Restricts allowed values. Including `null` auto-makes the type nullable |
186
+ | `default` | any | Default value hint |
187
+ | `nullable` | boolean (default `false`) | `true` → makes type nullable but keeps field in `required[]` |
188
+
189
+ #### Nested object descriptions
190
+
191
+ Pass a plain object as the description value to annotate fields inside a nested object:
192
+
193
+ ```javascript
194
+ model.json(
195
+ { user: { name: 'Alice', age: 30 } },
196
+ { user: { name: 'Full name', age: 'Age in years' } }
197
+ );
198
+ ```
199
+
200
+ To mark the object itself as optional, use a descriptor (only `description`/`required`/`nullable` keys) — it applies to the parent, not the children:
201
+
202
+ ```javascript
203
+ model.json(
204
+ { user: { name: 'Alice', age: 30 } },
205
+ { user: { description: 'User details', required: false } }
206
+ );
207
+ ```
208
+
209
+ #### Array item descriptions
210
+
211
+ Wrap descriptions in an array to annotate items of an array field:
212
+
213
+ ```javascript
214
+ model.json(
215
+ { countries: [{ name: 'France', capital: 'Paris' }] },
216
+ { countries: [{ name: 'Country name', capital: 'Capital in uppercase' }] }
217
+ );
218
+ ```
219
+
220
+ #### Automatic type and format detection
221
+
222
+ Schema types are inferred from example values: `integer` (whole numbers), `number` (floats), `boolean`, `null`, `string`, and special formats:
223
+ - `'user@example.com'` → `{ type: 'string', format: 'email' }`
224
+ - `'1990-01-01'` → `{ type: 'string', format: 'date' }`
225
+ - `'14:30'` / `'09:15:45'` → `{ type: 'string', format: 'time' }`
180
226
 
181
227
  #### Array auto-wrap
182
228
 
@@ -390,7 +436,7 @@ const model = ModelMix.new({
390
436
  - Store API keys in `.env` and load with `dotenv/config` or `process.loadEnvFile()`. Never hardcode keys.
391
437
  - Chain models for resilience: primary model first, fallbacks after.
392
438
  - When using MCP tools or `addTool()`, set `max_history` to at least 3 — tool call/response pairs consume history slots.
393
- - Use `.json()` for structured output instead of parsing text manually. Use descriptor objects `{ description, required, enum, default }` for richer schema control.
439
+ - Use `.json()` for structured output instead of parsing text manually. Use descriptor objects `{ description, required, enum, default, nullable }` for richer schema control.
394
440
  - Use `.message()` for simple text, `.raw()` when you need tokens/thinking/toolCalls.
395
441
  - For thinking models, append `think` to the method name (e.g. `sonnet45think()`).
396
442
  - Template placeholders use `{key}` syntax in both system prompts and user messages.
@@ -0,0 +1,20 @@
1
+ const { expect } = require('chai');
2
+ const { ModelMix } = require('../index.js');
3
+
4
+ describe('DeepSeek Model Registration Tests', () => {
5
+ it('should register Fireworks DeepSeek V4 Pro by default', () => {
6
+ const model = ModelMix.new();
7
+ model.deepseekV4Pro({ mix: { fireworks: true, openrouter: false } });
8
+
9
+ expect(model.models).to.have.length(1);
10
+ expect(model.models[0].key).to.equal('accounts/fireworks/models/deepseek-v4-pro');
11
+ });
12
+
13
+ it('should register Together DeepSeek V4 Pro when together mix is enabled', () => {
14
+ const model = ModelMix.new();
15
+ model.deepseekV4Pro({ mix: { fireworks: false, openrouter: false, together: true } });
16
+
17
+ expect(model.models).to.have.length(1);
18
+ expect(model.models[0].key).to.equal('deepseek-ai/DeepSeek-V4-Pro');
19
+ });
20
+ });
@@ -0,0 +1,21 @@
1
+ const { expect } = require('chai');
2
+ const { ModelMix } = require('../index.js');
3
+
4
+ describe('Grok Model Registration Tests', () => {
5
+ const grokModels = [
6
+ { method: 'grok43', key: 'grok-4.3' },
7
+ { method: 'grok420multiAgent', key: 'grok-4.20-multi-agent-0309' },
8
+ { method: 'grok420think', key: 'grok-4.20-0309-reasoning' },
9
+ { method: 'grok420', key: 'grok-4.20-0309-non-reasoning' }
10
+ ];
11
+
12
+ for (const grokModel of grokModels) {
13
+ it(`should register ${grokModel.key} with ${grokModel.method}()`, () => {
14
+ const model = ModelMix.new();
15
+ model[grokModel.method]();
16
+
17
+ expect(model.models).to.have.length(1);
18
+ expect(model.models[0].key).to.equal(grokModel.key);
19
+ });
20
+ }
21
+ });
package/test/live.mcp.js CHANGED
@@ -191,8 +191,8 @@ describe('Live MCP Integration Tests', function () {
191
191
 
192
192
  describe('Advanced MCP Tool Integration', function () {
193
193
 
194
- it('should use multiple MCP tools with Grok 3 Mini', async function () {
195
- const model = ModelMix.new(setup).grok3mini();
194
+ it('should use multiple MCP tools with Grok 4.3', async function () {
195
+ const model = ModelMix.new(setup).grok43();
196
196
 
197
197
  // Add multiple tools
198
198
  model.addTools([
package/test/live.test.js CHANGED
@@ -204,18 +204,38 @@ describe('Live Integration Tests', function () {
204
204
  expect(response.toLowerCase()).to.include('gptoss test successful');
205
205
  });
206
206
 
207
- it('should work with Grok3Mini model', async function () {
208
- const model = ModelMix.new(setup).grok3mini();
207
+ it('should work with Grok 4.1 model', async function () {
208
+ const model = ModelMix.new(setup).grok41();
209
209
 
210
- model.addText('Say "grok3mini test successful" and nothing else.');
210
+ model.addText('Say "grok41 test successful" and nothing else.');
211
211
 
212
212
  const response = await model.message();
213
- console.log(`Grok3Mini response: ${response}`);
213
+ console.log(`Grok 4.1 response: ${response}`);
214
214
 
215
215
  expect(response).to.be.a('string');
216
- expect(response.toLowerCase()).to.include('grok3mini test successful');
216
+ expect(response.toLowerCase()).to.include('grok41 test successful');
217
217
  });
218
218
 
219
+ const grokSeriesTests = [
220
+ { name: 'Grok 4.3', factory: (m) => m.grok43(), token: 'grok43' },
221
+ { name: 'Grok 4.20 reasoning', factory: (m) => m.grok420think(), token: 'grok420think' },
222
+ { name: 'Grok 4.20 non-reasoning', factory: (m) => m.grok420(), token: 'grok420' }
223
+ ];
224
+
225
+ for (const grokModel of grokSeriesTests) {
226
+ it(`should work with ${grokModel.name} model`, async function () {
227
+ const model = grokModel.factory(ModelMix.new(setup));
228
+
229
+ model.addText(`Say "${grokModel.token} test successful" and nothing else.`);
230
+
231
+ const response = await model.message();
232
+ console.log(`${grokModel.name} response: ${response}`);
233
+
234
+ expect(response).to.be.a('string');
235
+ expect(response.toLowerCase()).to.include(`${grokModel.token} test successful`);
236
+ });
237
+ }
238
+
219
239
  });
220
240
 
221
241
  describe('Image Processing with JSON Output', function () {
@@ -240,8 +260,8 @@ describe('Live Integration Tests', function () {
240
260
  expect(result.color).to.be.a('string').and.not.empty;
241
261
  });
242
262
 
243
- it('should process images and return JSON with Grok 4', async function () {
244
- const model = ModelMix.new(setup).grok4();
263
+ it('should process images and return JSON with Grok 4.3', async function () {
264
+ const model = ModelMix.new(setup).grok43();
245
265
 
246
266
  model.addImageFromUrl(blueSquareBase64)
247
267
  .addText('Analyze this image and provide details in JSON format.');
@@ -252,7 +272,7 @@ describe('Live Integration Tests', function () {
252
272
  description: "string"
253
273
  });
254
274
 
255
- console.log(`Grok3Mini image JSON result:`, result);
275
+ console.log(`Grok 4.3 image JSON result:`, result);
256
276
 
257
277
  expect(result).to.be.an('object');
258
278
  expect(result).to.have.property('color');
@@ -0,0 +1,20 @@
1
+ const { expect } = require('chai');
2
+ const { ModelMix } = require('../index.js');
3
+
4
+ describe('Qwen Model Registration Tests', () => {
5
+ it('should register Fireworks Qwen 3.6 Plus by default', () => {
6
+ const model = ModelMix.new();
7
+ model.qwen36plus({ mix: { fireworks: true, together: false } });
8
+
9
+ expect(model.models).to.have.length(1);
10
+ expect(model.models[0].key).to.equal('accounts/fireworks/models/qwen3p6-plus');
11
+ });
12
+
13
+ it('should register Together Qwen 3.6 Plus when together mix is enabled', () => {
14
+ const model = ModelMix.new();
15
+ model.qwen36plus({ mix: { fireworks: false, openrouter: false, together: true } });
16
+
17
+ expect(model.models).to.have.length(1);
18
+ expect(model.models[0].key).to.equal('Qwen/Qwen3.6-Plus');
19
+ });
20
+ });
@@ -76,6 +76,18 @@ describe('Token Usage Tracking', () => {
76
76
  expect(request.prompt_cache_retention).to.equal('24h');
77
77
  });
78
78
 
79
+ it('should register GPT-5.5 shortcuts with OpenAI Responses provider', function () {
80
+ const model = ModelMix.new()
81
+ .gpt55()
82
+ .gpt55pro();
83
+
84
+ expect(model.models).to.have.length(2);
85
+ expect(model.models[0].key).to.equal('gpt-5.5');
86
+ expect(model.models[1].key).to.equal('gpt-5.5-pro');
87
+ expect(model.models[0].provider).to.be.instanceOf(MixOpenAIResponses);
88
+ expect(model.models[1].provider).to.be.instanceOf(MixOpenAIResponses);
89
+ });
90
+
79
91
  it('should track tokens in OpenAI response', async function () {
80
92
  this.timeout(30000);
81
93