modelmix 5.0.5 → 5.1.1

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
@@ -15,9 +15,9 @@ Ever found yourself wanting to integrate AI models into your projects but worrie
15
15
  - [Token Usage Tracking](#-token-usage-tracking)
16
16
  - [Prompt Caching](#-prompt-caching)
17
17
  - [Model Context Protocol (MCP) Integration](#-model-context-protocol-mcp-integration)
18
- - [Enabling Debug Mode](#-enabling-debug-mode)
19
- - [Bottleneck Integration](#-bottleneck-integration)
20
18
  - [Retry (Opt-In)](#-retry-optin)
19
+ - [Bottleneck Integration](#-bottleneck-integration)
20
+ - [Enabling Debug Mode](#-enabling-debug-mode)
21
21
  - [Instance Plugins](#-instance-plugins)
22
22
  - [ModelMix Class Overview](#-modelmix-class-overview)
23
23
  - [Contributing](#-contributing)
@@ -136,60 +136,59 @@ This pattern allows you to:
136
136
  ## ⚡️ Shorthand Methods
137
137
 
138
138
  ModelMix provides convenient shorthand methods for quickly accessing different AI models.
139
- Here's a comprehensive list of available methods:
140
-
141
- | Method | Provider | Model | Price (I/O) per 1 M tokens |
142
- | ------------------- | ---------- | ---------------------------- | -------------------------- |
143
- | `gpt56sol()` | OpenAI | gpt-5.6-sol | [\$5.00/\$30.00][1] |
144
- | `gpt56terra()` | OpenAI | gpt-5.6-terra | [\$2.00/\$12.00][1] |
145
- | `gpt56luna()` | OpenAI | gpt-5.6-luna | [\$0.20/\$1.20][1] |
146
- | `gpt55()` | OpenAI | gpt-5.5 | [\$5.00/\$30.00][1] |
147
- | `gpt54()` | OpenAI | gpt-5.4 | [\$2.50/\$15.00][1] |
148
- | `gpt54mini()` | OpenAI | gpt-5.4-mini | [\$0.75/\$4.50][1] |
149
- | `gpt54nano()` | OpenAI | gpt-5.4-nano | [\$0.20/\$1.25][1] |
150
- | `gpt53codex()` | OpenAI | gpt-5.3-codex | [\$1.25/\$14.00][1] |
151
- | `gpt52()` | OpenAI | gpt-5.2 | [\$1.75/\$14.00][1] |
152
- | `gpt51()` | OpenAI | gpt-5.1 | [\$1.25/\$10.00][1] |
153
- | `gpt5mini()` | OpenAI | gpt-5-mini | [\$0.25/\$2.00][1] |
154
- | `gpt5nano()` | OpenAI | gpt-5-nano | [\$0.05/\$0.40][1] |
155
- | `gpt41()` | OpenAI | gpt-4.1 | [\$2.00/\$8.00][1] |
156
- | `gpt41mini()` | OpenAI | gpt-4.1-mini | [\$0.40/\$1.60][1] |
157
- | `gpt41nano()` | OpenAI | gpt-4.1-nano | [\$0.10/\$0.40][1] |
158
- | `gptOss()` | Together | gpt-oss-120B | [\$0.15/\$0.60][7] |
159
- | `fable5()` | Anthropic | claude-fable-5 | [\$10.00/\$50.00][2] |
160
- | `opus5()` | Anthropic | claude-opus-5 | [\$5.00/\$25.00][2] |
161
- | `opus48()` | Anthropic | claude-opus-4-8 | [\$5.00/\$25.00][2] |
162
- | `opus47()` | Anthropic | claude-opus-4-7 | [\$5.00/\$25.00][2] |
163
- | `opus46()` | Anthropic | claude-opus-4-6 | [\$5.00/\$25.00][2] |
164
- | `sonnet5()` | Anthropic | claude-sonnet-5 | [\$3.00/\$15.00][2] |
165
- | `sonnet46()` | Anthropic | claude-sonnet-4-6 | [\$3.00/\$15.00][2] |
166
- | `haiku45()` | Anthropic | claude-haiku-4-5-20251001 | [\$1.00/\$5.00][2] |
167
- | `gemini31pro()` | Google | gemini-3.1-pro-preview | [\$2.00/\$12.00][3] |
168
- | `gemini37flash()` | Google | gemini-3.7-flash | [\$0.75/\$3.75][3] |
169
- | `gemini36flash()` | Google | gemini-3.6-flash | [\$0.75/\$3.75][3] |
170
- | `gemini35flash()` | Google | gemini-3.5-flash | [\$0.75/\$4.50][3] |
171
- | `gemini35flashLite()`| Google | gemini-3.5-flash-lite | [\$0.30/\$2.50][3] |
172
- | `gemini31flashLite()`| Google | gemini-3.1-flash-lite-preview | [\$0.25/\$1.50][3] |
173
- | `grok46()` | Grok | grok-4.6 | [\$2.00/\$6.00][6] |
174
- | `grok45()` | Grok | grok-4.5 | [\$2.00/\$6.00][6] |
175
- | `grok43()` | Grok | grok-4.3 | [\$1.25/\$2.50][6] |
176
- | `grok420multiAgent()`| Grok | grok-4.20-multi-agent-0309 | [\$1.25/\$2.50][6] |
177
- | `grok420()` | Grok | grok-4.20-0309 (†) | [\$1.25/\$2.50][6] |
178
- | `qwen36plus()` | Fireworks | qwen3p6-plus | [\$0.50/\$3.00][10] |
179
- | `qwen37plus()` | Fireworks | models/qwen3p7-plus | [\$0.40/\$1.60][10] |
180
- | `qwen38max()` | OpenRouter | qwen/qwen3.8-max | [\$2.00/\$6.00][12] |
181
- | `deepseekV4Flash()` | Fireworks | models/deepseek-v4-flash | [\$0.14/\$0.28][10] |
182
- | `deepseekV4Pro()` | Fireworks | models/deepseek-v4-pro | [\$1.74/\$3.48][10] |
183
- | `GLM52()` | Together | zai-org/GLM-5.2 | [\$1.40/\$4.40][7] |
184
- | `GLM51()` | Fireworks | models/glm-5p1 | [\$1.05/\$3.50][10] |
185
- | `minimaxM3()` | MiniMax | MiniMax-M3 | [\$0.30/\$1.20][9] |
186
- | `minimaxM27()` | MiniMax | MiniMax-M2.7 | [\$0.30/\$1.20][9] |
187
- | `sonar()` | Perplexity | sonar | [\$1.00/\$1.00][4] |
188
- | `sonarPro()` | Perplexity | sonar-pro | [\$3.00/\$15.00][4] |
189
- | `hermes3()` | Lambda | Hermes-3-Llama-3.1-405B-FP8 | [\$0.80/\$0.80][8] |
190
- | `kimiK3()` | Moonshot | kimi-k3 | [\$3.00/\$15.00][11] |
191
- | `kimiK25()` | Together | Kimi-K2.5 | [\$0.50/\$2.80][7] |
192
- | `kimiK26()` | Fireworks | models/kimi-k2p6 | [\$0.95/\$4.00][10] |
139
+
140
+ | Method | Provider | Model | Input / 1M | Output / 1M |
141
+ | --- | --- | --- | ---: | ---: |
142
+ | `gpt56sol()` | OpenAI | gpt-5.6-sol | [\$5.00][1] | [\$30.00][1] |
143
+ | `gpt56terra()` | OpenAI | gpt-5.6-terra | [\$2.00][1] | [\$12.00][1] |
144
+ | `gpt56luna()` | OpenAI | gpt-5.6-luna | [\$0.20][1] | [\$1.20][1] |
145
+ | `gpt55()` | OpenAI | gpt-5.5 | [\$5.00][1] | [\$30.00][1] |
146
+ | `gpt54()` | OpenAI | gpt-5.4 | [\$2.50][1] | [\$15.00][1] |
147
+ | `gpt54mini()` | OpenAI | gpt-5.4-mini | [\$0.75][1] | [\$4.50][1] |
148
+ | `gpt54nano()` | OpenAI | gpt-5.4-nano | [\$0.20][1] | [\$1.25][1] |
149
+ | `gpt53codex()` | OpenAI | gpt-5.3-codex | [\$1.25][1] | [\$14.00][1] |
150
+ | `gpt52()` | OpenAI | gpt-5.2 | [\$1.75][1] | [\$14.00][1] |
151
+ | `gpt51()` | OpenAI | gpt-5.1 | [\$1.25][1] | [\$10.00][1] |
152
+ | `gpt5mini()` | OpenAI | gpt-5-mini | [\$0.25][1] | [\$2.00][1] |
153
+ | `gpt5nano()` | OpenAI | gpt-5-nano | [\$0.05][1] | [\$0.40][1] |
154
+ | `gptOss()` | Together | gpt-oss-120B | [\$0.15][7] | [\$0.60][7] |
155
+ | `fable5()` | Anthropic | claude-fable-5 | [\$10.00][2] | [\$50.00][2] |
156
+ | `opus5()` | Anthropic | claude-opus-5 | [\$5.00][2] | [\$25.00][2] |
157
+ | `opus48()` | Anthropic | claude-opus-4-8 | [\$5.00][2] | [\$25.00][2] |
158
+ | `opus47()` | Anthropic | claude-opus-4-7 | [\$5.00][2] | [\$25.00][2] |
159
+ | `opus46()` | Anthropic | claude-opus-4-6 | [\$5.00][2] | [\$25.00][2] |
160
+ | `sonnet5()` | Anthropic | claude-sonnet-5 | [\$3.00][2] | [\$15.00][2] |
161
+ | `sonnet46()` | Anthropic | claude-sonnet-4-6 | [\$3.00][2] | [\$15.00][2] |
162
+ | `haiku45()` | Anthropic | claude-haiku-4-5-20251001 | [\$1.00][2] | [\$5.00][2] |
163
+ | `gemini31pro()` | Google | gemini-3.1-pro-preview | [\$2.00][3] | [\$12.00][3] |
164
+ | `gemini37flash()` | Google | gemini-3.7-flash | [\$0.75][3] | [\$3.75][3] |
165
+ | `gemini36flash()` | Google | gemini-3.6-flash | [\$0.75][3] | [\$3.75][3] |
166
+ | `gemini35flash()` | Google | gemini-3.5-flash | [\$0.75][3] | [\$4.50][3] |
167
+ | `gemini35flashLite()` | Google | gemini-3.5-flash-lite | [\$0.30][3] | [\$2.50][3] |
168
+ | `gemini31flashLite()` | Google | gemini-3.1-flash-lite-preview | [\$0.25][3] | [\$1.50][3] |
169
+ | `grok46()` | Grok | grok-4.6 | [\$2.00][6] | [\$6.00][6] |
170
+ | `grok45()` | Grok | grok-4.5 | [\$2.00][6] | [\$6.00][6] |
171
+ | `grok43()` | Grok | grok-4.3 | [\$1.25][6] | [\$2.50][6] |
172
+ | `grok420multiAgent()` | Grok | grok-4.20-multi-agent-0309 | [\$1.25][6] | [\$2.50][6] |
173
+ | `grok420()` | Grok | grok-4.20-0309 (†) | [\$1.25][6] | [\$2.50][6] |
174
+ | `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
+ | `qwen37plus()` | Fireworks | models/qwen3p7-plus | [\$0.40][10] | [\$1.60][10] |
177
+ | `qwen38max()` | Fireworks | qwen3p8-2p4t-a95b | [\$2.00][10] | [\$6.00][10] |
178
+ | `deepseekV4Flash()` | Fireworks | models/deepseek-v4-flash | [\$0.14][10] | [\$0.28][10] |
179
+ | `deepseekV4Pro()` | Fireworks | models/deepseek-v4-pro-0813 | [\$1.32][12] | [\$3.96][12] |
180
+ | `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
+ | `minimaxM3()` | MiniMax | MiniMax-M3 | [\$0.30][9] | [\$1.20][9] |
183
+ | `minimaxM27()` | MiniMax | MiniMax-M2.7 | [\$0.30][9] | [\$1.20][9] |
184
+ | `sonar()` | Perplexity | sonar | [\$1.00][4] | [\$1.00][4] |
185
+ | `sonarPro()` | Perplexity | sonar-pro | [\$3.00][4] | [\$15.00][4] |
186
+ | `hermes470b()` | OpenRouter | nousresearch/hermes-4-70b | [\$0.13][13] | [\$0.40][13] |
187
+ | `hermes4405b()` | OpenRouter | nousresearch/hermes-4-405b | [\$1.00][13] | [\$3.00][13] |
188
+ | `hermes3()` | Lambda | Hermes-3-Llama-3.1-405B-FP8 | [\$0.80][8] | [\$0.80][8] |
189
+ | `kimiK3()` | Moonshot | kimi-k3 | [\$3.00][11] | [\$15.00][11] |
190
+ | `kimiK25()` | Together | Kimi-K2.5 | [\$0.50][7] | [\$2.80][7] |
191
+ | `kimiK26()` | Fireworks | models/kimi-k2p6 | [\$0.95][10] | [\$4.00][10] |
193
192
 
194
193
  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.
195
194
 
@@ -204,7 +203,9 @@ Gemini 3.7 Flash and 3.6 Flash use Google's introductory standard pricing throug
204
203
  [9]: https://platform.minimax.io/docs/api-reference/anthropic-api-compatible-cache#supported-models-and-pricing "MiniMax Pricing"
205
204
  [10]: https://fireworks.ai/pricing#serverless-pricing "Fireworks Pricing"
206
205
  [11]: https://platform.kimi.ai/docs/guide/kimi-k3-pricing "Kimi K3 Pricing"
207
- [12]: https://openrouter.ai/qwen/qwen3.8-max "OpenRouter Pricing"
206
+ [12]: https://fireworks.ai/models/deepseek-ai/deepseek-v4-pro-0813 "DeepSeek V4 Pro 0813 Pricing"
207
+ [13]: https://openrouter.ai/nousresearch "Nous Research Models on OpenRouter"
208
+ [14]: https://openrouter.ai/qwen/qwen3.5-397b-a17b "Qwen3.5 397B A17B on OpenRouter"
208
209
 
209
210
  Each method accepts optional `options`, `config`, and (for multi-provider methods) `mix` parameters to customize behavior.
210
211
 
@@ -244,6 +245,7 @@ ModelMix.new().effort(-1).minimaxM3().addText('...').message();
244
245
  ### Provider-specific behavior
245
246
 
246
247
  - **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
+ - **GPT-5.6:** `100` maps to `max`; 80–99 remains `xhigh`.
247
249
  - **DeepSeek:** `↑` means thinking is enabled; `off` means it is disabled.
248
250
  - **MiniMax:** `off` maps to `thinking.disabled`; `adaptive` maps to `thinking.type=adaptive`.
249
251
  - **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`.
@@ -836,56 +838,6 @@ This simple integration allows your model to:
836
838
 
837
839
  The Model Context Protocol makes it easy to add any capability to your models, from web search to code execution, database queries, or custom functions. All with just a few lines of code!
838
840
 
839
- ## 🐛 Enabling Debug Mode
840
-
841
- To activate debug mode in ModelMix and view detailed request information, follow these two steps:
842
-
843
- 1. In the ModelMix constructor, include a `debug` level in the configuration:
844
-
845
- ```javascript
846
- const mix = ModelMix.new({
847
- config: {
848
- debug: 4 // 0=silent, 1=minimal, 2=summary, 3=full (no truncate), 4=verbose (raw details)
849
- // ... other configuration options ...
850
- }
851
- });
852
- ```
853
-
854
- 2. When running your script from the command line, use the `DEBUG=ModelMix*` prefix:
855
-
856
- ```
857
- DEBUG=ModelMix* node your_script.js
858
- ```
859
-
860
- When you run your script this way, you'll see detailed information about the requests in the console, including the configuration and options used for each AI model request.
861
-
862
- This information is valuable for debugging and understanding how ModelMix is processing your requests.
863
-
864
- ## 🚦 Bottleneck Integration
865
-
866
- ModelMix now uses Bottleneck for efficient rate limiting of API requests. This integration helps prevent exceeding API rate limits and ensures smooth operation when working with multiple models or high request volumes.
867
-
868
- ### How it works:
869
-
870
- 1. **Configuration**: Bottleneck is configured in the ModelMix constructor. You can customize the settings or use the default configuration:
871
-
872
- ```javascript
873
- const setup = {
874
- config: {
875
- bottleneck: {
876
- maxConcurrent: 8, // Maximum number of concurrent requests
877
- minTime: 500 // Minimum time between requests (in ms)
878
- }
879
- }
880
- };
881
- ```
882
-
883
- 2. **Rate Limiting**: When you make a request using any of the attached models, Bottleneck automatically manages the request flow based on the configured settings.
884
-
885
- 3. **Automatic Queueing**: If the rate limit is reached, Bottleneck will automatically queue subsequent requests and process them as capacity becomes available.
886
-
887
- This integration ensures that your application respects API rate limits while maximizing throughput, providing a robust solution for managing multiple AI model interactions.
888
-
889
841
  ## 🔁 Retry (Opt-In)
890
842
 
891
843
  ModelMix supports optional intra-model retries for transient HTTP failures. When enabled, it retries the same provider before moving to fallback models.
@@ -909,6 +861,35 @@ Behavior summary:
909
861
  - If retry is enabled, ModelMix retries the same model only for configured transient status codes.
910
862
  - After retries are exhausted (or for non-retryable errors), ModelMix continues with normal fallback chain.
911
863
 
864
+ ## 🚦 Bottleneck Integration
865
+
866
+ ModelMix uses Bottleneck for efficient rate limiting of API requests.
867
+
868
+ ```javascript
869
+ const setup = {
870
+ config: {
871
+ bottleneck: {
872
+ maxConcurrent: 8,
873
+ minTime: 500
874
+ }
875
+ }
876
+ };
877
+ ```
878
+
879
+ Attached models share this limiter, which queues requests when capacity is exhausted.
880
+
881
+ ## 🐛 Enabling Debug Mode
882
+
883
+ Set `config.debug` to `0` (silent), `1` (minimal), `2` (summary), `3` (full), or `4` (verbose raw details), then run with `DEBUG=ModelMix*`:
884
+
885
+ ```javascript
886
+ const mix = ModelMix.new({ config: { debug: 4 } });
887
+ ```
888
+
889
+ ```bash
890
+ DEBUG=ModelMix* node your-script.js
891
+ ```
892
+
912
893
  ## 🔌 Instance Plugins
913
894
 
914
895
  Plugins wrap one ModelMix instance without changing global behavior. They run in registration order after templates are rendered and before provider-specific request conversion:
@@ -953,7 +934,7 @@ The separately publishable `@modelmix/rlm` workspace package keeps document pars
953
934
  const { ModelMix } = require('modelmix');
954
935
  const { rlm } = require('@modelmix/rlm');
955
936
 
956
- const fast = ModelMix.new().gpt41mini();
937
+ const fast = ModelMix.new().gpt5nano();
957
938
 
958
939
  const result = await ModelMix.new()
959
940
  .gpt56luna()
@@ -204,14 +204,14 @@ Proposed configuration:
204
204
  ```js
205
205
  const workers = {
206
206
  fast: {
207
- model: ModelMix.new().gpt41nano(),
207
+ model: ModelMix.new().gpt5nano(),
208
208
  intelligence: 1,
209
209
  cost: 1,
210
210
  speed: 5,
211
211
  description: 'Extraction, classification, and simple transformations'
212
212
  },
213
213
  balanced: {
214
- model: ModelMix.new().gpt41mini(),
214
+ model: ModelMix.new().gpt5nano(),
215
215
  intelligence: 3,
216
216
  cost: 2,
217
217
  speed: 4,
package/demo/fallback.js CHANGED
@@ -15,7 +15,7 @@ const mmix = new ModelMix({
15
15
  }
16
16
  });
17
17
 
18
- mmix.sonnet46({ config: { url: 'fail' } }).gpt41nano();
18
+ mmix.sonnet46({ config: { url: 'fail' } }).gpt5nano();
19
19
 
20
20
  async function main() {
21
21
  mmix.addText('hola, como estas?');
@@ -23,4 +23,4 @@ async function main() {
23
23
  console.log(response);
24
24
  }
25
25
 
26
- main();
26
+ main();
package/demo/gemini.js CHANGED
@@ -20,8 +20,8 @@ const flash = await mmix.gemini37flash()
20
20
 
21
21
  console.log(flash);
22
22
 
23
- // Using gemini3pro (Gemini 3 Pro) with custom config
24
- console.log("\n" + '--------| gemini3pro() with JSON response |--------');
23
+ // Using Gemini 3.1 Pro with custom config
24
+ console.log("\n" + '--------| gemini31pro() with JSON response |--------');
25
25
  const pro = mmix.new().gemini31pro();
26
26
 
27
27
  pro.addText('Give me a fun fact about cats');
package/demo/json.js CHANGED
@@ -6,7 +6,7 @@ const model = await ModelMix.new({ options: { max_tokens: 10000 }, config: { deb
6
6
  // .gptOss()
7
7
  // .o4mini()
8
8
  // .gpt45()
9
- // .gemini25flash()
9
+ // .gemini37flash()
10
10
  .addText("Name and capital of 3 South American countries.")
11
11
 
12
12
  const jsonResult = await model.json([{
@@ -24,4 +24,4 @@ const jsonResult = await model.json([{
24
24
  }], { addNote: true });
25
25
 
26
26
  console.log(jsonResult);
27
- console.log(model.lastRaw.tokens);
27
+ console.log(model.lastRaw.tokens);
@@ -8,7 +8,7 @@ async function simpleCalculator() {
8
8
  console.log('\n=== Smart Calculator ===');
9
9
 
10
10
  const mmix = ModelMix.new()
11
- .gpt41nano()
11
+ .gpt5nano()
12
12
  .setSystem('You are a smart calculator. Use the available tools to perform calculations.');
13
13
 
14
14
  // Add custom tool for advanced operations
@@ -93,7 +93,7 @@ async function contentGenerator() {
93
93
  console.log('\n=== Content Generator ===');
94
94
 
95
95
  const mmix = ModelMix.new({ config: { debug: 2, max_history: 1 } })
96
- .gemini3flash()
96
+ .gemini37flash()
97
97
  .setSystem('You are a creative assistant that can generate different types of content.');
98
98
 
99
99
  // Tool for generating passwords
package/demo/mcp-tools.js CHANGED
@@ -9,7 +9,7 @@ async function example1() {
9
9
  console.log('\n=== Example 1: Common Tools ===');
10
10
 
11
11
  const mmix = ModelMix.new({ config: { max_history: 10 } })
12
- .gpt41nano();
12
+ .gpt5nano();
13
13
 
14
14
  // Add custom tools
15
15
  mmix.addTool({
@@ -66,7 +66,7 @@ async function example2() {
66
66
  console.log('\n=== Example 2: Custom Tool - Read Files ===');
67
67
 
68
68
  const mmix = ModelMix.new({ config: { max_history: 10 } })
69
- .gpt41nano();
69
+ .gpt5nano();
70
70
 
71
71
  // Register custom tool for reading files
72
72
  mmix.addTool({
@@ -102,7 +102,7 @@ async function example3() {
102
102
  console.log('\n=== Example 3: HTTP Request Tool ===');
103
103
 
104
104
  const mmix = ModelMix.new({ config: { max_history: 10 } })
105
- .gpt41nano();
105
+ .gpt5nano();
106
106
 
107
107
  // Register tool for making HTTP requests
108
108
  mmix.addTool({
@@ -158,7 +158,7 @@ async function example4() {
158
158
  console.log('\n=== Example 4: Multiple Tools Working Together ===');
159
159
 
160
160
  const mmix = ModelMix.new({ config: { max_history: 10 } })
161
- .gpt41nano();
161
+ .gpt5nano();
162
162
 
163
163
  // Register multiple tools at once
164
164
  mmix.addTools([
@@ -248,7 +248,7 @@ async function example5() {
248
248
  console.log('\n=== Example 5: External MCP + Local Tools ===');
249
249
 
250
250
  const mmix = ModelMix.new({ config: { max_history: 10 } })
251
- .gpt41nano();
251
+ .gpt5nano();
252
252
 
253
253
  // Add external MCP (if available)
254
254
  try {
@@ -341,7 +341,7 @@ async function runExamples() {
341
341
  console.log('\n✅ All examples completed');
342
342
 
343
343
  // Show registered tools
344
- const mmix = ModelMix.new().gpt41nano();
344
+ const mmix = ModelMix.new().gpt5nano();
345
345
  mmix.addTool({
346
346
  name: "example_tool",
347
347
  description: "Example tool for demonstration",
package/demo/mcp.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { ModelMix } from '../index.js';
2
2
  try { process.loadEnvFile(); } catch {}
3
3
 
4
- const mmix = ModelMix.new({ config: { max_history: 10 } }).gpt41nano();
4
+ const mmix = ModelMix.new({ config: { max_history: 10 } }).gpt5nano();
5
5
  mmix.setSystem('You are an assistant and today is ' + new Date().toISOString());
6
6
 
7
7
  // Add web search capability through MCP
@@ -129,7 +129,7 @@ async function genericRLMExample(variableName, variableData, task) {
129
129
  }
130
130
  }
131
131
  })
132
- .gpt41nano()
132
+ .gpt5nano()
133
133
  .setSystem(`You are a Recursive Language Model (RLM) agent.
134
134
 
135
135
  Data is in the ENVIRONMENT as variables, not in your context. You work programmatically:
@@ -282,7 +282,7 @@ You decide the strategy based on the data and task.`);
282
282
  }
283
283
  });
284
284
 
285
- callMmix.gpt41mini();
285
+ callMmix.gpt5nano();
286
286
 
287
287
  if (call.system_prompt) {
288
288
  callMmix.setSystem(call.system_prompt);
@@ -376,7 +376,7 @@ You decide the strategy based on the data and task.`);
376
376
  max_history: 10
377
377
  }
378
378
  })
379
- .gpt41nano();
379
+ .gpt5nano();
380
380
 
381
381
  if (system_prompt) {
382
382
  recursiveMmix.setSystem(system_prompt);
package/demo/parallel.js CHANGED
@@ -14,7 +14,7 @@ const mix = new ModelMix({
14
14
  }
15
15
  })
16
16
 
17
- mix.gpt41nano();
17
+ mix.gpt5nano();
18
18
 
19
19
  // Function to create a promise that resolves after a random time
20
20
  const randomDelay = () => new Promise(resolve => setTimeout(resolve, Math.random() * 2000 + 1000));
@@ -48,4 +48,4 @@ async function runExample() {
48
48
  }
49
49
 
50
50
  // Run the example
51
- runExample().catch(console.error);
51
+ runExample().catch(console.error);
@@ -12,9 +12,9 @@ async function replPowersExample() {
12
12
  console.log('\n=== JavaScript REPL - Potencias de 2 ===\n');
13
13
  const gptArgs = { options: { reasoning_effort: "none", verbosity: null } };
14
14
  const mmix = ModelMix.new({ config: { debug: 2, max_history: 10 } })
15
- .gpt41nano()
15
+ .gpt5nano()
16
16
  .gpt52(gptArgs)
17
- .gemini3flash()
17
+ .gemini37flash()
18
18
  .setSystem('You are a helpful assistant with access to a JavaScript REPL. When you use the REPL and get results, always show them to the user in your response.');
19
19
 
20
20
  // Variable para capturar el resultado de la herramienta
@@ -74,4 +74,3 @@ try {
74
74
  } catch (error) {
75
75
  console.error('❌ Error:', error);
76
76
  }
77
-
package/demo/rlm-basic.js CHANGED
@@ -36,7 +36,7 @@ async function basicRLM() {
36
36
  console.log('🎯 Query: "Find top Engineering talents with Python skills"\n');
37
37
 
38
38
  const mmix = ModelMix.new({ config: { debug: false, max_history: 20 } })
39
- .gpt41nano()
39
+ .gpt5nano()
40
40
  .setSystem(`You are an RLM (Recursive Language Model).
41
41
 
42
42
  Instead of reading all data at once, you:
@@ -135,7 +135,7 @@ Be strategic: think about what information you need and query only that.`);
135
135
 
136
136
  // Create a focused recursive call
137
137
  const subMmix = ModelMix.new({ config: { debug: false } })
138
- .gpt41nano()
138
+ .gpt5nano()
139
139
  .setSystem(`Solve this focused sub-task concisely. Recursion level: ${recursionCount}`);
140
140
 
141
141
  subMmix.addText(`Context: ${context}\n\nTask: ${task}`);
package/demo/rlm-fast.js CHANGED
@@ -50,7 +50,7 @@ async function createIvmContext(isolate, contextData, mmixInstance) {
50
50
  await jail.set('__mmixCallback', new ivm.Reference(async (system, message, outputJson) => {
51
51
  const output = JSON.parse(outputJson);
52
52
  const result = await mmixInstance.new()
53
- .gpt41nano()
53
+ .gpt5nano()
54
54
  .setSystem(system)
55
55
  .addText(message)
56
56
  .json(output, output);
@@ -209,8 +209,8 @@ async function demo() {
209
209
  // Create base mmix instance for the callbacks
210
210
  const model = ModelMix.new({ config: { debug: 2, bottleneck: {} } })
211
211
  .gpt52({ options: { reasoning_effort: 'none', verbosity: null } })
212
- .gpt41nano()
213
- .gemini3flash();
212
+ .gpt5nano()
213
+ .gemini37flash();
214
214
 
215
215
  // Run the IVM task
216
216
  const result = await runIvmWithMmix({
@@ -105,9 +105,9 @@ async function rlmExample() {
105
105
 
106
106
  const gptArgs = { options: { reasoning_effort: "none", verbosity: null } };
107
107
  const mmix = ModelMix.new({ config: { debug: false, max_history: 15 } })
108
- .gpt41nano()
108
+ .gpt5nano()
109
109
  .gpt52(gptArgs)
110
- .gemini3flash()
110
+ .gemini37flash()
111
111
  .setSystem(`You are an RLM (Recursive Language Model) agent.
112
112
 
113
113
  KEY PRINCIPLE: Instead of processing the entire document directly, you can:
@@ -216,7 +216,7 @@ Current recursion depth: ${recursionDepth}/${maxDepth}`);
216
216
 
217
217
  // Crear una nueva instancia para la llamada recursiva
218
218
  const recursiveMmix = ModelMix.new({ config: { debug: false } })
219
- .gpt41nano()
219
+ .gpt5nano()
220
220
  .setSystem(`You are processing a sub-task. Be concise and direct.
221
221
  Recursion depth: ${recursionDepth}/${maxDepth}
222
222
  ${document_chunk ? 'Document chunk provided.' : 'No document chunk provided.'}`);
package/demo/short.js CHANGED
@@ -12,8 +12,11 @@ const mmix = await ModelMix.new(setup)
12
12
  .sonnet46() // (main model) Anthropic claude-sonnet-4-6
13
13
  .gpt56luna() // (fallback 1) OpenAI gpt-5.6-luna
14
14
  .gemini37flash() // (fallback 2) Google gemini-3.7-flash
15
- .gpt41nano() // (fallback 3) OpenAI gpt-4.1-nano
15
+ .gpt5nano() // (fallback 3) OpenAI gpt-5-nano
16
16
  .grok46() // (fallback 4) Grok grok-4.6
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
17
20
  .addText("What's your name?");
18
21
 
19
22
  console.log(await mmix.message());
package/demo/stream.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { ModelMix } from '../index.js';
2
2
  try { process.loadEnvFile(); } catch {}
3
3
 
4
- await ModelMix.new().gpt41nano()
4
+ await ModelMix.new().gpt5nano()
5
5
  .addImageFromUrl('https://pbs.twimg.com/media/F6-GsjraAAADDGy?format=jpg')
6
6
  .addText('describe')
7
7
  .stream((data) => { console.log(data.message); });
package/demo/tokens.js CHANGED
@@ -26,7 +26,7 @@ console.log('-'.repeat(60));
26
26
  const providers = [
27
27
  { name: 'OpenAI GPT-5-nano', fn: (m) => m.gpt5nano() },
28
28
  { name: 'Anthropic Haiku', fn: (m) => m.haiku45() },
29
- { name: 'Google Gemini', fn: (m) => m.gemini25flash() }
29
+ { name: 'Google Gemini', fn: (m) => m.gemini37flash() }
30
30
  ];
31
31
 
32
32
  const prompt = 'Explain quantum computing in one sentence.';
package/demo/verbose.js CHANGED
@@ -18,7 +18,7 @@ console.log('──────────────────────
18
18
 
19
19
  await ModelMix
20
20
  .new({ config: { verbose: 0 } })
21
- .gpt41nano()
21
+ .gpt5nano()
22
22
  .addText(prompt)
23
23
  .message();
24
24
 
@@ -33,7 +33,7 @@ console.log('──────────────────────
33
33
 
34
34
  await ModelMix
35
35
  .new({ config: { verbose: 1 } })
36
- .gpt41nano()
36
+ .gpt5nano()
37
37
  .addText(prompt)
38
38
  .message();
39
39
 
@@ -48,7 +48,7 @@ console.log('──────────────────────
48
48
 
49
49
  await ModelMix
50
50
  .new({ config: { verbose: 2 } })
51
- .gpt41nano()
51
+ .gpt5nano()
52
52
  .addText(prompt)
53
53
  .json({ message: 'string' });
54
54
 
@@ -63,7 +63,7 @@ console.log('──────────────────────
63
63
 
64
64
  await ModelMix
65
65
  .new({ config: { verbose: 3 } })
66
- .gpt41nano()
66
+ .gpt5nano()
67
67
  .addText(prompt)
68
68
  .message();
69
69
 
@@ -79,7 +79,7 @@ try {
79
79
  const resultFallback = await ModelMix
80
80
  .new({ config: { verbose: 2 } })
81
81
  .attach('fake-model-that-will-fail', new MixOpenAI())
82
- .gpt41nano() // This will be the fallback
82
+ .gpt5nano() // This will be the fallback
83
83
  .addText(prompt)
84
84
  .message();
85
85
 
package/effort.js CHANGED
@@ -7,6 +7,7 @@
7
7
  */
8
8
 
9
9
  const OPENAI_LEVELS = ['none', 'low', 'medium', 'high', 'xhigh'];
10
+ const OPENAI_LEVEL_LADDER = [...OPENAI_LEVELS, 'max'];
10
11
  const ANTHROPIC_LEVELS = ['low', 'medium', 'high', 'xhigh', 'max'];
11
12
  const GEMINI_LEVELS = ['minimal', 'low', 'medium', 'high'];
12
13
 
@@ -35,6 +36,10 @@ const GEMINI_BANDS = [
35
36
 
36
37
  /** Exact model → supported OpenAI reasoning_effort values */
37
38
  const OPENAI_MODEL_LEVELS = {
39
+ 'gpt-5.6-sol': ['none', 'low', 'medium', 'high', 'xhigh', 'max'],
40
+ 'gpt-5.6-terra': ['none', 'low', 'medium', 'high', 'xhigh', 'max'],
41
+ 'gpt-5.6-luna': ['none', 'low', 'medium', 'high', 'xhigh', 'max'],
42
+ 'accounts/fireworks/models/qwen3p8-2p4t-a95b': ['none', 'low', 'medium', 'high'],
38
43
  'grok-4.6': ['low', 'medium', 'high', 'xhigh'],
39
44
  'gpt-5': ['minimal', 'low', 'medium', 'high'],
40
45
  'gpt-5-mini': ['minimal', 'low', 'medium', 'high'],
@@ -372,8 +377,11 @@ function mapEffort(providerFamily, effort, modelKey) {
372
377
  if (modelKey === GROK420_NON_REASONING) {
373
378
  return null;
374
379
  }
375
- const desired = levelFromBands(normalized, OPENAI_BANDS);
376
- const level = pickNearestLevel(desired, OPENAI_LEVELS, supportedOpenAILevels(modelKey));
380
+ const supported = supportedOpenAILevels(modelKey);
381
+ const desired = normalized === 100 && supported.includes('max')
382
+ ? 'max'
383
+ : levelFromBands(normalized, OPENAI_BANDS);
384
+ const level = pickNearestLevel(desired, OPENAI_LEVEL_LADDER, supported);
377
385
  return { reasoning_effort: level };
378
386
  }
379
387
 
package/index.d.ts CHANGED
@@ -441,9 +441,6 @@ export declare class ModelMix {
441
441
  attach(key: string, provider: MixCustom): this;
442
442
 
443
443
  // OpenAI
444
- gpt41(args?: ModelAttachArgs): this;
445
- gpt41mini(args?: ModelAttachArgs): this;
446
- gpt41nano(args?: ModelAttachArgs): this;
447
444
  gpt5(args?: ModelAttachArgs): this;
448
445
  gpt5mini(args?: ModelAttachArgs): this;
449
446
  gpt5nano(args?: ModelAttachArgs): this;
@@ -479,16 +476,12 @@ export declare class ModelMix {
479
476
  haiku45(args?: ModelAttachArgs): this;
480
477
 
481
478
  // Google
482
- gemini25flash(args?: ModelAttachArgs): this;
483
479
  gemini31pro(args?: ModelAttachArgs): this;
484
- gemini3pro(args?: ModelAttachArgs): this;
485
- gemini3flash(args?: ModelAttachArgs): this;
486
480
  gemini37flash(args?: ModelAttachArgs): this;
487
481
  gemini36flash(args?: ModelAttachArgs): this;
488
482
  gemini35flash(args?: ModelAttachArgs): this;
489
483
  gemini35flashLite(args?: ModelAttachArgs): this;
490
484
  gemini31flashLite(args?: ModelAttachArgs): this;
491
- gemini25pro(args?: ModelAttachArgs): this;
492
485
 
493
486
  // Perplexity
494
487
  sonarPro(args?: ModelAttachArgs): this;
@@ -504,16 +497,18 @@ export declare class ModelMix {
504
497
 
505
498
  // Multi-provider
506
499
  qwen3(args?: ModelAttachArgs): this;
500
+ qwen35397b(args?: ModelAttachArgs): this;
507
501
  qwen36plus(args?: ModelAttachArgs): this;
508
502
  qwen37plus(args?: ModelAttachArgs): this;
509
503
  qwen38max(args?: ModelAttachArgs): this;
504
+ hermes470b(args?: ModelAttachArgs): this;
505
+ hermes4405b(args?: ModelAttachArgs): this;
510
506
  hermes3(args?: ModelAttachArgs): this;
511
507
  kimiK26(args?: ModelAttachArgs): this;
512
508
  kimiK27Code(args?: ModelAttachArgs): this;
513
509
  kimiK3(args?: ModelAttachArgs): this;
514
510
  kimiK25(args?: ModelAttachArgs): this;
515
511
  lmstudio(model?: string, args?: ModelAttachArgs): this;
516
- minimaxM25(args?: ModelAttachArgs): this;
517
512
  minimaxM27(args?: ModelAttachArgs): this;
518
513
  minimaxM3(args?: ModelAttachArgs): this;
519
514
  mimo25(args?: ModelAttachArgs): this;
package/index.js CHANGED
@@ -362,6 +362,7 @@ const MODEL_PRICING = {
362
362
  // Fireworks
363
363
  'accounts/fireworks/models/deepseek-v4-flash': { input: 0.14, output: 0.28 },
364
364
  'accounts/fireworks/models/deepseek-v4-pro': { input: 1.74, output: 3.48 },
365
+ 'accounts/fireworks/models/deepseek-v4-pro-0813': { input: 1.32, cachedInput: 0.044, output: 3.96 },
365
366
  'deepseek-ai/DeepSeek-V4-Flash': { input: 0.14, output: 0.28 },
366
367
  'deepseek-ai/DeepSeek-V4-Pro': { input: 2.10, output: 4.40 },
367
368
  'deepseek/deepseek-v4-flash': { input: 0.09, output: 0.18 },
@@ -369,10 +370,12 @@ const MODEL_PRICING = {
369
370
  'accounts/fireworks/models/glm-5p1': { input: 1.05, output: 3.50 },
370
371
  'zai-org/GLM-5.2': { input: 1.40, output: 4.40 },
371
372
  'accounts/fireworks/models/kimi-k2p5': { input: 0.50, output: 2.80 },
373
+ 'qwen/qwen3.5-397b-a17b': { input: 0.385, output: 2.45 },
372
374
  'accounts/fireworks/models/qwen3p6-plus': { input: 0.50, output: 3.00 },
373
375
  'Qwen/Qwen3.6-Plus': { input: 0.50, output: 3.00 },
374
376
  'accounts/fireworks/models/qwen3p7-plus': { input: 0.40, output: 1.60 },
375
377
  'qwen/qwen3.7-plus': { input: 0.32, output: 1.28 },
378
+ 'accounts/fireworks/models/qwen3p8-2p4t-a95b': { input: 2.00, cachedInput: 0.25, output: 6.00 },
376
379
  'qwen/qwen3.8-max': { input: 2.00, output: 6.00 },
377
380
  // MiniMax
378
381
  'MiniMax-M2.5': { input: 0.30, output: 1.20 },
@@ -384,7 +387,10 @@ const MODEL_PRICING = {
384
387
  // Perplexity
385
388
  'sonar': { input: 1.00, output: 1.00 },
386
389
  'sonar-pro': { input: 3.00, output: 15.00 },
387
- // Hermes3 (Lambda/OpenRouter)
390
+ // Hermes 4 (OpenRouter)
391
+ 'nousresearch/hermes-4-70b': { input: 0.13, output: 0.40 },
392
+ 'nousresearch/hermes-4-405b': { input: 1.00, output: 3.00 },
393
+ // Hermes 3 (Lambda/OpenRouter)
388
394
  'Hermes-3-Llama-3.1-405B-FP8': { input: 0.80, output: 0.80 },
389
395
  'nousresearch/hermes-3-llama-3.1-405b:free': { input: 0, output: 0 },
390
396
  // Qwen3 (Together/Cerebras)
@@ -402,19 +408,19 @@ const MODEL_PRICING = {
402
408
  };
403
409
 
404
410
  const CHAIN_MODEL_SHORTCUTS = new Set([
405
- 'gpt41', 'gpt41mini', 'gpt41nano', 'gpt5', 'gpt5mini', 'gpt5nano',
411
+ 'gpt5', 'gpt5mini', 'gpt5nano',
406
412
  'gpt51', 'gpt52', 'gpt54', 'gpt54mini', 'gpt54nano', 'gpt54pro',
407
413
  'gpt55', 'gpt55pro', 'gpt56sol', 'gpt56terra', 'gpt56luna',
408
414
  'gptRealtime', 'gptRealtimeMini', 'gpt53codex', 'gpt53chat', 'gptOss',
409
415
  'fable50', 'fable5', 'opus50', 'opus5', 'opus48', 'opus47', 'opus46',
410
416
  'sonnet50', 'sonnet5', 'sonnet46', 'sonnet45', 'haiku45',
411
- 'gemini25flash', 'gemini31pro', 'gemini3pro', 'gemini3flash',
412
- 'gemini37flash', 'gemini36flash', 'gemini35flash', 'gemini35flashLite',
413
- 'gemini31flashLite', 'gemini25pro', 'sonarPro', 'sonar',
417
+ 'gemini31pro', 'gemini37flash', 'gemini36flash', 'gemini35flash',
418
+ 'gemini35flashLite', 'gemini31flashLite', 'sonarPro', 'sonar',
414
419
  'grok46', 'grok45', 'grok43', 'grok420multiAgent', 'grok420',
415
- 'qwen3', 'qwen36plus', 'qwen37plus', 'qwen38max', 'hermes3',
420
+ 'qwen3', 'qwen35397b', 'qwen36plus', 'qwen37plus', 'qwen38max',
421
+ 'hermes470b', 'hermes4405b', 'hermes3',
416
422
  'kimiK26', 'kimiK27Code', 'kimiK3', 'kimiK25',
417
- 'minimaxM25', 'minimaxM27', 'minimaxM3', 'mimo25', 'mimo25pro',
423
+ 'minimaxM27', 'minimaxM3', 'mimo25', 'mimo25pro',
418
424
  'deepseekV4Pro', 'deepseekV4Flash', 'GLM51', 'GLM52'
419
425
  ]);
420
426
 
@@ -923,15 +929,6 @@ class ModelMix {
923
929
  return this;
924
930
  }
925
931
 
926
- gpt41({ options = {}, config = {} } = {}) {
927
- return this.attach('gpt-4.1', new MixOpenAI({ options, config }));
928
- }
929
- gpt41mini({ options = {}, config = {} } = {}) {
930
- return this.attach('gpt-4.1-mini', new MixOpenAI({ options, config }));
931
- }
932
- gpt41nano({ options = {}, config = {} } = {}) {
933
- return this.attach('gpt-4.1-nano', new MixOpenAI({ options, config }));
934
- }
935
932
  gpt5({ options = {}, config = {} } = {}) {
936
933
  return this.attach('gpt-5', new MixOpenAI({ options, config }));
937
934
  }
@@ -1030,18 +1027,9 @@ class ModelMix {
1030
1027
  haiku45({ options = {}, config = {} } = {}) {
1031
1028
  return this.attach('claude-haiku-4-5-20251001', new MixAnthropic({ options, config }));
1032
1029
  }
1033
- gemini25flash({ options = {}, config = {} } = {}) {
1034
- return this.attach('gemini-2.5-flash', new MixGoogle({ options, config }));
1035
- }
1036
1030
  gemini31pro({ options = {}, config = {} } = {}) {
1037
1031
  return this.attach('gemini-3.1-pro-preview', new MixGoogle({ options, config }));
1038
1032
  }
1039
- gemini3pro({ options = {}, config = {} } = {}) {
1040
- return this.attach('gemini-3-pro-preview', new MixGoogle({ options, config }));
1041
- }
1042
- gemini3flash({ options = {}, config = {} } = {}) {
1043
- return this.attach('gemini-3-flash-preview', new MixGoogle({ options, config }));
1044
- }
1045
1033
  gemini37flash({ options = {}, config = {} } = {}) {
1046
1034
  return this.attach('gemini-3.7-flash', new MixGoogle({ options, config }));
1047
1035
  }
@@ -1057,9 +1045,6 @@ class ModelMix {
1057
1045
  gemini31flashLite({ options = {}, config = {} } = {}) {
1058
1046
  return this.attach('gemini-3.1-flash-lite-preview', new MixGoogle({ options, config }));
1059
1047
  }
1060
- gemini25pro({ options = {}, config = {} } = {}) {
1061
- return this.attach('gemini-2.5-pro', new MixGoogle({ options, config }));
1062
- }
1063
1048
  sonarPro({ options = {}, config = {} } = {}) {
1064
1049
  return this.attach('sonar-pro', new MixPerplexity({ options, config }));
1065
1050
  }
@@ -1090,6 +1075,10 @@ class ModelMix {
1090
1075
  return this;
1091
1076
  }
1092
1077
 
1078
+ qwen35397b({ options = {}, config = {} } = {}) {
1079
+ return this.attach('qwen/qwen3.5-397b-a17b', new MixOpenRouter({ options, config }));
1080
+ }
1081
+
1093
1082
  qwen36plus({ options = {}, config = {}, mix = { fireworks: true } } = {}) {
1094
1083
  mix = { ...this.mix, ...mix };
1095
1084
  if (mix.fireworks) this.attach('accounts/fireworks/models/qwen3p6-plus', new MixFireworks({ options, config }));
@@ -1104,12 +1093,21 @@ class ModelMix {
1104
1093
  return this;
1105
1094
  }
1106
1095
 
1107
- qwen38max({ options = {}, config = {}, mix = { openrouter: true } } = {}) {
1096
+ qwen38max({ options = {}, config = {}, mix = { fireworks: true } } = {}) {
1108
1097
  mix = { ...this.mix, ...mix };
1098
+ if (mix.fireworks) this.attach('accounts/fireworks/models/qwen3p8-2p4t-a95b', new MixFireworks({ options, config }));
1109
1099
  if (mix.openrouter) this.attach('qwen/qwen3.8-max', new MixOpenRouter({ options, config }));
1110
1100
  return this;
1111
1101
  }
1112
1102
 
1103
+ hermes470b({ options = {}, config = {} } = {}) {
1104
+ return this.attach('nousresearch/hermes-4-70b', new MixOpenRouter({ options, config }));
1105
+ }
1106
+
1107
+ hermes4405b({ options = {}, config = {} } = {}) {
1108
+ return this.attach('nousresearch/hermes-4-405b', new MixOpenRouter({ options, config }));
1109
+ }
1110
+
1113
1111
  hermes3({ options = {}, config = {}, mix = {} } = {}) {
1114
1112
  mix = { ...this.mix, ...mix };
1115
1113
  if (mix.lambda) this.attach('Hermes-3-Llama-3.1-405B-FP8', new MixLambda({ options, config }));
@@ -1151,12 +1149,6 @@ class ModelMix {
1151
1149
  }
1152
1150
 
1153
1151
 
1154
- minimaxM25({ options = {}, config = {}, mix = { minimax: true } } = {}) {
1155
- mix = { ...this.mix, ...mix };
1156
- if (mix.minimax) this.attach('MiniMax-M2.5', new MixMiniMax({ options, config }));
1157
- return this;
1158
- }
1159
-
1160
1152
  minimaxM27({ options = {}, config = {}, mix = { openrouter: true, minimax: true } } = {}) {
1161
1153
  mix = { ...this.mix, ...mix };
1162
1154
  if (mix.nvidia) this.attach('minimaxai/minimax-m2.7', new MixNVIDIA({ options, config }));
@@ -1191,7 +1183,7 @@ class ModelMix {
1191
1183
  deepseekV4Pro({ options = {}, config = {}, mix = { fireworks: true } } = {}) {
1192
1184
  mix = { ...this.mix, ...mix };
1193
1185
  if (mix.nvidia) this.attach('deepseek-ai/deepseek-v4-pro', new MixNVIDIA({ options, config }));
1194
- if (mix.fireworks) this.attach('accounts/fireworks/models/deepseek-v4-pro', new MixFireworks({ options, config }));
1186
+ if (mix.fireworks) this.attach('accounts/fireworks/models/deepseek-v4-pro-0813', new MixFireworks({ options, config }));
1195
1187
  if (mix.openrouter) this.attach('deepseek/deepseek-v4-pro', new MixOpenRouter({ options, config }));
1196
1188
  if (mix.together) this.attach('deepseek-ai/DeepSeek-V4-Pro', new MixTogether({ options, config }));
1197
1189
  return this;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "modelmix",
3
- "version": "5.0.5",
3
+ "version": "5.1.1",
4
4
  "description": "🧬 Reliable interface with automatic fallback for AI LLMs.",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -86,11 +86,11 @@ Chain shorthand methods to attach providers. First model is primary; others are
86
86
  const model = ModelMix.new()
87
87
  .sonnet46() // primary
88
88
  .gpt52() // fallback 1
89
- .gemini3flash() // fallback 2
89
+ .gemini37flash() // fallback 2
90
90
  .addText("Hello!")
91
91
  ```
92
92
 
93
- If `sonnet46` fails, it automatically tries `gpt52`, then `gemini3flash`.
93
+ If `sonnet46` fails, it automatically tries `gpt52`, then `gemini37flash`.
94
94
 
95
95
  The equivalent `chain()` form accepts public shortcut names directly in the
96
96
  same order. Append `@effort` for a per-model unified effort override (`-1` or
@@ -99,7 +99,7 @@ provider default when no chain effort is configured:
99
99
 
100
100
  ```javascript
101
101
  const model = ModelMix.new()
102
- .chain('sonnet46', 'gpt52@20', 'gemini3flash@-1')
102
+ .chain('sonnet46', 'gpt52@20', 'gemini37flash@-1')
103
103
  .addText('Hello!');
104
104
  ```
105
105
 
@@ -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
- \* 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`. 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
 
@@ -151,7 +151,7 @@ ModelMix.new({ config: { effort: 80 } })
151
151
 
152
152
  Use `ModerationMix.new().openai()` with `.raw()` to classify text and images through OpenAI's Moderations endpoint. Read the results from `raw.moderation`. `ModerationMix` accepts moderation providers as ordered fallbacks, rejects generative providers, and does not generate text or support streaming.
153
153
 
154
- `gpt52()` `gpt52chat()` `gpt51()` `gpt5()` `gpt5mini()` `gpt5nano()` `gpt45()` `gpt41()` `gpt41mini()` `gpt41nano()` `o3()` `o4mini()`
154
+ `gpt52()` `gpt52chat()` `gpt51()` `gpt5()` `gpt5mini()` `gpt5nano()` `gpt45()` `o3()` `o4mini()`
155
155
 
156
156
  ### Anthropic
157
157
  `fable50()` `opus50()` `opus48()` `opus47()` `opus46()` `sonnet5()` `sonnet46()` `sonnet45()` `haiku45()`
@@ -159,7 +159,7 @@ Use `ModerationMix.new().openai()` with `.raw()` to classify text and images thr
159
159
  Use `.effort(n)` (or `config.effort`) to enable Anthropic thinking — e.g. `.effort(100).opus50()`. `fable5()` and `opus5()` remain available as compatibility aliases.
160
160
 
161
161
  ### Google
162
- `gemini3pro()` `gemini3flash()` `gemini37flash()` `gemini36flash()` `gemini35flash()` `gemini35flashLite()` `gemini31flashLite()` `gemini25pro()` `gemini25flash()`
162
+ `gemini31pro()` `gemini37flash()` `gemini36flash()` `gemini35flash()` `gemini35flashLite()` `gemini31flashLite()`
163
163
 
164
164
  ### Grok
165
165
  `grok46()` `grok45()` `grok43()` `grok420multiAgent()` `grok420()`
@@ -174,16 +174,16 @@ Use `.effort(n)` (or `config.effort`) to enable Anthropic thinking — e.g. `.ef
174
174
  `kimiK3()` — requires `MOONSHOT_API_KEY`; use `{ mix: { moonshot: false, openrouter: true } }` for OpenRouter.
175
175
 
176
176
  ### MiniMax
177
- `minimaxM25()` `minimaxM27()` `minimaxM3()`
177
+ `minimaxM27()` `minimaxM3()`
178
178
 
179
179
  ### Fireworks
180
- `qwen36plus()` `qwen37plus()` `deepseekV4Flash()` `deepseekV4Pro()` `kimiK26()`
180
+ `qwen36plus()` `qwen37plus()` `qwen38max()` `deepseekV4Flash()` `deepseekV4Pro()` `kimiK26()`
181
181
 
182
182
  ### Cerebras
183
183
  `GLM46()`
184
184
 
185
185
  ### OpenRouter
186
- `qwen38max()` `GLM45()`
186
+ `qwen35397b()` `hermes470b()` `hermes4405b()` `qwen38max()` `GLM45()`
187
187
 
188
188
  ### Multi-provider (auto-fallback across free/paid tiers)
189
189
  `hermes3()` `kimiK25()`
@@ -460,7 +460,7 @@ Omit all weights for equal probabilities. Otherwise every option needs a positiv
460
460
  const pool = ModelMix.new({ config: { roundRobin: true } })
461
461
  .gpt5mini()
462
462
  .sonnet45()
463
- .gemini3flash();
463
+ .gemini37flash();
464
464
 
465
465
  const r1 = await pool.new().addText("Request 1").message();
466
466
  const r2 = await pool.new().addText("Request 2").message();
@@ -7,7 +7,12 @@ describe('DeepSeek Model Registration Tests', () => {
7
7
  model.deepseekV4Pro({ mix: { fireworks: true, openrouter: false } });
8
8
 
9
9
  expect(model.models).to.have.length(1);
10
- expect(model.models[0].key).to.equal('accounts/fireworks/models/deepseek-v4-pro');
10
+ expect(model.models[0].key).to.equal('accounts/fireworks/models/deepseek-v4-pro-0813');
11
+ expect(ModelMix.calculateCost('accounts/fireworks/models/deepseek-v4-pro-0813', {
12
+ input: 1_000_000,
13
+ cached: 250_000,
14
+ output: 1_000_000
15
+ })).to.be.closeTo(4.961, 1e-10);
11
16
  });
12
17
 
13
18
  it('should register Together DeepSeek V4 Pro when together mix is enabled', () => {
@@ -69,6 +69,14 @@ describe('Unified effort scale', () => {
69
69
  expect(mapEffort('openai', 10)).to.deep.equal({ reasoning_effort: 'none' });
70
70
  expect(mapEffort('openai', 50)).to.deep.equal({ reasoning_effort: 'medium' });
71
71
  expect(mapEffort('openai', 90)).to.deep.equal({ reasoning_effort: 'xhigh' });
72
+ expect(mapEffort('openai', 100)).to.deep.equal({ reasoning_effort: 'xhigh' });
73
+ });
74
+
75
+ it('maps GPT-5.6 maximum unified effort to max', () => {
76
+ expect(mapEffort('openai', 99, 'gpt-5.6-luna')).to.deep.equal({ reasoning_effort: 'xhigh' });
77
+ for (const model of ['gpt-5.6-sol', 'gpt-5.6-terra', 'gpt-5.6-luna']) {
78
+ expect(mapEffort('openai', 100, model)).to.deep.equal({ reasoning_effort: 'max' });
79
+ }
72
80
  });
73
81
 
74
82
  it('sets OpenAI adaptive only when supported (otherwise no-op)', () => {
@@ -81,6 +89,12 @@ describe('Unified effort scale', () => {
81
89
  expect(mapEffort('openai', 10, 'gpt-oss-120b')).to.deep.equal({ reasoning_effort: 'low' });
82
90
  });
83
91
 
92
+ it('clamps Fireworks Qwen 3.8 Max to its supported reasoning levels', () => {
93
+ const key = 'accounts/fireworks/models/qwen3p8-2p4t-a95b';
94
+ expect(mapEffort('openai', 0, key)).to.deep.equal({ reasoning_effort: 'none' });
95
+ expect(mapEffort('openai', 100, key)).to.deep.equal({ reasoning_effort: 'high' });
96
+ });
97
+
84
98
  it('maps Anthropic adaptive models to thinking + output_config.effort', () => {
85
99
  expect(mapEffort('anthropic', 10, 'claude-opus-5')).to.deep.equal({
86
100
  thinking: { type: 'adaptive', display: 'summarized' },
@@ -178,6 +192,10 @@ describe('Unified effort scale', () => {
178
192
  reasoning_effort: 'max',
179
193
  thinking: { type: 'enabled' }
180
194
  });
195
+ expect(mapEffort('openai', 100, 'accounts/fireworks/models/deepseek-v4-pro-0813')).to.deep.equal({
196
+ reasoning_effort: 'max',
197
+ thinking: { type: 'enabled' }
198
+ });
181
199
  // No adaptive control on DeepSeek → no-op
182
200
  expect(mapEffort('openai', -1, 'deepseek/deepseek-v4-flash')).to.equal(null);
183
201
  });
@@ -349,6 +367,14 @@ describe('Unified effort scale', () => {
349
367
  expect(request.reasoning).to.deep.equal({ effort: 'none' });
350
368
  });
351
369
 
370
+ it('GPT-5.6 Luna .effort(100) sends max reasoning effort', () => {
371
+ const model = ModelMix.new().effort(100).gpt56luna();
372
+ const options = { model: 'gpt-5.6-luna', messages: [] };
373
+ applyUnifiedEffort(options, model.config, 'openai', 'gpt-5.6-luna');
374
+ const request = MixOpenAIResponses.buildResponsesRequest(options, {});
375
+ expect(request.reasoning).to.deep.equal({ effort: 'max' });
376
+ });
377
+
352
378
  it('Anthropic config.effort maps through .effort().opus50()', () => {
353
379
  const model = ModelMix.new().effort(100).opus50();
354
380
  expect(model.config.effort).to.equal(100);
@@ -60,6 +60,18 @@ describe('Provider Fallback Chain Tests', () => {
60
60
  .to.throw('Invalid chain model at index 0: expected a model shortcut string.');
61
61
  });
62
62
 
63
+ it('should reject removed shortcuts from the public chain API', () => {
64
+ for (const shortcut of [
65
+ 'gpt41', 'gpt41mini', 'gpt41nano',
66
+ 'gemini25flash', 'gemini25pro', 'gemini3pro', 'gemini3flash',
67
+ 'minimaxM25'
68
+ ]) {
69
+ expect(model[shortcut]).to.equal(undefined);
70
+ expect(() => model.chain(shortcut))
71
+ .to.throw(`Unknown model shortcut "${shortcut}" in chain().`);
72
+ }
73
+ });
74
+
63
75
  it('should use primary provider when available', async () => {
64
76
  model.gpt5mini().sonnet46().addText('Hello');
65
77
 
@@ -195,7 +207,7 @@ describe('Provider Fallback Chain Tests', () => {
195
207
  });
196
208
 
197
209
  it('should cascade through multiple fallbacks', async () => {
198
- model.gpt5mini().sonnet46().gemini3flash().addText('Hello');
210
+ model.gpt5mini().sonnet46().gemini37flash().addText('Hello');
199
211
 
200
212
  // Mock failed OpenAI response
201
213
  nock('https://api.openai.com')
@@ -279,7 +291,7 @@ describe('Provider Fallback Chain Tests', () => {
279
291
  });
280
292
 
281
293
  it('should fallback from Anthropic to Google', async () => {
282
- model.sonnet46().gemini3flash().addText('Test message');
294
+ model.sonnet46().gemini37flash().addText('Test message');
283
295
 
284
296
  // Mock Anthropic failure
285
297
  nock('https://api.anthropic.com')
@@ -665,7 +677,7 @@ describe('Provider Fallback Chain Tests', () => {
665
677
  });
666
678
 
667
679
  it('should provide detailed error information when all fallbacks fail', async () => {
668
- model.gpt5mini().sonnet46().gemini3flash().addText('Test');
680
+ model.gpt5mini().sonnet46().gemini37flash().addText('Test');
669
681
 
670
682
  // Mock all providers failing with different errors
671
683
  nock('https://api.openai.com')
@@ -0,0 +1,37 @@
1
+ const { expect } = require('chai');
2
+ const { ModelMix, MixOpenRouter } = require('../index.js');
3
+
4
+ describe('Hermes Model Registration Tests', () => {
5
+ it('should register Hermes 4 70B through OpenRouter', () => {
6
+ const model = ModelMix.new().hermes470b();
7
+
8
+ expect(model.models).to.have.length(1);
9
+ expect(model.models[0].key).to.equal('nousresearch/hermes-4-70b');
10
+ expect(model.models[0].provider).to.be.instanceOf(MixOpenRouter);
11
+ expect(ModelMix.calculateCost('nousresearch/hermes-4-70b', {
12
+ input: 1_000_000,
13
+ output: 1_000_000
14
+ })).to.equal(0.53);
15
+ });
16
+
17
+ it('should register Hermes 4 405B through OpenRouter', () => {
18
+ const model = ModelMix.new().hermes4405b();
19
+
20
+ expect(model.models).to.have.length(1);
21
+ expect(model.models[0].key).to.equal('nousresearch/hermes-4-405b');
22
+ expect(model.models[0].provider).to.be.instanceOf(MixOpenRouter);
23
+ expect(ModelMix.calculateCost('nousresearch/hermes-4-405b', {
24
+ input: 1_000_000,
25
+ output: 1_000_000
26
+ })).to.equal(4);
27
+ });
28
+
29
+ it('should support both Hermes 4 shortcuts in chain()', () => {
30
+ const model = ModelMix.new().chain('hermes470b', 'hermes4405b');
31
+
32
+ expect(model.models.map(({ key }) => key)).to.deep.equal([
33
+ 'nousresearch/hermes-4-70b',
34
+ 'nousresearch/hermes-4-405b'
35
+ ]);
36
+ });
37
+ });
@@ -570,7 +570,7 @@ describe('Conversation History Tests', () => {
570
570
  const model = ModelMix.new({
571
571
  config: { debug: false, max_history: 10 }
572
572
  });
573
- model.gemini3flash();
573
+ model.gemini37flash();
574
574
 
575
575
  model.addText('Hello');
576
576
  nock('https://generativelanguage.googleapis.com')
@@ -100,7 +100,7 @@ describe('OpenAI moderation', () => {
100
100
  it('rejects generative providers from the moderation chain', () => {
101
101
  const model = ModerationMix.new();
102
102
 
103
- expect(() => model.gpt41nano()).to.throw(
103
+ expect(() => model.gpt5nano({ config: { apiKey: 'test-key' } })).to.throw(
104
104
  'ModerationMix only accepts moderation providers.'
105
105
  );
106
106
  });
package/test/qwen.test.js CHANGED
@@ -1,5 +1,5 @@
1
1
  const { expect } = require('chai');
2
- const { ModelMix } = require('../index.js');
2
+ const { ModelMix, MixOpenRouter } = require('../index.js');
3
3
 
4
4
  describe('Qwen Model Registration Tests', () => {
5
5
  it('should register Fireworks Qwen 3.6 Plus by default', () => {
@@ -38,15 +38,45 @@ describe('Qwen Model Registration Tests', () => {
38
38
  expect(model.models[0].key).to.equal('qwen/qwen3.7-plus');
39
39
  });
40
40
 
41
- it('should register OpenRouter Qwen 3.8 Max by default', () => {
41
+ it('should register Fireworks Qwen 3.8 Max before the OpenRouter fallback by default', () => {
42
42
  const model = ModelMix.new();
43
43
  model.qwen38max();
44
44
 
45
+ expect(model.models.map(({ key }) => key)).to.deep.equal([
46
+ 'accounts/fireworks/models/qwen3p8-2p4t-a95b',
47
+ 'qwen/qwen3.8-max'
48
+ ]);
49
+ expect(ModelMix.calculateCost('accounts/fireworks/models/qwen3p8-2p4t-a95b', {
50
+ input: 1_000_000,
51
+ cached: 250_000,
52
+ output: 1_000_000
53
+ })).to.equal(7.5625);
54
+ });
55
+
56
+ it('should register only OpenRouter Qwen 3.8 Max when Fireworks is disabled', () => {
57
+ const model = ModelMix.new();
58
+ model.qwen38max({ mix: { fireworks: false, openrouter: true } });
59
+
45
60
  expect(model.models).to.have.length(1);
46
61
  expect(model.models[0].key).to.equal('qwen/qwen3.8-max');
47
- expect(ModelMix.calculateCost('qwen/qwen3.8-max', {
62
+ });
63
+
64
+ it('should register Qwen 3.5 397B A17B through OpenRouter', () => {
65
+ const model = ModelMix.new().qwen35397b();
66
+
67
+ expect(model.models).to.have.length(1);
68
+ expect(model.models[0].key).to.equal('qwen/qwen3.5-397b-a17b');
69
+ expect(model.models[0].provider).to.be.instanceOf(MixOpenRouter);
70
+ expect(ModelMix.calculateCost('qwen/qwen3.5-397b-a17b', {
48
71
  input: 1_000_000,
49
72
  output: 1_000_000
50
- })).to.equal(8.00);
73
+ })).to.equal(2.835);
74
+ });
75
+
76
+ it('should support Qwen 3.5 397B A17B in chain()', () => {
77
+ const model = ModelMix.new().chain('qwen35397b');
78
+
79
+ expect(model.models).to.have.length(1);
80
+ expect(model.models[0].key).to.equal('qwen/qwen3.5-397b-a17b');
51
81
  });
52
82
  });