modelmix 5.1.4 → 5.1.8
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 +10 -3
- package/demo/custom.js +2 -2
- package/demo/short.js +3 -2
- package/effort.js +11 -0
- package/index.d.ts +1 -2
- package/index.js +22 -16
- package/lib/model-chain.js +3 -2
- package/lib/providers/anthropic.js +3 -3
- package/lib/providers/base.js +3 -3
- package/lib/providers/google.js +2 -1
- package/lib/providers/openai-compatible.js +31 -30
- package/lib/providers/openai.js +6 -5
- package/lib/token-usage.js +35 -9
- package/package.json +1 -1
- package/skills/modelmix/SKILL.md +7 -4
- package/test/effort.test.js +36 -0
- package/test/fallback.test.js +1 -1
- package/test/glm.test.js +28 -4
- package/test/kimi.test.js +34 -4
- package/test/muse.test.js +61 -0
- package/test/provider-expansion.test.js +83 -0
- package/test/public-api.test.js +42 -0
- package/test/qwen.test.js +32 -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,8 +171,9 @@ 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] |
|
|
178
179
|
| `qwen3827b()` | OpenRouter | qwen/qwen3.8-27b | [\$0.45][15] | [\$3.20][15] |
|
|
@@ -180,7 +181,6 @@ ModelMix provides convenient shorthand methods for quickly accessing different A
|
|
|
180
181
|
| `deepseekV4Pro()` | Fireworks | models/deepseek-v4-pro-0813 | [\$1.32][12] | [\$3.96][12] |
|
|
181
182
|
| `GLM53()` | OpenRouter | z-ai/glm-5.3 | [\$1.40][16] | [\$4.40][16] |
|
|
182
183
|
| `GLM52()` | Together | zai-org/GLM-5.2 | [\$1.40][7] | [\$4.40][7] |
|
|
183
|
-
| `GLM51()` | Fireworks | models/glm-5p1 | [\$1.05][10] | [\$3.50][10] |
|
|
184
184
|
| `minimaxM3()` | MiniMax | MiniMax-M3 | [\$0.30][9] | [\$1.20][9] |
|
|
185
185
|
| `minimaxM27()` | MiniMax | MiniMax-M2.7 | [\$0.30][9] | [\$1.20][9] |
|
|
186
186
|
| `sonar()` | Perplexity | sonar | [\$1.00][4] | [\$1.00][4] |
|
|
@@ -189,11 +189,16 @@ ModelMix provides convenient shorthand methods for quickly accessing different A
|
|
|
189
189
|
| `hermes4405b()` | OpenRouter | nousresearch/hermes-4-405b | [\$1.00][13] | [\$3.00][13] |
|
|
190
190
|
| `hermes3()` | Lambda | Hermes-3-Llama-3.1-405B-FP8 | [\$0.80][8] | [\$0.80][8] |
|
|
191
191
|
| `kimiK3()` | Moonshot | kimi-k3 | [\$3.00][11] | [\$15.00][11] |
|
|
192
|
+
| `kimiK27Code()` | Together | Kimi-K2.7-Code | [\$0.95][7] | [\$4.00][7] |
|
|
192
193
|
| `kimiK25()` | Together | Kimi-K2.5 | [\$0.50][7] | [\$2.80][7] |
|
|
193
194
|
| `kimiK26()` | Fireworks | models/kimi-k2p6 | [\$0.95][10] | [\$4.00][10] |
|
|
194
195
|
|
|
195
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.
|
|
196
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
|
+
|
|
197
202
|
[1]: https://platform.openai.com/docs/pricing "Pricing | OpenAI"
|
|
198
203
|
[2]: https://docs.anthropic.com/en/docs/about-claude/pricing "Pricing - Anthropic"
|
|
199
204
|
[3]: https://ai.google.dev/gemini-api/docs/pricing "Google AI for Developers"
|
|
@@ -210,6 +215,8 @@ Gemini 3.7 Flash and 3.6 Flash use Google's introductory standard pricing throug
|
|
|
210
215
|
[14]: https://openrouter.ai/qwen/qwen3.5-397b-a17b "Qwen3.5 397B A17B on OpenRouter"
|
|
211
216
|
[15]: https://openrouter.ai/qwen/qwen3.8-27b "Qwen3.8 27B on OpenRouter"
|
|
212
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"
|
|
213
220
|
|
|
214
221
|
Each method accepts optional `options`, `config`, and (for multi-provider methods) `mix` parameters to customize behavior.
|
|
215
222
|
|
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
|
@@ -17,8 +17,9 @@ const mmix = await ModelMix.new(setup)
|
|
|
17
17
|
.qwen35397b() // (fallback 5) OpenRouter qwen/qwen3.5-397b-a17b
|
|
18
18
|
.qwen3827b() // (fallback 6) OpenRouter qwen/qwen3.8-27b
|
|
19
19
|
.GLM53() // (fallback 7) OpenRouter z-ai/glm-5.3
|
|
20
|
-
.
|
|
21
|
-
.
|
|
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
|
|
22
23
|
.addText("What's your name?");
|
|
23
24
|
|
|
24
25
|
console.log(await mmix.message());
|
package/effort.js
CHANGED
|
@@ -45,6 +45,9 @@ const OPENAI_MODEL_LEVELS = {
|
|
|
45
45
|
'gpt-5.6-sol': ['none', 'low', 'medium', 'high', 'xhigh', 'max'],
|
|
46
46
|
'gpt-5.6-terra': ['none', 'low', 'medium', 'high', 'xhigh', 'max'],
|
|
47
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'],
|
|
48
51
|
'accounts/fireworks/models/qwen3p8-2p4t-a95b': ['none', 'low', 'medium', 'high'],
|
|
49
52
|
'qwen/qwen3.8-27b': ['low', 'medium', 'xhigh'],
|
|
50
53
|
'z-ai/glm-5.3': ['low', 'high', 'max'],
|
|
@@ -55,6 +58,14 @@ const OPENAI_MODEL_LEVELS = {
|
|
|
55
58
|
'gpt-5.3-codex': ['low', 'medium', 'high', 'xhigh'],
|
|
56
59
|
'gpt-oss-120b': ['low', 'medium', 'high'],
|
|
57
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'],
|
|
58
69
|
};
|
|
59
70
|
|
|
60
71
|
/**
|
package/index.d.ts
CHANGED
|
@@ -496,7 +496,7 @@ 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;
|
|
@@ -516,7 +516,6 @@ export declare class ModelMix {
|
|
|
516
516
|
mimo25pro(args?: ModelAttachArgs): this;
|
|
517
517
|
deepseekV4Pro(args?: ModelAttachArgs): this;
|
|
518
518
|
deepseekV4Flash(args?: ModelAttachArgs): this;
|
|
519
|
-
GLM51(args?: ModelAttachArgs): this;
|
|
520
519
|
GLM52(args?: ModelAttachArgs): this;
|
|
521
520
|
GLM53(args?: ModelAttachArgs): this;
|
|
522
521
|
|
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
|
|
|
@@ -635,13 +642,17 @@ class ModelMix {
|
|
|
635
642
|
kimiK27Code({ options = {}, config = {}, mix = { together: true } } = {}) {
|
|
636
643
|
mix = { ...this.mix, ...mix };
|
|
637
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 }));
|
|
638
647
|
return this;
|
|
639
648
|
}
|
|
640
649
|
|
|
641
650
|
kimiK3({ options = {}, config = {}, mix = { moonshot: true, openrouter: false } } = {}) {
|
|
642
651
|
mix = { ...this.mix, ...mix };
|
|
643
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 }));
|
|
644
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 }));
|
|
645
656
|
return this;
|
|
646
657
|
}
|
|
647
658
|
|
|
@@ -661,14 +672,16 @@ class ModelMix {
|
|
|
661
672
|
minimaxM27({ options = {}, config = {}, mix = { openrouter: true, minimax: true } } = {}) {
|
|
662
673
|
mix = { ...this.mix, ...mix };
|
|
663
674
|
if (mix.nvidia) this.attach('minimaxai/minimax-m2.7', new MixNVIDIA({ options, config }));
|
|
664
|
-
if (mix.
|
|
665
|
-
if (mix.
|
|
666
|
-
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 }));
|
|
667
679
|
return this;
|
|
668
680
|
}
|
|
669
681
|
|
|
670
682
|
minimaxM3({ options = {}, config = {}, mix = { minimax: true, openrouter: false } } = {}) {
|
|
671
683
|
mix = { ...this.mix, ...mix };
|
|
684
|
+
if (mix.fireworks) this.attach('accounts/fireworks/models/minimax-m3', new MixFireworks({ options, config }));
|
|
672
685
|
if (mix.openrouter) this.attach('minimax/minimax-m3', new MixOpenRouter({ options, config }));
|
|
673
686
|
if (mix.minimax) this.attach('MiniMax-M3', new MixMiniMax({ options, config }));
|
|
674
687
|
if (mix.together) this.attach('MiniMaxAI/MiniMax-M3', new MixTogether({ options, config }));
|
|
@@ -707,18 +720,11 @@ class ModelMix {
|
|
|
707
720
|
return this;
|
|
708
721
|
}
|
|
709
722
|
|
|
710
|
-
GLM51({ options = {}, config = {}, mix = { fireworks: true } } = {}) {
|
|
711
|
-
mix = { ...this.mix, ...mix };
|
|
712
|
-
if (mix.nvidia) this.attach('z-ai/glm-5.1', new MixNVIDIA({ options, config }));
|
|
713
|
-
if (mix.fireworks) this.attach('accounts/fireworks/models/glm-5p1', new MixFireworks({ options, config }));
|
|
714
|
-
if (mix.openrouter) this.attach('z-ai/glm-5.1', new MixOpenRouter({ options, config }));
|
|
715
|
-
if (mix.together) this.attach('zai-org/GLM-5.1', new MixTogether({ options, config }));
|
|
716
|
-
return this;
|
|
717
|
-
}
|
|
718
|
-
|
|
719
723
|
GLM52({ options = {}, config = {}, mix = { together: true } } = {}) {
|
|
720
724
|
mix = { ...this.mix, ...mix };
|
|
721
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 }));
|
|
722
728
|
return this;
|
|
723
729
|
}
|
|
724
730
|
|
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) {
|
|
@@ -48,14 +48,14 @@ function createAnthropicProviders({ ModelMix, MixCustom, log }) {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
getDefaultConfig(customConfig) {
|
|
51
|
-
|
|
52
|
-
if (!
|
|
51
|
+
const apiKey = customConfig.apiKey || process.env.ANTHROPIC_API_KEY;
|
|
52
|
+
if (!apiKey) {
|
|
53
53
|
throw new Error('Anthropic API key not found. Please provide it in config or set ANTHROPIC_API_KEY environment variable.');
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
return super.getDefaultConfig({
|
|
57
57
|
url: 'https://api.anthropic.com/v1/messages',
|
|
58
|
-
apiKey
|
|
58
|
+
apiKey,
|
|
59
59
|
...customConfig
|
|
60
60
|
});
|
|
61
61
|
}
|
package/lib/providers/base.js
CHANGED
|
@@ -274,14 +274,14 @@ function createBaseProviders({ ModelMix }) {
|
|
|
274
274
|
}
|
|
275
275
|
|
|
276
276
|
getDefaultConfig(customConfig) {
|
|
277
|
-
|
|
278
|
-
if (!
|
|
277
|
+
const apiKey = customConfig.apiKey || process.env.OPENAI_API_KEY;
|
|
278
|
+
if (!apiKey) {
|
|
279
279
|
throw new Error('OpenAI API key not found. Please provide it in config or set OPENAI_API_KEY environment variable.');
|
|
280
280
|
}
|
|
281
281
|
|
|
282
282
|
return super.getDefaultConfig({
|
|
283
283
|
url: 'https://api.openai.com/v1/chat/completions',
|
|
284
|
-
apiKey
|
|
284
|
+
apiKey,
|
|
285
285
|
...customConfig
|
|
286
286
|
});
|
|
287
287
|
}
|
package/lib/providers/google.js
CHANGED
|
@@ -4,9 +4,10 @@ const { configForDebug } = require('../provider-debug');
|
|
|
4
4
|
function createGoogleProviders({ ModelMix, MixCustom }) {
|
|
5
5
|
class MixGoogle extends MixCustom {
|
|
6
6
|
getDefaultConfig(customConfig) {
|
|
7
|
+
const apiKey = customConfig.apiKey || process.env.GEMINI_API_KEY;
|
|
7
8
|
return super.getDefaultConfig({
|
|
8
9
|
url: 'https://generativelanguage.googleapis.com/v1beta/models',
|
|
9
|
-
apiKey
|
|
10
|
+
apiKey,
|
|
10
11
|
...customConfig
|
|
11
12
|
});
|
|
12
13
|
}
|
|
@@ -6,14 +6,14 @@ const {
|
|
|
6
6
|
function createCompatibleProviders({ MixCustom, MixOpenAI }) {
|
|
7
7
|
class MixMiniMax extends MixOpenAI {
|
|
8
8
|
getDefaultConfig(customConfig) {
|
|
9
|
-
|
|
10
|
-
if (!
|
|
9
|
+
const apiKey = customConfig.apiKey || process.env.MINIMAX_API_KEY;
|
|
10
|
+
if (!apiKey) {
|
|
11
11
|
throw new Error('MiniMax API key not found. Please provide it in config or set MINIMAX_API_KEY environment variable.');
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
return MixCustom.prototype.getDefaultConfig.call(this, {
|
|
15
15
|
url: 'https://api.minimax.io/v1/chat/completions',
|
|
16
|
-
apiKey
|
|
16
|
+
apiKey,
|
|
17
17
|
...customConfig
|
|
18
18
|
});
|
|
19
19
|
}
|
|
@@ -29,13 +29,14 @@ function createCompatibleProviders({ MixCustom, MixOpenAI }) {
|
|
|
29
29
|
|
|
30
30
|
class MixMiMo extends MixOpenAI {
|
|
31
31
|
getDefaultConfig(customConfig) {
|
|
32
|
-
|
|
32
|
+
const apiKey = customConfig.apiKey || process.env.MIMO_API_KEY;
|
|
33
|
+
if (!apiKey) {
|
|
33
34
|
throw new Error('MiMo API key not found. Please provide it in config or set MIMO_API_KEY environment variable.');
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
return MixCustom.prototype.getDefaultConfig.call(this, {
|
|
37
38
|
url: 'https://api.xiaomimimo.com/v1/chat/completions',
|
|
38
|
-
apiKey
|
|
39
|
+
apiKey,
|
|
39
40
|
...customConfig
|
|
40
41
|
});
|
|
41
42
|
}
|
|
@@ -52,14 +53,14 @@ function createCompatibleProviders({ MixCustom, MixOpenAI }) {
|
|
|
52
53
|
|
|
53
54
|
class MixPerplexity extends MixCustom {
|
|
54
55
|
getDefaultConfig(customConfig) {
|
|
55
|
-
|
|
56
|
-
if (!
|
|
56
|
+
const apiKey = customConfig.apiKey || process.env.PPLX_API_KEY;
|
|
57
|
+
if (!apiKey) {
|
|
57
58
|
throw new Error('Perplexity API key not found. Please provide it in config or set PPLX_API_KEY environment variable.');
|
|
58
59
|
}
|
|
59
60
|
|
|
60
61
|
return super.getDefaultConfig({
|
|
61
62
|
url: 'https://api.perplexity.ai/chat/completions',
|
|
62
|
-
apiKey
|
|
63
|
+
apiKey,
|
|
63
64
|
...customConfig
|
|
64
65
|
});
|
|
65
66
|
}
|
|
@@ -132,14 +133,14 @@ function createCompatibleProviders({ MixCustom, MixOpenAI }) {
|
|
|
132
133
|
|
|
133
134
|
class MixGrok extends MixOpenAI {
|
|
134
135
|
getDefaultConfig(customConfig) {
|
|
135
|
-
|
|
136
|
-
if (!
|
|
136
|
+
const apiKey = customConfig.apiKey || process.env.XAI_API_KEY;
|
|
137
|
+
if (!apiKey) {
|
|
137
138
|
throw new Error('Grok API key not found. Please provide it in config or set XAI_API_KEY environment variable.');
|
|
138
139
|
}
|
|
139
140
|
|
|
140
141
|
return super.getDefaultConfig({
|
|
141
142
|
url: 'https://api.x.ai/v1/chat/completions',
|
|
142
|
-
apiKey
|
|
143
|
+
apiKey,
|
|
143
144
|
...customConfig
|
|
144
145
|
});
|
|
145
146
|
}
|
|
@@ -154,14 +155,14 @@ function createCompatibleProviders({ MixCustom, MixOpenAI }) {
|
|
|
154
155
|
|
|
155
156
|
class MixLambda extends MixCustom {
|
|
156
157
|
getDefaultConfig(customConfig) {
|
|
157
|
-
|
|
158
|
-
if (!
|
|
158
|
+
const apiKey = customConfig.apiKey || process.env.LAMBDA_API_KEY;
|
|
159
|
+
if (!apiKey) {
|
|
159
160
|
throw new Error('Lambda API key not found. Please provide it in config or set LAMBDA_API_KEY environment variable.');
|
|
160
161
|
}
|
|
161
162
|
|
|
162
163
|
return super.getDefaultConfig({
|
|
163
164
|
url: 'https://api.lambda.ai/v1/chat/completions',
|
|
164
|
-
apiKey
|
|
165
|
+
apiKey,
|
|
165
166
|
...customConfig
|
|
166
167
|
});
|
|
167
168
|
}
|
|
@@ -234,14 +235,14 @@ function createCompatibleProviders({ MixCustom, MixOpenAI }) {
|
|
|
234
235
|
|
|
235
236
|
class MixGroq extends MixCustom {
|
|
236
237
|
getDefaultConfig(customConfig) {
|
|
237
|
-
|
|
238
|
-
if (!
|
|
238
|
+
const apiKey = customConfig.apiKey || process.env.GROQ_API_KEY;
|
|
239
|
+
if (!apiKey) {
|
|
239
240
|
throw new Error('Groq API key not found. Please provide it in config or set GROQ_API_KEY environment variable.');
|
|
240
241
|
}
|
|
241
242
|
|
|
242
243
|
return super.getDefaultConfig({
|
|
243
244
|
url: 'https://api.groq.com/openai/v1/chat/completions',
|
|
244
|
-
apiKey
|
|
245
|
+
apiKey,
|
|
245
246
|
...customConfig
|
|
246
247
|
});
|
|
247
248
|
}
|
|
@@ -249,14 +250,14 @@ function createCompatibleProviders({ MixCustom, MixOpenAI }) {
|
|
|
249
250
|
|
|
250
251
|
class MixTogether extends MixCustom {
|
|
251
252
|
getDefaultConfig(customConfig) {
|
|
252
|
-
|
|
253
|
-
if (!
|
|
253
|
+
const apiKey = customConfig.apiKey || process.env.TOGETHER_API_KEY;
|
|
254
|
+
if (!apiKey) {
|
|
254
255
|
throw new Error('Together API key not found. Please provide it in config or set TOGETHER_API_KEY environment variable.');
|
|
255
256
|
}
|
|
256
257
|
|
|
257
258
|
return super.getDefaultConfig({
|
|
258
259
|
url: 'https://api.together.xyz/v1/chat/completions',
|
|
259
|
-
apiKey
|
|
260
|
+
apiKey,
|
|
260
261
|
...customConfig
|
|
261
262
|
});
|
|
262
263
|
}
|
|
@@ -271,14 +272,14 @@ function createCompatibleProviders({ MixCustom, MixOpenAI }) {
|
|
|
271
272
|
|
|
272
273
|
class MixCerebras extends MixCustom {
|
|
273
274
|
getDefaultConfig(customConfig) {
|
|
274
|
-
|
|
275
|
-
if (!
|
|
276
|
-
throw new Error('
|
|
275
|
+
const apiKey = customConfig.apiKey || process.env.CEREBRAS_API_KEY;
|
|
276
|
+
if (!apiKey) {
|
|
277
|
+
throw new Error('Cerebras API key not found. Please provide it in config or set CEREBRAS_API_KEY environment variable.');
|
|
277
278
|
}
|
|
278
279
|
|
|
279
280
|
return super.getDefaultConfig({
|
|
280
281
|
url: 'https://api.cerebras.ai/v1/chat/completions',
|
|
281
|
-
apiKey
|
|
282
|
+
apiKey,
|
|
282
283
|
...customConfig
|
|
283
284
|
});
|
|
284
285
|
}
|
|
@@ -291,14 +292,14 @@ function createCompatibleProviders({ MixCustom, MixOpenAI }) {
|
|
|
291
292
|
|
|
292
293
|
class MixFireworks extends MixCustom {
|
|
293
294
|
getDefaultConfig(customConfig) {
|
|
294
|
-
|
|
295
|
-
if (!
|
|
295
|
+
const apiKey = customConfig.apiKey || process.env.FIREWORKS_API_KEY;
|
|
296
|
+
if (!apiKey) {
|
|
296
297
|
throw new Error('Fireworks API key not found. Please provide it in config or set FIREWORKS_API_KEY environment variable.');
|
|
297
298
|
}
|
|
298
299
|
|
|
299
300
|
return super.getDefaultConfig({
|
|
300
301
|
url: 'https://api.fireworks.ai/inference/v1/chat/completions',
|
|
301
|
-
apiKey
|
|
302
|
+
apiKey,
|
|
302
303
|
...customConfig
|
|
303
304
|
});
|
|
304
305
|
}
|
|
@@ -306,14 +307,14 @@ function createCompatibleProviders({ MixCustom, MixOpenAI }) {
|
|
|
306
307
|
|
|
307
308
|
class MixNVIDIA extends MixCustom {
|
|
308
309
|
getDefaultConfig(customConfig) {
|
|
309
|
-
|
|
310
|
-
if (!
|
|
310
|
+
const apiKey = customConfig.apiKey || process.env.NVIDIA_API_KEY;
|
|
311
|
+
if (!apiKey) {
|
|
311
312
|
throw new Error('NVIDIA API key not found. Please provide it in config or set NVIDIA_API_KEY environment variable.');
|
|
312
313
|
}
|
|
313
314
|
|
|
314
315
|
return super.getDefaultConfig({
|
|
315
316
|
url: 'https://integrate.api.nvidia.com/v1/chat/completions',
|
|
316
|
-
apiKey
|
|
317
|
+
apiKey,
|
|
317
318
|
...customConfig
|
|
318
319
|
});
|
|
319
320
|
}
|
package/lib/providers/openai.js
CHANGED
|
@@ -556,14 +556,14 @@ function createOpenAIProviders({
|
|
|
556
556
|
|
|
557
557
|
class MixOpenRouter extends MixOpenAI {
|
|
558
558
|
getDefaultConfig(customConfig) {
|
|
559
|
-
|
|
560
|
-
if (!
|
|
559
|
+
const apiKey = customConfig.apiKey || process.env.OPENROUTER_API_KEY;
|
|
560
|
+
if (!apiKey) {
|
|
561
561
|
throw new Error('OpenRouter API key not found. Please provide it in config or set OPENROUTER_API_KEY environment variable.');
|
|
562
562
|
}
|
|
563
563
|
|
|
564
564
|
return MixCustom.prototype.getDefaultConfig.call(this, {
|
|
565
565
|
url: 'https://openrouter.ai/api/v1/chat/completions',
|
|
566
|
-
apiKey
|
|
566
|
+
apiKey,
|
|
567
567
|
...customConfig
|
|
568
568
|
});
|
|
569
569
|
}
|
|
@@ -571,13 +571,14 @@ function createOpenAIProviders({
|
|
|
571
571
|
|
|
572
572
|
class MixKimi extends MixOpenAI {
|
|
573
573
|
getDefaultConfig(customConfig) {
|
|
574
|
-
|
|
574
|
+
const apiKey = customConfig.apiKey || process.env.MOONSHOT_API_KEY;
|
|
575
|
+
if (!apiKey) {
|
|
575
576
|
throw new Error('Moonshot API key not found. Please provide it in config or set MOONSHOT_API_KEY environment variable.');
|
|
576
577
|
}
|
|
577
578
|
|
|
578
579
|
return MixCustom.prototype.getDefaultConfig.call(this, {
|
|
579
580
|
url: 'https://api.moonshot.ai/v1/chat/completions',
|
|
580
|
-
apiKey
|
|
581
|
+
apiKey,
|
|
581
582
|
...customConfig
|
|
582
583
|
});
|
|
583
584
|
}
|
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,15 +87,29 @@ 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 },
|
|
87
93
|
'z-ai/glm-5.3': { input: 1.40, cachedInput: 0.26, output: 4.40 },
|
|
88
94
|
'accounts/fireworks/models/kimi-k2p5': { input: 0.50, output: 2.80 },
|
|
89
95
|
'qwen/qwen3.5-397b-a17b': { input: 0.385, output: 2.45 },
|
|
90
96
|
'accounts/fireworks/models/qwen3p6-plus': { input: 0.50, output: 3.00 },
|
|
91
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
|
+
},
|
|
92
104
|
'accounts/fireworks/models/qwen3p7-plus': { input: 0.40, output: 1.60 },
|
|
93
|
-
'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 },
|
|
94
113
|
'accounts/fireworks/models/qwen3p8-2p4t-a95b': { input: 2.00, cachedInput: 0.25, output: 6.00 },
|
|
95
114
|
'qwen/qwen3.8-max': { input: 2.00, output: 6.00 },
|
|
96
115
|
'qwen/qwen3.8-27b': { input: 0.45, cachedInput: 0.05, output: 3.20 },
|
|
@@ -100,7 +119,11 @@ const MODEL_PRICING = {
|
|
|
100
119
|
'MiniMax-M3': { input: 0.30, output: 1.20 },
|
|
101
120
|
'minimax/minimax-m2.7': { input: 0.30, output: 1.20 },
|
|
102
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 },
|
|
103
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 },
|
|
104
127
|
// Perplexity
|
|
105
128
|
'sonar': { input: 1.00, output: 1.00 },
|
|
106
129
|
'sonar-pro': { input: 3.00, output: 15.00 },
|
|
@@ -110,15 +133,18 @@ const MODEL_PRICING = {
|
|
|
110
133
|
// Hermes 3 (Lambda/OpenRouter)
|
|
111
134
|
'Hermes-3-Llama-3.1-405B-FP8': { input: 0.80, output: 0.80 },
|
|
112
135
|
'nousresearch/hermes-3-llama-3.1-405b:free': { input: 0, output: 0 },
|
|
113
|
-
// Qwen3 (Together/Cerebras)
|
|
114
|
-
'Qwen/Qwen3-235B-A22B-fp8-tput': { input: 0.20, output: 0.60 },
|
|
115
|
-
'qwen-3-32b': { input: 0.20, output: 0.60 },
|
|
116
136
|
// Kimi K2.5 (Together/Fireworks/OpenRouter)
|
|
117
137
|
'moonshotai/Kimi-K2.5': { input: 0.50, output: 2.80 },
|
|
118
138
|
'moonshotai/kimi-k2.5': { input: 0.50, output: 2.80 },
|
|
119
139
|
// Kimi K3
|
|
120
|
-
'kimi-k3': { input: 3.00, output: 15.00 },
|
|
121
|
-
'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 },
|
|
122
148
|
// GLM 4.7 (OpenRouter/Cerebras)
|
|
123
149
|
'z-ai/glm-4.7': { input: 0.55, output: 2.19 },
|
|
124
150
|
'zai-glm-4.7': { input: 0.55, output: 2.19 },
|
package/package.json
CHANGED
package/skills/modelmix/SKILL.md
CHANGED
|
@@ -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()` `qwen3827b()` `hermes470b()` `hermes4405b()` `qwen38max()` `GLM45()` `GLM53()`
|
|
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,42 @@ 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
|
+
|
|
99
135
|
it('maps Qwen 3.8 27B to its supported reasoning levels', () => {
|
|
100
136
|
const key = 'qwen/qwen3.8-27b';
|
|
101
137
|
expect(mapEffort('openai', 0, key)).to.deep.equal({ reasoning_effort: 'low' });
|
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,13 +1,37 @@
|
|
|
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('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).to.
|
|
10
|
-
|
|
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);
|
|
11
35
|
});
|
|
12
36
|
|
|
13
37
|
it('should register GLM 5.3 through OpenRouter', () => {
|
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/public-api.test.js
CHANGED
|
@@ -48,6 +48,48 @@ describe('public module boundary', () => {
|
|
|
48
48
|
expect(model.models[0].provider.constructor).to.equal(api.MixOpenRouter);
|
|
49
49
|
});
|
|
50
50
|
|
|
51
|
+
const explicitApiKeyCases = [
|
|
52
|
+
['OpenAI', config => new api.MixOpenAIResponses({ config })],
|
|
53
|
+
['OpenAI moderation', config => new api.MixOpenAIModeration({ config })],
|
|
54
|
+
['Anthropic', config => new api.MixAnthropic({ config })],
|
|
55
|
+
['Gemini', config => new api.MixGoogle({ config })],
|
|
56
|
+
['MiniMax', config => new api.MixMiniMax({ config })],
|
|
57
|
+
['MiMo', config => new api.MixMiMo({ config })],
|
|
58
|
+
['Perplexity', config => new api.MixPerplexity({ config })],
|
|
59
|
+
['Grok', config => new api.MixGrok({ config })],
|
|
60
|
+
['Lambda', config => api.ModelMix.new({ mix: { openrouter: false, lambda: true } })
|
|
61
|
+
.hermes3({ config }).models[0].provider],
|
|
62
|
+
['Groq', config => new api.MixGroq({ config })],
|
|
63
|
+
['Together', config => new api.MixTogether({ config })],
|
|
64
|
+
['Cerebras', config => new api.MixCerebras({ config })],
|
|
65
|
+
['Fireworks', config => new api.MixFireworks({ config })],
|
|
66
|
+
['NVIDIA', config => new api.MixNVIDIA({ config })],
|
|
67
|
+
['OpenRouter', config => new api.MixOpenRouter({ config })],
|
|
68
|
+
['Moonshot', config => new api.MixKimi({ config })]
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
for (const [providerName, createProvider] of explicitApiKeyCases) {
|
|
72
|
+
it(`uses an explicit API key for ${providerName} without reading environment credentials`, () => {
|
|
73
|
+
const originalEnv = process.env;
|
|
74
|
+
process.env = new Proxy(originalEnv, {
|
|
75
|
+
get(target, property, receiver) {
|
|
76
|
+
if (typeof property === 'string' && property.endsWith('_API_KEY')) {
|
|
77
|
+
throw new Error(`${property} should not be read`);
|
|
78
|
+
}
|
|
79
|
+
return Reflect.get(target, property, receiver);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
const provider = createProvider({ apiKey: 'explicit-key' });
|
|
85
|
+
|
|
86
|
+
expect(provider.config.apiKey).to.equal('explicit-key');
|
|
87
|
+
} finally {
|
|
88
|
+
process.env = originalEnv;
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
51
93
|
it('keeps the mutable pricing catalog private', () => {
|
|
52
94
|
expect(require('../lib/token-usage')).to.not.have.property('MODEL_PRICING');
|
|
53
95
|
});
|
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();
|