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 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()` | Together | gpt-oss-120B | [\$0.15][7] | [\$0.60][7] |
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()` | Fireworks | qwen3p6-plus | [\$0.50][10] | [\$3.00][10] |
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-235B-A22B-fp8-tput', new MixTogether());
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
- .hermes470b() // (fallback 6) OpenRouter nousresearch/hermes-4-70b
19
- .hermes4405b() // (fallback 7) OpenRouter nousresearch/hermes-4-405b
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 = normalized === 100 && supported.includes('max')
381
- ? 'max'
382
- : levelFromBands(normalized, OPENAI_BANDS);
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
- qwen3(args?: ModelAttachArgs): this;
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
- qwen3({ options = {}, config = {}, mix = { together: true, cerebras: false } } = {}) {
578
- if (mix.together) this.attach('Qwen/Qwen3-235B-A22B-fp8-tput', new MixTogether({ options, config }));
579
- if (mix.cerebras) this.attach('qwen-3-32b', new MixCerebras({ options, config }));
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.openrouter) return this.attach('minimax/minimax-m2.7', new MixOpenRouter({ options, config }));
661
- if (mix.minimax) return this.attach('MiniMax-M2.7', new MixMiniMax({ options, config }));
662
- if (mix.together) return this.attach('MiniMaxAI/MiniMax-M2.7', new MixTogether({ options, config }));
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,
@@ -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
- 'qwen3', 'qwen35397b', 'qwen36plus', 'qwen37plus', 'qwen38max',
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', 'GLM51', 'GLM52'
18
+ 'deepseekV4Pro', 'deepseekV4Flash', 'GLM52', 'GLM53'
18
19
  ]);
19
20
 
20
21
  function parseChainModels(modelSpecs) {
@@ -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
- 'accounts/fireworks/models/glm-5p1': { input: 1.05, output: 3.50 },
86
- 'zai-org/GLM-5.2': { input: 1.40, output: 4.40 },
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': { input: 0.32, output: 1.28 },
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "modelmix",
3
- "version": "5.1.2",
3
+ "version": "5.1.6",
4
4
  "description": "🧬 Reliable interface with automatic fallback for AI LLMs.",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -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`; use `{ mix: { moonshot: false, openrouter: true } }` for OpenRouter.
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()`
@@ -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' },
@@ -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('zai-org/GLM-5.2');
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.have.length(1);
11
- expect(model.models[0].key).to.equal('moonshotai/Kimi-K2.7-Code');
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 Fireworks Qwen 3.6 Plus by default', () => {
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