modelmix 5.1.2 → 5.1.6
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 +16 -3
- package/demo/custom.js +2 -2
- package/demo/short.js +5 -2
- package/effort.js +24 -3
- package/index.d.ts +3 -2
- package/index.js +30 -16
- package/lib/model-chain.js +3 -2
- package/lib/token-usage.js +37 -9
- package/package.json +1 -1
- package/skills/modelmix/SKILL.md +8 -5
- package/test/effort.test.js +53 -0
- package/test/fallback.test.js +1 -1
- package/test/glm.test.js +47 -3
- package/test/kimi.test.js +34 -4
- package/test/muse.test.js +61 -0
- package/test/provider-expansion.test.js +83 -0
- package/test/qwen.test.js +52 -3
package/README.md
CHANGED
|
@@ -151,7 +151,7 @@ ModelMix provides convenient shorthand methods for quickly accessing different A
|
|
|
151
151
|
| `gpt51()` | OpenAI | gpt-5.1 | [\$1.25][1] | [\$10.00][1] |
|
|
152
152
|
| `gpt5mini()` | OpenAI | gpt-5-mini | [\$0.25][1] | [\$2.00][1] |
|
|
153
153
|
| `gpt5nano()` | OpenAI | gpt-5-nano | [\$0.05][1] | [\$0.40][1] |
|
|
154
|
-
| `gptOss()` |
|
|
154
|
+
| `gptOss()` | Cerebras/Groq/OpenRouter | gpt-oss-120B | [\$0.15][7] | [\$0.60][7] |
|
|
155
155
|
| `fable5()` | Anthropic | claude-fable-5 | [\$10.00][2] | [\$50.00][2] |
|
|
156
156
|
| `opus5()` | Anthropic | claude-opus-5 | [\$5.00][2] | [\$25.00][2] |
|
|
157
157
|
| `opus48()` | Anthropic | claude-opus-4-8 | [\$5.00][2] | [\$25.00][2] |
|
|
@@ -171,14 +171,16 @@ ModelMix provides convenient shorthand methods for quickly accessing different A
|
|
|
171
171
|
| `grok43()` | Grok | grok-4.3 | [\$1.25][6] | [\$2.50][6] |
|
|
172
172
|
| `grok420multiAgent()` | Grok | grok-4.20-multi-agent-0309 | [\$1.25][6] | [\$2.50][6] |
|
|
173
173
|
| `grok420()` | Grok | grok-4.20-0309 (†) | [\$1.25][6] | [\$2.50][6] |
|
|
174
|
+
| `museGlimmer30b()` | Fireworks | models/muse-glimmer-30b | [\$0.35][17] | [\$1.50][17] |
|
|
174
175
|
| `qwen35397b()` | OpenRouter | qwen/qwen3.5-397b-a17b | [\$0.385][14] | [\$2.45][14] |
|
|
175
|
-
| `qwen36plus()` |
|
|
176
|
+
| `qwen36plus()` | OpenRouter | qwen/qwen3.6-plus | [\$0.325][18] | [\$1.95][18] |
|
|
176
177
|
| `qwen37plus()` | Fireworks | models/qwen3p7-plus | [\$0.40][10] | [\$1.60][10] |
|
|
177
178
|
| `qwen38max()` | Fireworks | qwen3p8-2p4t-a95b | [\$2.00][10] | [\$6.00][10] |
|
|
179
|
+
| `qwen3827b()` | OpenRouter | qwen/qwen3.8-27b | [\$0.45][15] | [\$3.20][15] |
|
|
178
180
|
| `deepseekV4Flash()` | Fireworks | models/deepseek-v4-flash | [\$0.14][10] | [\$0.28][10] |
|
|
179
181
|
| `deepseekV4Pro()` | Fireworks | models/deepseek-v4-pro-0813 | [\$1.32][12] | [\$3.96][12] |
|
|
182
|
+
| `GLM53()` | OpenRouter | z-ai/glm-5.3 | [\$1.40][16] | [\$4.40][16] |
|
|
180
183
|
| `GLM52()` | Together | zai-org/GLM-5.2 | [\$1.40][7] | [\$4.40][7] |
|
|
181
|
-
| `GLM51()` | Fireworks | models/glm-5p1 | [\$1.05][10] | [\$3.50][10] |
|
|
182
184
|
| `minimaxM3()` | MiniMax | MiniMax-M3 | [\$0.30][9] | [\$1.20][9] |
|
|
183
185
|
| `minimaxM27()` | MiniMax | MiniMax-M2.7 | [\$0.30][9] | [\$1.20][9] |
|
|
184
186
|
| `sonar()` | Perplexity | sonar | [\$1.00][4] | [\$1.00][4] |
|
|
@@ -187,11 +189,16 @@ ModelMix provides convenient shorthand methods for quickly accessing different A
|
|
|
187
189
|
| `hermes4405b()` | OpenRouter | nousresearch/hermes-4-405b | [\$1.00][13] | [\$3.00][13] |
|
|
188
190
|
| `hermes3()` | Lambda | Hermes-3-Llama-3.1-405B-FP8 | [\$0.80][8] | [\$0.80][8] |
|
|
189
191
|
| `kimiK3()` | Moonshot | kimi-k3 | [\$3.00][11] | [\$15.00][11] |
|
|
192
|
+
| `kimiK27Code()` | Together | Kimi-K2.7-Code | [\$0.95][7] | [\$4.00][7] |
|
|
190
193
|
| `kimiK25()` | Together | Kimi-K2.5 | [\$0.50][7] | [\$2.80][7] |
|
|
191
194
|
| `kimiK26()` | Fireworks | models/kimi-k2p6 | [\$0.95][10] | [\$4.00][10] |
|
|
192
195
|
|
|
193
196
|
Gemini 3.7 Flash and 3.6 Flash use Google's introductory standard pricing through December 31, 2026; standard rates double on January 1, 2027.
|
|
194
197
|
|
|
198
|
+
`museGlimmer30b()` uses Fireworks first and OpenRouter as its default fallback. NVIDIA NIM and Together are also available through `mix.nvidia` and `mix.together`; disable either default provider with `mix.fireworks: false` or `mix.openrouter: false`.
|
|
199
|
+
|
|
200
|
+
The multi-provider shortcuts also expose the current catalog alternatives: `gptOss()` supports NVIDIA and Fireworks; `qwen37plus()` supports Together; `kimiK27Code()` supports Fireworks and OpenRouter; `kimiK3()` supports Fireworks, OpenRouter, and Together; `GLM52()` supports Fireworks and OpenRouter; and both MiniMax shortcuts support Fireworks. `minimaxM27()` keeps every enabled provider in its fallback chain. `qwen36plus()` now defaults to OpenRouter because Fireworks retired its serverless deployment; Fireworks remains available explicitly for private or on-demand deployments.
|
|
201
|
+
|
|
195
202
|
[1]: https://platform.openai.com/docs/pricing "Pricing | OpenAI"
|
|
196
203
|
[2]: https://docs.anthropic.com/en/docs/about-claude/pricing "Pricing - Anthropic"
|
|
197
204
|
[3]: https://ai.google.dev/gemini-api/docs/pricing "Google AI for Developers"
|
|
@@ -206,6 +213,10 @@ Gemini 3.7 Flash and 3.6 Flash use Google's introductory standard pricing throug
|
|
|
206
213
|
[12]: https://fireworks.ai/models/deepseek-ai/deepseek-v4-pro-0813 "DeepSeek V4 Pro 0813 Pricing"
|
|
207
214
|
[13]: https://openrouter.ai/nousresearch "Nous Research Models on OpenRouter"
|
|
208
215
|
[14]: https://openrouter.ai/qwen/qwen3.5-397b-a17b "Qwen3.5 397B A17B on OpenRouter"
|
|
216
|
+
[15]: https://openrouter.ai/qwen/qwen3.8-27b "Qwen3.8 27B on OpenRouter"
|
|
217
|
+
[16]: https://openrouter.ai/z-ai/glm-5.3 "GLM 5.3 on OpenRouter"
|
|
218
|
+
[17]: https://fireworks.ai/models/fireworks/muse-glimmer-30b "Muse Glimmer 30B on Fireworks"
|
|
219
|
+
[18]: https://openrouter.ai/qwen/qwen3.6-plus "Qwen 3.6 Plus on OpenRouter"
|
|
209
220
|
|
|
210
221
|
Each method accepts optional `options`, `config`, and (for multi-provider methods) `mix` parameters to customize behavior.
|
|
211
222
|
|
|
@@ -246,6 +257,8 @@ ModelMix.new().effort(-1).minimaxM3().addText('...').message();
|
|
|
246
257
|
|
|
247
258
|
- **Gemini:** Gemini 3+ uses bands 0–24 / 25–49 / 50–74 / 75–100. Gemini 3.7 Flash clamps these bands to `low` / `low` / `medium` / `high`; `-1` leaves its native `medium` default unchanged. Gemini 2.5 maps 0–100 to `thinkingBudget`.
|
|
248
259
|
- **GPT-5.6:** `100` maps to `max`; 80–99 remains `xhigh`.
|
|
260
|
+
- **Qwen 3.8 27B:** 0–39 / 40–79 / 80–100 map to `low` / `medium` / `xhigh`; `-1` leaves the native `xhigh` default unchanged.
|
|
261
|
+
- **GLM 5.3:** reasoning is mandatory; 0–39 / 40–79 / 80–100 map to `low` / `high` / `max`; `-1` leaves the native `max` default unchanged.
|
|
249
262
|
- **DeepSeek:** `↑` means thinking is enabled; `off` means it is disabled.
|
|
250
263
|
- **MiniMax:** `off` maps to `thinking.disabled`; `adaptive` maps to `thinking.type=adaptive`.
|
|
251
264
|
- **Anthropic:** Claude 5, Fable, Opus 4.6+, and Sonnet 4.6+ use adaptive thinking with `output_config.effort`. Sonnet 4.5 and Haiku 4.5 use `thinking.type=enabled` with `budget_tokens`.
|
package/demo/custom.js
CHANGED
|
@@ -50,8 +50,8 @@ class MixTogether extends MixCustom {
|
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
mmix.attach('Qwen/Qwen3-
|
|
53
|
+
mmix.attach('Qwen/Qwen3.6-Plus', new MixTogether());
|
|
54
54
|
|
|
55
55
|
let r = mmix.addText('hi there');
|
|
56
56
|
r = await r.addText('do you like cats?').message();
|
|
57
|
-
console.log(r);
|
|
57
|
+
console.log(r);
|
package/demo/short.js
CHANGED
|
@@ -15,8 +15,11 @@ const mmix = await ModelMix.new(setup)
|
|
|
15
15
|
.gpt5nano() // (fallback 3) OpenAI gpt-5-nano
|
|
16
16
|
.grok46() // (fallback 4) Grok grok-4.6
|
|
17
17
|
.qwen35397b() // (fallback 5) OpenRouter qwen/qwen3.5-397b-a17b
|
|
18
|
-
.
|
|
19
|
-
.
|
|
18
|
+
.qwen3827b() // (fallback 6) OpenRouter qwen/qwen3.8-27b
|
|
19
|
+
.GLM53() // (fallback 7) OpenRouter z-ai/glm-5.3
|
|
20
|
+
.museGlimmer30b({ mix: { fireworks: false, openrouter: true } }) // (fallback 8) OpenRouter meta/muse-glimmer-30b
|
|
21
|
+
.hermes470b() // (fallback 9) OpenRouter nousresearch/hermes-4-70b
|
|
22
|
+
.hermes4405b() // (fallback 10) OpenRouter nousresearch/hermes-4-405b
|
|
20
23
|
.addText("What's your name?");
|
|
21
24
|
|
|
22
25
|
console.log(await mmix.message());
|
package/effort.js
CHANGED
|
@@ -34,12 +34,23 @@ const GEMINI_BANDS = [
|
|
|
34
34
|
[75, 100, 'high'],
|
|
35
35
|
];
|
|
36
36
|
|
|
37
|
+
const GLM53_BANDS = [
|
|
38
|
+
[0, 39, 'low'],
|
|
39
|
+
[40, 79, 'high'],
|
|
40
|
+
[80, 100, 'max'],
|
|
41
|
+
];
|
|
42
|
+
|
|
37
43
|
/** Exact model → supported OpenAI reasoning_effort values */
|
|
38
44
|
const OPENAI_MODEL_LEVELS = {
|
|
39
45
|
'gpt-5.6-sol': ['none', 'low', 'medium', 'high', 'xhigh', 'max'],
|
|
40
46
|
'gpt-5.6-terra': ['none', 'low', 'medium', 'high', 'xhigh', 'max'],
|
|
41
47
|
'gpt-5.6-luna': ['none', 'low', 'medium', 'high', 'xhigh', 'max'],
|
|
48
|
+
'meta/muse-glimmer-30b': ['low', 'medium', 'high'],
|
|
49
|
+
'accounts/fireworks/models/muse-glimmer-30b': ['low', 'medium', 'high', 'xhigh'],
|
|
50
|
+
'meta-models/Muse-Glimmer-30B': ['low', 'medium', 'high', 'xhigh'],
|
|
42
51
|
'accounts/fireworks/models/qwen3p8-2p4t-a95b': ['none', 'low', 'medium', 'high'],
|
|
52
|
+
'qwen/qwen3.8-27b': ['low', 'medium', 'xhigh'],
|
|
53
|
+
'z-ai/glm-5.3': ['low', 'high', 'max'],
|
|
43
54
|
'grok-4.6': ['low', 'medium', 'high', 'xhigh'],
|
|
44
55
|
'gpt-5': ['minimal', 'low', 'medium', 'high'],
|
|
45
56
|
'gpt-5-mini': ['minimal', 'low', 'medium', 'high'],
|
|
@@ -47,6 +58,14 @@ const OPENAI_MODEL_LEVELS = {
|
|
|
47
58
|
'gpt-5.3-codex': ['low', 'medium', 'high', 'xhigh'],
|
|
48
59
|
'gpt-oss-120b': ['low', 'medium', 'high'],
|
|
49
60
|
'openai/gpt-oss-120b': ['low', 'medium', 'high'],
|
|
61
|
+
'accounts/fireworks/models/gpt-oss-120b': ['low', 'medium', 'high'],
|
|
62
|
+
'kimi-k3': ['low', 'high', 'max'],
|
|
63
|
+
'moonshotai/kimi-k3': ['low', 'high', 'max'],
|
|
64
|
+
'accounts/fireworks/models/kimi-k3': ['low', 'high', 'max'],
|
|
65
|
+
'moonshotai/Kimi-K3': ['low', 'high', 'max'],
|
|
66
|
+
'zai-org/GLM-5.2': ['high', 'xhigh'],
|
|
67
|
+
'accounts/fireworks/models/glm-5p2': ['high', 'xhigh'],
|
|
68
|
+
'z-ai/glm-5.2': ['high', 'xhigh'],
|
|
50
69
|
};
|
|
51
70
|
|
|
52
71
|
/**
|
|
@@ -377,9 +396,11 @@ function mapEffort(providerFamily, effort, modelKey) {
|
|
|
377
396
|
return null;
|
|
378
397
|
}
|
|
379
398
|
const supported = supportedOpenAILevels(modelKey);
|
|
380
|
-
const desired =
|
|
381
|
-
?
|
|
382
|
-
:
|
|
399
|
+
const desired = modelKey === 'z-ai/glm-5.3'
|
|
400
|
+
? levelFromBands(normalized, GLM53_BANDS)
|
|
401
|
+
: normalized === 100 && supported.includes('max')
|
|
402
|
+
? 'max'
|
|
403
|
+
: levelFromBands(normalized, OPENAI_BANDS);
|
|
383
404
|
const level = pickNearestLevel(desired, OPENAI_LEVEL_LADDER, supported);
|
|
384
405
|
return { reasoning_effort: level };
|
|
385
406
|
}
|
package/index.d.ts
CHANGED
|
@@ -496,11 +496,12 @@ export declare class ModelMix {
|
|
|
496
496
|
grok420(args?: ModelAttachArgs): this;
|
|
497
497
|
|
|
498
498
|
// Multi-provider
|
|
499
|
-
|
|
499
|
+
museGlimmer30b(args?: ModelAttachArgs): this;
|
|
500
500
|
qwen35397b(args?: ModelAttachArgs): this;
|
|
501
501
|
qwen36plus(args?: ModelAttachArgs): this;
|
|
502
502
|
qwen37plus(args?: ModelAttachArgs): this;
|
|
503
503
|
qwen38max(args?: ModelAttachArgs): this;
|
|
504
|
+
qwen3827b(args?: ModelAttachArgs): this;
|
|
504
505
|
hermes470b(args?: ModelAttachArgs): this;
|
|
505
506
|
hermes4405b(args?: ModelAttachArgs): this;
|
|
506
507
|
hermes3(args?: ModelAttachArgs): this;
|
|
@@ -515,8 +516,8 @@ export declare class ModelMix {
|
|
|
515
516
|
mimo25pro(args?: ModelAttachArgs): this;
|
|
516
517
|
deepseekV4Pro(args?: ModelAttachArgs): this;
|
|
517
518
|
deepseekV4Flash(args?: ModelAttachArgs): this;
|
|
518
|
-
GLM51(args?: ModelAttachArgs): this;
|
|
519
519
|
GLM52(args?: ModelAttachArgs): this;
|
|
520
|
+
GLM53(args?: ModelAttachArgs): this;
|
|
520
521
|
|
|
521
522
|
addText(text: string, options?: RoleOptions): this;
|
|
522
523
|
addTextFromFile(filePath: string, options?: RoleOptions): this;
|
package/index.js
CHANGED
|
@@ -490,6 +490,8 @@ class ModelMix {
|
|
|
490
490
|
}
|
|
491
491
|
gptOss({ options = {}, config = {}, mix = {} } = {}) {
|
|
492
492
|
mix = { ...this.mix, ...mix };
|
|
493
|
+
if (mix.nvidia) this.attach('openai/gpt-oss-120b', new MixNVIDIA({ options, config }));
|
|
494
|
+
if (mix.fireworks) this.attach('accounts/fireworks/models/gpt-oss-120b', new MixFireworks({ options, config }));
|
|
493
495
|
if (mix.together) this.attach('openai/gpt-oss-120b', new MixTogether({ options, config }));
|
|
494
496
|
if (mix.cerebras) this.attach('gpt-oss-120b', new MixCerebras({ options, config }));
|
|
495
497
|
if (mix.groq) this.attach('openai/gpt-oss-120b', new MixGroq({ options, config }));
|
|
@@ -574,9 +576,12 @@ class ModelMix {
|
|
|
574
576
|
return this.attach('grok-4.20-0309', new MixGrok({ options, config }));
|
|
575
577
|
}
|
|
576
578
|
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
if (mix.
|
|
579
|
+
museGlimmer30b({ options = {}, config = {}, mix = { fireworks: true } } = {}) {
|
|
580
|
+
mix = { ...this.mix, ...mix };
|
|
581
|
+
if (mix.nvidia) this.attach('meta/muse-glimmer-30b', new MixNVIDIA({ options, config }));
|
|
582
|
+
if (mix.fireworks) this.attach('accounts/fireworks/models/muse-glimmer-30b', new MixFireworks({ options, config }));
|
|
583
|
+
if (mix.openrouter) this.attach('meta/muse-glimmer-30b', new MixOpenRouter({ options, config }));
|
|
584
|
+
if (mix.together) this.attach('meta-models/Muse-Glimmer-30B', new MixTogether({ options, config }));
|
|
580
585
|
return this;
|
|
581
586
|
}
|
|
582
587
|
|
|
@@ -584,9 +589,10 @@ class ModelMix {
|
|
|
584
589
|
return this.attach('qwen/qwen3.5-397b-a17b', new MixOpenRouter({ options, config }));
|
|
585
590
|
}
|
|
586
591
|
|
|
587
|
-
qwen36plus({ options = {}, config = {}, mix = { fireworks: true } } = {}) {
|
|
592
|
+
qwen36plus({ options = {}, config = {}, mix = { fireworks: false, openrouter: true } } = {}) {
|
|
588
593
|
mix = { ...this.mix, ...mix };
|
|
589
594
|
if (mix.fireworks) this.attach('accounts/fireworks/models/qwen3p6-plus', new MixFireworks({ options, config }));
|
|
595
|
+
if (mix.openrouter) this.attach('qwen/qwen3.6-plus', new MixOpenRouter({ options, config }));
|
|
590
596
|
if (mix.together) this.attach('Qwen/Qwen3.6-Plus', new MixTogether({ options, config }));
|
|
591
597
|
return this;
|
|
592
598
|
}
|
|
@@ -595,6 +601,7 @@ class ModelMix {
|
|
|
595
601
|
mix = { ...this.mix, ...mix };
|
|
596
602
|
if (mix.fireworks) this.attach('accounts/fireworks/models/qwen3p7-plus', new MixFireworks({ options, config }));
|
|
597
603
|
if (mix.openrouter) this.attach('qwen/qwen3.7-plus', new MixOpenRouter({ options, config }));
|
|
604
|
+
if (mix.together) this.attach('Qwen/Qwen3.7-Plus', new MixTogether({ options, config }));
|
|
598
605
|
return this;
|
|
599
606
|
}
|
|
600
607
|
|
|
@@ -605,6 +612,10 @@ class ModelMix {
|
|
|
605
612
|
return this;
|
|
606
613
|
}
|
|
607
614
|
|
|
615
|
+
qwen3827b({ options = {}, config = {} } = {}) {
|
|
616
|
+
return this.attach('qwen/qwen3.8-27b', new MixOpenRouter({ options, config }));
|
|
617
|
+
}
|
|
618
|
+
|
|
608
619
|
hermes470b({ options = {}, config = {} } = {}) {
|
|
609
620
|
return this.attach('nousresearch/hermes-4-70b', new MixOpenRouter({ options, config }));
|
|
610
621
|
}
|
|
@@ -631,13 +642,17 @@ class ModelMix {
|
|
|
631
642
|
kimiK27Code({ options = {}, config = {}, mix = { together: true } } = {}) {
|
|
632
643
|
mix = { ...this.mix, ...mix };
|
|
633
644
|
if (mix.together) this.attach('moonshotai/Kimi-K2.7-Code', new MixTogether({ options, config }));
|
|
645
|
+
if (mix.fireworks) this.attach('accounts/fireworks/models/kimi-k2p7-code', new MixFireworks({ options, config }));
|
|
646
|
+
if (mix.openrouter) this.attach('moonshotai/kimi-k2.7-code', new MixOpenRouter({ options, config }));
|
|
634
647
|
return this;
|
|
635
648
|
}
|
|
636
649
|
|
|
637
650
|
kimiK3({ options = {}, config = {}, mix = { moonshot: true, openrouter: false } } = {}) {
|
|
638
651
|
mix = { ...this.mix, ...mix };
|
|
639
652
|
if (mix.moonshot) this.attach('kimi-k3', new MixKimi({ options, config }));
|
|
653
|
+
if (mix.fireworks) this.attach('accounts/fireworks/models/kimi-k3', new MixFireworks({ options, config }));
|
|
640
654
|
if (mix.openrouter) this.attach('moonshotai/kimi-k3', new MixOpenRouter({ options, config }));
|
|
655
|
+
if (mix.together) this.attach('moonshotai/Kimi-K3', new MixTogether({ options, config }));
|
|
641
656
|
return this;
|
|
642
657
|
}
|
|
643
658
|
|
|
@@ -657,14 +672,16 @@ class ModelMix {
|
|
|
657
672
|
minimaxM27({ options = {}, config = {}, mix = { openrouter: true, minimax: true } } = {}) {
|
|
658
673
|
mix = { ...this.mix, ...mix };
|
|
659
674
|
if (mix.nvidia) this.attach('minimaxai/minimax-m2.7', new MixNVIDIA({ options, config }));
|
|
660
|
-
if (mix.
|
|
661
|
-
if (mix.
|
|
662
|
-
if (mix.
|
|
675
|
+
if (mix.fireworks) this.attach('accounts/fireworks/models/minimax-m2p7', new MixFireworks({ options, config }));
|
|
676
|
+
if (mix.openrouter) this.attach('minimax/minimax-m2.7', new MixOpenRouter({ options, config }));
|
|
677
|
+
if (mix.minimax) this.attach('MiniMax-M2.7', new MixMiniMax({ options, config }));
|
|
678
|
+
if (mix.together) this.attach('MiniMaxAI/MiniMax-M2.7', new MixTogether({ options, config }));
|
|
663
679
|
return this;
|
|
664
680
|
}
|
|
665
681
|
|
|
666
682
|
minimaxM3({ options = {}, config = {}, mix = { minimax: true, openrouter: false } } = {}) {
|
|
667
683
|
mix = { ...this.mix, ...mix };
|
|
684
|
+
if (mix.fireworks) this.attach('accounts/fireworks/models/minimax-m3', new MixFireworks({ options, config }));
|
|
668
685
|
if (mix.openrouter) this.attach('minimax/minimax-m3', new MixOpenRouter({ options, config }));
|
|
669
686
|
if (mix.minimax) this.attach('MiniMax-M3', new MixMiniMax({ options, config }));
|
|
670
687
|
if (mix.together) this.attach('MiniMaxAI/MiniMax-M3', new MixTogether({ options, config }));
|
|
@@ -703,21 +720,18 @@ class ModelMix {
|
|
|
703
720
|
return this;
|
|
704
721
|
}
|
|
705
722
|
|
|
706
|
-
GLM51({ options = {}, config = {}, mix = { fireworks: true } } = {}) {
|
|
707
|
-
mix = { ...this.mix, ...mix };
|
|
708
|
-
if (mix.nvidia) this.attach('z-ai/glm-5.1', new MixNVIDIA({ options, config }));
|
|
709
|
-
if (mix.fireworks) this.attach('accounts/fireworks/models/glm-5p1', new MixFireworks({ options, config }));
|
|
710
|
-
if (mix.openrouter) this.attach('z-ai/glm-5.1', new MixOpenRouter({ options, config }));
|
|
711
|
-
if (mix.together) this.attach('zai-org/GLM-5.1', new MixTogether({ options, config }));
|
|
712
|
-
return this;
|
|
713
|
-
}
|
|
714
|
-
|
|
715
723
|
GLM52({ options = {}, config = {}, mix = { together: true } } = {}) {
|
|
716
724
|
mix = { ...this.mix, ...mix };
|
|
717
725
|
if (mix.together) this.attach('zai-org/GLM-5.2', new MixTogether({ options, config }));
|
|
726
|
+
if (mix.fireworks) this.attach('accounts/fireworks/models/glm-5p2', new MixFireworks({ options, config }));
|
|
727
|
+
if (mix.openrouter) this.attach('z-ai/glm-5.2', new MixOpenRouter({ options, config }));
|
|
718
728
|
return this;
|
|
719
729
|
}
|
|
720
730
|
|
|
731
|
+
GLM53({ options = {}, config = {} } = {}) {
|
|
732
|
+
return this.attach('z-ai/glm-5.3', new MixOpenRouter({ options, config }));
|
|
733
|
+
}
|
|
734
|
+
|
|
721
735
|
addText(text, { role = "user", cache } = {}) {
|
|
722
736
|
return this._addText(text, {
|
|
723
737
|
role,
|
package/lib/model-chain.js
CHANGED
|
@@ -10,11 +10,12 @@ const CHAIN_MODEL_SHORTCUTS = new Set([
|
|
|
10
10
|
'gemini31pro', 'gemini37flash', 'gemini36flash', 'gemini35flash',
|
|
11
11
|
'gemini35flashLite', 'gemini31flashLite', 'sonarPro', 'sonar',
|
|
12
12
|
'grok46', 'grok45', 'grok43', 'grok420multiAgent', 'grok420',
|
|
13
|
-
'
|
|
13
|
+
'museGlimmer30b',
|
|
14
|
+
'qwen35397b', 'qwen36plus', 'qwen37plus', 'qwen38max', 'qwen3827b',
|
|
14
15
|
'hermes470b', 'hermes4405b', 'hermes3',
|
|
15
16
|
'kimiK26', 'kimiK27Code', 'kimiK3', 'kimiK25',
|
|
16
17
|
'minimaxM27', 'minimaxM3', 'mimo25', 'mimo25pro',
|
|
17
|
-
'deepseekV4Pro', 'deepseekV4Flash', '
|
|
18
|
+
'deepseekV4Pro', 'deepseekV4Flash', 'GLM52', 'GLM53'
|
|
18
19
|
]);
|
|
19
20
|
|
|
20
21
|
function parseChainModels(modelSpecs) {
|
package/lib/token-usage.js
CHANGED
|
@@ -42,9 +42,10 @@ const MODEL_PRICING = {
|
|
|
42
42
|
'gpt-4.1': { input: 2.00, cachedInput: 0.50, output: 8.00 },
|
|
43
43
|
'gpt-4.1-mini': { input: 0.40, cachedInput: 0.10, output: 1.60 },
|
|
44
44
|
'gpt-4.1-nano': { input: 0.10, cachedInput: 0.025, output: 0.40 },
|
|
45
|
-
// gptOss (Together/Groq/Cerebras/OpenRouter)
|
|
45
|
+
// gptOss (NVIDIA/Fireworks/Together/Groq/Cerebras/OpenRouter)
|
|
46
46
|
'openai/gpt-oss-120b': { input: 0.15, output: 0.60 },
|
|
47
47
|
'gpt-oss-120b': { input: 0.15, output: 0.60 },
|
|
48
|
+
'accounts/fireworks/models/gpt-oss-120b': { input: 0.15, cachedInput: 0.014, output: 0.60 },
|
|
48
49
|
// Anthropic
|
|
49
50
|
'claude-fable-5': { input: 10.00, cachedInput: 1.00, cacheWrite: 12.50, cacheWrite1h: 20.00, output: 50.00 },
|
|
50
51
|
'claude-opus-5': { input: 5.00, cachedInput: 0.50, cacheWrite: 6.25, cacheWrite1h: 10.00, output: 25.00 },
|
|
@@ -74,6 +75,10 @@ const MODEL_PRICING = {
|
|
|
74
75
|
'grok-4.20-0309': { input: 1.25, output: 2.50 },
|
|
75
76
|
'grok-4.20-0309-reasoning': { input: 1.25, output: 2.50 },
|
|
76
77
|
'grok-4.20-0309-non-reasoning': { input: 1.25, output: 2.50 },
|
|
78
|
+
// Muse Glimmer 30B (NVIDIA/OpenRouter, Fireworks, Together)
|
|
79
|
+
'meta/muse-glimmer-30b': { input: 0.35, cachedInput: 0.04, output: 1.50 },
|
|
80
|
+
'accounts/fireworks/models/muse-glimmer-30b': { input: 0.35, cachedInput: 0.04, output: 1.50 },
|
|
81
|
+
'meta-models/Muse-Glimmer-30B': { input: 0.35, cachedInput: 0.04, output: 1.50 },
|
|
77
82
|
// Fireworks
|
|
78
83
|
'accounts/fireworks/models/deepseek-v4-flash': { input: 0.14, output: 0.28 },
|
|
79
84
|
'accounts/fireworks/models/deepseek-v4-pro': { input: 1.74, output: 3.48 },
|
|
@@ -82,23 +87,43 @@ const MODEL_PRICING = {
|
|
|
82
87
|
'deepseek-ai/DeepSeek-V4-Pro': { input: 2.10, output: 4.40 },
|
|
83
88
|
'deepseek/deepseek-v4-flash': { input: 0.09, output: 0.18 },
|
|
84
89
|
'accounts/fireworks/models/glm-4p7': { input: 0.55, output: 2.19 },
|
|
85
|
-
'
|
|
86
|
-
'
|
|
90
|
+
'zai-org/GLM-5.2': { input: 1.40, cachedInput: 0.26, output: 4.40 },
|
|
91
|
+
'accounts/fireworks/models/glm-5p2': { input: 1.40, cachedInput: 0.14, output: 4.40 },
|
|
92
|
+
'z-ai/glm-5.2': { input: 0.966, cachedInput: 0.1932, output: 3.036 },
|
|
93
|
+
'z-ai/glm-5.3': { input: 1.40, cachedInput: 0.26, output: 4.40 },
|
|
87
94
|
'accounts/fireworks/models/kimi-k2p5': { input: 0.50, output: 2.80 },
|
|
88
95
|
'qwen/qwen3.5-397b-a17b': { input: 0.385, output: 2.45 },
|
|
89
96
|
'accounts/fireworks/models/qwen3p6-plus': { input: 0.50, output: 3.00 },
|
|
90
97
|
'Qwen/Qwen3.6-Plus': { input: 0.50, output: 3.00 },
|
|
98
|
+
'qwen/qwen3.6-plus': {
|
|
99
|
+
input: 0.325,
|
|
100
|
+
cacheWrite: 0.40625,
|
|
101
|
+
output: 1.95,
|
|
102
|
+
longContext: { inputThreshold: 256_000, inputMultiplier: 4, outputMultiplier: 2, inclusive: true }
|
|
103
|
+
},
|
|
91
104
|
'accounts/fireworks/models/qwen3p7-plus': { input: 0.40, output: 1.60 },
|
|
92
|
-
'qwen/qwen3.7-plus': {
|
|
105
|
+
'qwen/qwen3.7-plus': {
|
|
106
|
+
input: 0.32,
|
|
107
|
+
cachedInput: 0.064,
|
|
108
|
+
cacheWrite: 0.40,
|
|
109
|
+
output: 1.28,
|
|
110
|
+
longContext: { inputThreshold: 256_000, inputMultiplier: 3, outputMultiplier: 3, inclusive: true }
|
|
111
|
+
},
|
|
112
|
+
'Qwen/Qwen3.7-Plus': { input: 0.32, output: 1.28 },
|
|
93
113
|
'accounts/fireworks/models/qwen3p8-2p4t-a95b': { input: 2.00, cachedInput: 0.25, output: 6.00 },
|
|
94
114
|
'qwen/qwen3.8-max': { input: 2.00, output: 6.00 },
|
|
115
|
+
'qwen/qwen3.8-27b': { input: 0.45, cachedInput: 0.05, output: 3.20 },
|
|
95
116
|
// MiniMax
|
|
96
117
|
'MiniMax-M2.5': { input: 0.30, output: 1.20 },
|
|
97
118
|
'MiniMax-M2.7': { input: 0.30, output: 1.20 },
|
|
98
119
|
'MiniMax-M3': { input: 0.30, output: 1.20 },
|
|
99
120
|
'minimax/minimax-m2.7': { input: 0.30, output: 1.20 },
|
|
100
121
|
'minimax/minimax-m3': { input: 0.30, output: 1.20 },
|
|
122
|
+
'minimaxai/minimax-m2.7': { input: 0.30, output: 1.20 },
|
|
123
|
+
'MiniMaxAI/MiniMax-M2.7': { input: 0.30, output: 1.20 },
|
|
101
124
|
'MiniMaxAI/MiniMax-M3': { input: 0.30, output: 1.20 },
|
|
125
|
+
'accounts/fireworks/models/minimax-m2p7': { input: 0.30, cachedInput: 0.059, output: 1.20 },
|
|
126
|
+
'accounts/fireworks/models/minimax-m3': { input: 0.30, cachedInput: 0.059, output: 1.20 },
|
|
102
127
|
// Perplexity
|
|
103
128
|
'sonar': { input: 1.00, output: 1.00 },
|
|
104
129
|
'sonar-pro': { input: 3.00, output: 15.00 },
|
|
@@ -108,15 +133,18 @@ const MODEL_PRICING = {
|
|
|
108
133
|
// Hermes 3 (Lambda/OpenRouter)
|
|
109
134
|
'Hermes-3-Llama-3.1-405B-FP8': { input: 0.80, output: 0.80 },
|
|
110
135
|
'nousresearch/hermes-3-llama-3.1-405b:free': { input: 0, output: 0 },
|
|
111
|
-
// Qwen3 (Together/Cerebras)
|
|
112
|
-
'Qwen/Qwen3-235B-A22B-fp8-tput': { input: 0.20, output: 0.60 },
|
|
113
|
-
'qwen-3-32b': { input: 0.20, output: 0.60 },
|
|
114
136
|
// Kimi K2.5 (Together/Fireworks/OpenRouter)
|
|
115
137
|
'moonshotai/Kimi-K2.5': { input: 0.50, output: 2.80 },
|
|
116
138
|
'moonshotai/kimi-k2.5': { input: 0.50, output: 2.80 },
|
|
117
139
|
// Kimi K3
|
|
118
|
-
'kimi-k3': { input: 3.00, output: 15.00 },
|
|
119
|
-
'moonshotai/kimi-k3': { input: 3.00, output: 15.00 },
|
|
140
|
+
'kimi-k3': { input: 3.00, cachedInput: 0.30, output: 15.00 },
|
|
141
|
+
'moonshotai/kimi-k3': { input: 3.00, cachedInput: 0.30, output: 15.00 },
|
|
142
|
+
'accounts/fireworks/models/kimi-k3': { input: 3.00, cachedInput: 0.30, output: 15.00 },
|
|
143
|
+
'moonshotai/Kimi-K3': { input: 3.00, cachedInput: 0.30, output: 15.00 },
|
|
144
|
+
// Kimi K2.7 Code
|
|
145
|
+
'moonshotai/Kimi-K2.7-Code': { input: 0.95, cachedInput: 0.19, output: 4.00 },
|
|
146
|
+
'accounts/fireworks/models/kimi-k2p7-code': { input: 0.95, cachedInput: 0.19, output: 4.00 },
|
|
147
|
+
'moonshotai/kimi-k2.7-code': { input: 0.71, cachedInput: 0.15, output: 3.50 },
|
|
120
148
|
// GLM 4.7 (OpenRouter/Cerebras)
|
|
121
149
|
'z-ai/glm-4.7': { input: 0.55, output: 2.19 },
|
|
122
150
|
'zai-glm-4.7': { input: 0.55, output: 2.19 },
|
package/package.json
CHANGED
package/skills/modelmix/SKILL.md
CHANGED
|
@@ -143,7 +143,7 @@ ModelMix.new({ config: { effort: 80 } })
|
|
|
143
143
|
| DeepSeek V4 | off | `low`↑ | `high`↑ | `high`↑ | `max`↑ | — |
|
|
144
144
|
| MiniMax M3 | off | adaptive | adaptive | adaptive | adaptive | adaptive |
|
|
145
145
|
|
|
146
|
-
\* GPT-5.6 maps `100` to `max`; 80–99 remains `xhigh`. Gemini bands: 0–24 / 25–49 / 50–74 / 75–100. Gemini 3.7 Flash supports only `low` / `medium` / `high`, so the first two bands clamp to `low`; `-1` keeps its native `medium` default. 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`. Grok 4.6 maps 0–39 / 40–59 / 60–79 / 80–100 to `low` / `medium` / `high` / `xhigh`; without effort it uses native `high`. `-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.
|
|
146
|
+
\* GPT-5.6 maps `100` to `max`; 80–99 remains `xhigh`. Qwen 3.8 27B maps 0–39 / 40–79 / 80–100 to `low` / `medium` / `xhigh`. GLM 5.3 requires reasoning and maps those bands to `low` / `high` / `max`. Gemini bands: 0–24 / 25–49 / 50–74 / 75–100. Gemini 3.7 Flash supports only `low` / `medium` / `high`, so the first two bands clamp to `low`; `-1` keeps its native `medium` default. 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`. Grok 4.6 maps 0–39 / 40–59 / 60–79 / 80–100 to `low` / `medium` / `high` / `xhigh`; without effort it uses native `high`. `-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.
|
|
147
147
|
|
|
148
148
|
## Available Model Shorthands
|
|
149
149
|
|
|
@@ -168,22 +168,25 @@ Use `.effort(n)` (or `config.effort`) to enable Anthropic thinking — e.g. `.ef
|
|
|
168
168
|
`sonar()` `sonarPro()`
|
|
169
169
|
|
|
170
170
|
### Together
|
|
171
|
-
`qwen36plus()` `GLM52()` `kimiK25()` `kimiK26()` `gptOss()`
|
|
171
|
+
`museGlimmer30b()` `qwen36plus()` `qwen37plus()` `GLM52()` `kimiK25()` `kimiK26()` `kimiK27Code()` `kimiK3()` `minimaxM27()` `minimaxM3()` `gptOss()`
|
|
172
|
+
|
|
173
|
+
### NVIDIA
|
|
174
|
+
`museGlimmer30b()` `gptOss()` `minimaxM27()`
|
|
172
175
|
|
|
173
176
|
### Moonshot
|
|
174
|
-
`kimiK3()` — requires `MOONSHOT_API_KEY`;
|
|
177
|
+
`kimiK3()` — requires `MOONSHOT_API_KEY`; Fireworks, OpenRouter, and Together are available through `mix`.
|
|
175
178
|
|
|
176
179
|
### MiniMax
|
|
177
180
|
`minimaxM27()` `minimaxM3()`
|
|
178
181
|
|
|
179
182
|
### Fireworks
|
|
180
|
-
`qwen36plus()` `qwen37plus()` `qwen38max()` `deepseekV4Flash()` `deepseekV4Pro()` `kimiK26()`
|
|
183
|
+
`museGlimmer30b()` `gptOss()` `qwen36plus()` (private/on-demand only) `qwen37plus()` `qwen38max()` `deepseekV4Flash()` `deepseekV4Pro()` `kimiK26()` `kimiK27Code()` `kimiK3()` `minimaxM27()` `minimaxM3()` `GLM52()`
|
|
181
184
|
|
|
182
185
|
### Cerebras
|
|
183
186
|
`GLM46()`
|
|
184
187
|
|
|
185
188
|
### OpenRouter
|
|
186
|
-
`qwen35397b()` `hermes470b()` `hermes4405b()` `qwen38max()` `GLM45()`
|
|
189
|
+
`museGlimmer30b()` `gptOss()` `qwen35397b()` `qwen36plus()` `qwen37plus()` `qwen3827b()` `hermes470b()` `hermes4405b()` `qwen38max()` `kimiK27Code()` `kimiK3()` `minimaxM27()` `minimaxM3()` `GLM45()` `GLM52()` `GLM53()`
|
|
187
190
|
|
|
188
191
|
### Multi-provider (auto-fallback across free/paid tiers)
|
|
189
192
|
`hermes3()` `kimiK25()`
|
package/test/effort.test.js
CHANGED
|
@@ -96,6 +96,59 @@ describe('Unified effort scale', () => {
|
|
|
96
96
|
expect(mapEffort('openai', 100, key)).to.deep.equal({ reasoning_effort: 'high' });
|
|
97
97
|
});
|
|
98
98
|
|
|
99
|
+
it('maps the new GPT OSS providers to low, medium, and high reasoning', () => {
|
|
100
|
+
expect(mapEffort('openai', 0, 'accounts/fireworks/models/gpt-oss-120b')).to.deep.equal({
|
|
101
|
+
reasoning_effort: 'low'
|
|
102
|
+
});
|
|
103
|
+
expect(mapEffort('openai', 50, 'openai/gpt-oss-120b')).to.deep.equal({
|
|
104
|
+
reasoning_effort: 'medium'
|
|
105
|
+
});
|
|
106
|
+
expect(mapEffort('openai', 100, 'accounts/fireworks/models/gpt-oss-120b')).to.deep.equal({
|
|
107
|
+
reasoning_effort: 'high'
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('maps Kimi K3 and GLM 5.2 to their provider-supported levels', () => {
|
|
112
|
+
expect(mapEffort('openai', 100, 'moonshotai/Kimi-K3')).to.deep.equal({
|
|
113
|
+
reasoning_effort: 'max'
|
|
114
|
+
});
|
|
115
|
+
expect(mapEffort('openai', 0, 'accounts/fireworks/models/glm-5p2')).to.deep.equal({
|
|
116
|
+
reasoning_effort: 'high'
|
|
117
|
+
});
|
|
118
|
+
expect(mapEffort('openai', 100, 'z-ai/glm-5.2')).to.deep.equal({
|
|
119
|
+
reasoning_effort: 'xhigh'
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('maps Muse Glimmer 30B to provider-supported reasoning levels', () => {
|
|
124
|
+
expect(mapEffort('openai', 0, 'accounts/fireworks/models/muse-glimmer-30b')).to.deep.equal({
|
|
125
|
+
reasoning_effort: 'low'
|
|
126
|
+
});
|
|
127
|
+
expect(mapEffort('openai', 100, 'accounts/fireworks/models/muse-glimmer-30b')).to.deep.equal({
|
|
128
|
+
reasoning_effort: 'xhigh'
|
|
129
|
+
});
|
|
130
|
+
expect(mapEffort('openai', 100, 'meta/muse-glimmer-30b')).to.deep.equal({
|
|
131
|
+
reasoning_effort: 'high'
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('maps Qwen 3.8 27B to its supported reasoning levels', () => {
|
|
136
|
+
const key = 'qwen/qwen3.8-27b';
|
|
137
|
+
expect(mapEffort('openai', 0, key)).to.deep.equal({ reasoning_effort: 'low' });
|
|
138
|
+
expect(mapEffort('openai', 50, key)).to.deep.equal({ reasoning_effort: 'medium' });
|
|
139
|
+
expect(mapEffort('openai', 100, key)).to.deep.equal({ reasoning_effort: 'xhigh' });
|
|
140
|
+
expect(mapEffort('openai', -1, key)).to.equal(null);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('maps GLM 5.3 to mandatory low, high, and max reasoning', () => {
|
|
144
|
+
const key = 'z-ai/glm-5.3';
|
|
145
|
+
expect(mapEffort('openai', 39, key)).to.deep.equal({ reasoning_effort: 'low' });
|
|
146
|
+
expect(mapEffort('openai', 40, key)).to.deep.equal({ reasoning_effort: 'high' });
|
|
147
|
+
expect(mapEffort('openai', 79, key)).to.deep.equal({ reasoning_effort: 'high' });
|
|
148
|
+
expect(mapEffort('openai', 80, key)).to.deep.equal({ reasoning_effort: 'max' });
|
|
149
|
+
expect(mapEffort('openai', -1, key)).to.equal(null);
|
|
150
|
+
});
|
|
151
|
+
|
|
99
152
|
it('maps Anthropic adaptive models to thinking + output_config.effort', () => {
|
|
100
153
|
expect(mapEffort('anthropic', 10, 'claude-opus-5')).to.deep.equal({
|
|
101
154
|
thinking: { type: 'adaptive', display: 'summarized' },
|
package/test/fallback.test.js
CHANGED
|
@@ -64,7 +64,7 @@ describe('Provider Fallback Chain Tests', () => {
|
|
|
64
64
|
for (const shortcut of [
|
|
65
65
|
'gpt41', 'gpt41mini', 'gpt41nano',
|
|
66
66
|
'gemini25flash', 'gemini25pro', 'gemini3pro', 'gemini3flash',
|
|
67
|
-
'minimaxM25'
|
|
67
|
+
'minimaxM25', 'qwen3', 'GLM51'
|
|
68
68
|
]) {
|
|
69
69
|
expect(model[shortcut]).to.equal(undefined);
|
|
70
70
|
expect(() => model.chain(shortcut))
|
package/test/glm.test.js
CHANGED
|
@@ -1,12 +1,56 @@
|
|
|
1
1
|
const { expect } = require('chai');
|
|
2
|
-
const { ModelMix } = require('../index.js');
|
|
2
|
+
const { ModelMix, MixFireworks, MixOpenRouter, MixTogether } = require('../index.js');
|
|
3
3
|
|
|
4
4
|
describe('GLM Model Registration Tests', () => {
|
|
5
|
-
it('should register Together GLM 5.2 by default', () => {
|
|
5
|
+
it('should register Together GLM 5.2 before the OpenRouter fallback by default', () => {
|
|
6
6
|
const model = ModelMix.new();
|
|
7
7
|
model.GLM52();
|
|
8
8
|
|
|
9
|
+
expect(model.models.map(({ key }) => key)).to.deep.equal([
|
|
10
|
+
'zai-org/GLM-5.2',
|
|
11
|
+
'z-ai/glm-5.2'
|
|
12
|
+
]);
|
|
13
|
+
expect(model.models[0].provider).to.be.instanceOf(MixTogether);
|
|
14
|
+
expect(model.models[1].provider).to.be.instanceOf(MixOpenRouter);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should register every requested GLM 5.2 provider', () => {
|
|
18
|
+
const model = ModelMix.new().GLM52({
|
|
19
|
+
mix: { together: true, fireworks: true, openrouter: true }
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
expect(model.models.map(({ key }) => key)).to.deep.equal([
|
|
23
|
+
'zai-org/GLM-5.2',
|
|
24
|
+
'accounts/fireworks/models/glm-5p2',
|
|
25
|
+
'z-ai/glm-5.2'
|
|
26
|
+
]);
|
|
27
|
+
expect(model.models[0].provider).to.be.instanceOf(MixTogether);
|
|
28
|
+
expect(model.models[1].provider).to.be.instanceOf(MixFireworks);
|
|
29
|
+
expect(model.models[2].provider).to.be.instanceOf(MixOpenRouter);
|
|
30
|
+
expect(ModelMix.calculateCost('accounts/fireworks/models/glm-5p2', {
|
|
31
|
+
input: 1_000_000,
|
|
32
|
+
cached: 500_000,
|
|
33
|
+
output: 1_000_000
|
|
34
|
+
})).to.equal(5.17);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should register GLM 5.3 through OpenRouter', () => {
|
|
38
|
+
const model = ModelMix.new().GLM53();
|
|
39
|
+
|
|
40
|
+
expect(model.models).to.have.length(1);
|
|
41
|
+
expect(model.models[0].key).to.equal('z-ai/glm-5.3');
|
|
42
|
+
expect(model.models[0].provider).to.be.instanceOf(MixOpenRouter);
|
|
43
|
+
expect(ModelMix.calculateCost('z-ai/glm-5.3', {
|
|
44
|
+
input: 1_000_000,
|
|
45
|
+
cached: 500_000,
|
|
46
|
+
output: 1_000_000
|
|
47
|
+
})).to.equal(5.23);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should support GLM 5.3 in chain()', () => {
|
|
51
|
+
const model = ModelMix.new().chain('GLM53');
|
|
52
|
+
|
|
9
53
|
expect(model.models).to.have.length(1);
|
|
10
|
-
expect(model.models[0].key).to.equal('
|
|
54
|
+
expect(model.models[0].key).to.equal('z-ai/glm-5.3');
|
|
11
55
|
});
|
|
12
56
|
});
|
package/test/kimi.test.js
CHANGED
|
@@ -1,15 +1,32 @@
|
|
|
1
1
|
const { expect } = require('chai');
|
|
2
2
|
const nock = require('nock');
|
|
3
|
-
const { ModelMix, MixKimi, MixOpenRouter, MixTogether } = require('../index.js');
|
|
3
|
+
const { ModelMix, MixFireworks, MixKimi, MixOpenRouter, MixTogether } = require('../index.js');
|
|
4
4
|
|
|
5
5
|
describe('Kimi Model Registration Tests', () => {
|
|
6
|
-
it('should register Together Kimi K2.7 Code by default', () => {
|
|
6
|
+
it('should register Together Kimi K2.7 Code before the OpenRouter fallback by default', () => {
|
|
7
7
|
const model = ModelMix.new();
|
|
8
8
|
model.kimiK27Code();
|
|
9
9
|
|
|
10
|
-
expect(model.models).to.
|
|
11
|
-
|
|
10
|
+
expect(model.models.map(({ key }) => key)).to.deep.equal([
|
|
11
|
+
'moonshotai/Kimi-K2.7-Code',
|
|
12
|
+
'moonshotai/kimi-k2.7-code'
|
|
13
|
+
]);
|
|
12
14
|
expect(model.models[0].provider).to.be.instanceOf(MixTogether);
|
|
15
|
+
expect(model.models[1].provider).to.be.instanceOf(MixOpenRouter);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('should register every requested Kimi K2.7 Code provider', () => {
|
|
19
|
+
const model = ModelMix.new().kimiK27Code({
|
|
20
|
+
mix: { together: true, fireworks: true, openrouter: true }
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
expect(model.models.map(({ key }) => key)).to.deep.equal([
|
|
24
|
+
'moonshotai/Kimi-K2.7-Code',
|
|
25
|
+
'accounts/fireworks/models/kimi-k2p7-code',
|
|
26
|
+
'moonshotai/kimi-k2.7-code'
|
|
27
|
+
]);
|
|
28
|
+
expect(model.models[1].provider).to.be.instanceOf(MixFireworks);
|
|
29
|
+
expect(model.models[2].provider).to.be.instanceOf(MixOpenRouter);
|
|
13
30
|
});
|
|
14
31
|
|
|
15
32
|
it('should register Kimi K3 with the native Moonshot provider by default', () => {
|
|
@@ -45,6 +62,19 @@ describe('Kimi Model Registration Tests', () => {
|
|
|
45
62
|
}
|
|
46
63
|
});
|
|
47
64
|
|
|
65
|
+
it('should register Fireworks and Together Kimi K3 when requested', () => {
|
|
66
|
+
const model = ModelMix.new().kimiK3({
|
|
67
|
+
mix: { moonshot: false, fireworks: true, openrouter: false, together: true }
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
expect(model.models.map(({ key }) => key)).to.deep.equal([
|
|
71
|
+
'accounts/fireworks/models/kimi-k3',
|
|
72
|
+
'moonshotai/Kimi-K3'
|
|
73
|
+
]);
|
|
74
|
+
expect(model.models[0].provider).to.be.instanceOf(MixFireworks);
|
|
75
|
+
expect(model.models[1].provider).to.be.instanceOf(MixTogether);
|
|
76
|
+
});
|
|
77
|
+
|
|
48
78
|
it('should adapt Kimi K3 requests to its fixed sampling API', async () => {
|
|
49
79
|
const originalMoonshotApiKey = process.env.MOONSHOT_API_KEY;
|
|
50
80
|
process.env.MOONSHOT_API_KEY = 'test-moonshot-key';
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
const { expect } = require('chai');
|
|
2
|
+
const {
|
|
3
|
+
ModelMix,
|
|
4
|
+
MixFireworks,
|
|
5
|
+
MixNVIDIA,
|
|
6
|
+
MixOpenRouter,
|
|
7
|
+
MixTogether
|
|
8
|
+
} = require('../index.js');
|
|
9
|
+
|
|
10
|
+
describe('Muse Glimmer Model Registration Tests', () => {
|
|
11
|
+
it('registers Fireworks before the OpenRouter fallback by default', () => {
|
|
12
|
+
const model = ModelMix.new().museGlimmer30b();
|
|
13
|
+
|
|
14
|
+
expect(model.models.map(({ key }) => key)).to.deep.equal([
|
|
15
|
+
'accounts/fireworks/models/muse-glimmer-30b',
|
|
16
|
+
'meta/muse-glimmer-30b'
|
|
17
|
+
]);
|
|
18
|
+
expect(model.models[0].provider).to.be.instanceOf(MixFireworks);
|
|
19
|
+
expect(model.models[1].provider).to.be.instanceOf(MixOpenRouter);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('registers every supported provider in fallback order', () => {
|
|
23
|
+
const model = ModelMix.new().museGlimmer30b({
|
|
24
|
+
mix: { nvidia: true, fireworks: true, openrouter: true, together: true }
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
expect(model.models.map(({ key }) => key)).to.deep.equal([
|
|
28
|
+
'meta/muse-glimmer-30b',
|
|
29
|
+
'accounts/fireworks/models/muse-glimmer-30b',
|
|
30
|
+
'meta/muse-glimmer-30b',
|
|
31
|
+
'meta-models/Muse-Glimmer-30B'
|
|
32
|
+
]);
|
|
33
|
+
expect(model.models.map(({ provider }) => provider.constructor)).to.deep.equal([
|
|
34
|
+
MixNVIDIA,
|
|
35
|
+
MixFireworks,
|
|
36
|
+
MixOpenRouter,
|
|
37
|
+
MixTogether
|
|
38
|
+
]);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('calculates cached input pricing for every provider model ID', () => {
|
|
42
|
+
const usage = { input: 1_000_000, cached: 250_000, output: 1_000_000 };
|
|
43
|
+
|
|
44
|
+
for (const key of [
|
|
45
|
+
'meta/muse-glimmer-30b',
|
|
46
|
+
'accounts/fireworks/models/muse-glimmer-30b',
|
|
47
|
+
'meta-models/Muse-Glimmer-30B'
|
|
48
|
+
]) {
|
|
49
|
+
expect(ModelMix.calculateCost(key, usage)).to.equal(1.7725);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('supports Muse Glimmer 30B in chain()', () => {
|
|
54
|
+
const model = ModelMix.new().chain('museGlimmer30b');
|
|
55
|
+
|
|
56
|
+
expect(model.models.map(({ key }) => key)).to.deep.equal([
|
|
57
|
+
'accounts/fireworks/models/muse-glimmer-30b',
|
|
58
|
+
'meta/muse-glimmer-30b'
|
|
59
|
+
]);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
const { expect } = require('chai');
|
|
2
|
+
const {
|
|
3
|
+
ModelMix,
|
|
4
|
+
MixFireworks,
|
|
5
|
+
MixMiniMax,
|
|
6
|
+
MixNVIDIA,
|
|
7
|
+
MixOpenRouter,
|
|
8
|
+
MixTogether
|
|
9
|
+
} = require('../index.js');
|
|
10
|
+
|
|
11
|
+
describe('Provider expansion regressions', () => {
|
|
12
|
+
it('should retain every enabled GPT OSS provider, including shared model keys', () => {
|
|
13
|
+
const model = ModelMix.new().gptOss({
|
|
14
|
+
mix: {
|
|
15
|
+
nvidia: true,
|
|
16
|
+
fireworks: true,
|
|
17
|
+
together: true,
|
|
18
|
+
cerebras: true,
|
|
19
|
+
groq: true,
|
|
20
|
+
openrouter: true
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
expect(model.models.map(({ key }) => key)).to.deep.equal([
|
|
25
|
+
'openai/gpt-oss-120b',
|
|
26
|
+
'accounts/fireworks/models/gpt-oss-120b',
|
|
27
|
+
'openai/gpt-oss-120b',
|
|
28
|
+
'gpt-oss-120b',
|
|
29
|
+
'openai/gpt-oss-120b',
|
|
30
|
+
'openai/gpt-oss-120b'
|
|
31
|
+
]);
|
|
32
|
+
expect(model.models[0].provider).to.be.instanceOf(MixNVIDIA);
|
|
33
|
+
expect(model.models[1].provider).to.be.instanceOf(MixFireworks);
|
|
34
|
+
expect(ModelMix.calculateCost('accounts/fireworks/models/gpt-oss-120b', {
|
|
35
|
+
input: 1_000_000,
|
|
36
|
+
cached: 500_000,
|
|
37
|
+
output: 1_000_000
|
|
38
|
+
})).to.equal(0.682);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should retain every enabled MiniMax M2.7 provider in fallback order', () => {
|
|
42
|
+
const originalMiniMaxApiKey = process.env.MINIMAX_API_KEY;
|
|
43
|
+
process.env.MINIMAX_API_KEY = 'test-minimax-key';
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
const model = ModelMix.new().minimaxM27({
|
|
47
|
+
mix: {
|
|
48
|
+
nvidia: true,
|
|
49
|
+
fireworks: true,
|
|
50
|
+
openrouter: true,
|
|
51
|
+
minimax: true,
|
|
52
|
+
together: true
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
expect(model.models.map(({ key }) => key)).to.deep.equal([
|
|
57
|
+
'minimaxai/minimax-m2.7',
|
|
58
|
+
'accounts/fireworks/models/minimax-m2p7',
|
|
59
|
+
'minimax/minimax-m2.7',
|
|
60
|
+
'MiniMax-M2.7',
|
|
61
|
+
'MiniMaxAI/MiniMax-M2.7'
|
|
62
|
+
]);
|
|
63
|
+
expect(model.models[0].provider).to.be.instanceOf(MixNVIDIA);
|
|
64
|
+
expect(model.models[1].provider).to.be.instanceOf(MixFireworks);
|
|
65
|
+
expect(model.models[2].provider).to.be.instanceOf(MixOpenRouter);
|
|
66
|
+
expect(model.models[3].provider).to.be.instanceOf(MixMiniMax);
|
|
67
|
+
expect(model.models[4].provider).to.be.instanceOf(MixTogether);
|
|
68
|
+
} finally {
|
|
69
|
+
if (originalMiniMaxApiKey === undefined) delete process.env.MINIMAX_API_KEY;
|
|
70
|
+
else process.env.MINIMAX_API_KEY = originalMiniMaxApiKey;
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('should register Fireworks for MiniMax M3 without changing its default', () => {
|
|
75
|
+
const model = ModelMix.new().minimaxM3({
|
|
76
|
+
mix: { fireworks: true, openrouter: false, minimax: false, together: false }
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
expect(model.models).to.have.length(1);
|
|
80
|
+
expect(model.models[0].key).to.equal('accounts/fireworks/models/minimax-m3');
|
|
81
|
+
expect(model.models[0].provider).to.be.instanceOf(MixFireworks);
|
|
82
|
+
});
|
|
83
|
+
});
|
package/test/qwen.test.js
CHANGED
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
const { expect } = require('chai');
|
|
2
|
-
const { ModelMix, MixOpenRouter } = require('../index.js');
|
|
2
|
+
const { ModelMix, MixFireworks, MixOpenRouter, MixTogether } = require('../index.js');
|
|
3
3
|
|
|
4
4
|
describe('Qwen Model Registration Tests', () => {
|
|
5
|
-
it('should register
|
|
5
|
+
it('should register OpenRouter Qwen 3.6 Plus by default', () => {
|
|
6
|
+
const model = ModelMix.new().qwen36plus();
|
|
7
|
+
|
|
8
|
+
expect(model.models).to.have.length(1);
|
|
9
|
+
expect(model.models[0].key).to.equal('qwen/qwen3.6-plus');
|
|
10
|
+
expect(model.models[0].provider).to.be.instanceOf(MixOpenRouter);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('should retain Fireworks Qwen 3.6 Plus as an explicit deployment option', () => {
|
|
6
14
|
const model = ModelMix.new();
|
|
7
|
-
model.qwen36plus({ mix: { fireworks: true, together: false } });
|
|
15
|
+
model.qwen36plus({ mix: { fireworks: true, openrouter: false, together: false } });
|
|
8
16
|
|
|
9
17
|
expect(model.models).to.have.length(1);
|
|
10
18
|
expect(model.models[0].key).to.equal('accounts/fireworks/models/qwen3p6-plus');
|
|
19
|
+
expect(model.models[0].provider).to.be.instanceOf(MixFireworks);
|
|
11
20
|
});
|
|
12
21
|
|
|
13
22
|
it('should register Together Qwen 3.6 Plus when together mix is enabled', () => {
|
|
@@ -38,6 +47,26 @@ describe('Qwen Model Registration Tests', () => {
|
|
|
38
47
|
expect(model.models[0].key).to.equal('qwen/qwen3.7-plus');
|
|
39
48
|
});
|
|
40
49
|
|
|
50
|
+
it('should register Together Qwen 3.7 Plus when together mix is enabled', () => {
|
|
51
|
+
const model = ModelMix.new();
|
|
52
|
+
model.qwen37plus({ mix: { fireworks: false, openrouter: false, together: true } });
|
|
53
|
+
|
|
54
|
+
expect(model.models).to.have.length(1);
|
|
55
|
+
expect(model.models[0].key).to.equal('Qwen/Qwen3.7-Plus');
|
|
56
|
+
expect(model.models[0].provider).to.be.instanceOf(MixTogether);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('should apply OpenRouter long-context pricing to Qwen 3.6 and 3.7 Plus', () => {
|
|
60
|
+
expect(ModelMix.calculateCost('qwen/qwen3.6-plus', {
|
|
61
|
+
input: 256_000,
|
|
62
|
+
output: 1_000_000
|
|
63
|
+
})).to.equal(4.2328);
|
|
64
|
+
expect(ModelMix.calculateCost('qwen/qwen3.7-plus', {
|
|
65
|
+
input: 256_000,
|
|
66
|
+
output: 1_000_000
|
|
67
|
+
})).to.equal(4.08576);
|
|
68
|
+
});
|
|
69
|
+
|
|
41
70
|
it('should register Fireworks Qwen 3.8 Max before the OpenRouter fallback by default', () => {
|
|
42
71
|
const model = ModelMix.new();
|
|
43
72
|
model.qwen38max();
|
|
@@ -61,6 +90,26 @@ describe('Qwen Model Registration Tests', () => {
|
|
|
61
90
|
expect(model.models[0].key).to.equal('qwen/qwen3.8-max');
|
|
62
91
|
});
|
|
63
92
|
|
|
93
|
+
it('should register Qwen 3.8 27B through OpenRouter', () => {
|
|
94
|
+
const model = ModelMix.new().qwen3827b();
|
|
95
|
+
|
|
96
|
+
expect(model.models).to.have.length(1);
|
|
97
|
+
expect(model.models[0].key).to.equal('qwen/qwen3.8-27b');
|
|
98
|
+
expect(model.models[0].provider).to.be.instanceOf(MixOpenRouter);
|
|
99
|
+
expect(ModelMix.calculateCost('qwen/qwen3.8-27b', {
|
|
100
|
+
input: 1_000_000,
|
|
101
|
+
cached: 250_000,
|
|
102
|
+
output: 1_000_000
|
|
103
|
+
})).to.equal(3.55);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('should support Qwen 3.8 27B in chain()', () => {
|
|
107
|
+
const model = ModelMix.new().chain('qwen3827b');
|
|
108
|
+
|
|
109
|
+
expect(model.models).to.have.length(1);
|
|
110
|
+
expect(model.models[0].key).to.equal('qwen/qwen3.8-27b');
|
|
111
|
+
});
|
|
112
|
+
|
|
64
113
|
it('should register Qwen 3.5 397B A17B through OpenRouter', () => {
|
|
65
114
|
const model = ModelMix.new().qwen35397b();
|
|
66
115
|
|