modelmix 4.6.14 → 4.7.0

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
@@ -124,7 +124,9 @@ ModelMix.new().effort(-1).minimaxM3().addText('...').message();
124
124
  | DeepSeek V4 | off | `low`↑ | `high`↑ | `high`↑ | `max`↑ | — |
125
125
  | MiniMax M3 | off | adaptive | adaptive | adaptive | adaptive | adaptive |
126
126
 
127
- \* Gemini bands: 0–24 / 25–49 / 50–74 / 75–100. DeepSeek `↑` = thinking on; `off` = thinking disabled. MiniMax `off`/`adaptive` = `thinking.disabled` / `thinking.type=adaptive`. Gemini 2.5 maps 0–100 to `thinkingBudget`. `-1` = provider adaptive/dynamic when available, else no-op. Levels clamp to what each model supports.
127
+ \* Gemini bands: 0–24 / 25–49 / 50–74 / 75–100. DeepSeek `↑` = thinking on; `off` = thinking disabled. MiniMax `off`/`adaptive` = `thinking.disabled` / `thinking.type=adaptive`. Gemini 2.5 maps 0–100 to `thinkingBudget`. Anthropic maps adaptive thinking + `output_config.effort` on Claude 5 / Fable / Opus 4.6+ / Sonnet 4.6+; older models (Sonnet 4.5, Haiku 4.5) get `thinking.type=enabled` + `budget_tokens`. `-1` = provider adaptive/dynamic when available, else no-op. Levels clamp to what each model supports.
128
+
129
+ Migration: former Anthropic `*think()` shorthands are removed — use `.effort(100).opus5()` (or any 0–100 / `-1`) instead.
128
130
 
129
131
  ## 🔧 Model Context Protocol (MCP) Integration
130
132
 
@@ -177,23 +179,25 @@ Here's a comprehensive list of available methods:
177
179
  | `gpt41mini()` | OpenAI | gpt-4.1-mini | [\$0.40 / \$1.60][1] |
178
180
  | `gpt41nano()` | OpenAI | gpt-4.1-nano | [\$0.10 / \$0.40][1] |
179
181
  | `gptOss()` | Together | gpt-oss-120B | [\$0.15 / \$0.60][7] |
180
- | `fable5[think]()` | Anthropic | claude-fable-5 | [\$10.00 / \$50.00][2] |
181
- | `opus5[think]()` | Anthropic | claude-opus-5 | [\$5.00 / \$25.00][2] |
182
- | `opus48[think]()` | Anthropic | claude-opus-4-8 | [\$5.00 / \$25.00][2] |
183
- | `opus47[think]()` | Anthropic | claude-opus-4-7 | [\$5.00 / \$25.00][2] |
184
- | `opus46[think]()` | Anthropic | claude-opus-4-6 | [\$5.00 / \$25.00][2] |
185
- | `sonnet5[think]()` | Anthropic | claude-sonnet-5 | [\$3.00 / \$15.00][2] |
186
- | `sonnet46[think]()` | Anthropic | claude-sonnet-4-6 | [\$3.00 / \$15.00][2] |
187
- | `haiku45[think]()` | Anthropic | claude-haiku-4-5-20251001 | [\$1.00 / \$5.00][2] |
182
+ | `fable5()` | Anthropic | claude-fable-5 | [\$10.00 / \$50.00][2] |
183
+ | `opus5()` | Anthropic | claude-opus-5 | [\$5.00 / \$25.00][2] |
184
+ | `opus48()` | Anthropic | claude-opus-4-8 | [\$5.00 / \$25.00][2] |
185
+ | `opus47()` | Anthropic | claude-opus-4-7 | [\$5.00 / \$25.00][2] |
186
+ | `opus46()` | Anthropic | claude-opus-4-6 | [\$5.00 / \$25.00][2] |
187
+ | `sonnet5()` | Anthropic | claude-sonnet-5 | [\$3.00 / \$15.00][2] |
188
+ | `sonnet46()` | Anthropic | claude-sonnet-4-6 | [\$3.00 / \$15.00][2] |
189
+ | `haiku45()` | Anthropic | claude-haiku-4-5-20251001 | [\$1.00 / \$5.00][2] |
188
190
  | `gemini31pro()` | Google | gemini-3.1-pro-preview | [\$2.00 / \$12.00][3] |
189
191
  | `gemini36flash()` | Google | gemini-3.6-flash | [\$1.50 / \$7.50][3] |
190
192
  | `gemini35flash()` | Google | gemini-3.5-flash | [\$0.75 / \$4.50][3] |
191
193
  | `gemini31flashLite()`| Google | gemini-3.1-flash-lite-preview | [\$0.25 / \$1.50][3] |
194
+ | `grok45()` | Grok | grok-4.5 | [\$2.00 / \$6.00][6] |
192
195
  | `grok43()` | Grok | grok-4.3 | [\$1.25 / \$2.50][6] |
193
196
  | `grok420multiAgent()`| Grok | grok-4.20-multi-agent-0309 | [\$1.25 / \$2.50][6] |
194
197
  | `grok420[think]()` | Grok | grok-4.20-0309 | [\$1.25 / \$2.50][6] |
195
- | `grok41[think]()` | Grok | grok-4-1-fast | [\$0.20 / \$0.50][6] |
196
198
  | `qwen36plus()` | Fireworks/Together | qwen3p6-plus / Qwen3.6-Plus | [\$0.50 / \$3.00][10] |
199
+ | `qwen37plus()` | Fireworks | models/qwen3p7-plus | [\$0.40 / \$1.60][10] |
200
+ | `qwen38max()` | OpenRouter | qwen/qwen3.8-max | [\$2.00 / \$6.00][12] |
197
201
  | `deepseekV4Flash()` | Fireworks | models/deepseek-v4-flash | [\$0.14 / \$0.28][10] |
198
202
  | `deepseekV4Pro()` | Fireworks | models/deepseek-v4-pro | [\$1.74 / \$3.48][10] |
199
203
  | `GLM52()` | Together | zai-org/GLM-5.2 | [\$1.40 / \$4.40][7] |
@@ -218,10 +222,12 @@ Here's a comprehensive list of available methods:
218
222
  [9]: https://platform.minimax.io/docs/api-reference/anthropic-api-compatible-cache#supported-models-and-pricing "MiniMax Pricing"
219
223
  [10]: https://fireworks.ai/pricing#serverless-pricing "Fireworks Pricing"
220
224
  [11]: https://platform.kimi.ai/docs/guide/kimi-k3-pricing "Kimi K3 Pricing"
225
+ [12]: https://openrouter.ai/qwen/qwen3.8-max "OpenRouter Pricing"
221
226
 
