modelmix 4.6.6 β†’ 4.6.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -90,7 +90,6 @@ console.log(ETH.price);
90
90
  ModelMix.new()
91
91
  .gptOss()
92
92
  .kimiK25think()
93
- .deepseekR1()
94
93
  .hermes3()
95
94
  .addText('What is the capital of France?');
96
95
  ```
@@ -102,6 +101,32 @@ This pattern allows you to:
102
101
  - Track token usage across all providers
103
102
  - Keep your code clean and maintainable
104
103
 
104
+ ## πŸŽ›οΈ Unified Effort Scale
105
+
106
+ Control reasoning depth with one ModelMix policy value (`-1` adaptive, or `0`–`100`). It lives **outside** native `options` and is mapped to each provider’s effort API at request time.
107
+
108
+ ```javascript
109
+ // In config (ModelMix.new or per-model shorthand)
110
+ ModelMix.new({ config: { effort: 50 } }).sonnet46().addText('...').message();
111
+ ModelMix.new().deepseekV4Flash({ config: { effort: 100 } }).addText('...').message();
112
+
113
+ // Fluent
114
+ ModelMix.new().effort(-1).minimaxM3().addText('...').message();
115
+ ```
116
+
117
+ **Native wins:** if you already set a provider-native field (`reasoning_effort`, `output_config.effort`, `thinkingConfig`, etc.), unified `effort` is ignored for that request.
118
+
119
+ | Scale | OpenAI | Anthropic | Gemini 3+ | DeepSeek V4 | MiniMax M3 |
120
+ |------|--------|-----------|-----------|-------------|------------|
121
+ | 0–19 / 0–24* | `none` | `low` | `minimal` | thinking `disabled` | `thinking.disabled` |
122
+ | 20–39 / 25–49* | `low` | `medium` | `low` | `low` + thinking on | `thinking.adaptive` |
123
+ | 40–59 / 50–74* | `medium` | `high` | `medium` | `high` + thinking on | `thinking.adaptive` |
124
+ | 60–79 / 75–100* | `high` | `xhigh` | `high` | `high` + thinking on | `thinking.adaptive` |
125
+ | 80–100 | `xhigh` | `max` | β€” | `max` + thinking on | `thinking.adaptive` |
126
+ | `-1` | no-op (no adaptive API) | `thinking.type = adaptive` | `thinkingBudget: -1` | no-op (no adaptive API) | `thinking.type = adaptive` |
127
+
128
+ \* Gemini bands use 0–24 / 25–49 / 50–74 / 75–100. Gemini 2.5 maps 0–100 to `thinkingBudget` (0β†’0, 100β†’model max). `-1` sets the provider’s adaptive/dynamic control when it exists; otherwise it is a no-op. Levels are clamped to what each model supports. Providers without effort control are a no-op.
129
+
105
130
  ## πŸ”§ Model Context Protocol (MCP) Integration
106
131
 
107
132
  ModelMix makes it incredibly easy to enhance your AI models with powerful capabilities through the Model Context Protocol. With just a few lines of code, you can add features like web search, code execution, or any custom functionality to your models.
@@ -170,6 +195,7 @@ Here's a comprehensive list of available methods:
170
195
  | `grok420[think]()` | Grok | grok-4.20-0309 | [\$1.25 / \$2.50][6] |
171
196
  | `grok41[think]()` | Grok | grok-4-1-fast | [\$0.20 / \$0.50][6] |
172
197
  | `qwen36plus()` | Fireworks/Together | qwen3p6-plus / Qwen3.6-Plus | [\$0.50 / \$3.00][10] |
198
+ | `deepseekV4Flash()` | Fireworks | models/deepseek-v4-flash | [\$0.14 / \$0.28][10] |
173
199
  | `deepseekV4Pro()` | Fireworks | models/deepseek-v4-pro | [\$1.74 / \$3.48][10] |
174
200
  | `GLM52()` | Together | zai-org/GLM-5.2 | [\$1.40 / \$4.40][7] |
175
201
  | `GLM51()` | Fireworks | models/glm-5p1 | [\$1.05 / \$3.50][10] |
@@ -195,7 +221,7 @@ Here's a comprehensive list of available methods:
195
221
  [11]: https://platform.kimi.ai/docs/guide/kimi-k3-pricing "Kimi K3 Pricing"
196
222
 
197
223
  Each method accepts optional `options`, `config`, and (for multi-provider methods) `mix` parameters to customize behavior.
198
- For NVIDIA on DeepSeek V4 Pro, use `deepseekV4Pro({ mix: { nvidia: true } })`.
224
+ For NVIDIA on DeepSeek V4 Flash/Pro, use `deepseekV4Flash({ mix: { nvidia: true } })` or `deepseekV4Pro({ mix: { nvidia: true } })`.
199
225
  For Together on Qwen 3.6 Plus, use `qwen36plus({ mix: { fireworks: false, together: true } })`.
200
226
  For OpenRouter instead of Moonshot's native API, use `kimiK3({ mix: { moonshot: false, openrouter: true } })`.
201
227
 
@@ -617,6 +643,7 @@ new ModelMix(args = { options: {}, config: {} })
617
643
  - **config**: This object contains configuration settings that control the behavior of the `ModelMix` instance. These settings can also be overridden for specific model instances. Examples of configuration settings include:
618
644
  - `system`: Sets the default system message for the model, e.g., "You are an assistant."
619
645
  - `max_history`: Limits the number of historical messages to retain, e.g., 1.
646
+ - `effort`: Unified reasoning effort (`-1` adaptive, or `0`–`100`). Not a native provider field β€” use `config.effort` or `.effort(n)`.
620
647
  - `roundRobin`: When `true`, rotates through attached models on each request for load balancing. When `false` (default), uses fallback mode where models are tried sequentially only if previous ones fail.
621
648
  - `bottleneck`: Configures the rate limiting behavior using Bottleneck. For example:
622
649
  - `maxConcurrent`: Maximum number of concurrent requests
@@ -637,6 +664,7 @@ new ModelMix(args = { options: {}, config: {} })
637
664
  - `attach(modelKey, modelInstance)`: Attaches a model instance to the `ModelMix`.
638
665
  - `new()`: `static` Creates a new `ModelMix`.
639
666
  - `new()`: Creates a new `ModelMix` using instance setup.
667
+ - `effort(n)`: Sets unified effort (`-1` or `0`–`100`) on `config.effort`.
640
668
 
641
669
  - `setSystem(text)`: Sets the system prompt.
642
670
  - `setSystemFromFile(filePath)`: Sets the system prompt from a file.
package/demo/fireworks.js CHANGED
@@ -1,14 +1,12 @@
1
1
  import { ModelMix } from '../index.js';
2
- try { process.loadEnvFile(); } catch {}
2
+ try { process.loadEnvFile(); } catch { }
3
3
 
4
4
  async function main() {
5
5
  try {
6
6
  const ai = ModelMix.new();
7
7
 
8
- const response = await ai
9
- .GLM46()
10
- .deepseekV32()
11
- .GLM47()
8
+ const response = await ai.effort(50)
9
+ .deepseekV4Flash()
12
10
  .addText('What is the capital of France?')
13
11
  .message();
14
12
 
@@ -20,4 +18,3 @@ async function main() {
20
18
  }
21
19
 
22
20
  main();
23
-
package/demo/free.js CHANGED
@@ -4,7 +4,6 @@ try { process.loadEnvFile(); } catch {}
4
4
  const ai = ModelMix.new({ config: { debug: 2 } })
5
5
  .gptOss()
6
6
  .kimiK25think()
7
- .deepseekR1()
8
7
  .hermes3()
9
8
  .addText('What is the capital of France?');
10
9
 
package/demo/groq.js CHANGED
@@ -13,7 +13,7 @@ const mmix = new ModelMix({
13
13
  }
14
14
  });
15
15
 
16
- mmix.attach('deepseek-r1-distill-llama-70b', new MixGroq({
16
+ mmix.attach('openai/gpt-oss-120b', new MixGroq({
17
17
  config: {
18
18
  apiKey: env.GROQ_API_KEY,
19
19
  }
package/demo/nvidia.js CHANGED
@@ -2,7 +2,7 @@ import { ModelMix } from '../index.js';
2
2
  try { process.loadEnvFile(); } catch { }
3
3
 
4
4
  const model = ModelMix.new()
5
- .deepseekV4Flash()
5
+ .deepseekV4Flash({ mix: { nvidia: true } })
6
6
  .addText("Create exactly 5 characters for a narrative game.")
7
7
 
8
8
  const jsonResult = await model.json([], [{
package/demo/opus5.js ADDED
@@ -0,0 +1,23 @@
1
+ import { ModelMix } from '../index.js';
2
+ try { process.loadEnvFile(); } catch {}
3
+
4
+
5
+ const mmix = new ModelMix({
6
+ config: {
7
+ debug: 3,
8
+ }
9
+ });
10
+
11
+ console.log("\n" + '--------| opus5() |--------');
12
+
13
+ const opus = mmix.opus5();
14
+ opus.addText("Explain quantum entanglement in simple terms.");
15
+ const response = await opus.message();
16
+ console.log(response);
17
+
18
+ console.log("\n" + '--------| opus5think() |--------');
19
+
20
+ const opusThink = mmix.new().opus5think();
21
+ opusThink.addText("A bat and a ball cost $1.10 in total. The bat costs $1.00 more than the ball. How much does the ball cost?");
22
+ const thinkResponse = await opusThink.raw();
23
+ console.log(thinkResponse);
package/demo/together.js CHANGED
@@ -4,7 +4,7 @@ try { process.loadEnvFile(); } catch {}
4
4
  const setup = { config: { system: "You are ALF from Melmac." } };
5
5
 
6
6
  let r = ModelMix.new()
7
- .attach('deepseek-ai/DeepSeek-R1', new MixTogether(setup))
7
+ .attach('deepseek-ai/DeepSeek-V4-Flash', new MixTogether(setup))
8
8
  .addText('hi there')
9
9
  .addText('do you like cats?')
10
10
  .message();
package/effort.js ADDED
@@ -0,0 +1,370 @@
1
+ /**
2
+ * Unified effort scale for ModelMix.
3
+ *
4
+ * Policy value: integer -1 (adaptive) or 0..100.
5
+ * Stored in config.effort (never in native options).
6
+ * Mapped to provider-native fields only when native effort controls are absent.
7
+ */
8
+
9
+ const OPENAI_LEVELS = ['none', 'low', 'medium', 'high', 'xhigh'];
10
+ const ANTHROPIC_LEVELS = ['low', 'medium', 'high', 'xhigh', 'max'];
11
+ const GEMINI_LEVELS = ['minimal', 'low', 'medium', 'high'];
12
+
13
+ const OPENAI_BANDS = [
14
+ [0, 19, 'none'],
15
+ [20, 39, 'low'],
16
+ [40, 59, 'medium'],
17
+ [60, 79, 'high'],
18
+ [80, 100, 'xhigh'],
19
+ ];
20
+
21
+ const ANTHROPIC_BANDS = [
22
+ [0, 19, 'low'],
23
+ [20, 39, 'medium'],
24
+ [40, 59, 'high'],
25
+ [60, 79, 'xhigh'],
26
+ [80, 100, 'max'],
27
+ ];
28
+
29
+ const GEMINI_BANDS = [
30
+ [0, 24, 'minimal'],
31
+ [25, 49, 'low'],
32
+ [50, 74, 'medium'],
33
+ [75, 100, 'high'],
34
+ ];
35
+
36
+ /** Exact model β†’ supported OpenAI reasoning_effort values */
37
+ const OPENAI_MODEL_LEVELS = {
38
+ 'gpt-5': ['minimal', 'low', 'medium', 'high'],
39
+ 'gpt-5-mini': ['minimal', 'low', 'medium', 'high'],
40
+ 'gpt-5-nano': ['minimal', 'low', 'medium', 'high'],
41
+ 'gpt-5.3-codex': ['low', 'medium', 'high', 'xhigh'],
42
+ 'gpt-oss-120b': ['low', 'medium', 'high'],
43
+ 'openai/gpt-oss-120b': ['low', 'medium', 'high'],
44
+ 'openai/gpt-oss-120b:free': ['low', 'medium', 'high'],
45
+ };
46
+
47
+ /**
48
+ * DeepSeek V4 (OpenAI-compatible): reasoning_effort low|high|max + thinking toggle.
49
+ * Official mapping: low/high/max; xhigh remaps per model (flash→high, pro→max).
50
+ * @see https://api-docs.deepseek.com/guides/thinking_mode
51
+ */
52
+ const DEEPSEEK_LEVELS = ['none', 'low', 'high', 'max'];
53
+ const DEEPSEEK_BANDS = [
54
+ [0, 19, 'none'],
55
+ [20, 39, 'low'],
56
+ [40, 79, 'high'],
57
+ [80, 100, 'max'],
58
+ ];
59
+
60
+ /**
61
+ * MiniMax M3 (OpenAI-compatible): thinking.type disabled | adaptive.
62
+ * @see https://platform.minimax.io/docs/api-reference/text-openai-api
63
+ */
64
+ const MINIMAX_LEVELS = ['disabled', 'adaptive'];
65
+ const MINIMAX_BANDS = [
66
+ [0, 19, 'disabled'],
67
+ [20, 100, 'adaptive'],
68
+ ];
69
+
70
+ /** Exact model β†’ supported Gemini thinkingLevel values */
71
+ const GEMINI_MODEL_LEVELS = {
72
+ 'gemini-3-pro-preview': ['low', 'high'],
73
+ 'gemini-3.1-pro-preview': ['low', 'medium', 'high'],
74
+ 'gemini-2.5-pro': ['low', 'medium', 'high'],
75
+ 'gemini-2.5-flash': ['low', 'medium', 'high'],
76
+ 'gemini-2.5-flash-lite': ['low', 'medium', 'high'],
77
+ };
78
+
79
+ const GEMINI_25_BUDGET_MAX = {
80
+ 'gemini-2.5-pro': 32768,
81
+ 'gemini-2.5-flash': 24576,
82
+ 'gemini-2.5-flash-lite': 24576,
83
+ };
84
+
85
+ const PROVIDER_FAMILY_BY_CLASS = {
86
+ MixAnthropic: 'anthropic',
87
+ MixGoogle: 'google',
88
+ MixOpenAI: 'openai',
89
+ MixOpenAIResponses: 'openai',
90
+ MixOpenAIWebSocket: 'openai',
91
+ MixOpenRouter: 'openai',
92
+ MixKimi: 'openai',
93
+ MixMiniMax: 'openai',
94
+ MixMiMo: 'openai',
95
+ MixGrok: 'openai',
96
+ MixGroq: 'openai',
97
+ MixTogether: 'openai',
98
+ MixCerebras: 'openai',
99
+ MixFireworks: 'openai',
100
+ MixNVIDIA: 'openai',
101
+ MixPerplexity: null,
102
+ MixOllama: null,
103
+ MixLMStudio: null,
104
+ MixLambda: null,
105
+ MixCustom: null,
106
+ };
107
+
108
+ function normalizeEffort(value) {
109
+ if (typeof value !== 'number' || !Number.isFinite(value) || !Number.isInteger(value)) {
110
+ throw new Error(`Invalid effort: expected integer -1 or 0..100, got ${JSON.stringify(value)}`);
111
+ }
112
+ if (value === -1) return -1;
113
+ if (value < 0 || value > 100) {
114
+ throw new Error(`Invalid effort: expected integer -1 or 0..100, got ${value}`);
115
+ }
116
+ return value;
117
+ }
118
+
119
+ function levelFromBands(effort, bands) {
120
+ for (const [lo, hi, level] of bands) {
121
+ if (effort >= lo && effort <= hi) return level;
122
+ }
123
+ return bands[bands.length - 1][2];
124
+ }
125
+
126
+ function pickNearestLevel(desired, ladder, supported) {
127
+ if (supported.includes(desired)) return desired;
128
+ const desiredIdx = ladder.indexOf(desired);
129
+ if (desiredIdx === -1) return supported[Math.floor(supported.length / 2)];
130
+
131
+ let best = supported[0];
132
+ let bestDist = Infinity;
133
+ for (const level of supported) {
134
+ const idx = ladder.indexOf(level);
135
+ if (idx === -1) continue;
136
+ const dist = Math.abs(idx - desiredIdx);
137
+ if (dist < bestDist) {
138
+ bestDist = dist;
139
+ best = level;
140
+ }
141
+ }
142
+ return best;
143
+ }
144
+
145
+ function supportedOpenAILevels(modelKey) {
146
+ if (modelKey && OPENAI_MODEL_LEVELS[modelKey]) {
147
+ return OPENAI_MODEL_LEVELS[modelKey];
148
+ }
149
+ return OPENAI_LEVELS;
150
+ }
151
+
152
+ function supportedGeminiLevels(modelKey) {
153
+ if (modelKey && GEMINI_MODEL_LEVELS[modelKey]) {
154
+ return GEMINI_MODEL_LEVELS[modelKey];
155
+ }
156
+ return GEMINI_LEVELS;
157
+ }
158
+
159
+ function isGemini25(modelKey) {
160
+ return typeof modelKey === 'string' && modelKey.includes('2.5');
161
+ }
162
+
163
+ function isDeepSeekV4(modelKey) {
164
+ if (typeof modelKey !== 'string') return false;
165
+ const key = modelKey.toLowerCase();
166
+ return key.includes('deepseek-v4') || key.includes('deepseek_v4');
167
+ }
168
+
169
+ function isMiniMax(modelKey) {
170
+ return typeof modelKey === 'string' && modelKey.toLowerCase().includes('minimax');
171
+ }
172
+
173
+ function mapDeepSeekEffort(normalized) {
174
+ // DeepSeek V4 has no adaptive mode (only enabled/disabled + low|high|max)
175
+ if (normalized === -1) return null;
176
+ const level = levelFromBands(normalized, DEEPSEEK_BANDS);
177
+ if (level === 'none') {
178
+ return { thinking: { type: 'disabled' } };
179
+ }
180
+ return {
181
+ reasoning_effort: level,
182
+ thinking: { type: 'enabled' }
183
+ };
184
+ }
185
+
186
+ function mapMiniMaxEffort(normalized) {
187
+ if (normalized === -1) {
188
+ return { thinking: { type: 'adaptive' } };
189
+ }
190
+ const level = levelFromBands(normalized, MINIMAX_BANDS);
191
+ return { thinking: { type: level } };
192
+ }
193
+
194
+ /**
195
+ * Explicit adaptive mapping when the provider/model exposes it.
196
+ * Returns null when adaptive is not a settable native control.
197
+ */
198
+ function mapAdaptiveEffort(providerFamily, modelKey) {
199
+ if (providerFamily === 'anthropic') {
200
+ return { thinking: { type: 'adaptive' } };
201
+ }
202
+ if (providerFamily === 'google') {
203
+ // Gemini dynamic thinking: thinkingBudget -1 (2.5 official; accepted on 3.x as dynamic)
204
+ return { thinkingConfig: { thinkingBudget: -1 } };
205
+ }
206
+ if (providerFamily === 'openai' && isMiniMax(modelKey)) {
207
+ return { thinking: { type: 'adaptive' } };
208
+ }
209
+ // OpenAI / DeepSeek: no adaptive enum β€” cannot set adaptive
210
+ return null;
211
+ }
212
+
213
+ function gemini25BudgetMax(modelKey) {
214
+ if (modelKey && GEMINI_25_BUDGET_MAX[modelKey] != null) {
215
+ return GEMINI_25_BUDGET_MAX[modelKey];
216
+ }
217
+ return 24576;
218
+ }
219
+
220
+ /**
221
+ * @returns {'openai'|'anthropic'|'google'|null}
222
+ */
223
+ function resolveProviderFamily(providerInstance) {
224
+ if (!providerInstance || !providerInstance.constructor) return null;
225
+ let proto = providerInstance;
226
+ while (proto) {
227
+ const name = proto.constructor?.name;
228
+ if (name && Object.prototype.hasOwnProperty.call(PROVIDER_FAMILY_BY_CLASS, name)) {
229
+ return PROVIDER_FAMILY_BY_CLASS[name];
230
+ }
231
+ proto = Object.getPrototypeOf(proto);
232
+ if (!proto || proto === Object.prototype) break;
233
+ }
234
+ return null;
235
+ }
236
+
237
+ function hasNativeEffort(family, options = {}, modelKey) {
238
+ if (family === 'openai') {
239
+ if (options.reasoning_effort != null && options.reasoning_effort !== '') return true;
240
+ // DeepSeek / MiniMax use thinking.type as the on/off (or adaptive) control
241
+ if ((isDeepSeekV4(modelKey) || isMiniMax(modelKey)) && options.thinking != null) return true;
242
+ return false;
243
+ }
244
+ if (family === 'anthropic') {
245
+ return options.output_config?.effort != null && options.output_config.effort !== '';
246
+ }
247
+ if (family === 'google') {
248
+ if (options.thinkingConfig != null) return true;
249
+ if (options.thinkingLevel != null) return true;
250
+ if (options.thinkingBudget != null) return true;
251
+ return false;
252
+ }
253
+ return false;
254
+ }
255
+
256
+ /**
257
+ * Map unified effort to a provider-native patch object.
258
+ * Returns null when there is nothing to set (unsupported family, or -1 on a
259
+ * model with no adaptive control).
260
+ *
261
+ * @returns {object|null}
262
+ */
263
+ function mapEffort(providerFamily, effort, modelKey) {
264
+ const normalized = normalizeEffort(effort);
265
+
266
+ if (!providerFamily) return null;
267
+
268
+ if (normalized === -1) {
269
+ return mapAdaptiveEffort(providerFamily, modelKey);
270
+ }
271
+
272
+ if (providerFamily === 'openai') {
273
+ if (isDeepSeekV4(modelKey)) {
274
+ return mapDeepSeekEffort(normalized);
275
+ }
276
+ if (isMiniMax(modelKey)) {
277
+ return mapMiniMaxEffort(normalized);
278
+ }
279
+ const desired = levelFromBands(normalized, OPENAI_BANDS);
280
+ const level = pickNearestLevel(desired, OPENAI_LEVELS, supportedOpenAILevels(modelKey));
281
+ return { reasoning_effort: level };
282
+ }
283
+
284
+ if (providerFamily === 'anthropic') {
285
+ const desired = levelFromBands(normalized, ANTHROPIC_BANDS);
286
+ const level = pickNearestLevel(desired, ANTHROPIC_LEVELS, ANTHROPIC_LEVELS);
287
+ return { output_config: { effort: level } };
288
+ }
289
+
290
+ if (providerFamily === 'google') {
291
+ if (isGemini25(modelKey)) {
292
+ const max = gemini25BudgetMax(modelKey);
293
+ const budget = Math.round((normalized / 100) * max);
294
+ return { thinkingConfig: { thinkingBudget: budget } };
295
+ }
296
+ const desired = levelFromBands(normalized, GEMINI_BANDS);
297
+ const level = pickNearestLevel(desired, GEMINI_LEVELS, supportedGeminiLevels(modelKey));
298
+ return { thinkingConfig: { thinkingLevel: level } };
299
+ }
300
+
301
+ return null;
302
+ }
303
+
304
+ /**
305
+ * Apply config.effort onto options when native controls are absent.
306
+ * Mutates and returns options. Never writes ModelMix `effort` into the HTTP body.
307
+ */
308
+ function applyUnifiedEffort(options, config, providerFamily, modelKey) {
309
+ if (!config || config.effort === undefined || config.effort === null) {
310
+ return options;
311
+ }
312
+ if (!providerFamily) return options;
313
+ if (hasNativeEffort(providerFamily, options, modelKey)) return options;
314
+
315
+ const patch = mapEffort(providerFamily, config.effort, modelKey);
316
+ if (!patch) return options;
317
+
318
+ if (patch.reasoning_effort !== undefined) {
319
+ options.reasoning_effort = patch.reasoning_effort;
320
+ }
321
+ if (patch.output_config) {
322
+ options.output_config = {
323
+ ...(options.output_config || {}),
324
+ ...patch.output_config
325
+ };
326
+ }
327
+ if (patch.thinking) {
328
+ options.thinking = {
329
+ ...(options.thinking || {}),
330
+ ...patch.thinking
331
+ };
332
+ // Adaptive thinking rejects budget_tokens; drop it if we switched mode.
333
+ if (patch.thinking.type === 'adaptive') {
334
+ delete options.thinking.budget_tokens;
335
+ }
336
+ // DeepSeek non-thinking: do not send reasoning_effort
337
+ if (patch.thinking.type === 'disabled') {
338
+ delete options.reasoning_effort;
339
+ }
340
+ }
341
+ if (patch.thinkingConfig) {
342
+ options.thinkingConfig = {
343
+ ...(options.thinkingConfig || {}),
344
+ ...patch.thinkingConfig
345
+ };
346
+ }
347
+
348
+ return options;
349
+ }
350
+
351
+ module.exports = {
352
+ normalizeEffort,
353
+ mapEffort,
354
+ mapAdaptiveEffort,
355
+ applyUnifiedEffort,
356
+ hasNativeEffort,
357
+ resolveProviderFamily,
358
+ isDeepSeekV4,
359
+ isMiniMax,
360
+ levelFromBands,
361
+ pickNearestLevel,
362
+ OPENAI_BANDS,
363
+ ANTHROPIC_BANDS,
364
+ GEMINI_BANDS,
365
+ DEEPSEEK_BANDS,
366
+ OPENAI_LEVELS,
367
+ ANTHROPIC_LEVELS,
368
+ GEMINI_LEVELS,
369
+ DEEPSEEK_LEVELS,
370
+ };