modelmix 4.7.0 → 4.7.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 +7 -5
- package/demo/free.js +1 -1
- package/effort.js +42 -0
- package/index.d.ts +4 -3
- package/index.js +23 -11
- package/package.json +2 -2
- package/skills/modelmix/SKILL.md +8 -8
- package/test/grok.test.js +38 -2
- package/test/live.test.js +3 -3
- package/test/tokens.test.js +5 -2
package/README.md
CHANGED
|
@@ -89,7 +89,7 @@ console.log(ETH.price);
|
|
|
89
89
|
```javascript
|
|
90
90
|
ModelMix.new()
|
|
91
91
|
.gptOss()
|
|
92
|
-
.
|
|
92
|
+
.kimiK25()
|
|
93
93
|
.hermes3()
|
|
94
94
|
.addText('What is the capital of France?');
|
|
95
95
|
```
|
|
@@ -126,7 +126,7 @@ ModelMix.new().effort(-1).minimaxM3().addText('...').message();
|
|
|
126
126
|
|
|
127
127
|
\* Gemini bands: 0–24 / 25–49 / 50–74 / 75–100. DeepSeek `↑` = thinking on; `off` = thinking disabled. MiniMax `off`/`adaptive` = `thinking.disabled` / `thinking.type=adaptive`. Gemini 2.5 maps 0–100 to `thinkingBudget`. Anthropic maps adaptive thinking + `output_config.effort` on Claude 5 / Fable / Opus 4.6+ / Sonnet 4.6+; older models (Sonnet 4.5, Haiku 4.5) get `thinking.type=enabled` + `budget_tokens`. `-1` = provider adaptive/dynamic when available, else no-op. Levels clamp to what each model supports.
|
|
128
128
|
|
|
129
|
-
Migration: former
|
|
129
|
+
Migration: former `*think()` shorthands are removed — use `.effort(n).<model>()` (or any 0–100 / `-1`). Kimi: `kimiK25()` / `kimiK26()`. Grok 4.20: `.grok420()` is non-reasoning; `.effort(20+).grok420()` (or `-1`) selects the reasoning model.
|
|
130
130
|
|
|
131
131
|
## 🔧 Model Context Protocol (MCP) Integration
|
|
132
132
|
|
|
@@ -190,11 +190,12 @@ Here's a comprehensive list of available methods:
|
|
|
190
190
|
| `gemini31pro()` | Google | gemini-3.1-pro-preview | [\$2.00 / \$12.00][3] |
|
|
191
191
|
| `gemini36flash()` | Google | gemini-3.6-flash | [\$1.50 / \$7.50][3] |
|
|
192
192
|
| `gemini35flash()` | Google | gemini-3.5-flash | [\$0.75 / \$4.50][3] |
|
|
193
|
+
| `gemini35flashLite()`| Google | gemini-3.5-flash-lite | [\$0.30 / \$2.50][3] |
|
|
193
194
|
| `gemini31flashLite()`| Google | gemini-3.1-flash-lite-preview | [\$0.25 / \$1.50][3] |
|
|
194
195
|
| `grok45()` | Grok | grok-4.5 | [\$2.00 / \$6.00][6] |
|
|
195
196
|
| `grok43()` | Grok | grok-4.3 | [\$1.25 / \$2.50][6] |
|
|
196
197
|
| `grok420multiAgent()`| Grok | grok-4.20-multi-agent-0309 | [\$1.25 / \$2.50][6] |
|
|
197
|
-
| `grok420
|
|
198
|
+
| `grok420()` | Grok | grok-4.20-0309 (†) | [\$1.25 / \$2.50][6] |
|
|
198
199
|
| `qwen36plus()` | Fireworks/Together | qwen3p6-plus / Qwen3.6-Plus | [\$0.50 / \$3.00][10] |
|
|
199
200
|
| `qwen37plus()` | Fireworks | models/qwen3p7-plus | [\$0.40 / \$1.60][10] |
|
|
200
201
|
| `qwen38max()` | OpenRouter | qwen/qwen3.8-max | [\$2.00 / \$6.00][12] |
|
|
@@ -208,8 +209,8 @@ Here's a comprehensive list of available methods:
|
|
|
208
209
|
| `sonarPro()` | Perplexity | sonar-pro | [\$3.00 / \$15.00][4] |
|
|
209
210
|
| `hermes3()` | Lambda | Hermes-3-Llama-3.1-405B-FP8 | [\$0.80 / \$0.80][8] |
|
|
210
211
|
| `kimiK3()` | Moonshot | kimi-k3 | [\$3.00 / \$15.00][11] |
|
|
211
|
-
| `
|
|
212
|
-
| `
|
|
212
|
+
| `kimiK25()` | Together | Kimi-K2.5 | [\$0.50 / \$2.80][7] |
|
|
213
|
+
| `kimiK26()` | Fireworks | models/kimi-k2p6 | [\$0.95 / \$4.00][10] |
|
|
213
214
|
|
|
214
215
|
[1]: https://platform.openai.com/docs/pricing "Pricing | OpenAI"
|
|
215
216
|
[2]: https://docs.anthropic.com/en/docs/about-claude/pricing "Pricing - Anthropic"
|
|
@@ -225,6 +226,7 @@ Here's a comprehensive list of available methods:
|
|
|
225
226
|
[12]: https://openrouter.ai/qwen/qwen3.8-max "OpenRouter Pricing"
|
|
226
227
|
|
|
227
228
|
Each method accepts optional `options`, `config`, and (for multi-provider methods) `mix` parameters to customize behavior.
|
|
229
|
+
† `grok420()` resolves to `grok-4.20-0309-non-reasoning` by default, or `grok-4.20-0309-reasoning` when `.effort(20+)` / `-1` (or native non-`none` `reasoning_effort`) is set.
|
|
228
230
|
For NVIDIA on DeepSeek V4 Flash/Pro, use `deepseekV4Flash({ mix: { nvidia: true } })` or `deepseekV4Pro({ mix: { nvidia: true } })`.
|
|
229
231
|
For Together on Qwen 3.6 Plus, use `qwen36plus({ mix: { fireworks: false, together: true } })`.
|
|
230
232
|
For OpenRouter on Qwen 3.7 Plus, use `qwen37plus({ mix: { fireworks: false, openrouter: true } })`.
|
package/demo/free.js
CHANGED
package/effort.js
CHANGED
|
@@ -105,6 +105,39 @@ const PROVIDER_FAMILY_BY_CLASS = {
|
|
|
105
105
|
MixCustom: null,
|
|
106
106
|
};
|
|
107
107
|
|
|
108
|
+
/** Logical alias from `.grok420()` — resolved to reasoning / non-reasoning at request time. */
|
|
109
|
+
const GROK420_ALIAS = 'grok-4.20-0309';
|
|
110
|
+
const GROK420_REASONING = 'grok-4.20-0309-reasoning';
|
|
111
|
+
const GROK420_NON_REASONING = 'grok-4.20-0309-non-reasoning';
|
|
112
|
+
|
|
113
|
+
function isGrok420Alias(modelKey) {
|
|
114
|
+
return modelKey === GROK420_ALIAS;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Pick Grok 4.20 concrete model from unified effort (and native reasoning_effort).
|
|
119
|
+
* - no effort / OpenAI band `none` (0–19) / native `none` → non-reasoning
|
|
120
|
+
* - effort -1 or 20–100 / native non-none reasoning_effort → reasoning
|
|
121
|
+
*/
|
|
122
|
+
function resolveGrok420ModelKey(modelKey, effort, options = {}) {
|
|
123
|
+
if (!isGrok420Alias(modelKey)) return modelKey;
|
|
124
|
+
|
|
125
|
+
const native = options.reasoning_effort;
|
|
126
|
+
if (native != null && native !== '') {
|
|
127
|
+
return native === 'none' ? GROK420_NON_REASONING : GROK420_REASONING;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (effort === undefined || effort === null) {
|
|
131
|
+
return GROK420_NON_REASONING;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const normalized = normalizeEffort(effort);
|
|
135
|
+
if (normalized === -1) return GROK420_REASONING;
|
|
136
|
+
|
|
137
|
+
const level = levelFromBands(normalized, OPENAI_BANDS);
|
|
138
|
+
return level === 'none' ? GROK420_NON_REASONING : GROK420_REASONING;
|
|
139
|
+
}
|
|
140
|
+
|
|
108
141
|
function normalizeEffort(value) {
|
|
109
142
|
if (typeof value !== 'number' || !Number.isFinite(value) || !Number.isInteger(value)) {
|
|
110
143
|
throw new Error(`Invalid effort: expected integer -1 or 0..100, got ${JSON.stringify(value)}`);
|
|
@@ -332,6 +365,10 @@ function mapEffort(providerFamily, effort, modelKey) {
|
|
|
332
365
|
if (isMiniMax(modelKey)) {
|
|
333
366
|
return mapMiniMaxEffort(normalized);
|
|
334
367
|
}
|
|
368
|
+
// Non-reasoning Grok 4.20 has no reasoning_effort control
|
|
369
|
+
if (modelKey === GROK420_NON_REASONING) {
|
|
370
|
+
return null;
|
|
371
|
+
}
|
|
335
372
|
const desired = levelFromBands(normalized, OPENAI_BANDS);
|
|
336
373
|
const level = pickNearestLevel(desired, OPENAI_LEVELS, supportedOpenAILevels(modelKey));
|
|
337
374
|
return { reasoning_effort: level };
|
|
@@ -409,6 +446,8 @@ module.exports = {
|
|
|
409
446
|
applyUnifiedEffort,
|
|
410
447
|
hasNativeEffort,
|
|
411
448
|
resolveProviderFamily,
|
|
449
|
+
resolveGrok420ModelKey,
|
|
450
|
+
isGrok420Alias,
|
|
412
451
|
isDeepSeekV4,
|
|
413
452
|
isMiniMax,
|
|
414
453
|
usesAnthropicAdaptiveThinking,
|
|
@@ -423,4 +462,7 @@ module.exports = {
|
|
|
423
462
|
GEMINI_LEVELS,
|
|
424
463
|
DEEPSEEK_LEVELS,
|
|
425
464
|
ANTHROPIC_MANUAL_BUDGET_MAX,
|
|
465
|
+
GROK420_ALIAS,
|
|
466
|
+
GROK420_REASONING,
|
|
467
|
+
GROK420_NON_REASONING,
|
|
426
468
|
};
|
package/index.d.ts
CHANGED
|
@@ -302,6 +302,7 @@ export declare class ModelMix {
|
|
|
302
302
|
gemini3flash(args?: ModelAttachArgs): this;
|
|
303
303
|
gemini36flash(args?: ModelAttachArgs): this;
|
|
304
304
|
gemini35flash(args?: ModelAttachArgs): this;
|
|
305
|
+
gemini35flashLite(args?: ModelAttachArgs): this;
|
|
305
306
|
gemini31flashLite(args?: ModelAttachArgs): this;
|
|
306
307
|
gemini25pro(args?: ModelAttachArgs): this;
|
|
307
308
|
|
|
@@ -313,7 +314,7 @@ export declare class ModelMix {
|
|
|
313
314
|
grok45(args?: ModelAttachArgs): this;
|
|
314
315
|
grok43(args?: ModelAttachArgs): this;
|
|
315
316
|
grok420multiAgent(args?: ModelAttachArgs): this;
|
|
316
|
-
|
|
317
|
+
/** Non-reasoning by default; with `.effort(20+)` / `-1` uses the reasoning model at request time. */
|
|
317
318
|
grok420(args?: ModelAttachArgs): this;
|
|
318
319
|
|
|
319
320
|
// Multi-provider
|
|
@@ -322,10 +323,10 @@ export declare class ModelMix {
|
|
|
322
323
|
qwen37plus(args?: ModelAttachArgs): this;
|
|
323
324
|
qwen38max(args?: ModelAttachArgs): this;
|
|
324
325
|
hermes3(args?: ModelAttachArgs): this;
|
|
325
|
-
|
|
326
|
+
kimiK26(args?: ModelAttachArgs): this;
|
|
326
327
|
kimiK27Code(args?: ModelAttachArgs): this;
|
|
327
328
|
kimiK3(args?: ModelAttachArgs): this;
|
|
328
|
-
|
|
329
|
+
kimiK25(args?: ModelAttachArgs): this;
|
|
329
330
|
lmstudio(model?: string, args?: ModelAttachArgs): this;
|
|
330
331
|
minimaxM25(args?: ModelAttachArgs): this;
|
|
331
332
|
minimaxM27(args?: ModelAttachArgs): this;
|
package/index.js
CHANGED
|
@@ -23,7 +23,8 @@ const {
|
|
|
23
23
|
const {
|
|
24
24
|
normalizeEffort,
|
|
25
25
|
applyUnifiedEffort,
|
|
26
|
-
resolveProviderFamily
|
|
26
|
+
resolveProviderFamily,
|
|
27
|
+
resolveGrok420ModelKey
|
|
27
28
|
} = require('./effort');
|
|
28
29
|
|
|
29
30
|
const DEFAULT_RETRYABLE_STATUS_CODES = [408, 425, 429, 500, 502, 503, 504, 529];
|
|
@@ -81,6 +82,7 @@ const MODEL_PRICING = {
|
|
|
81
82
|
'gemini-3-flash-preview': [0.50, 3.00],
|
|
82
83
|
'gemini-3.6-flash': [1.50, 7.50],
|
|
83
84
|
'gemini-3.5-flash': [0.75, 4.50],
|
|
85
|
+
'gemini-3.5-flash-lite': [0.30, 2.50],
|
|
84
86
|
'gemini-2.5-pro': [1.25, 10.00],
|
|
85
87
|
'gemini-2.5-flash': [0.30, 2.50],
|
|
86
88
|
'gemini-3.1-flash-lite-preview': [0.25, 1.50],
|
|
@@ -88,6 +90,7 @@ const MODEL_PRICING = {
|
|
|
88
90
|
'grok-4.5': [2.00, 6.00],
|
|
89
91
|
'grok-4.3': [1.25, 2.50],
|
|
90
92
|
'grok-4.20-multi-agent-0309': [1.25, 2.50],
|
|
93
|
+
'grok-4.20-0309': [1.25, 2.50],
|
|
91
94
|
'grok-4.20-0309-reasoning': [1.25, 2.50],
|
|
92
95
|
'grok-4.20-0309-non-reasoning': [1.25, 2.50],
|
|
93
96
|
// Fireworks
|
|
@@ -427,6 +430,9 @@ class ModelMix {
|
|
|
427
430
|
gemini35flash({ options = {}, config = {} } = {}) {
|
|
428
431
|
return this.attach('gemini-3.5-flash', new MixGoogle({ options, config }));
|
|
429
432
|
}
|
|
433
|
+
gemini35flashLite({ options = {}, config = {} } = {}) {
|
|
434
|
+
return this.attach('gemini-3.5-flash-lite', new MixGoogle({ options, config }));
|
|
435
|
+
}
|
|
430
436
|
gemini31flashLite({ options = {}, config = {} } = {}) {
|
|
431
437
|
return this.attach('gemini-3.1-flash-lite-preview', new MixGoogle({ options, config }));
|
|
432
438
|
}
|
|
@@ -449,11 +455,9 @@ class ModelMix {
|
|
|
449
455
|
grok420multiAgent({ options = {}, config = {} } = {}) {
|
|
450
456
|
return this.attach('grok-4.20-multi-agent-0309', new MixGrok({ options, config }));
|
|
451
457
|
}
|
|
452
|
-
|
|
453
|
-
return this.attach('grok-4.20-0309-reasoning', new MixGrok({ options, config }));
|
|
454
|
-
}
|
|
458
|
+
/** Non-reasoning by default; with `.effort(20+)` / `-1` resolves to the reasoning model at request time. */
|
|
455
459
|
grok420({ options = {}, config = {} } = {}) {
|
|
456
|
-
return this.attach('grok-4.20-0309
|
|
460
|
+
return this.attach('grok-4.20-0309', new MixGrok({ options, config }));
|
|
457
461
|
}
|
|
458
462
|
|
|
459
463
|
qwen3({ options = {}, config = {}, mix = { together: true, cerebras: false } } = {}) {
|
|
@@ -489,7 +493,7 @@ class ModelMix {
|
|
|
489
493
|
return this;
|
|
490
494
|
}
|
|
491
495
|
|
|
492
|
-
|
|
496
|
+
kimiK26({ options = {}, config = {}, mix = { fireworks: true } } = {}) {
|
|
493
497
|
mix = { ...this.mix, ...mix };
|
|
494
498
|
if (mix.fireworks) this.attach('accounts/fireworks/models/kimi-k2p6', new MixFireworks({ options, config }));
|
|
495
499
|
if (mix.openrouter) this.attach('moonshotai/kimi-k2.6', new MixOpenRouter({ options, config }));
|
|
@@ -510,7 +514,7 @@ class ModelMix {
|
|
|
510
514
|
return this;
|
|
511
515
|
}
|
|
512
516
|
|
|
513
|
-
|
|
517
|
+
kimiK25({ options = {}, config = {}, mix = { together: true } } = {}) {
|
|
514
518
|
mix = { ...this.mix, ...mix };
|
|
515
519
|
if (mix.together) this.attach('moonshotai/Kimi-K2.5', new MixTogether({ options, config }));
|
|
516
520
|
if (mix.fireworks) this.attach('accounts/fireworks/models/kimi-k2p5', new MixFireworks({ options, config }));
|
|
@@ -991,9 +995,17 @@ class ModelMix {
|
|
|
991
995
|
}
|
|
992
996
|
};
|
|
993
997
|
|
|
998
|
+
// Grok 4.20 alias → reasoning / non-reasoning from unified effort
|
|
999
|
+
const resolvedModelKey = resolveGrok420ModelKey(
|
|
1000
|
+
currentModelKey,
|
|
1001
|
+
currentConfig.effort,
|
|
1002
|
+
currentOptions
|
|
1003
|
+
);
|
|
1004
|
+
currentOptions.model = resolvedModelKey;
|
|
1005
|
+
|
|
994
1006
|
// Unified effort → native provider fields (skipped if native already set)
|
|
995
1007
|
const providerFamily = resolveProviderFamily(providerInstance);
|
|
996
|
-
applyUnifiedEffort(currentOptions, currentConfig, providerFamily,
|
|
1008
|
+
applyUnifiedEffort(currentOptions, currentConfig, providerFamily, resolvedModelKey);
|
|
997
1009
|
|
|
998
1010
|
if (currentConfig.debug >= 1) {
|
|
999
1011
|
const isPrimary = i === 0;
|
|
@@ -1003,7 +1015,7 @@ class ModelMix {
|
|
|
1003
1015
|
: ' (fallback)';
|
|
1004
1016
|
// Extract provider name from class name (e.g., "MixOpenRouter" -> "openrouter")
|
|
1005
1017
|
const providerName = providerInstance.constructor.name.replace(/^Mix/, '').toLowerCase();
|
|
1006
|
-
const header = `\n${prefix} [${providerName}:${
|
|
1018
|
+
const header = `\n${prefix} [${providerName}:${resolvedModelKey}] #${originalIndex + 1}${suffix}`;
|
|
1007
1019
|
|
|
1008
1020
|
if (currentConfig.debug >= 2) {
|
|
1009
1021
|
console.log(`${header}\n${ModelMix.formatInputSummary(this.messages, currentConfig.system, currentConfig.debug)}`);
|
|
@@ -1048,7 +1060,7 @@ class ModelMix {
|
|
|
1048
1060
|
if (currentConfig.debug >= 1) {
|
|
1049
1061
|
const nextAttempt = attempt + 2;
|
|
1050
1062
|
const totalAttempts = retries + 1;
|
|
1051
|
-
console.log(`↺ Retrying [${
|
|
1063
|
+
console.log(`↺ Retrying [${resolvedModelKey}] due to status ${statusCode} (${nextAttempt}/${totalAttempts})`);
|
|
1052
1064
|
}
|
|
1053
1065
|
|
|
1054
1066
|
const delay = Math.min(baseDelayMs * Math.pow(2, attempt), maxDelayMs);
|
|
@@ -1060,7 +1072,7 @@ class ModelMix {
|
|
|
1060
1072
|
const elapsedMs = Date.now() - startTime;
|
|
1061
1073
|
|
|
1062
1074
|
if (result.tokens) {
|
|
1063
|
-
result.tokens.cost = ModelMix.calculateCost(
|
|
1075
|
+
result.tokens.cost = ModelMix.calculateCost(resolvedModelKey, result.tokens);
|
|
1064
1076
|
const elapsedSec = elapsedMs / 1000;
|
|
1065
1077
|
result.tokens.speed = elapsedSec > 0 ? Math.round(result.tokens.output / elapsedSec) : 0;
|
|
1066
1078
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "modelmix",
|
|
3
|
-
"version": "4.7.
|
|
3
|
+
"version": "4.7.4",
|
|
4
4
|
"description": "🧬 Reliable interface with automatic fallback for AI LLMs.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -72,6 +72,6 @@
|
|
|
72
72
|
"test:live": "mocha test/live.test.js --timeout 10000 --require test/setup.js",
|
|
73
73
|
"test:live.mcp": "mocha test/live.mcp.js --timeout 60000 --require test/setup.js",
|
|
74
74
|
"test:tokens": "mocha test/tokens.test.js --timeout 10000 --require test/setup.js",
|
|
75
|
-
"test:offline": "mocha test/json.test.js test/fallback.test.js test/templates.test.js test/images.test.js test/bottleneck.test.js test/tokens.test.js test/history.test.js test/anthropic.test.js test/effort.test.js --timeout 10000 --require test/setup.js"
|
|
75
|
+
"test:offline": "mocha test/json.test.js test/fallback.test.js test/templates.test.js test/images.test.js test/bottleneck.test.js test/tokens.test.js test/history.test.js test/anthropic.test.js test/effort.test.js test/grok.test.js --timeout 10000 --require test/setup.js"
|
|
76
76
|
}
|
|
77
77
|
}
|
package/skills/modelmix/SKILL.md
CHANGED
|
@@ -114,7 +114,7 @@ ModelMix.new({ config: { effort: 80 } })
|
|
|
114
114
|
| DeepSeek V4 | off | `low`↑ | `high`↑ | `high`↑ | `max`↑ | — |
|
|
115
115
|
| MiniMax M3 | off | adaptive | adaptive | adaptive | adaptive | adaptive |
|
|
116
116
|
|
|
117
|
-
\* Gemini bands: 0–24 / 25–49 / 50–74 / 75–100. DeepSeek `↑` = thinking on; `off` = thinking disabled. MiniMax `off`/`adaptive` = `thinking.disabled` / `thinking.type=adaptive`. Gemini 2.5 maps 0–100 to `thinkingBudget`. Anthropic: adaptive + `output_config.effort` on Claude 5 / Fable / Opus 4.6+ / Sonnet 4.6+; Sonnet 4.5 / Haiku 4.5 use `thinking.type=enabled` + `budget_tokens`. `-1` = adaptive/dynamic when available, else no-op. Levels clamp per model. Former
|
|
117
|
+
\* Gemini bands: 0–24 / 25–49 / 50–74 / 75–100. DeepSeek `↑` = thinking on; `off` = thinking disabled. MiniMax `off`/`adaptive` = `thinking.disabled` / `thinking.type=adaptive`. Gemini 2.5 maps 0–100 to `thinkingBudget`. Anthropic: adaptive + `output_config.effort` on Claude 5 / Fable / Opus 4.6+ / Sonnet 4.6+; Sonnet 4.5 / Haiku 4.5 use `thinking.type=enabled` + `budget_tokens`. `-1` = adaptive/dynamic when available, else no-op. Levels clamp per model. Former `*think()` methods are removed — use `.effort(n).<model>()`. Kimi: `kimiK25()` / `kimiK26()`. Grok 4.20: `.grok420()` non-reasoning; `.effort(20+|-1).grok420()` selects reasoning.
|
|
118
118
|
|
|
119
119
|
## Available Model Shorthands
|
|
120
120
|
|
|
@@ -127,16 +127,16 @@ ModelMix.new({ config: { effort: 80 } })
|
|
|
127
127
|
Use `.effort(n)` (or `config.effort`) to enable Anthropic thinking — e.g. `.effort(100).opus5()`.
|
|
128
128
|
|
|
129
129
|
### Google
|
|
130
|
-
`gemini3pro()` `gemini3flash()` `gemini36flash()` `gemini35flash()` `gemini25pro()` `gemini25flash()`
|
|
130
|
+
`gemini3pro()` `gemini3flash()` `gemini36flash()` `gemini35flash()` `gemini35flashLite()` `gemini31flashLite()` `gemini25pro()` `gemini25flash()`
|
|
131
131
|
|
|
132
132
|
### Grok
|
|
133
|
-
`grok45()` `grok43()` `grok420multiAgent()` `grok420()`
|
|
133
|
+
`grok45()` `grok43()` `grok420multiAgent()` `grok420()`
|
|
134
134
|
|
|
135
135
|
### Perplexity
|
|
136
136
|
`sonar()` `sonarPro()`
|
|
137
137
|
|
|
138
138
|
### Together
|
|
139
|
-
`qwen36plus()` `GLM52()` `
|
|
139
|
+
`qwen36plus()` `GLM52()` `kimiK25()` `kimiK26()` `gptOss()`
|
|
140
140
|
|
|
141
141
|
### Moonshot
|
|
142
142
|
`kimiK3()` — requires `MOONSHOT_API_KEY`; use `{ mix: { moonshot: false, openrouter: true } }` for OpenRouter.
|
|
@@ -145,7 +145,7 @@ Use `.effort(n)` (or `config.effort`) to enable Anthropic thinking — e.g. `.ef
|
|
|
145
145
|
`minimaxM25()` `minimaxM27()` `minimaxM3()`
|
|
146
146
|
|
|
147
147
|
### Fireworks
|
|
148
|
-
`qwen36plus()` `qwen37plus()` `deepseekV4Flash()` `deepseekV4Pro()`
|
|
148
|
+
`qwen36plus()` `qwen37plus()` `deepseekV4Flash()` `deepseekV4Pro()` `kimiK26()`
|
|
149
149
|
|
|
150
150
|
### Cerebras
|
|
151
151
|
`GLM46()`
|
|
@@ -154,7 +154,7 @@ Use `.effort(n)` (or `config.effort`) to enable Anthropic thinking — e.g. `.ef
|
|
|
154
154
|
`qwen38max()` `GLM45()`
|
|
155
155
|
|
|
156
156
|
### Multi-provider (auto-fallback across free/paid tiers)
|
|
157
|
-
`hermes3()` `
|
|
157
|
+
`hermes3()` `kimiK25()`
|
|
158
158
|
|
|
159
159
|
### Local
|
|
160
160
|
`lmstudio()` — for LM Studio local models
|
|
@@ -429,7 +429,7 @@ For full debug output, also set: `DEBUG=ModelMix* node script.js`
|
|
|
429
429
|
```javascript
|
|
430
430
|
const model = ModelMix.new()
|
|
431
431
|
.gptOss()
|
|
432
|
-
.
|
|
432
|
+
.kimiK25()
|
|
433
433
|
.hermes3()
|
|
434
434
|
.addText("What is the capital of France?");
|
|
435
435
|
console.log(await model.message());
|
|
@@ -452,7 +452,7 @@ const model = ModelMix.new({
|
|
|
452
452
|
minimax: false, // default: false
|
|
453
453
|
fireworks: false // default: false
|
|
454
454
|
}
|
|
455
|
-
}).
|
|
455
|
+
}).kimiK25();
|
|
456
456
|
```
|
|
457
457
|
|
|
458
458
|
## Agent Usage Rules
|
package/test/grok.test.js
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
const { expect } = require('chai');
|
|
2
2
|
const { ModelMix } = require('../index.js');
|
|
3
|
+
const {
|
|
4
|
+
resolveGrok420ModelKey,
|
|
5
|
+
GROK420_ALIAS,
|
|
6
|
+
GROK420_REASONING,
|
|
7
|
+
GROK420_NON_REASONING
|
|
8
|
+
} = require('../effort.js');
|
|
3
9
|
|
|
4
10
|
describe('Grok Model Registration Tests', () => {
|
|
5
11
|
const grokModels = [
|
|
6
12
|
{ method: 'grok45', key: 'grok-4.5' },
|
|
7
13
|
{ method: 'grok43', key: 'grok-4.3' },
|
|
8
14
|
{ method: 'grok420multiAgent', key: 'grok-4.20-multi-agent-0309' },
|
|
9
|
-
{ method: '
|
|
10
|
-
{ method: 'grok420', key: 'grok-4.20-0309-non-reasoning' }
|
|
15
|
+
{ method: 'grok420', key: GROK420_ALIAS }
|
|
11
16
|
];
|
|
12
17
|
|
|
13
18
|
for (const grokModel of grokModels) {
|
|
@@ -20,3 +25,34 @@ describe('Grok Model Registration Tests', () => {
|
|
|
20
25
|
});
|
|
21
26
|
}
|
|
22
27
|
});
|
|
28
|
+
|
|
29
|
+
describe('Grok 4.20 effort → model resolution', () => {
|
|
30
|
+
it('uses non-reasoning when effort is unset', () => {
|
|
31
|
+
expect(resolveGrok420ModelKey(GROK420_ALIAS, undefined)).to.equal(GROK420_NON_REASONING);
|
|
32
|
+
expect(resolveGrok420ModelKey(GROK420_ALIAS, null)).to.equal(GROK420_NON_REASONING);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('uses non-reasoning for OpenAI none band (0–19)', () => {
|
|
36
|
+
expect(resolveGrok420ModelKey(GROK420_ALIAS, 0)).to.equal(GROK420_NON_REASONING);
|
|
37
|
+
expect(resolveGrok420ModelKey(GROK420_ALIAS, 19)).to.equal(GROK420_NON_REASONING);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('uses reasoning for effort 20–100 and -1', () => {
|
|
41
|
+
expect(resolveGrok420ModelKey(GROK420_ALIAS, 20)).to.equal(GROK420_REASONING);
|
|
42
|
+
expect(resolveGrok420ModelKey(GROK420_ALIAS, 50)).to.equal(GROK420_REASONING);
|
|
43
|
+
expect(resolveGrok420ModelKey(GROK420_ALIAS, 100)).to.equal(GROK420_REASONING);
|
|
44
|
+
expect(resolveGrok420ModelKey(GROK420_ALIAS, -1)).to.equal(GROK420_REASONING);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('honors native reasoning_effort over config.effort', () => {
|
|
48
|
+
expect(resolveGrok420ModelKey(GROK420_ALIAS, 0, { reasoning_effort: 'high' }))
|
|
49
|
+
.to.equal(GROK420_REASONING);
|
|
50
|
+
expect(resolveGrok420ModelKey(GROK420_ALIAS, 100, { reasoning_effort: 'none' }))
|
|
51
|
+
.to.equal(GROK420_NON_REASONING);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('leaves non-alias keys unchanged', () => {
|
|
55
|
+
expect(resolveGrok420ModelKey(GROK420_REASONING, 50)).to.equal(GROK420_REASONING);
|
|
56
|
+
expect(resolveGrok420ModelKey('grok-4.3', 50)).to.equal('grok-4.3');
|
|
57
|
+
});
|
|
58
|
+
});
|
package/test/live.test.js
CHANGED
|
@@ -194,8 +194,8 @@ describe('Live Integration Tests', function () {
|
|
|
194
194
|
|
|
195
195
|
const grokSeriesTests = [
|
|
196
196
|
{ name: 'Grok 4.3', factory: (m) => m.grok43(), token: 'grok43' },
|
|
197
|
-
{ name: 'Grok 4.20 reasoning', factory: (m) => m.
|
|
198
|
-
{ name: 'Grok 4.20 non-reasoning', factory: (m) => m.grok420(), token: '
|
|
197
|
+
{ name: 'Grok 4.20 reasoning', factory: (m) => m.effort(50).grok420(), token: 'grok420' },
|
|
198
|
+
{ name: 'Grok 4.20 non-reasoning', factory: (m) => m.grok420(), token: 'grok420nr' }
|
|
199
199
|
];
|
|
200
200
|
|
|
201
201
|
for (const grokModel of grokSeriesTests) {
|
|
@@ -242,7 +242,7 @@ describe('Live Integration Tests', function () {
|
|
|
242
242
|
describe('JSON Structured Output for New Models', function () {
|
|
243
243
|
|
|
244
244
|
it('should return structured JSON with KimiK25 Thinking', async function () {
|
|
245
|
-
const model = ModelMix.new(setup).
|
|
245
|
+
const model = ModelMix.new(setup).kimiK25();
|
|
246
246
|
|
|
247
247
|
model.addText('Generate information about a fictional vehicle.');
|
|
248
248
|
|
package/test/tokens.test.js
CHANGED
|
@@ -108,15 +108,18 @@ describe('Token Usage Tracking', () => {
|
|
|
108
108
|
it('should register Gemini Flash shortcuts with Google provider', function () {
|
|
109
109
|
const model = ModelMix.new()
|
|
110
110
|
.gemini36flash()
|
|
111
|
-
.gemini35flash()
|
|
111
|
+
.gemini35flash()
|
|
112
|
+
.gemini35flashLite();
|
|
112
113
|
|
|
113
114
|
expect(model.models.map(({ key }) => key)).to.deep.equal([
|
|
114
115
|
'gemini-3.6-flash',
|
|
115
|
-
'gemini-3.5-flash'
|
|
116
|
+
'gemini-3.5-flash',
|
|
117
|
+
'gemini-3.5-flash-lite'
|
|
116
118
|
]);
|
|
117
119
|
expect(model.models.every(({ provider }) => provider instanceof MixGoogle)).to.equal(true);
|
|
118
120
|
expect(ModelMix.calculateCost('gemini-3.6-flash', { input: 1_000_000, output: 1_000_000 })).to.equal(9);
|
|
119
121
|
expect(ModelMix.calculateCost('gemini-3.5-flash', { input: 1_000_000, output: 1_000_000 })).to.equal(5.25);
|
|
122
|
+
expect(ModelMix.calculateCost('gemini-3.5-flash-lite', { input: 1_000_000, output: 1_000_000 })).to.equal(2.8);
|
|
120
123
|
});
|
|
121
124
|
|
|
122
125
|
it('should register MiMo shortcuts with native and OpenRouter providers', function () {
|