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 +1 -1
- package/README.md +98 -12
- package/demo/grok.js +1 -1
- package/demo/short.js +1 -1
- package/index.js +29 -25
- package/package.json +1 -1
- package/skills/modelmix/SKILL.md +51 -5
- package/test/deepseek.test.js +20 -0
- package/test/grok.test.js +21 -0
- package/test/live.mcp.js +2 -2
- package/test/live.test.js +28 -8
- package/test/qwen.test.js +20 -0
- package/test/tokens.test.js +12 -0
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
|
-
.
|
|
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
|
-
| `
|
|
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
|
|
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 `
|
|
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` | — |
|
|
364
|
-
| `default` | `any` | — | Default value for the
|
|
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
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
|
-
.
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
428
|
-
return this.attach('grok-
|
|
439
|
+
grok420multiAgent({ options = {}, config = {} } = {}) {
|
|
440
|
+
return this.attach('grok-4.20-multi-agent-0309', new MixGrok({ options, config }));
|
|
429
441
|
}
|
|
430
|
-
|
|
431
|
-
return this.attach('grok-4-
|
|
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
|
-
|
|
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
package/skills/modelmix/SKILL.md
CHANGED
|
@@ -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
|
-
`
|
|
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
|
-
`
|
|
116
|
+
`qwen36plus()` `kimiK25think()` `gptOss()`
|
|
117
117
|
|
|
118
118
|
### MiniMax
|
|
119
|
-
`minimaxM25()` `
|
|
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:
|
|
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
|
|
195
|
-
const model = ModelMix.new(setup).
|
|
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
|
|
208
|
-
const model = ModelMix.new(setup).
|
|
207
|
+
it('should work with Grok 4.1 model', async function () {
|
|
208
|
+
const model = ModelMix.new(setup).grok41();
|
|
209
209
|
|
|
210
|
-
model.addText('Say "
|
|
210
|
+
model.addText('Say "grok41 test successful" and nothing else.');
|
|
211
211
|
|
|
212
212
|
const response = await model.message();
|
|
213
|
-
console.log(`
|
|
213
|
+
console.log(`Grok 4.1 response: ${response}`);
|
|
214
214
|
|
|
215
215
|
expect(response).to.be.a('string');
|
|
216
|
-
expect(response.toLowerCase()).to.include('
|
|
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).
|
|
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(`
|
|
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
|
+
});
|
package/test/tokens.test.js
CHANGED
|
@@ -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
|
|