modelmix 5.2.3 → 5.3.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
@@ -160,7 +160,9 @@ ModelMix provides convenient shorthand methods for quickly accessing different A
160
160
  | Method | Provider | Model | Input / 1M | Output / 1M |
161
161
  | --- | --- | --- | ---: | ---: |
162
162
  | `gpt6astra()` | OpenAI | gpt-6-astra | [\$10.00][1] | [\$50.00][1] |
163
- | `gpt56sol()` | OpenAI | gpt-5.6-sol | [\$5.00][1] | [\$30.00][1] |
163
+ | `gpt6sol()` | OpenAI | gpt-6-sol | [\$2.00][1] | [\$10.00][1] |
164
+ | `gpt6luna()` | OpenAI | gpt-6-luna | [\$0.10][1] | [\$0.50][1] |
165
+ | `gpt56sol()` | OpenAI | gpt-5.6-sol | [\$4.00][1] | [\$20.00][1] |
164
166
  | `gpt56terra()` | OpenAI | gpt-5.6-terra | [\$2.00][1] | [\$12.00][1] |
165
167
  | `gpt56luna()` | OpenAI | gpt-5.6-luna | [\$0.20][1] | [\$1.20][1] |
166
168
  | `gpt55()` | OpenAI | gpt-5.5 | [\$5.00][1] | [\$30.00][1] |
@@ -320,6 +322,7 @@ ModelMix.new().effort(-1).minimaxM3().addText('...').message();
320
322
 
321
323
  - **Gemini:** Gemini 3+ uses bands 0–24 / 25–49 / 50–74 / 75–100. Gemini 3.8 Flash and 3.7 Flash clamp these bands to `low` / `low` / `medium` / `high`; `-1` leaves their native `medium` default unchanged. Gemini 2.5 maps 0–100 to `thinkingBudget`.