222
227
  Each method accepts optional `options`, `config`, and (for multi-provider methods) `mix` parameters to customize behavior.
223
228
  For NVIDIA on DeepSeek V4 Flash/Pro, use `deepseekV4Flash({ mix: { nvidia: true } })` or `deepseekV4Pro({ mix: { nvidia: true } })`.
224
229
  For Together on Qwen 3.6 Plus, use `qwen36plus({ mix: { fireworks: false, together: true } })`.
230
+ For OpenRouter on Qwen 3.7 Plus, use `qwen37plus({ mix: { fireworks: false, openrouter: true } })`.
225
231
  For OpenRouter instead of Moonshot's native API, use `kimiK3({ mix: { moonshot: false, openrouter: true } })`.
226
232
 
227
233
  ```javascript
package/demo/opus5.js CHANGED
@@ -15,9 +15,9 @@ opus.addText("Explain quantum entanglement in simple terms.");
15
15
  const response = await opus.message();
16
16
  console.log(response);
17
17
 
18
- console.log("\n" + '--------| opus5think() |--------');
18
+ console.log("\n" + '--------| effort(100).opus5() |--------');
19
19
 
20
- const opusThink = mmix.new().opus5think();
20
+ const opusThink = mmix.new().effort(100).opus5();
21
21
  opusThink.addText("A bat and a ball cost $1.10 in total. The bat costs $1.00 more than the ball. How much does the ball cost?");
22
22
  const thinkResponse = await opusThink.raw();
23
23
  console.log(thinkResponse);
package/demo/short.js CHANGED
@@ -9,9 +9,9 @@ const setup = {
9
9
  };
10
10
 
11
11
  const mmix = await ModelMix.new(setup)
12
- .sonnet4() // (main model) Anthropic claude-sonnet-4-20250514
13
- .o4mini() // (fallback 1) OpenAI o4-mini
14
- .gemini25proExp({ config: { temperature: 0 } }) // (fallback 2) Google gemini-2.5-pro-exp-03-25
12
+ .sonnet46() // (main model) Anthropic claude-sonnet-4-6
13
+ .gpt56luna() // (fallback 1) OpenAI gpt-5.6-luna
14
+ .gemini36flash({ config: { temperature: 0 } }) // (fallback 2) Google gemini-3.6-flash
15
15
  .gpt41nano() // (fallback 3) OpenAI gpt-4.1-nano
16
16
  .grok43() // (fallback 4) Grok grok-4.3
17
17
  .addText("What's your name?");
package/demo/stream.js CHANGED
@@ -6,7 +6,7 @@ await ModelMix.new().gpt41nano()
6
6
  .addText('describe')
7
7
  .stream((data) => { console.log(data.message); });
8
8
 
9
- await ModelMix.new().haiku35()
9
+ await ModelMix.new().haiku45()
10
10
  .addImageFromUrl('https://pbs.twimg.com/media/F6-GsjraAAADDGy?format=jpg')
11
11
  .addText('describe')
12
12
  .stream((data) => { console.log(data.message); });
package/demo/tokens.js CHANGED
@@ -25,7 +25,7 @@ console.log('-'.repeat(60));
25
25
 
26
26
  const providers = [
27
27
  { name: 'OpenAI GPT-5-nano', fn: (m) => m.gpt5nano() },
28
- { name: 'Anthropic Haiku', fn: (m) => m.haiku35() },
28
+ { name: 'Anthropic Haiku', fn: (m) => m.haiku45() },
29
29
  { name: 'Google Gemini', fn: (m) => m.gemini25flash() }
30
30
  ];
31
31
 
package/effort.js CHANGED
@@ -170,6 +170,62 @@ function isMiniMax(modelKey) {
170
170
  return typeof modelKey === 'string' && modelKey.toLowerCase().includes('minimax');
171
171
  }
172
172
 
173
+ /** Max budget_tokens when mapping unified effort onto manual Anthropic thinking. */
174
+ const ANTHROPIC_MANUAL_BUDGET_MAX = 16384;
175
+
176
+ /**
177
+ * Models that use adaptive thinking + output_config.effort (Claude 5 / Fable /
178
+ * Opus 4.6+ / Sonnet 4.6+). Older ones (Sonnet 4.5, Haiku 4.5, Opus 4.5) use
179
+ * thinking.type=enabled + budget_tokens.
180
+ */
181
+ function usesAnthropicAdaptiveThinking(modelKey) {
182
+ const id = String(modelKey || '').toLowerCase();
183
+ if (!id) return true;
184
+ if (!id.includes('claude')) return true;
185
+ if (id.includes('fable') || id.includes('mythos')) return true;
186
+
187
+ const opus = id.match(/claude-opus-(\d+)(?:-(\d+))?/);
188
+ if (opus) {
189
+ const major = Number(opus[1]);
190
+ const minor = opus[2] !== undefined ? Number(opus[2]) : 0;
191
+ return major > 4 || (major === 4 && minor >= 6);
192
+ }
193
+
194
+ const sonnet = id.match(/claude-sonnet-(\d+)(?:-(\d+))?/);
195
+ if (sonnet) {
196
+ const major = Number(sonnet[1]);
197
+ const minor = sonnet[2] !== undefined ? Number(sonnet[2]) : 0;
198
+ return major > 4 || (major === 4 && minor >= 6);
199
+ }
200
+
201
+ // Haiku 4.5 and earlier: manual extended thinking only
202
+ if (id.includes('haiku')) return false;
203
+
204
+ return true;
205
+ }
206
+
207
+ function mapAnthropicManualBudget(normalized) {
208
+ return Math.max(1024, Math.round((normalized / 100) * ANTHROPIC_MANUAL_BUDGET_MAX));
209
+ }
210
+
211
+ function mapAnthropicEffort(normalized, modelKey) {
212
+ if (!usesAnthropicAdaptiveThinking(modelKey)) {
213
+ return {
214
+ thinking: {
215
+ type: 'enabled',
216
+ budget_tokens: mapAnthropicManualBudget(normalized)
217
+ }
218
+ };
219
+ }
220
+
221
+ const desired = levelFromBands(normalized, ANTHROPIC_BANDS);
222
+ const level = pickNearestLevel(desired, ANTHROPIC_LEVELS, ANTHROPIC_LEVELS);
223
+ return {
224
+ thinking: { type: 'adaptive', display: 'summarized' },
225
+ output_config: { effort: level }
226
+ };
227
+ }
228
+
173
229
  function mapDeepSeekEffort(normalized) {
174
230
  // DeepSeek V4 has no adaptive mode (only enabled/disabled + low|high|max)
175
231
  if (normalized === -1) return null;
@@ -282,9 +338,7 @@ function mapEffort(providerFamily, effort, modelKey) {
282
338
  }
283
339
 
284
340
  if (providerFamily === 'anthropic') {
285
- const desired = levelFromBands(normalized, ANTHROPIC_BANDS);
286
- const level = pickNearestLevel(desired, ANTHROPIC_LEVELS, ANTHROPIC_LEVELS);
287
- return { output_config: { effort: level } };
341
+ return mapAnthropicEffort(normalized, modelKey);
288
342
  }
289
343
 
290
344
  if (providerFamily === 'google') {
@@ -357,6 +411,7 @@ module.exports = {
357
411
  resolveProviderFamily,
358
412
  isDeepSeekV4,
359
413
  isMiniMax,
414
+ usesAnthropicAdaptiveThinking,
360
415
  levelFromBands,
361
416
  pickNearestLevel,
362
417
  OPENAI_BANDS,
@@ -367,4 +422,5 @@ module.exports = {
367
422
  ANTHROPIC_LEVELS,
368
423
  GEMINI_LEVELS,
369
424
  DEEPSEEK_LEVELS,
425
+ ANTHROPIC_MANUAL_BUDGET_MAX,
370
426
  };
package/index.d.ts CHANGED
@@ -286,28 +286,14 @@ export declare class ModelMix {
286
286
 
287
287
  // Anthropic
288
288
  fable5(args?: ModelAttachArgs): this;
289
- fable5think(args?: ModelAttachArgs): this;
290
289
  opus5(args?: ModelAttachArgs): this;
291
- opus5think(args?: ModelAttachArgs): this;
292
- opus48think(args?: ModelAttachArgs): this;
293
- opus47think(args?: ModelAttachArgs): this;
294
- opus46think(args?: ModelAttachArgs): this;
295
290
  opus48(args?: ModelAttachArgs): this;
296
291
  opus47(args?: ModelAttachArgs): this;
297
292
  opus46(args?: ModelAttachArgs): this;
298
- opus41(args?: ModelAttachArgs): this;
299
- opus41think(args?: ModelAttachArgs): this;
300
293
  sonnet5(args?: ModelAttachArgs): this;
301
- sonnet5think(args?: ModelAttachArgs): this;
302
- sonnet4(args?: ModelAttachArgs): this;
303
- sonnet4think(args?: ModelAttachArgs): this;
304
294
  sonnet46(args?: ModelAttachArgs): this;
305
- sonnet46think(args?: ModelAttachArgs): this;
306
295
  sonnet45(args?: ModelAttachArgs): this;
307
- sonnet45think(args?: ModelAttachArgs): this;
308
- haiku35(args?: ModelAttachArgs): this;
309
296
  haiku45(args?: ModelAttachArgs): this;
310
- haiku45think(args?: ModelAttachArgs): this;
311
297
 
312
298
  // Google
313
299
  gemini25flash(args?: ModelAttachArgs): this;
@@ -324,16 +310,17 @@ export declare class ModelMix {
324
310
  sonar(args?: ModelAttachArgs): this;
325
311
 
326
312
  // Grok
313
+ grok45(args?: ModelAttachArgs): this;
327
314
  grok43(args?: ModelAttachArgs): this;
328
315
  grok420multiAgent(args?: ModelAttachArgs): this;
329
316
  grok420think(args?: ModelAttachArgs): this;
330
317
  grok420(args?: ModelAttachArgs): this;
331
- grok41think(args?: ModelAttachArgs): this;
332
- grok41(args?: ModelAttachArgs): this;
333
318
 
334
319
  // Multi-provider
335
320
  qwen3(args?: ModelAttachArgs): this;
336
321
  qwen36plus(args?: ModelAttachArgs): this;
322
+ qwen37plus(args?: ModelAttachArgs): this;
323
+ qwen38max(args?: ModelAttachArgs): this;
337
324
  hermes3(args?: ModelAttachArgs): this;
338
325
  kimiK26think(args?: ModelAttachArgs): this;
339
326
  kimiK27Code(args?: ModelAttachArgs): this;
@@ -349,7 +336,6 @@ export declare class ModelMix {
349
336
  deepseekV4Flash(args?: ModelAttachArgs): this;
350
337
  GLM51(args?: ModelAttachArgs): this;
351
338
  GLM52(args?: ModelAttachArgs): this;
352
- GLM5(args?: ModelAttachArgs): this;
353
339
 
354
340
  addText(text: string, options?: RoleOptions): this;
355
341
  addTextFromFile(filePath: string, options?: RoleOptions): this;
package/index.js CHANGED
@@ -72,11 +72,8 @@ const MODEL_PRICING = {
72
72
  'claude-opus-4-8': [5.00, 25.00],
73
73
  'claude-opus-4-7': [5.00, 25.00],
74
74
  'claude-opus-4-6': [5.00, 25.00],
75
- 'claude-opus-4-1-20250805': [15.00, 75.00],
76
75
  'claude-sonnet-4-6': [3.00, 15.00],
77
76
  'claude-sonnet-4-5-20250929': [3.00, 15.00],
78
- 'claude-sonnet-4-20250514': [3.00, 15.00],
79
- 'claude-3-5-haiku-20241022': [0.80, 4.00],
80
77
  'claude-haiku-4-5-20251001': [1.00, 5.00],
81
78
  // Google
82
79
  'gemini-3.1-pro-preview':[2.00, 12.00],
@@ -88,12 +85,11 @@ const MODEL_PRICING = {
88
85
  'gemini-2.5-flash': [0.30, 2.50],
89
86
  'gemini-3.1-flash-lite-preview': [0.25, 1.50],
90
87
  // Grok
88
+ 'grok-4.5': [2.00, 6.00],
91
89
  'grok-4.3': [1.25, 2.50],
92
90
  'grok-4.20-multi-agent-0309': [1.25, 2.50],
93
91
  'grok-4.20-0309-reasoning': [1.25, 2.50],
94
92
  'grok-4.20-0309-non-reasoning': [1.25, 2.50],
95
- 'grok-4-1-fast-reasoning': [0.20, 0.50],
96
- 'grok-4-1-fast-non-reasoning': [0.20, 0.50],
97
93
  // Fireworks
98
94
  'accounts/fireworks/models/deepseek-v4-flash': [0.14, 0.28],
99
95
  'accounts/fireworks/models/deepseek-v4-pro': [1.74, 3.48],
@@ -106,12 +102,13 @@ const MODEL_PRICING = {
106
102
  'accounts/fireworks/models/kimi-k2p5': [0.50, 2.80],
107
103
  'accounts/fireworks/models/qwen3p6-plus': [0.50, 3.00],
108
104
  'Qwen/Qwen3.6-Plus': [0.50, 3.00],
109
- 'fireworks/glm-5': [1.00, 3.20],
105
+ 'accounts/fireworks/models/qwen3p7-plus': [0.40, 1.60],
106
+ 'qwen/qwen3.7-plus': [0.32, 1.28],
107
+ 'qwen/qwen3.8-max': [2.00, 6.00],
110
108
  // MiniMax
111
109
  'MiniMax-M2.5': [0.30, 1.20],
112
110
  'MiniMax-M2.7': [0.30, 1.20],
113
111
  'MiniMax-M3': [0.30, 1.20],
114
- 'fireworks/minimax-m2p5': [0.30, 1.20],
115
112
  'minimax/minimax-m2.7': [0.30, 1.20],
116
113
  'minimax/minimax-m3': [0.30, 1.20],
117
114
  'MiniMaxAI/MiniMax-M3': [0.30, 1.20],
@@ -388,29 +385,9 @@ class ModelMix {
388
385
  fable5({ options = {}, config = {} } = {}) {
389
386
  return this.attach('claude-fable-5', new MixAnthropic({ options, config }));
390
387
  }
391
- fable5think({ options = {}, config = {} } = {}) {
392
- options = { ...MixAnthropic.maxEffortThinkingOptions, ...options };
393
- return this.attach('claude-fable-5', new MixAnthropic({ options, config }));
394
- }
395
388
  opus5({ options = {}, config = {} } = {}) {
396
389
  return this.attach('claude-opus-5', new MixAnthropic({ options, config }));
397
390
  }
398
- opus5think({ options = {}, config = {} } = {}) {
399
- options = { ...MixAnthropic.maxEffortThinkingOptions, ...options };
400
- return this.attach('claude-opus-5', new MixAnthropic({ options, config }));
401
- }
402
- opus48think({ options = {}, config = {} } = {}) {
403
- options = { ...MixAnthropic.thinkingOptions, ...options };
404
- return this.attach('claude-opus-4-8', new MixAnthropic({ options, config }));
405
- }
406
- opus47think({ options = {}, config = {} } = {}) {
407
- options = { ...MixAnthropic.thinkingOptions, ...options };
408
- return this.attach('claude-opus-4-7', new MixAnthropic({ options, config }));
409
- }
410
- opus46think({ options = {}, config = {} } = {}) {
411
- options = { ...MixAnthropic.thinkingOptions, ...options };
412
- return this.attach('claude-opus-4-6', new MixAnthropic({ options, config }));
413
- }
414
391
  opus48({ options = {}, config = {} } = {}) {
415
392
  return this.attach('claude-opus-4-8', new MixAnthropic({ options, config }));
416
393
  }
@@ -420,52 +397,18 @@ class ModelMix {
420
397
  opus46({ options = {}, config = {} } = {}) {
421
398
  return this.attach('claude-opus-4-6', new MixAnthropic({ options, config }));
422
399
  }
423
- opus41({ options = {}, config = {} } = {}) {
424
- return this.attach('claude-opus-4-1-20250805', new MixAnthropic({ options, config }));
425
- }
426
- opus41think({ options = {}, config = {} } = {}) {
427
- options = { ...MixAnthropic.thinkingOptions, ...options };
428
- return this.attach('claude-opus-4-1-20250805', new MixAnthropic({ options, config }));
429
- }
430
400
  sonnet5({ options = {}, config = {} } = {}) {
431
401
  return this.attach('claude-sonnet-5', new MixAnthropic({ options, config }));
432
402
  }
433
- sonnet5think({ options = {}, config = {} } = {}) {
434
- options = { ...MixAnthropic.thinkingOptions, ...options };
435
- return this.attach('claude-sonnet-5', new MixAnthropic({ options, config }));
436
- }
437
- sonnet4({ options = {}, config = {} } = {}) {
438
- return this.attach('claude-sonnet-4-20250514', new MixAnthropic({ options, config }));
439
- }
440
- sonnet4think({ options = {}, config = {} } = {}) {
441
- options = { ...MixAnthropic.thinkingOptions, ...options };
442
- return this.attach('claude-sonnet-4-20250514', new MixAnthropic({ options, config }));
443
- }
444
403
  sonnet46({ options = {}, config = {} } = {}) {
445
404
  return this.attach('claude-sonnet-4-6', new MixAnthropic({ options, config }));
446
405
  }
447
- sonnet46think({ options = {}, config = {} } = {}) {
448
- options = { ...MixAnthropic.thinkingOptions, ...options };
449
- return this.attach('claude-sonnet-4-6', new MixAnthropic({ options, config }));
450
- }
451
-
452
406
  sonnet45({ options = {}, config = {} } = {}) {
453
407
  return this.attach('claude-sonnet-4-5-20250929', new MixAnthropic({ options, config }));
454
408
  }
455
- sonnet45think({ options = {}, config = {} } = {}) {
456
- options = { ...MixAnthropic.thinkingOptions, ...options };
457
- return this.attach('claude-sonnet-4-5-20250929', new MixAnthropic({ options, config }));
458
- }
459
- haiku35({ options = {}, config = {} } = {}) {
460
- return this.attach('claude-3-5-haiku-20241022', new MixAnthropic({ options, config }));
461
- }
462
409
  haiku45({ options = {}, config = {} } = {}) {
463
410
  return this.attach('claude-haiku-4-5-20251001', new MixAnthropic({ options, config }));
464
411
  }
465
- haiku45think({ options = {}, config = {} } = {}) {
466
- options = { ...MixAnthropic.thinkingOptions, ...options };
467
- return this.attach('claude-haiku-4-5-20251001', new MixAnthropic({ options, config }));
468
- }
469
412
  gemini25flash({ options = {}, config = {} } = {}) {
470
413
  return this.attach('gemini-2.5-flash', new MixGoogle({ options, config }));
471
414
  }
@@ -497,6 +440,9 @@ class ModelMix {
497
440
  return this.attach('sonar', new MixPerplexity({ options, config }));
498
441
  }
499
442
 
443
+ grok45({ options = {}, config = {} } = {}) {
444
+ return this.attach('grok-4.5', new MixGrok({ options, config }));
445
+ }
500
446
  grok43({ options = {}, config = {} } = {}) {
501
447
  return this.attach('grok-4.3', new MixGrok({ options, config }));
502
448
  }
@@ -509,12 +455,6 @@ class ModelMix {
509
455
  grok420({ options = {}, config = {} } = {}) {
510
456
  return this.attach('grok-4.20-0309-non-reasoning', new MixGrok({ options, config }));
511
457
  }
512
- grok41think({ options = {}, config = {} } = {}) {
513
- return this.attach('grok-4-1-fast-reasoning', new MixGrok({ options, config }));
514
- }
515
- grok41({ options = {}, config = {} } = {}) {
516
- return this.attach('grok-4-1-fast-non-reasoning', new MixGrok({ options, config }));
517
- }
518
458
 
519
459
  qwen3({ options = {}, config = {}, mix = { together: true, cerebras: false } } = {}) {
520
460
  if (mix.together) this.attach('Qwen/Qwen3-235B-A22B-fp8-tput', new MixTogether({ options, config }));
@@ -527,7 +467,20 @@ class ModelMix {
527
467
  if (mix.fireworks) this.attach('accounts/fireworks/models/qwen3p6-plus', new MixFireworks({ options, config }));
528
468
  if (mix.together) this.attach('Qwen/Qwen3.6-Plus', new MixTogether({ options, config }));
529
469
  return this;
530
- }
470
+ }
471
+
472
+ qwen37plus({ options = {}, config = {}, mix = { fireworks: true } } = {}) {
473
+ mix = { ...this.mix, ...mix };
474
+ if (mix.fireworks) this.attach('accounts/fireworks/models/qwen3p7-plus', new MixFireworks({ options, config }));
475
+ if (mix.openrouter) this.attach('qwen/qwen3.7-plus', new MixOpenRouter({ options, config }));
476
+ return this;
477
+ }
478
+
479
+ qwen38max({ options = {}, config = {}, mix = { openrouter: true } } = {}) {
480
+ mix = { ...this.mix, ...mix };
481
+ if (mix.openrouter) this.attach('qwen/qwen3.8-max', new MixOpenRouter({ options, config }));
482
+ return this;
483
+ }
531
484
 
532
485
  hermes3({ options = {}, config = {}, mix = {} } = {}) {
533
486
  mix = { ...this.mix, ...mix };
@@ -573,7 +526,6 @@ class ModelMix {
573
526
  minimaxM25({ options = {}, config = {}, mix = { minimax: true } } = {}) {
574
527
  mix = { ...this.mix, ...mix };
575
528
  if (mix.minimax) this.attach('MiniMax-M2.5', new MixMiniMax({ options, config }));
576
- if (mix.fireworks) this.attach('fireworks/minimax-m2p5', new MixFireworks({ options, config }));
577
529
  return this;
578
530
  }
579
531
 
@@ -641,12 +593,6 @@ class ModelMix {
641
593
  return this;
642
594
  }
643
595
 
644
- GLM5({ options = {}, config = {}, mix = { fireworks: true } } = {}) {
645
- mix = { ...this.mix, ...mix };
646
- if (mix.fireworks) this.attach('fireworks/glm-5', new MixFireworks({ options, config }));
647
- return this;
648
- }
649
-
650
596
  addText(text, { role = "user" } = {}) {
651
597
  const content = [{
652
598
  type: "text",
@@ -987,7 +933,7 @@ class ModelMix {
987
933
 
988
934
  async execute({ config = {}, options = {} } = {}) {
989
935
  if (!this.models || this.models.length === 0) {
990
- throw new Error("No models specified. Use methods like .gpt5(), .sonnet4() first.");
936
+ throw new Error("No models specified. Use methods like .gpt5(), .sonnet46() first.");
991
937
  }
992
938
 
993
939
  return this.limiter.schedule(async () => {
@@ -2197,19 +2143,6 @@ class MixKimi extends MixOpenAI {
2197
2143
 
2198
2144
  class MixAnthropic extends MixCustom {
2199
2145
 
2200
- static thinkingOptions = {
2201
- thinking: {
2202
- "type": "enabled",
2203
- "budget_tokens": 1638
2204
- },
2205
- temperature: 1
2206
- };
2207
-
2208
- static maxEffortThinkingOptions = {
2209
- output_config: { effort: 'max' },
2210
- thinking: { display: 'summarized' }
2211
- };
2212
-
2213
2146
  /**
2214
2147
  * Opus 4.7+ and Claude 5 family reject sampling params (temperature/top_p/top_k).
2215
2148
  * See: https://platform.claude.com/docs/en/about-claude/models/migration-guide
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "modelmix",
3
- "version": "4.6.14",
3
+ "version": "4.7.0",
4
4
  "description": "🧬 Reliable interface with automatic fallback for AI LLMs.",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -114,7 +114,7 @@ ModelMix.new({ config: { effort: 80 } })
114
114
  | DeepSeek V4 | off | `low`↑ | `high`↑ | `high`↑ | `max`↑ | — |
115
115
  | MiniMax M3 | off | adaptive | adaptive | adaptive | adaptive | adaptive |
116
116
 
117
- \* Gemini bands: 0–24 / 25–49 / 50–74 / 75–100. DeepSeek `↑` = thinking on; `off` = thinking disabled. MiniMax `off`/`adaptive` = `thinking.disabled` / `thinking.type=adaptive`. Gemini 2.5 maps 0–100 to `thinkingBudget`. `-1` = adaptive/dynamic when available, else no-op. Levels clamp per model. `*think()` shorthands still win over unified `effort`.
117
+ \* Gemini bands: 0–24 / 25–49 / 50–74 / 75–100. DeepSeek `↑` = thinking on; `off` = thinking disabled. MiniMax `off`/`adaptive` = `thinking.disabled` / `thinking.type=adaptive`. Gemini 2.5 maps 0–100 to `thinkingBudget`. Anthropic: adaptive + `output_config.effort` on Claude 5 / Fable / Opus 4.6+ / Sonnet 4.6+; Sonnet 4.5 / Haiku 4.5 use `thinking.type=enabled` + `budget_tokens`. `-1` = adaptive/dynamic when available, else no-op. Levels clamp per model. Former Anthropic `*think()` methods are removed use `.effort(n).<model>()`.
118
118
 
119
119
  ## Available Model Shorthands
120
120
 
@@ -122,15 +122,15 @@ ModelMix.new({ config: { effort: 80 } })
122
122
  `gpt52()` `gpt52chat()` `gpt51()` `gpt5()` `gpt5mini()` `gpt5nano()` `gpt45()` `gpt41()` `gpt41mini()` `gpt41nano()` `o3()` `o4mini()`
123
123
 
124
124
  ### Anthropic
125
- `fable5()` `opus5()` `opus48()` `opus47()` `opus46()` `opus41()` `sonnet5()` `sonnet46()` `sonnet45()` `sonnet4()` `haiku45()` `haiku35()`
125
+ `fable5()` `opus5()` `opus48()` `opus47()` `opus46()` `sonnet5()` `sonnet46()` `sonnet45()` `haiku45()`
126
126
 
127
- Thinking variants: append `think` — e.g. `fable5think()` `opus5think()` `opus48think()` `opus47think()` `opus46think()` `sonnet5think()` `sonnet46think()` `sonnet45think()` `sonnet4think()` `opus41think()` `haiku45think()`
127
+ Use `.effort(n)` (or `config.effort`) to enable Anthropic thinking e.g. `.effort(100).opus5()`.
128
128
 
129
129
  ### Google
130
130
  `gemini3pro()` `gemini3flash()` `gemini36flash()` `gemini35flash()` `gemini25pro()` `gemini25flash()`
131
131
 
132
132
  ### Grok
133
- `grok43()` `grok420multiAgent()` `grok420()` `grok420think()` `grok41()` `grok41think()`
133
+ `grok45()` `grok43()` `grok420multiAgent()` `grok420()` `grok420think()`
134
134
 
135
135
  ### Perplexity
136
136
  `sonar()` `sonarPro()`
@@ -145,13 +145,13 @@ Thinking variants: append `think` — e.g. `fable5think()` `opus5think()` `opus4
145
145
  `minimaxM25()` `minimaxM27()` `minimaxM3()`
146
146
 
147
147
  ### Fireworks
148
- `deepseekV4Flash()` `deepseekV4Pro()` `GLM5()`
148
+ `qwen36plus()` `qwen37plus()` `deepseekV4Flash()` `deepseekV4Pro()`
149
149
 
150
150
  ### Cerebras
151
151
  `GLM46()`
152
152
 
153
153
  ### OpenRouter
154
- `GLM45()`
154
+ `qwen38max()` `GLM45()`
155
155
 
156
156
  ### Multi-provider (auto-fallback across free/paid tiers)
157
157
  `hermes3()` `kimiK25think()`
@@ -286,7 +286,8 @@ const code = await ModelMix.new()
286
286
 
287
287
  ```javascript
288
288
  const raw = await ModelMix.new()
289
- .sonnet45think()
289
+ .effort(100)
290
+ .sonnet45()
290
291
  .addText("Solve this step by step: 2+2*3")
291
292
  .raw();
292
293
  // raw.message, raw.think, raw.tokens, raw.toolCalls, raw.response
@@ -463,8 +464,7 @@ const model = ModelMix.new({
463
464
  - When using MCP tools or `addTool()`, set `max_history` to at least 3 — tool call/response pairs consume history slots.
464
465
  - Use `.json()` for structured output instead of parsing text manually. Use descriptor objects `{ description, required, enum, default, nullable }` for richer schema control.
465
466
  - Use `.message()` for simple text, `.raw()` when you need tokens/thinking/toolCalls.
466
- - For thinking models, append `think` to the method name (e.g. `sonnet45think()`).
467
- - For cross-provider reasoning intensity, use unified `effort` (`-1` or `0`–`100`) via `config.effort` or `.effort(n)` — never put it in `options`. Native fields win if already set.
467
+ - For Anthropic thinking, use unified `effort` (`-1` or `0`–`100`) via `config.effort` or `.effort(n)` — e.g. `.effort(100).opus5()`. Never put `effort` in `options`. Native fields win if already set.
468
468
  - Template placeholders use `{key}` syntax in both system prompts and user messages.
469
469
  - The library uses CommonJS internally but supports ESM import via `{ ModelMix }`.
470
470
  - GPT-5+ models automatically use `max_completion_tokens` instead of `max_tokens`.
@@ -12,15 +12,15 @@ describe('Anthropic Model Registration Tests', () => {
12
12
  expect(model.models[0].provider).to.be.instanceOf(MixAnthropic);
13
13
  });
14
14
 
15
- it('should register Claude Fable 5 with max effort thinking', () => {
16
- const model = ModelMix.new();
17
- model.fable5think();
18
-
19
- expect(model.models).to.have.length(1);
20
- expect(model.models[0].key).to.equal('claude-fable-5');
21
- expect(model.models[0].provider.options.output_config).to.deep.equal({ effort: 'max' });
22
- expect(model.models[0].provider.options.thinking).to.deep.equal({ display: 'summarized' });
23
- expect(model.models[0].provider.options).to.not.have.property('temperature');
15
+ it('should apply max effort thinking via .effort(100).fable5()', () => {
16
+ const model = ModelMix.new().effort(100).fable5();
17
+ const { applyUnifiedEffort } = require('../effort.js');
18
+
19
+ expect(model.config.effort).to.equal(100);
20
+ const options = { model: 'claude-fable-5' };
21
+ applyUnifiedEffort(options, model.config, 'anthropic', 'claude-fable-5');
22
+ expect(options.output_config).to.deep.equal({ effort: 'max' });
23
+ expect(options.thinking).to.deep.equal({ type: 'adaptive', display: 'summarized' });
24
24
  });
25
25
 
26
26
  it('should register Claude Opus 5', () => {
@@ -32,15 +32,15 @@ describe('Anthropic Model Registration Tests', () => {
32
32
  expect(model.models[0].provider).to.be.instanceOf(MixAnthropic);
33
33
  });
34
34
 
35
- it('should register Claude Opus 5 with max effort thinking', () => {
36
- const model = ModelMix.new();
37
- model.opus5think();
35
+ it('should apply max effort thinking via .effort(100).opus5()', () => {
36
+ const model = ModelMix.new().effort(100).opus5();
37
+ const { applyUnifiedEffort } = require('../effort.js');
38
38
 
39
- expect(model.models).to.have.length(1);
40
- expect(model.models[0].key).to.equal('claude-opus-5');
41
- expect(model.models[0].provider.options.output_config).to.deep.equal({ effort: 'max' });
42
- expect(model.models[0].provider.options.thinking).to.deep.equal({ display: 'summarized' });
43
- expect(model.models[0].provider.options).to.not.have.property('temperature');
39
+ expect(model.config.effort).to.equal(100);
40
+ const options = { model: 'claude-opus-5' };
41
+ applyUnifiedEffort(options, model.config, 'anthropic', 'claude-opus-5');
42
+ expect(options.output_config).to.deep.equal({ effort: 'max' });
43
+ expect(options.thinking).to.deep.equal({ type: 'adaptive', display: 'summarized' });
44
44
  });
45
45
 
46
46
  describe('Sampling params (temperature/top_p/top_k)', () => {
@@ -53,7 +53,6 @@ describe('Anthropic Model Registration Tests', () => {
53
53
  expect(MixAnthropic.rejectsSamplingParams('anthropic/claude-opus-5')).to.equal(true);
54
54
 
55
55
  expect(MixAnthropic.rejectsSamplingParams('claude-opus-4-6')).to.equal(false);
56
- expect(MixAnthropic.rejectsSamplingParams('claude-opus-4-1-20250805')).to.equal(false);
57
56
  expect(MixAnthropic.rejectsSamplingParams('claude-sonnet-4-6')).to.equal(false);
58
57
  expect(MixAnthropic.rejectsSamplingParams('claude-haiku-4-5-20251001')).to.equal(false);
59
58
  });
@@ -143,16 +142,14 @@ describe('Anthropic Model Registration Tests', () => {
143
142
  expect(model.models[0].provider).to.be.instanceOf(MixAnthropic);
144
143
  });
145
144
 
146
- it('should register Claude Opus 4.8 with thinking enabled', () => {
147
- const model = ModelMix.new();
148
- model.opus48think();
145
+ it('should apply adaptive thinking via .effort(100).opus48()', () => {
146
+ const model = ModelMix.new().effort(100).opus48();
147
+ const { applyUnifiedEffort } = require('../effort.js');
149
148
 
150
- expect(model.models).to.have.length(1);
151
- expect(model.models[0].key).to.equal('claude-opus-4-8');
152
- expect(model.models[0].provider.options.thinking).to.deep.equal({
153
- type: 'enabled',
154
- budget_tokens: 1638
155
- });
149
+ const options = { model: 'claude-opus-4-8' };
150
+ applyUnifiedEffort(options, model.config, 'anthropic', 'claude-opus-4-8');
151
+ expect(options.thinking).to.deep.equal({ type: 'adaptive', display: 'summarized' });
152
+ expect(options.output_config).to.deep.equal({ effort: 'max' });
156
153
  });
157
154
 
158
155
  it('should register Claude Sonnet 5', () => {
@@ -164,16 +161,14 @@ describe('Anthropic Model Registration Tests', () => {
164
161
  expect(model.models[0].provider).to.be.instanceOf(MixAnthropic);
165
162
  });
166
163
 
167
- it('should register Claude Sonnet 5 with thinking enabled', () => {
168
- const model = ModelMix.new();
169
- model.sonnet5think();
164
+ it('should apply adaptive thinking via .effort(100).sonnet5()', () => {
165
+ const model = ModelMix.new().effort(100).sonnet5();
166
+ const { applyUnifiedEffort } = require('../effort.js');
170
167
 
171
- expect(model.models).to.have.length(1);
172
- expect(model.models[0].key).to.equal('claude-sonnet-5');
173
- expect(model.models[0].provider.options.thinking).to.deep.equal({
174
- type: 'enabled',
175
- budget_tokens: 1638
176
- });
168
+ const options = { model: 'claude-sonnet-5' };
169
+ applyUnifiedEffort(options, model.config, 'anthropic', 'claude-sonnet-5');
170
+ expect(options.thinking).to.deep.equal({ type: 'adaptive', display: 'summarized' });
171
+ expect(options.output_config).to.deep.equal({ effort: 'max' });
177
172
  });
178
173
 
179
174
  describe('Thinking block extraction', () => {
@@ -81,9 +81,31 @@ describe('Unified effort scale', () => {
81
81
  expect(mapEffort('openai', 10, 'gpt-oss-120b')).to.deep.equal({ reasoning_effort: 'low' });
82
82
  });
83
83
 
84
- it('maps Anthropic effort to output_config.effort', () => {
85
- expect(mapEffort('anthropic', 10)).to.deep.equal({ output_config: { effort: 'low' } });
86
- expect(mapEffort('anthropic', 90)).to.deep.equal({ output_config: { effort: 'max' } });
84
+ it('maps Anthropic adaptive models to thinking + output_config.effort', () => {
85
+ expect(mapEffort('anthropic', 10, 'claude-opus-5')).to.deep.equal({
86
+ thinking: { type: 'adaptive', display: 'summarized' },
87
+ output_config: { effort: 'low' }
88
+ });
89
+ expect(mapEffort('anthropic', 90, 'claude-fable-5')).to.deep.equal({
90
+ thinking: { type: 'adaptive', display: 'summarized' },
91
+ output_config: { effort: 'max' }
92
+ });
93
+ expect(mapEffort('anthropic', 50, 'claude-sonnet-4-6')).to.deep.equal({
94
+ thinking: { type: 'adaptive', display: 'summarized' },
95
+ output_config: { effort: 'high' }
96
+ });
97
+ });
98
+
99
+ it('maps Anthropic manual models to thinking.type=enabled + budget_tokens', () => {
100
+ expect(mapEffort('anthropic', 50, 'claude-sonnet-4-5-20250929')).to.deep.equal({
101
+ thinking: { type: 'enabled', budget_tokens: 8192 }
102
+ });
103
+ expect(mapEffort('anthropic', 100, 'claude-haiku-4-5-20251001')).to.deep.equal({
104
+ thinking: { type: 'enabled', budget_tokens: 16384 }
105
+ });
106
+ expect(mapEffort('anthropic', 0, 'claude-sonnet-4-5-20250929')).to.deep.equal({
107
+ thinking: { type: 'enabled', budget_tokens: 1024 }
108
+ });
87
109
  });
88
110
 
89
111
  it('maps Anthropic adaptive to thinking.type=adaptive', () => {
@@ -223,6 +245,14 @@ describe('Unified effort scale', () => {
223
245
  const options = {};
224
246
  applyUnifiedEffort(options, { effort: 90 }, 'anthropic', 'claude-opus-5');
225
247
  expect(options.output_config).to.deep.equal({ effort: 'max' });
248
+ expect(options.thinking).to.deep.equal({ type: 'adaptive', display: 'summarized' });
249
+ });
250
+
251
+ it('applies Anthropic manual thinking for Sonnet 4.5', () => {
252
+ const options = {};
253
+ applyUnifiedEffort(options, { effort: 50 }, 'anthropic', 'claude-sonnet-4-5-20250929');
254
+ expect(options.thinking).to.deep.equal({ type: 'enabled', budget_tokens: 8192 });
255
+ expect(options.output_config).to.equal(undefined);
226
256
  });
227
257
 
228
258
  it('skips Anthropic mapping when output_config.effort is set', () => {
@@ -306,16 +336,29 @@ describe('Unified effort scale', () => {
306
336
  expect(request.reasoning).to.deep.equal({ effort: 'none' });
307
337
  });
308
338
 
309
- it('Anthropic *think() native effort wins over config.effort', () => {
310
- const model = ModelMix.new({ config: { effort: 20 } }).opus5think();
311
- expect(model.models[0].provider.options.output_config.effort).to.equal('max');
339
+ it('Anthropic config.effort maps through .effort().opus5()', () => {
340
+ const model = ModelMix.new().effort(100).opus5();
341
+ expect(model.config.effort).to.equal(100);
312
342
 
313
343
  const options = {
314
344
  ...model.models[0].provider.options,
315
345
  model: 'claude-opus-5'
316
346
  };
317
- applyUnifiedEffort(options, { effort: 20 }, 'anthropic', 'claude-opus-5');
347
+ applyUnifiedEffort(options, model.config, 'anthropic', 'claude-opus-5');
318
348
  expect(options.output_config.effort).to.equal('max');
349
+ expect(options.thinking).to.deep.equal({ type: 'adaptive', display: 'summarized' });
350
+ });
351
+
352
+ it('Anthropic .effort(50).sonnet45() maps to manual budget_tokens', () => {
353
+ const model = ModelMix.new().effort(50).sonnet45();
354
+ expect(model.config.effort).to.equal(50);
355
+
356
+ const options = {
357
+ ...model.models[0].provider.options,
358
+ model: 'claude-sonnet-4-5-20250929'
359
+ };
360
+ applyUnifiedEffort(options, model.config, 'anthropic', 'claude-sonnet-4-5-20250929');
361
+ expect(options.thinking).to.deep.equal({ type: 'enabled', budget_tokens: 8192 });
319
362
  });
320
363
 
321
364
  it('MixGoogle generationConfig includes thinkingConfig from options', async () => {
package/test/grok.test.js CHANGED
@@ -3,6 +3,7 @@ const { ModelMix } = require('../index.js');
3
3
 
4
4
  describe('Grok Model Registration Tests', () => {
5
5
  const grokModels = [
6
+ { method: 'grok45', key: 'grok-4.5' },
6
7
  { method: 'grok43', key: 'grok-4.3' },
7
8
  { method: 'grok420multiAgent', key: 'grok-4.20-multi-agent-0309' },
8
9
  { method: 'grok420think', key: 'grok-4.20-0309-reasoning' },
@@ -522,7 +522,7 @@ describe('Conversation History Tests', () => {
522
522
  const model = ModelMix.new({
523
523
  config: { debug: false, max_history: 10 }
524
524
  });
525
- model.opus5think();
525
+ model.effort(100).opus5();
526
526
 
527
527
  model.addText('2+2?');
528
528
  nock('https://api.anthropic.com')
package/test/live.test.js CHANGED
@@ -98,7 +98,7 @@ describe('Live Integration Tests', function () {
98
98
  });
99
99
 
100
100
  it('should return structured JSON with Sonnet 4.6 thinking', async function () {
101
- const model = ModelMix.new(setup).sonnet46think();
101
+ const model = ModelMix.new(setup).effort(100).sonnet46();
102
102
 
103
103
  model.addText('Generate information about a fictional city.');
104
104
 
@@ -192,18 +192,6 @@ describe('Live Integration Tests', function () {
192
192
  expect(response.toLowerCase()).to.include('gptoss test successful');
193
193
  });
194
194
 
195
- it('should work with Grok 4.1 model', async function () {
196
- const model = ModelMix.new(setup).grok41();
197
-
198
- model.addText('Say "grok41 test successful" and nothing else.');
199
-
200
- const response = await model.message();
201
- console.log(`Grok 4.1 response: ${response}`);
202
-
203
- expect(response).to.be.a('string');
204
- expect(response.toLowerCase()).to.include('grok41 test successful');
205
- });
206
-
207
195
  const grokSeriesTests = [
208
196
  { name: 'Grok 4.3', factory: (m) => m.grok43(), token: 'grok43' },
209
197
  { name: 'Grok 4.20 reasoning', factory: (m) => m.grok420think(), token: 'grok420think' },
@@ -275,8 +263,8 @@ describe('Live Integration Tests', function () {
275
263
  expect(result.features).to.be.an('array');
276
264
  });
277
265
 
278
- it('should return structured JSON with Grok3Mini', async function () {
279
- const model = ModelMix.new(setup).grok41();
266
+ it('should return structured JSON with Grok 4.3', async function () {
267
+ const model = ModelMix.new(setup).grok43();
280
268
 
281
269
  model.addText('Generate information about a fictional technology.');
282
270
 
@@ -287,7 +275,7 @@ describe('Live Integration Tests', function () {
287
275
  power: "1000 qubits"
288
276
  });
289
277
 
290
- console.log(`Grok3Mini JSON result:`, result);
278
+ console.log(`Grok 4.3 JSON result:`, result);
291
279
 
292
280
  expect(result).to.be.an('object');
293
281
  expect(result).to.have.property('name');
package/test/qwen.test.js CHANGED
@@ -17,4 +17,36 @@ describe('Qwen Model Registration Tests', () => {
17
17
  expect(model.models).to.have.length(1);
18
18
  expect(model.models[0].key).to.equal('Qwen/Qwen3.6-Plus');
19
19
  });
20
+
21
+ it('should register Fireworks Qwen 3.7 Plus by default', () => {
22
+ const model = ModelMix.new();
23
+ model.qwen37plus({ mix: { fireworks: true, openrouter: false } });
24
+
25
+ expect(model.models).to.have.length(1);
26
+ expect(model.models[0].key).to.equal('accounts/fireworks/models/qwen3p7-plus');
27
+ expect(ModelMix.calculateCost('accounts/fireworks/models/qwen3p7-plus', {
28
+ input: 1_000_000,
29
+ output: 1_000_000
30
+ })).to.equal(2.00);
31
+ });
32
+
33
+ it('should register OpenRouter Qwen 3.7 Plus when openrouter mix is enabled', () => {
34
+ const model = ModelMix.new();
35
+ model.qwen37plus({ mix: { fireworks: false, openrouter: true } });
36
+
37
+ expect(model.models).to.have.length(1);
38
+ expect(model.models[0].key).to.equal('qwen/qwen3.7-plus');
39
+ });
40
+
41
+ it('should register OpenRouter Qwen 3.8 Max by default', () => {
42
+ const model = ModelMix.new();
43
+ model.qwen38max();
44
+
45
+ expect(model.models).to.have.length(1);
46
+ expect(model.models[0].key).to.equal('qwen/qwen3.8-max');
47
+ expect(ModelMix.calculateCost('qwen/qwen3.8-max', {
48
+ input: 1_000_000,
49
+ output: 1_000_000
50
+ })).to.equal(8.00);
51
+ });
20
52
  });