322
324
  - **GPT-6 Astra:** 0–39 maps to `low`, 40–59 to `medium`, 60–79 to `high`, 80–99 to `xhigh`, and 100 to `max`. [Model details](https://developers.openai.com/api/docs/models/gpt-6-astra). Cache reads cost $1.00 and cache writes $12.50 per 1M tokens; requests over 272K input tokens apply 2× input/cache and 1.5× output rates.
325
+ - **GPT-6 Sol and GPT-6 Luna:** `100` maps to `max`; the remaining bands match OpenAI, so 0–19 is `none` and 80–99 is `xhigh`. [Sol model details](https://developers.openai.com/api/docs/models/gpt-6-sol), [Luna model details](https://developers.openai.com/api/docs/models/gpt-6-luna). Cache reads cost $0.20 and $0.01 and cache writes $2.50 and $0.125 per 1M tokens; requests over 272K input tokens apply the same 2× input/cache and 1.5× output rates as Astra.
323
326
  - **GPT-5.6:** `100` maps to `max`; 80–99 remains `xhigh`.
324
327
  - **Qwen 3.8 27B and Flash:** 0–39 / 40–79 / 80–100 map to `low` / `medium` / `xhigh`; `-1` leaves the native `xhigh` default unchanged. Qwen 3.8 Flash is the managed production version based on the open-weight Flash-Next architecture.
325
328
  - **GLM 5.3 and GLM 5.3 Flash:** reasoning is mandatory; 0–39 / 40–79 / 80–100 map to `low` / `high` / `max`; `-1` leaves the native `max` default unchanged.
@@ -831,9 +834,9 @@ await ask('Summarize support ticket 456.');
831
834
 
832
835
  The contents of `support.md` and the cache key stay the same between calls; only the final question changes. The first request may report `cacheWrite > 0`, while later requests confirm reuse with `cached > 0`. For GPT-5.6, the stable prefix must contain at least 1,024 tokens. Keep all variable content after the breakpoint, and change `prompt_cache_key` when the stable instructions change.
833
836
 
834
- ### GPT-5.6 prompt caching
837
+ ### GPT-5.6 and GPT-6 prompt caching
835
838
 
836
- GPT-5.6 supports implicit or explicit caching through `prompt_cache_options`. Put the explicit breakpoint at the end of the stable prefix; the provider only caches prompts with at least 1,024 tokens.
839
+ GPT-5.6 and the GPT-6 models (Astra, Sol, and Luna) support implicit or explicit caching through `prompt_cache_options`. Put the explicit breakpoint at the end of the stable prefix; the provider only caches prompts with at least 1,024 tokens.
837
840
 
838
841
  ```javascript
839
842
  const model = ModelMix.new()
@@ -854,16 +857,18 @@ console.log(result.tokens.cached, result.tokens.cacheWrite, result.tokens.cost);
854
857
 
855
858
  The provider-neutral `cache: { breakpoint: true }` option is accepted by `addTextFromFile()`, `addImage()`, `addImageFromUrl()`, and `addImageFromBuffer()`. Responses-native `input_text`, `input_image`, and `input_file` blocks preserve the native `prompt_cache_breakpoint` field when supplied directly through `options.messages`.
856
859
 
857
- GPT-5.6 uses `prompt_cache_options.ttl`; `prompt_cache_retention` remains available for earlier OpenAI models. ModelMix rejects the incompatible control instead of silently dropping it. For GPT-5.6 requests over 272K input tokens, the cost calculation applies the documented 2× input and 1.5× output multipliers to the complete request, including cache reads and writes.
860
+ GPT-5.6 and GPT-6 use `prompt_cache_options.ttl`; `prompt_cache_retention` remains available for earlier OpenAI models. ModelMix rejects the incompatible control instead of silently dropping it. For GPT-5.6 requests over 272K input tokens, the cost calculation applies the documented 2× input and 1.5× output multipliers to the complete request, including cache reads and writes.
858
861
 
859
862
  GPT-5.6 prices per 1M tokens:
860
863
 
861
864
  | Model | Input | Cached input | Cache write | Output |
862
865
  | --- | ---: | ---: | ---: | ---: |
863
- | `gpt-5.6-sol` | $5.00 | $0.50 | $6.25 | $30.00 |
866
+ | `gpt-5.6-sol` | $4.00 | $0.40 | $5.00 | $20.00 |
864
867
  | `gpt-5.6-terra` | $2.00 | $0.20 | $2.50 | $12.00 |
865
868
  | `gpt-5.6-luna` | $0.20 | $0.02 | $0.25 | $1.20 |
866
869
 
870
+ GPT-5.6 Sol's $4.00 input and $20.00 output rates are promotional and available at least through November 21, 2026.
871
+
867
872
  ### Cross-provider cache fallback
868
873
 
869
874
  Neutral breakpoints are translated at the last moment by each provider adapter. Native request policies remain scoped to their model, so they cannot leak into a fallback request:
@@ -885,7 +890,7 @@ const model = ModelMix.new()
885
890
  .addText('Answer this variable request.');
886
891
  ```
887
892
 
888
- GPT-5.6 receives `prompt_cache_breakpoint`; Anthropic receives `cache_control`; older OpenAI models and providers without an equivalent omit the marker. When a neutral explicit breakpoint is present for Anthropic, its model-scoped `cache_control` becomes that block's policy instead of adding an automatic breakpoint after the variable suffix.
893
+ GPT-5.6 and GPT-6 receive `prompt_cache_breakpoint`; Anthropic receives `cache_control`; older OpenAI models and providers without an equivalent omit the marker. When a neutral explicit breakpoint is present for Anthropic, its model-scoped `cache_control` becomes that block's policy instead of adding an automatic breakpoint after the variable suffix.
889
894
 
890
895
  ## 🔧 Model Context Protocol (MCP) Integration
891
896
 
@@ -1055,7 +1060,7 @@ Pass `mix` to `benchmark()` to select providers for the criteria model, particip
1055
1060
 
1056
1061
  JSON parsing accepts a Markdown code block or a lone closing triple-backtick delimiter after valid JSON. Other extra content, malformed JSON, and invalid evaluation scores remain errors.
1057
1062
 
1058
- Run `node demo/benchmark.js` for a complete five-model comparison. It allows up to 32,768 output tokens per call, prints intermediate progress, a final ranking and error details, then saves each response as Markdown together with the full JSON report under `demo/results/`.
1063
+ Run `node demo/benchmark.js` for a complete model comparison. It allows up to 32,768 output tokens per call, prints intermediate progress, a final ranking and error details, then saves each response as Markdown together with the full JSON report under `demo/results/`.
1059
1064
 
1060
1065
  ### Recursive Language Model plugin
1061
1066
 
package/demo/benchmark.js CHANGED
@@ -11,16 +11,13 @@ const { benchmark } = benchmarkPlugin;
11
11
  const task = await readFile(process.argv[2] || new URL('./prompts/story.txt', import.meta.url), 'utf8');
12
12
 
13
13
  const models = [
14
- // 'opus50@20',
15
- 'gpt6astra@0',
16
- 'gpt6astra@20',
14
+ 'gpt6sol@40',
15
+ 'gpt6luna@40',
17
16
  'gpt56sol@40',
18
- // 'gemini38flash@20',
19
- // 'grok46@20',
20
- 'deepseekV41Flash@60'
17
+ 'gpt56luna@40'
21
18
  ];
22
19
 
23
- console.log(`Running benchmark: ${models.length} models, 26 sequential model calls.`);
20
+ console.log(`Running benchmark: ${models.length} models, 17 sequential model calls.`);
24
21
 
25
22
  const report = await ModelMix.new({
26
23
  config: { debug: 1 },
@@ -0,0 +1,516 @@
1
+ {
2
+ "name": "demo",
3
+ "version": "1.0.0",
4
+ "lockfileVersion": 3,
5
+ "requires": true,
6
+ "packages": {
7
+ "": {
8
+ "name": "demo",
9
+ "version": "1.0.0",
10
+ "license": "ISC",
11
+ "dependencies": {
12
+ "dotenv": "^17.2.3",
13
+ "isolated-vm": "^6.0.2",
14
+ "lemonlog": "^1.1.4",
15
+ "pathmix": "^1.0.0"
16
+ }
17
+ },
18
+ ".api/apis/pplx": {
19
+ "name": "@api/pplx",
20
+ "version": "0.1.0",
21
+ "extraneous": true,
22
+ "dependencies": {
23
+ "api": "^6.1.1",
24
+ "json-schema-to-ts": "^2.8.0-beta.0",
25
+ "oas": "^20.10.3"
26
+ }
27
+ },
28
+ "node_modules/base64-js": {
29
+ "version": "1.5.1",
30
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
31
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
32
+ "funding": [
33
+ {
34
+ "type": "github",
35
+ "url": "https://github.com/sponsors/feross"
36
+ },
37
+ {
38
+ "type": "patreon",
39
+ "url": "https://www.patreon.com/feross"
40
+ },
41
+ {
42
+ "type": "consulting",
43
+ "url": "https://feross.org/support"
44
+ }
45
+ ],
46
+ "license": "MIT"
47
+ },
48
+ "node_modules/bl": {
49
+ "version": "4.1.0",
50
+ "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
51
+ "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
52
+ "license": "MIT",
53
+ "dependencies": {
54
+ "buffer": "^5.5.0",
55
+ "inherits": "^2.0.4",
56
+ "readable-stream": "^3.4.0"
57
+ }
58
+ },
59
+ "node_modules/buffer": {
60
+ "version": "5.7.1",
61
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
62
+ "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
63
+ "funding": [
64
+ {
65
+ "type": "github",
66
+ "url": "https://github.com/sponsors/feross"
67
+ },
68
+ {
69
+ "type": "patreon",
70
+ "url": "https://www.patreon.com/feross"
71
+ },
72
+ {
73
+ "type": "consulting",
74
+ "url": "https://feross.org/support"
75
+ }
76
+ ],
77
+ "license": "MIT",
78
+ "dependencies": {
79
+ "base64-js": "^1.3.1",
80
+ "ieee754": "^1.1.13"
81
+ }
82
+ },
83
+ "node_modules/chownr": {
84
+ "version": "1.1.4",
85
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
86
+ "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
87
+ "license": "ISC"
88
+ },
89
+ "node_modules/debug": {
90
+ "version": "4.4.1",
91
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
92
+ "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
93
+ "license": "MIT",
94
+ "dependencies": {
95
+ "ms": "^2.1.3"
96
+ },
97
+ "engines": {
98
+ "node": ">=6.0"
99
+ },
100
+ "peerDependenciesMeta": {
101
+ "supports-color": {
102
+ "optional": true
103
+ }
104
+ }
105
+ },
106
+ "node_modules/decompress-response": {
107
+ "version": "6.0.0",
108
+ "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
109
+ "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
110
+ "license": "MIT",
111
+ "dependencies": {
112
+ "mimic-response": "^3.1.0"
113
+ },
114
+ "engines": {
115
+ "node": ">=10"
116
+ },
117
+ "funding": {
118
+ "url": "https://github.com/sponsors/sindresorhus"
119
+ }
120
+ },
121
+ "node_modules/deep-extend": {
122
+ "version": "0.6.0",
123
+ "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
124
+ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
125
+ "license": "MIT",
126
+ "engines": {
127
+ "node": ">=4.0.0"
128
+ }
129
+ },
130
+ "node_modules/detect-libc": {
131
+ "version": "2.1.2",
132
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
133
+ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
134
+ "license": "Apache-2.0",
135
+ "engines": {
136
+ "node": ">=8"
137
+ }
138
+ },
139
+ "node_modules/dotenv": {
140
+ "version": "17.2.3",
141
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz",
142
+ "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==",
143
+ "license": "BSD-2-Clause",
144
+ "engines": {
145
+ "node": ">=12"
146
+ },
147
+ "funding": {
148
+ "url": "https://dotenvx.com"
149
+ }
150
+ },
151
+ "node_modules/end-of-stream": {
152
+ "version": "1.4.5",
153
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
154
+ "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
155
+ "license": "MIT",
156
+ "dependencies": {
157
+ "once": "^1.4.0"
158
+ }
159
+ },
160
+ "node_modules/expand-template": {
161
+ "version": "2.0.3",
162
+ "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
163
+ "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==",
164
+ "license": "(MIT OR WTFPL)",
165
+ "engines": {
166
+ "node": ">=6"
167
+ }
168
+ },
169
+ "node_modules/fs-constants": {
170
+ "version": "1.0.0",
171
+ "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
172
+ "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
173
+ "license": "MIT"
174
+ },
175
+ "node_modules/github-from-package": {
176
+ "version": "0.0.0",
177
+ "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
178
+ "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==",
179
+ "license": "MIT"
180
+ },
181
+ "node_modules/ieee754": {
182
+ "version": "1.2.1",
183
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
184
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
185
+ "funding": [
186
+ {
187
+ "type": "github",
188
+ "url": "https://github.com/sponsors/feross"
189
+ },
190
+ {
191
+ "type": "patreon",
192
+ "url": "https://www.patreon.com/feross"
193
+ },
194
+ {
195
+ "type": "consulting",
196
+ "url": "https://feross.org/support"
197
+ }
198
+ ],
199
+ "license": "BSD-3-Clause"
200
+ },
201
+ "node_modules/inherits": {
202
+ "version": "2.0.4",
203
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
204
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
205
+ "license": "ISC"
206
+ },
207
+ "node_modules/ini": {
208
+ "version": "1.3.8",
209
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
210
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
211
+ "license": "ISC"
212
+ },
213
+ "node_modules/isolated-vm": {
214
+ "version": "6.0.2",
215
+ "resolved": "https://registry.npmjs.org/isolated-vm/-/isolated-vm-6.0.2.tgz",
216
+ "integrity": "sha512-Qw6AJuagG/VJuh2AIcSWmQPsAArti/L+lKhjXU+lyhYkbt3J57XZr+ZjgfTnOr4NJcY1r3f8f0eePS7MRGp+pg==",
217
+ "hasInstallScript": true,
218
+ "license": "ISC",
219
+ "dependencies": {
220
+ "prebuild-install": "^7.1.3"
221
+ },
222
+ "engines": {
223
+ "node": ">=22.0.0"
224
+ }
225
+ },
226
+ "node_modules/lemonlog": {
227
+ "version": "1.1.4",
228
+ "resolved": "https://registry.npmjs.org/lemonlog/-/lemonlog-1.1.4.tgz",
229
+ "integrity": "sha512-NWcXK7Nl+K5E0xxzcux9ktR9hiRSSjK0xpFbuAm/qy/wg5TuIYGfg+lIDWthVvFscrSsYCPLyPk/AvxY+w7n6A==",
230
+ "license": "MIT",
231
+ "dependencies": {
232
+ "debug": "^4.1.1"
233
+ }
234
+ },
235
+ "node_modules/mimic-response": {
236
+ "version": "3.1.0",
237
+ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
238
+ "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
239
+ "license": "MIT",
240
+ "engines": {
241
+ "node": ">=10"
242
+ },
243
+ "funding": {
244
+ "url": "https://github.com/sponsors/sindresorhus"
245
+ }
246
+ },
247
+ "node_modules/minimist": {
248
+ "version": "1.2.8",
249
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
250
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
251
+ "license": "MIT",
252
+ "funding": {
253
+ "url": "https://github.com/sponsors/ljharb"
254
+ }
255
+ },
256
+ "node_modules/mkdirp-classic": {
257
+ "version": "0.5.3",
258
+ "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
259
+ "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==",
260
+ "license": "MIT"
261
+ },
262
+ "node_modules/ms": {
263
+ "version": "2.1.3",
264
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
265
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
266
+ },
267
+ "node_modules/napi-build-utils": {
268
+ "version": "2.0.0",
269
+ "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz",
270
+ "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==",
271
+ "license": "MIT"
272
+ },
273
+ "node_modules/node-abi": {
274
+ "version": "3.85.0",
275
+ "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.85.0.tgz",
276
+ "integrity": "sha512-zsFhmbkAzwhTft6nd3VxcG0cvJsT70rL+BIGHWVq5fi6MwGrHwzqKaxXE+Hl2GmnGItnDKPPkO5/LQqjVkIdFg==",
277
+ "license": "MIT",
278
+ "dependencies": {
279
+ "semver": "^7.3.5"
280
+ },
281
+ "engines": {
282
+ "node": ">=10"
283
+ }
284
+ },
285
+ "node_modules/once": {
286
+ "version": "1.4.0",
287
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
288
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
289
+ "license": "ISC",
290
+ "dependencies": {
291
+ "wrappy": "1"
292
+ }
293
+ },
294
+ "node_modules/pathmix": {
295
+ "version": "1.0.0",
296
+ "resolved": "https://registry.npmjs.org/pathmix/-/pathmix-1.0.0.tgz",
297
+ "integrity": "sha512-oLbvoOKuyV6TjkKLEYqH5O+q+d+qZwtRNzMrBI93IsCYN0liDw8W8aZq3BPvIaF4jJU+igeO/1p6lCwFfy8E5Q==",
298
+ "license": "ISC",
299
+ "engines": {
300
+ "node": ">=16.0.0"
301
+ }
302
+ },
303
+ "node_modules/prebuild-install": {
304
+ "version": "7.1.3",
305
+ "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz",
306
+ "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==",
307
+ "license": "MIT",
308
+ "dependencies": {
309
+ "detect-libc": "^2.0.0",
310
+ "expand-template": "^2.0.3",
311
+ "github-from-package": "0.0.0",
312
+ "minimist": "^1.2.3",
313
+ "mkdirp-classic": "^0.5.3",
314
+ "napi-build-utils": "^2.0.0",
315
+ "node-abi": "^3.3.0",
316
+ "pump": "^3.0.0",
317
+ "rc": "^1.2.7",
318
+ "simple-get": "^4.0.0",
319
+ "tar-fs": "^2.0.0",
320
+ "tunnel-agent": "^0.6.0"
321
+ },
322
+ "bin": {
323
+ "prebuild-install": "bin.js"
324
+ },
325
+ "engines": {
326
+ "node": ">=10"
327
+ }
328
+ },
329
+ "node_modules/pump": {
330
+ "version": "3.0.3",
331
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz",
332
+ "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==",
333
+ "license": "MIT",
334
+ "dependencies": {
335
+ "end-of-stream": "^1.1.0",
336
+ "once": "^1.3.1"
337
+ }
338
+ },
339
+ "node_modules/rc": {
340
+ "version": "1.2.8",
341
+ "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
342
+ "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
343
+ "license": "(BSD-2-Clause OR MIT OR Apache-2.0)",
344
+ "dependencies": {
345
+ "deep-extend": "^0.6.0",
346
+ "ini": "~1.3.0",
347
+ "minimist": "^1.2.0",
348
+ "strip-json-comments": "~2.0.1"
349
+ },
350
+ "bin": {
351
+ "rc": "cli.js"
352
+ }
353
+ },
354
+ "node_modules/readable-stream": {
355
+ "version": "3.6.2",
356
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
357
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
358
+ "license": "MIT",
359
+ "dependencies": {
360
+ "inherits": "^2.0.3",
361
+ "string_decoder": "^1.1.1",
362
+ "util-deprecate": "^1.0.1"
363
+ },
364
+ "engines": {
365
+ "node": ">= 6"
366
+ }
367
+ },
368
+ "node_modules/safe-buffer": {
369
+ "version": "5.2.1",
370
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
371
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
372
+ "funding": [
373
+ {
374
+ "type": "github",
375
+ "url": "https://github.com/sponsors/feross"
376
+ },
377
+ {
378
+ "type": "patreon",
379
+ "url": "https://www.patreon.com/feross"
380
+ },
381
+ {
382
+ "type": "consulting",
383
+ "url": "https://feross.org/support"
384
+ }
385
+ ],
386
+ "license": "MIT"
387
+ },
388
+ "node_modules/semver": {
389
+ "version": "7.7.3",
390
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
391
+ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
392
+ "license": "ISC",
393
+ "bin": {
394
+ "semver": "bin/semver.js"
395
+ },
396
+ "engines": {
397
+ "node": ">=10"
398
+ }
399
+ },
400
+ "node_modules/simple-concat": {
401
+ "version": "1.0.1",
402
+ "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
403
+ "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
404
+ "funding": [
405
+ {
406
+ "type": "github",
407
+ "url": "https://github.com/sponsors/feross"
408
+ },
409
+ {
410
+ "type": "patreon",
411
+ "url": "https://www.patreon.com/feross"
412
+ },
413
+ {
414
+ "type": "consulting",
415
+ "url": "https://feross.org/support"
416
+ }
417
+ ],
418
+ "license": "MIT"
419
+ },
420
+ "node_modules/simple-get": {
421
+ "version": "4.0.1",
422
+ "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz",
423
+ "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
424
+ "funding": [
425
+ {
426
+ "type": "github",
427
+ "url": "https://github.com/sponsors/feross"
428
+ },
429
+ {
430
+ "type": "patreon",
431
+ "url": "https://www.patreon.com/feross"
432
+ },
433
+ {
434
+ "type": "consulting",
435
+ "url": "https://feross.org/support"
436
+ }
437
+ ],
438
+ "license": "MIT",
439
+ "dependencies": {
440
+ "decompress-response": "^6.0.0",
441
+ "once": "^1.3.1",
442
+ "simple-concat": "^1.0.0"
443
+ }
444
+ },
445
+ "node_modules/string_decoder": {
446
+ "version": "1.3.0",
447
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
448
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
449
+ "license": "MIT",
450
+ "dependencies": {
451
+ "safe-buffer": "~5.2.0"
452
+ }
453
+ },
454
+ "node_modules/strip-json-comments": {
455
+ "version": "2.0.1",
456
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
457
+ "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
458
+ "license": "MIT",
459
+ "engines": {
460
+ "node": ">=0.10.0"
461
+ }
462
+ },
463
+ "node_modules/tar-fs": {
464
+ "version": "2.1.4",
465
+ "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz",
466
+ "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==",
467
+ "license": "MIT",
468
+ "dependencies": {
469
+ "chownr": "^1.1.1",
470
+ "mkdirp-classic": "^0.5.2",
471
+ "pump": "^3.0.0",
472
+ "tar-stream": "^2.1.4"
473
+ }
474
+ },
475
+ "node_modules/tar-stream": {
476
+ "version": "2.2.0",
477
+ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
478
+ "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
479
+ "license": "MIT",
480
+ "dependencies": {
481
+ "bl": "^4.0.3",
482
+ "end-of-stream": "^1.4.1",
483
+ "fs-constants": "^1.0.0",
484
+ "inherits": "^2.0.3",
485
+ "readable-stream": "^3.1.1"
486
+ },
487
+ "engines": {
488
+ "node": ">=6"
489
+ }
490
+ },
491
+ "node_modules/tunnel-agent": {
492
+ "version": "0.6.0",
493
+ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
494
+ "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
495
+ "license": "Apache-2.0",
496
+ "dependencies": {
497
+ "safe-buffer": "^5.0.1"
498
+ },
499
+ "engines": {
500
+ "node": "*"
501
+ }
502
+ },
503
+ "node_modules/util-deprecate": {
504
+ "version": "1.0.2",
505
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
506
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
507
+ "license": "MIT"
508
+ },
509
+ "node_modules/wrappy": {
510
+ "version": "1.0.2",
511
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
512
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
513
+ "license": "ISC"
514
+ }
515
+ }
516
+ }
package/effort.js CHANGED
@@ -43,6 +43,8 @@ const GLM53_BANDS = [
43
43
  /** Exact model → supported OpenAI reasoning_effort values */
44
44
  const OPENAI_MODEL_LEVELS = {
45
45
  'gpt-6-astra': ['low', 'medium', 'high', 'xhigh', 'max'],
46
+ 'gpt-6-sol': ['none', 'low', 'medium', 'high', 'xhigh', 'max'],
47
+ 'gpt-6-luna': ['none', 'low', 'medium', 'high', 'xhigh', 'max'],
46
48
  'gpt-5.6-sol': ['none', 'low', 'medium', 'high', 'xhigh', 'max'],
47
49
  'gpt-5.6-terra': ['none', 'low', 'medium', 'high', 'xhigh', 'max'],
48
50
  'gpt-5.6-luna': ['none', 'low', 'medium', 'high', 'xhigh', 'max'],
package/index.d.ts CHANGED
@@ -457,6 +457,8 @@ export declare class ModelMix {
457
457
  gpt55(args?: ModelAttachArgs): this;
458
458
  gpt55pro(args?: ModelAttachArgs): this;
459
459
  gpt6astra(args?: ModelAttachArgs): this;
460
+ gpt6sol(args?: ModelAttachArgs): this;
461
+ gpt6luna(args?: ModelAttachArgs): this;
460
462
  gpt56sol(args?: ModelAttachArgs): this;
461
463
  gpt56terra(args?: ModelAttachArgs): this;
462
464
  gpt56luna(args?: ModelAttachArgs): this;
package/index.js CHANGED
@@ -499,6 +499,12 @@ class ModelMix {
499
499
  gpt6astra(args = {}) {
500
500
  return this._attachOpenAIWithOpenRouter('gpt-6-astra', MixOpenAIResponses, args);
501
501
  }
502
+ gpt6sol(args = {}) {
503
+ return this._attachOpenAIWithOpenRouter('gpt-6-sol', MixOpenAIResponses, args);
504
+ }
505
+ gpt6luna(args = {}) {
506
+ return this._attachOpenAIWithOpenRouter('gpt-6-luna', MixOpenAIResponses, args);
507
+ }
502
508
  gpt56sol(args = {}) {
503
509
  return this._attachOpenAIWithOpenRouter('gpt-5.6-sol', MixOpenAIResponses, args);
504
510
  }
@@ -3,7 +3,7 @@ const { normalizeEffort } = require('../effort');
3
3
  const CHAIN_MODEL_SHORTCUTS = new Set([
4
4
  'gpt5', 'gpt5mini', 'gpt5nano',
5
5
  'gpt51', 'gpt52', 'gpt54', 'gpt54mini', 'gpt54nano', 'gpt54pro',
6
- 'gpt6astra', 'gpt55', 'gpt55pro', 'gpt56sol', 'gpt56terra', 'gpt56luna',
6
+ 'gpt6astra', 'gpt6sol', 'gpt6luna', 'gpt55', 'gpt55pro', 'gpt56sol', 'gpt56terra', 'gpt56luna',
7
7
  'gptRealtime', 'gptRealtimeMini', 'gpt53codex', 'gpt53chat', 'gptOss',
8
8
  'fable51', 'fable50', 'fable5', 'opus55', 'opus50', 'opus5', 'opus48', 'opus47', 'opus46',
9
9
  'sonnet50', 'sonnet5', 'sonnet46', 'sonnet45', 'haiku45',
@@ -3,7 +3,7 @@ function normalizeOpenAIOptions(options) {
3
3
  delete options.max_tokens;
4
4
  delete options.temperature;
5
5
  }
6
- if (options.model?.includes('gpt-5') || /^(?:openai\/)?gpt-6-astra(?:-|$)/.test(options.model || '')) {
6
+ if (options.model?.includes('gpt-5') || /^(?:openai\/)?gpt-6(?:-|$)/.test(options.model || '')) {
7
7
  if (options.max_tokens) {
8
8
  options.max_completion_tokens = options.max_tokens;
9
9
  delete options.max_tokens;
@@ -11,6 +11,12 @@ const {
11
11
  validateProviderExecution
12
12
  } = require('../abort-signal');
13
13
 
14
+ /** Explicit prompt cache controls belong to the documented "GPT-5.6 and later" family. */
15
+ function isGPT56OrLater(model) {
16
+ if (typeof model !== 'string') return false;
17
+ return model.startsWith('gpt-5.6') || /^(?:openai\/)?gpt-6(?:-|$)/.test(model);
18
+ }
19
+
14
20
  function createOpenAIProviders({
15
21
  ModelMix,
16
22
  MixCustom,
@@ -38,9 +44,8 @@ function createOpenAIProviders({
38
44
  }
39
45
 
40
46
  static buildResponsesRequest(options = {}, config = {}) {
41
- const isGPT56 = typeof options.model === 'string' && options.model.startsWith('gpt-5.6');
42
47
  const input = MixOpenAIResponses.messagesToResponsesInput(options.messages, {
43
- translateNeutralCache: isGPT56
48
+ translateNeutralCache: isGPT56OrLater(options.model)
44
49
  });
45
50
  if (config.system) {
46
51
  input.unshift({ role: 'developer', content: [{ type: 'input_text', text: config.system }] });
@@ -106,7 +111,7 @@ function createOpenAIProviders({
106
111
  }
107
112
 
108
113
  static validatePromptCaching(options, input) {
109
- const isGPT56 = typeof options.model === 'string' && options.model.startsWith('gpt-5.6');
114
+ const supportsCacheControls = isGPT56OrLater(options.model);
110
115
  const cacheOptions = options.prompt_cache_options;
111
116
  const breakpoints = input.flatMap(message => Array.isArray(message.content)
112
117
  ? message.content
@@ -114,14 +119,14 @@ function createOpenAIProviders({
114
119
  .map(block => block.prompt_cache_breakpoint)
115
120
  : []);
116
121
 
117
- if (isGPT56 && options.prompt_cache_retention !== undefined) {
118
- throw new Error('GPT-5.6 does not support prompt_cache_retention; use prompt_cache_options.ttl instead.');
122
+ if (supportsCacheControls && options.prompt_cache_retention !== undefined) {
123
+ throw new Error('GPT-5.6 and later models do not support prompt_cache_retention; use prompt_cache_options.ttl instead.');
119
124
  }
120
- if (!isGPT56 && cacheOptions !== undefined) {
121
- throw new Error('prompt_cache_options is only supported by GPT-5.6 models.');
125
+ if (!supportsCacheControls && cacheOptions !== undefined) {
126
+ throw new Error('prompt_cache_options is only supported by GPT-5.6 and later models.');
122
127
  }
123
- if (!isGPT56 && breakpoints.length > 0) {
124
- throw new Error('prompt_cache_breakpoint is only supported by GPT-5.6 models.');
128
+ if (!supportsCacheControls && breakpoints.length > 0) {
129
+ throw new Error('prompt_cache_breakpoint is only supported by GPT-5.6 and later models.');
125
130
  }
126
131
  if (cacheOptions !== undefined) {
127
132
  if (!isPlainObject(cacheOptions)) {
@@ -22,9 +22,11 @@ function usesLongContextRates(pricing, inputTokens) {
22
22
  const MODEL_PRICING = {
23
23
  // OpenAI
24
24
  'gpt-6-astra': { input: 10.00, cachedInput: 1.00, cacheWrite: 12.50, output: 50.00, longContext: OPENAI_LONG_CONTEXT_PRICING },
25
+ 'gpt-6-sol': { input: 2.00, cachedInput: 0.20, cacheWrite: 2.50, output: 10.00, longContext: OPENAI_LONG_CONTEXT_PRICING },
26
+ 'gpt-6-luna': { input: 0.10, cachedInput: 0.01, cacheWrite: 0.125, output: 0.50, longContext: OPENAI_LONG_CONTEXT_PRICING },
25
27
  'gpt-realtime-mini': { input: 0.60, cachedInput: 0.06, output: 2.40 },
26
28
  'gpt-realtime': { input: 4.00, cachedInput: 0.40, output: 16.00 },
27
- 'gpt-5.6-sol': { input: 5.00, cachedInput: 0.50, cacheWrite: 6.25, output: 30.00, longContext: OPENAI_LONG_CONTEXT_PRICING },
29
+ 'gpt-5.6-sol': { input: 4.00, cachedInput: 0.40, cacheWrite: 5.00, output: 20.00, longContext: OPENAI_LONG_CONTEXT_PRICING },
28
30
  'gpt-5.6-terra': { input: 2.00, cachedInput: 0.20, cacheWrite: 2.50, output: 12.00, longContext: OPENAI_LONG_CONTEXT_PRICING },
29
31
  'gpt-5.6-luna': { input: 0.20, cachedInput: 0.02, cacheWrite: 0.25, output: 1.20, longContext: OPENAI_LONG_CONTEXT_PRICING },
30
32
  'gpt-5.5-pro': { input: 30.00, output: 180.00 },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "modelmix",
3
- "version": "5.2.3",
3
+ "version": "5.3.0",
4
4
  "description": "🧬 Reliable interface with automatic fallback for AI LLMs.",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -82,5 +82,6 @@
82
82
  "test:benchmark": "mocha plugins/benchmark/test/**/*.test.js --timeout 10000 --require test/setup.js",
83
83
  "test:rlm": "mocha plugins/rlm/test/**/*.test.js --timeout 10000 --require test/setup.js",
84
84
  "test:offline": "mocha test/abort.test.js test/json.test.js test/fallback.test.js test/templates.test.js test/images.test.js test/bottleneck.test.js test/tokens.test.js test/history.test.js test/anthropic.test.js test/effort.test.js test/grok.test.js test/google.test.js test/moderation.test.js test/plugins.test.js plugins/skills/test/**/*.test.js plugins/benchmark/test/**/*.test.js plugins/rlm/test/**/*.test.js --timeout 10000 --require test/setup.js"
85
- }
86
- }
85
+ },
86
+ "packageManager": "pnpm@12.4.1"
87
+ }
@@ -164,7 +164,7 @@ ModelMix.new({ config: { effort: 80 } })
164
164
  | DeepSeek V4 | off | `low`↑ | `high`↑ | `high`↑ | `max`↑ | — |
165
165
  | MiniMax M3 | off | adaptive | adaptive | adaptive | adaptive | adaptive |
166
166
 
167
- \* GPT-6 Astra maps 0–39 / 40–59 / 60–79 / 80–99 / 100 to `low` / `medium` / `high` / `xhigh` / `max`. GPT-5.6 maps `100` to `max`; 80–99 remains `xhigh`. Qwen 3.8 27B and Flash map 0–39 / 40–79 / 80–100 to `low` / `medium` / `xhigh`; Qwen 3.8 Flash is the managed production version based on Flash-Next. GLM 5.3 and GLM 5.3 Flash require reasoning and map those bands to `low` / `high` / `max`. Gemini bands: 0–24 / 25–49 / 50–74 / 75–100. Gemini 3.8 Flash and 3.7 Flash support only `low` / `medium` / `high`, so the first two bands clamp to `low`; `-1` keeps their 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.
167
+ \* GPT-6 Astra maps 0–39 / 40–59 / 60–79 / 80–99 / 100 to `low` / `medium` / `high` / `xhigh` / `max`. GPT-6 Sol, GPT-6 Luna, and GPT-5.6 map `100` to `max` and keep `xhigh` for 80–99, with OpenAI's `none` band at 0–19. Qwen 3.8 27B and Flash map 0–39 / 40–79 / 80–100 to `low` / `medium` / `xhigh`; Qwen 3.8 Flash is the managed production version based on Flash-Next. GLM 5.3 and GLM 5.3 Flash require reasoning and map those bands to `low` / `high` / `max`. Gemini bands: 0–24 / 25–49 / 50–74 / 75–100. Gemini 3.8 Flash and 3.7 Flash support only `low` / `medium` / `high`, so the first two bands clamp to `low`; `-1` keeps their 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.
168
168
 
169
169
  ## Available Model Shorthands
170
170
 
@@ -172,7 +172,7 @@ ModelMix.new({ config: { effort: 80 } })
172
172
 
173
173
  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.
174
174
 
175
- `gpt6astra()` `gpt56sol()` `gpt56terra()` `gpt56luna()` `gpt55()` `gpt55pro()` `gpt54()` `gpt54mini()` `gpt54nano()` `gpt54pro()` `gpt53codex()` `gpt53chat()` `gpt52()` `gpt51()` `gpt5()` `gpt5mini()` `gpt5nano()` `gptRealtime()` `gptRealtimeMini()` `gptOss()`
175
+ `gpt6astra()` `gpt6sol()` `gpt6luna()` `gpt56sol()` `gpt56terra()` `gpt56luna()` `gpt55()` `gpt55pro()` `gpt54()` `gpt54mini()` `gpt54nano()` `gpt54pro()` `gpt53codex()` `gpt53chat()` `gpt52()` `gpt51()` `gpt5()` `gpt5mini()` `gpt5nano()` `gptRealtime()` `gptRealtimeMini()` `gptOss()`
176
176
 
177
177
  Every textual GPT-5 and GPT-6 shortcut registers only the official OpenAI model by default. Pass `mix: { openrouter: true }` to `ModelMix.new()` or to an individual shortcut to append its `openai/*` OpenRouter route as a fallback. `gpt53chat()` uses `gpt-5.3-chat-latest` officially and `openai/gpt-5.3-chat` through OpenRouter. Both API keys are required when that fallback is enabled. Realtime shortcuts remain official-only.
178
178
 
@@ -411,7 +411,7 @@ console.log(model.lastRaw.think); // reasoning content (if available)
411
411
  console.log(model.lastRaw.response); // raw API response
412
412
  ```
413
413
 
414
- ### GPT-5.6 explicit prompt caching
414
+ ### GPT-5.6 and GPT-6 explicit prompt caching
415
415
 
416
416
  ```javascript
417
417
  const model = ModelMix.new()
@@ -434,9 +434,9 @@ const model = ModelMix.new()
434
434
  const result = await model.raw();
435
435
  ```
436
436
 
437
- `cache: { breakpoint: true }` is provider-neutral: GPT-5.6 receives `prompt_cache_breakpoint`, Anthropic receives `cache_control`, and unsupported providers omit it. Keep native request policies inside each model shorthand so they do not leak across fallbacks. Anthropic usage separates `cacheWrite5m` and `cacheWrite1h`; `cacheWrite` stays as their compatible aggregate.
437
+ `cache: { breakpoint: true }` is provider-neutral: GPT-5.6 and GPT-6 receive `prompt_cache_breakpoint`, Anthropic receives `cache_control`, and unsupported providers omit it. Keep native request policies inside each model shorthand so they do not leak across fallbacks. Anthropic usage separates `cacheWrite5m` and `cacheWrite1h`; `cacheWrite` stays as their compatible aggregate.
438
438
 
439
- GPT-5.6 replaces `prompt_cache_retention` with `prompt_cache_options.ttl`. Explicit breakpoints also work on image methods and Responses-native `input_text`, `input_image`, and `input_file` blocks. Prompts need at least 1,024 tokens to be cached. Requests over 272K input tokens use 2× input and 1.5× output prices for the complete request; ModelMix applies these multipliers to `cost`, `costBreakdown`, and cache economics.
439
+ GPT-5.6 and GPT-6 replace `prompt_cache_retention` with `prompt_cache_options.ttl`. Explicit breakpoints also work on image methods and Responses-native `input_text`, `input_image`, and `input_file` blocks. Prompts need at least 1,024 tokens to be cached. Requests over 272K input tokens use 2× input and 1.5× output prices for the complete request; ModelMix applies these multipliers to `cost`, `costBreakdown`, and cache economics.
440
440
 
441
441
  ### Add images
442
442
 
@@ -90,6 +90,15 @@ describe('Unified effort scale', () => {
90
90
  }
91
91
  });
92
92
 
93
+ it('maps GPT-6 Sol and Luna effort from none to max', () => {
94
+ for (const model of ['gpt-6-sol', 'gpt-6-luna']) {
95
+ for (const [effort, level] of [[0, 'none'], [20, 'low'], [40, 'medium'], [60, 'high'], [99, 'xhigh'], [100, 'max']]) {
96
+ expect(mapEffort('openai', effort, model)).to.deep.equal({ reasoning_effort: level });
97
+ }
98
+ expect(mapEffort('openai', 100, `openai/${model}`)).to.deep.equal({ reasoning_effort: 'max' });
99
+ }
100
+ });
101
+
93
102
  it('sets OpenAI adaptive only when supported (otherwise no-op)', () => {
94
103
  expect(mapEffort('openai', -1)).to.equal(null);
95
104
  expect(mapEffort('openai', -1, 'gpt-5.2')).to.equal(null);
@@ -115,6 +115,8 @@ describe('Provider Fallback Chain Tests', () => {
115
115
  expect(ModelMix.new().mix.openrouter).to.equal(false);
116
116
  const shortcuts = [
117
117
  ['gpt6astra', 'gpt-6-astra', 'openai/gpt-6-astra', MixOpenAIResponses],
118
+ ['gpt6sol', 'gpt-6-sol', 'openai/gpt-6-sol', MixOpenAIResponses],
119
+ ['gpt6luna', 'gpt-6-luna', 'openai/gpt-6-luna', MixOpenAIResponses],
118
120
  ['gpt5', 'gpt-5', 'openai/gpt-5', MixOpenAI],
119
121
  ['gpt5mini', 'gpt-5-mini', 'openai/gpt-5-mini', MixOpenAI],
120
122
  ['gpt5nano', 'gpt-5-nano', 'openai/gpt-5-nano', MixOpenAI],
@@ -232,6 +234,48 @@ describe('Provider Fallback Chain Tests', () => {
232
234
  expect(openRouterRequest).to.not.have.property('temperature');
233
235
  });
234
236
 
237
+ it('should fallback from the official GPT-6 Sol and Luna endpoints to OpenRouter', async () => {
238
+ const cases = [
239
+ ['gpt6sol', 'gpt-6-sol', 'Hello from GPT-6 Sol through OpenRouter!'],
240
+ ['gpt6luna', 'gpt-6-luna', 'Hello from GPT-6 Luna through OpenRouter!']
241
+ ];
242
+
243
+ for (const [shortcut, officialKey, content] of cases) {
244
+ let openRouterRequest;
245
+ const instance = ModelMix.new().effort(100);
246
+ instance[shortcut]({ mix: { openrouter: true } }).addText('Hello');
247
+
248
+ nock('https://api.openai.com')
249
+ .post('/v1/responses', body => {
250
+ expect(body.model).to.equal(officialKey);
251
+ expect(body.reasoning).to.deep.equal({ effort: 'max' });
252
+ expect(body.max_output_tokens).to.equal(8192);
253
+ expect(body).to.not.have.property('temperature');
254
+ return true;
255
+ })
256
+ .reply(503, { error: 'Service unavailable' });
257
+
258
+ nock('https://openrouter.ai')
259
+ .post('/api/v1/chat/completions', body => {
260
+ openRouterRequest = body;
261
+ return true;
262
+ })
263
+ .reply(200, {
264
+ choices: [{
265
+ message: {
266
+ role: 'assistant',
267
+ content
268
+ }
269
+ }]
270
+ });
271
+
272
+ expect(await instance.message()).to.equal(content);
273
+ expect(openRouterRequest.model).to.equal(`openai/${officialKey}`);
274
+ expect(openRouterRequest.max_completion_tokens).to.equal(8192);
275
+ expect(openRouterRequest).to.not.have.property('temperature');
276
+ }
277
+ });
278
+
235
279
  it('should keep the default fable51 chain on Anthropic', () => {
236
280
  model.chain('fable51@80');
237
281
 
@@ -252,29 +252,35 @@ describe('Token Usage Tracking', () => {
252
252
  ]);
253
253
  });
254
254
 
255
- it('should translate neutral cache breakpoints for GPT-5.6 and filter them for older models', async function () {
255
+ it('should translate neutral cache breakpoints for GPT-5.6 and GPT-6 and filter them for older models', async function () {
256
256
  const breakpoint = { mode: 'explicit' };
257
257
  const model = ModelMix.new()
258
258
  .addText('Stable text', { role: 'developer', cache: { breakpoint: true } })
259
259
  .addImageFromUrl('data:image/png;base64,AAAA', { cache: { breakpoint: true } });
260
260
  const messages = await model.prepareMessages();
261
- const gpt56Request = MixOpenAIResponses.buildResponsesRequest({
262
- model: 'gpt-5.6-luna',
263
- messages,
264
- prompt_cache_options: { mode: 'explicit', ttl: '30m' }
265
- });
266
- const olderRequest = MixOpenAIResponses.buildResponsesRequest({
267
- model: 'gpt-5.4',
268
- messages
269
- });
270
261
 
271
262
  expect(messages[0].content[0]).to.deep.include({ cache: { breakpoint: true } });
272
263
  expect(messages[0].content[0]).to.not.have.property('prompt_cache_breakpoint');
273
- expect(gpt56Request.input[0].content[0].prompt_cache_breakpoint).to.deep.equal(breakpoint);
274
- expect(gpt56Request.input[1].content[0]).to.deep.equal({
275
- type: 'input_image',
276
- image_url: 'data:image/png;base64,AAAA',
277
- prompt_cache_breakpoint: breakpoint
264
+
265
+ for (const key of ['gpt-5.6-luna', 'gpt-6-astra', 'gpt-6-sol', 'gpt-6-luna']) {
266
+ const request = MixOpenAIResponses.buildResponsesRequest({
267
+ model: key,
268
+ messages,
269
+ prompt_cache_options: { mode: 'explicit', ttl: '30m' }
270
+ });
271
+
272
+ expect(request.prompt_cache_options).to.deep.equal({ mode: 'explicit', ttl: '30m' });
273
+ expect(request.input[0].content[0].prompt_cache_breakpoint).to.deep.equal(breakpoint);
274
+ expect(request.input[1].content[0]).to.deep.equal({
275
+ type: 'input_image',
276
+ image_url: 'data:image/png;base64,AAAA',
277
+ prompt_cache_breakpoint: breakpoint
278
+ });
279
+ }
280
+
281
+ const olderRequest = MixOpenAIResponses.buildResponsesRequest({
282
+ model: 'gpt-5.4',
283
+ messages
278
284
  });
279
285
  expect(olderRequest.input[0].content[0]).to.not.have.property('cache');
280
286
  expect(olderRequest.input[0].content[0]).to.not.have.property('prompt_cache_breakpoint');
@@ -347,6 +353,12 @@ describe('Token Usage Tracking', () => {
347
353
  prompt_cache_retention: '24h'
348
354
  })).to.throw('prompt_cache_options.ttl');
349
355
 
356
+ expect(() => MixOpenAIResponses.buildResponsesRequest({
357
+ model: 'gpt-6-sol',
358
+ messages: [{ role: 'user', content: 'Hi' }],
359
+ prompt_cache_retention: '24h'
360
+ })).to.throw('prompt_cache_options.ttl');
361
+
350
362
  expect(() => MixOpenAIResponses.buildResponsesRequest({
351
363
  model: 'gpt-5.4',
352
364
  messages: [{ role: 'user', content: 'Hi' }],
@@ -417,6 +429,35 @@ describe('Token Usage Tracking', () => {
417
429
  }
418
430
  });
419
431
 
432
+ it('should register GPT-6 Sol and Luna shortcuts with OpenAI Responses provider', function () {
433
+ const model = ModelMix.new({ mix: { openrouter: true } })
434
+ .gpt6sol()
435
+ .gpt6luna();
436
+
437
+ expect(model.models.map(({ key }) => key)).to.deep.equal([
438
+ 'gpt-6-sol',
439
+ 'openai/gpt-6-sol',
440
+ 'gpt-6-luna',
441
+ 'openai/gpt-6-luna'
442
+ ]);
443
+ expect(model.models[0].provider).to.be.instanceOf(MixOpenAIResponses);
444
+ expect(model.models[1].provider).to.be.instanceOf(MixOpenRouter);
445
+ expect(model.models[2].provider).to.be.instanceOf(MixOpenAIResponses);
446
+ expect(model.models[3].provider).to.be.instanceOf(MixOpenRouter);
447
+ });
448
+
449
+ it('should account for GPT-6 Sol and Luna cache usage and the long-context boundary', function () {
450
+ const tokens = { input: 272_000, cached: 100_000, cacheWrite: 20_000, output: 1_000 };
451
+ for (const key of ['gpt-6-sol', 'openai/gpt-6-sol']) {
452
+ expect(ModelMix.calculateCost(key, tokens)).to.be.closeTo(0.384, 1e-10);
453
+ expect(ModelMix.calculateCost(key, { ...tokens, input: 272_001 })).to.be.closeTo(0.763004, 1e-10);
454
+ }
455
+ for (const key of ['gpt-6-luna', 'openai/gpt-6-luna']) {
456
+ expect(ModelMix.calculateCost(key, tokens)).to.be.closeTo(0.0192, 1e-10);
457
+ expect(ModelMix.calculateCost(key, { ...tokens, input: 272_001 })).to.be.closeTo(0.0381502, 1e-10);
458
+ }
459
+ });
460
+
420
461
  it('should register GPT-5.6 shortcuts with OpenAI Responses provider', function () {
421
462
  const model = ModelMix.new({ mix: { openrouter: true } })
422
463
  .gpt56sol()
@@ -437,8 +478,8 @@ describe('Token Usage Tracking', () => {
437
478
  expect(model.models[3].provider).to.be.instanceOf(MixOpenRouter);
438
479
  expect(model.models[4].provider).to.be.instanceOf(MixOpenAIResponses);
439
480
  expect(model.models[5].provider).to.be.instanceOf(MixOpenRouter);
440
- expect(ModelMix.calculateCost('gpt-5.6-sol', { input: 1_000_000, output: 1_000_000 })).to.equal(55);
441
- expect(ModelMix.calculateCost('openai/gpt-5.6-sol', { input: 1_000_000, output: 1_000_000 })).to.equal(55);
481
+ expect(ModelMix.calculateCost('gpt-5.6-sol', { input: 1_000_000, output: 1_000_000 })).to.equal(38);
482
+ expect(ModelMix.calculateCost('openai/gpt-5.6-sol', { input: 1_000_000, output: 1_000_000 })).to.equal(38);
442
483
  expect(ModelMix.calculateCost('gpt-5.6-terra', { input: 1_000_000, output: 1_000_000 })).to.equal(22);
443
484
  expect(ModelMix.calculateCost('gpt-5.6-luna', { input: 1_000_000, output: 1_000_000 })).to.equal(2.2);
444
485
  expect(ModelMix.calculateCost('openai/gpt-5.3-chat', { input: 1_000_000, output: 1_000_000 })).to.equal(15.75);
package/.gitignore DELETED
@@ -1,138 +0,0 @@
1
- # Logs
2
- logs
3
- *.log
4
- npm-debug.log*
5
- yarn-debug.log*
6
- yarn-error.log*
7
- lerna-debug.log*
8
- .pnpm-debug.log*
9
-
10
- # Diagnostic reports (https://nodejs.org/api/report.html)
11
- report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
12
-
13
- # Runtime data
14
- pids
15
- *.pid
16
- *.seed
17
- *.pid.lock
18
-
19
- # Directory for instrumented libs generated by jscoverage/JSCover
20
- lib-cov
21
-
22
- # Coverage directory used by tools like istanbul
23
- coverage
24
- *.lcov
25
-
26
- # nyc test coverage
27
- .nyc_output
28
-
29
- # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
30
- .grunt
31
-
32
- # Bower dependency directory (https://bower.io/)
33
- bower_components
34
-
35
- # node-waf configuration
36
- .lock-wscript
37
-
38
- # Compiled binary addons (https://nodejs.org/api/addons.html)
39
- build/Release
40
-
41
- # Dependency directories
42
- node_modules/
43
- jspm_packages/
44
-
45
- # Snowpack dependency directory (https://snowpack.dev/)
46
- web_modules/
47
-
48
- # TypeScript cache
49
- *.tsbuildinfo
50
-
51
- # Optional npm cache directory
52
- .npm
53
-
54
- # Optional eslint cache
55
- .eslintcache
56
-
57
- # Optional stylelint cache
58
- .stylelintcache
59
-
60
- # Microbundle cache
61
- .rpt2_cache/
62
- .rts2_cache_cjs/
63
- .rts2_cache_es/
64
- .rts2_cache_umd/
65
-
66
- # Optional REPL history
67
- .node_repl_history
68
-
69
- # Output of 'npm pack'
70
- *.tgz
71
-
72
- # Yarn Integrity file
73
- .yarn-integrity
74
-
75
- # dotenv environment variable files
76
- .env
77
- .env.development.local
78
- .env.test.local
79
- .env.production.local
80
- .env.local
81
-
82
- # parcel-bundler cache (https://parceljs.org/)
83
- .cache
84
- .parcel-cache
85
-
86
- # Next.js build output
87
- .next
88
- out
89
-
90
- # Nuxt.js build / generate output
91
- .nuxt
92
- dist
93
-
94
- # Gatsby files
95
- .cache/
96
- # Comment in the public line in if your project uses Gatsby and not Next.js
97
- # https://nextjs.org/blog/next-9-1#public-directory-support
98
- # public
99
-
100
- # vuepress build output
101
- .vuepress/dist
102
-
103
- # vuepress v2.x temp and cache directory
104
- .temp
105
- .cache
106
-
107
- # Docusaurus cache and generated files
108
- .docusaurus
109
-
110
- # Serverless directories
111
- .serverless/
112
-
113
- # FuseBox cache
114
- .fusebox/
115
-
116
- # DynamoDB Local files
117
- .dynamodb/
118
-
119
- # TernJS port file
120
- .tern-port
121
-
122
- # Stores VSCode versions used for testing VSCode extensions
123
- .vscode-test
124
-
125
- # yarn v2
126
- .yarn/cache
127
- .yarn/unplugged
128
- .yarn/build-state.yml
129
- .yarn/install-state.gz
130
- .pnp.*
131
-
132
- .DS_Store
133
- demo/jailbreak.mjs
134
- CLAUDE.md
135
- demo/jailbreak.js
136
- /demo/lab
137
- /demo/results
138
- /.pnpm-store/