converse-mcp-server 3.4.0 → 3.5.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 +1 -1
- package/docs/API.md +5 -5
- package/package.json +1 -1
- package/src/prompts/helpPrompt.js +2 -1
- package/src/providers/anthropic.js +27 -23
- package/src/providers/codex.js +11 -57
- package/src/providers/copilot.js +7 -12
- package/src/providers/deepseek.js +16 -6
- package/src/providers/gemini-cli.js +11 -17
- package/src/providers/google.js +13 -31
- package/src/providers/mistral.js +10 -6
- package/src/providers/openai.js +58 -23
- package/src/providers/openrouter.js +5 -9
- package/src/providers/xai.js +8 -19
- package/src/tools/chat.js +3 -2
- package/src/utils/reasoningEffort.js +75 -0
package/README.md
CHANGED
|
@@ -267,7 +267,7 @@ Reasoning maps to `high` (enabled) or `none` (disabled) on Medium 3.5 and Small;
|
|
|
267
267
|
- **deepseek-v4-pro** (default; aliases: `deepseek`, `deepseek-pro`): Flagship MoE model with thinking mode (1M context, 384K max output, text-only)
|
|
268
268
|
- **deepseek-v4-flash** (alias: `deepseek-flash`): Faster, lower-cost V4 tier with thinking mode (1M context, 384K max output, text-only)
|
|
269
269
|
|
|
270
|
-
Thinking mode maps `reasoning_effort` to `none` (off), `high` (enabled levels), or `max
|
|
270
|
+
Thinking mode maps `reasoning_effort` to `none` (off), `high` (enabled levels up to `high`), or `max` (`xhigh` and `max`).
|
|
271
271
|
|
|
272
272
|
### OpenRouter Models
|
|
273
273
|
|
package/docs/API.md
CHANGED
|
@@ -84,8 +84,8 @@ MCP_TRANSPORT=stdio npm start
|
|
|
84
84
|
},
|
|
85
85
|
"reasoning_effort": {
|
|
86
86
|
"type": "string",
|
|
87
|
-
"enum": ["none", "minimal", "low", "medium", "high", "max"],
|
|
88
|
-
"description": "Reasoning depth for thinking models
|
|
87
|
+
"enum": ["none", "minimal", "low", "medium", "high", "xhigh", "max"],
|
|
88
|
+
"description": "Reasoning depth for thinking models, weakest to strongest. Passed through by name when the model accepts it, otherwise clamped to the nearest tier it does. Default: 'medium'."
|
|
89
89
|
},
|
|
90
90
|
"async": {
|
|
91
91
|
"type": "boolean",
|
|
@@ -404,7 +404,7 @@ Provide models as plain name strings in the `models` array. Bare names and alias
|
|
|
404
404
|
|-------|---------|---------|-------|
|
|
405
405
|
| `grok-4.5` | `grok`, `grok-4.5-latest`, `grok-build-latest` | 500K | Flagship: image input, reasoning content, native web/X search via Agent Tools |
|
|
406
406
|
|
|
407
|
-
`reasoning_effort`
|
|
407
|
+
`reasoning_effort` clamps into Grok's `low`/`medium`/`high` (`xhigh` and `max` become `high`); Grok 4.5 always reasons and cannot be disabled. Web search is attached automatically and the model decides whether to use it.
|
|
408
408
|
|
|
409
409
|
### Anthropic Models (API-based)
|
|
410
410
|
|
|
@@ -417,7 +417,7 @@ Provide models as plain name strings in the `models` array. Bare names and alias
|
|
|
417
417
|
| `claude-sonnet-4-6` | `sonnet`, `sonnet-4.6` | 200K (1M beta) | 64K | Best speed/intelligence balance, adaptive thinking |
|
|
418
418
|
| `claude-haiku-4-5-20251001` | `haiku`, `haiku-4.5` | 200K | 64K | Fast and intelligent |
|
|
419
419
|
|
|
420
|
-
Models with adaptive thinking control depth via `reasoning_effort`, which
|
|
420
|
+
Models with adaptive thinking control depth via `reasoning_effort`, which is passed by name to Anthropic's `effort` parameter and clamped to what each model accepts: Fable 5, Opus 5, Opus 4.8, and Opus 4.7 take `low`–`max`; Opus 4.6 and Sonnet 4.6 lack `xhigh` (it becomes `max`); Opus 4.5 tops out at `high`. `none` and `minimal` become `low` everywhere. System prompts are automatically cached for 1 hour; cache stats appear in response metadata as `cache_creation_input_tokens` / `cache_read_input_tokens`.
|
|
421
421
|
|
|
422
422
|
### Mistral Models
|
|
423
423
|
|
|
@@ -436,7 +436,7 @@ Models with adaptive thinking control depth via `reasoning_effort`, which maps t
|
|
|
436
436
|
| `deepseek-v4-pro` | `deepseek`, `deepseek-pro` | 1M | 384K | Flagship MoE, thinking mode, text-only |
|
|
437
437
|
| `deepseek-v4-flash` | `deepseek-flash` | 1M | 384K | Faster, lower-cost V4 tier, text-only |
|
|
438
438
|
|
|
439
|
-
`reasoning_effort`: `none` disables thinking; enabled levels use `high`; `max`
|
|
439
|
+
`reasoning_effort`: `none` disables thinking; enabled levels up to `high` use `high`; `xhigh` and `max` use `max`.
|
|
440
440
|
|
|
441
441
|
### OpenRouter Models
|
|
442
442
|
|
package/package.json
CHANGED
|
@@ -295,11 +295,12 @@ function generateConfigurationTips(tools) {
|
|
|
295
295
|
if (effortSchema.enum) {
|
|
296
296
|
for (const value of effortSchema.enum) {
|
|
297
297
|
const descriptions = {
|
|
298
|
-
none: '
|
|
298
|
+
none: 'Reasoning off, fastest response (where the model allows it)',
|
|
299
299
|
minimal: 'Quick responses with minimal reasoning',
|
|
300
300
|
low: 'Light analysis, simple problems',
|
|
301
301
|
medium: 'Balanced reasoning (default)',
|
|
302
302
|
high: 'Deep analysis, complex problems',
|
|
303
|
+
xhigh: 'Very deep analysis, one step below the ceiling',
|
|
303
304
|
max: 'Maximum reasoning capability',
|
|
304
305
|
};
|
|
305
306
|
output += `- **${value}**: ${descriptions[value] || value}\n`;
|
|
@@ -10,6 +10,14 @@
|
|
|
10
10
|
|
|
11
11
|
import { debugLog, debugError } from '../utils/console.js';
|
|
12
12
|
import { ProviderError, ErrorCodes, StopReasons } from './interface.js';
|
|
13
|
+
import { clampReasoningEffort } from '../utils/reasoningEffort.js';
|
|
14
|
+
|
|
15
|
+
// Values each model accepts for the `effort` parameter. Anthropic cannot
|
|
16
|
+
// switch reasoning off via effort, so 'none' and 'minimal' clamp up to 'low'.
|
|
17
|
+
// Not every model that supports 'max' supports 'xhigh'.
|
|
18
|
+
const EFFORT_TIERS_FULL = ['low', 'medium', 'high', 'xhigh', 'max'];
|
|
19
|
+
const EFFORT_TIERS_NO_XHIGH = ['low', 'medium', 'high', 'max'];
|
|
20
|
+
const EFFORT_TIERS_LEGACY = ['low', 'medium', 'high'];
|
|
13
21
|
|
|
14
22
|
// Define supported Claude models with their capabilities
|
|
15
23
|
const SUPPORTED_MODELS = {
|
|
@@ -26,6 +34,7 @@ const SUPPORTED_MODELS = {
|
|
|
26
34
|
timeout: 1800000,
|
|
27
35
|
supportsEffort: true,
|
|
28
36
|
effortGA: true,
|
|
37
|
+
effortTiers: EFFORT_TIERS_FULL,
|
|
29
38
|
supportsCompaction: true,
|
|
30
39
|
description:
|
|
31
40
|
'Claude Fable 5 - Most capable model for the most demanding reasoning and long-horizon agentic work',
|
|
@@ -51,6 +60,7 @@ const SUPPORTED_MODELS = {
|
|
|
51
60
|
timeout: 1800000,
|
|
52
61
|
supportsEffort: true,
|
|
53
62
|
effortGA: true,
|
|
63
|
+
effortTiers: EFFORT_TIERS_FULL,
|
|
54
64
|
supportsCompaction: true,
|
|
55
65
|
description:
|
|
56
66
|
'Claude Opus 5 - Most capable Opus for complex agentic coding and deep reasoning',
|
|
@@ -79,6 +89,7 @@ const SUPPORTED_MODELS = {
|
|
|
79
89
|
timeout: 1800000,
|
|
80
90
|
supportsEffort: true,
|
|
81
91
|
effortGA: true,
|
|
92
|
+
effortTiers: EFFORT_TIERS_FULL,
|
|
82
93
|
supports1MContext: true,
|
|
83
94
|
supportsCompaction: true,
|
|
84
95
|
description:
|
|
@@ -109,6 +120,7 @@ const SUPPORTED_MODELS = {
|
|
|
109
120
|
timeout: 1800000,
|
|
110
121
|
supportsEffort: true,
|
|
111
122
|
effortGA: true,
|
|
123
|
+
effortTiers: EFFORT_TIERS_FULL,
|
|
112
124
|
supports1MContext: true,
|
|
113
125
|
supportsCompaction: true,
|
|
114
126
|
description:
|
|
@@ -139,6 +151,7 @@ const SUPPORTED_MODELS = {
|
|
|
139
151
|
timeout: 1800000,
|
|
140
152
|
supportsEffort: true,
|
|
141
153
|
effortGA: true,
|
|
154
|
+
effortTiers: EFFORT_TIERS_NO_XHIGH,
|
|
142
155
|
supports1MContext: true,
|
|
143
156
|
supportsCompaction: true,
|
|
144
157
|
description:
|
|
@@ -167,6 +180,7 @@ const SUPPORTED_MODELS = {
|
|
|
167
180
|
maxThinkingTokens: 64000,
|
|
168
181
|
timeout: 900000,
|
|
169
182
|
supportsEffort: true, // Opus 4.5 effort parameter (requires beta header)
|
|
183
|
+
effortTiers: EFFORT_TIERS_LEGACY,
|
|
170
184
|
description:
|
|
171
185
|
'Claude Opus 4.5 - Previous most intelligent model combining maximum capability with practical performance',
|
|
172
186
|
aliases: [
|
|
@@ -223,6 +237,7 @@ const SUPPORTED_MODELS = {
|
|
|
223
237
|
timeout: 900000,
|
|
224
238
|
supportsEffort: true,
|
|
225
239
|
effortGA: true, // Effort is generally available, no beta header required
|
|
240
|
+
effortTiers: EFFORT_TIERS_NO_XHIGH,
|
|
226
241
|
supports1MContext: true, // Beta 1M context support
|
|
227
242
|
supportsCompaction: true, // Beta server-side context compaction
|
|
228
243
|
description:
|
|
@@ -311,22 +326,10 @@ const THINKING_BUDGETS = {
|
|
|
311
326
|
low: 0.15, // 15% of max thinking tokens
|
|
312
327
|
medium: 0.33, // 33% of max thinking tokens (default)
|
|
313
328
|
high: 0.67, // 67% of max thinking tokens
|
|
329
|
+
xhigh: 0.85, // 85% of max thinking tokens
|
|
314
330
|
max: 1.0, // 100% of max thinking tokens
|
|
315
331
|
};
|
|
316
332
|
|
|
317
|
-
/**
|
|
318
|
-
* Effort parameter mapping for Opus 5, Opus 4.8, Opus 4.7, Opus 4.6, Sonnet 4.6, and Opus 4.5
|
|
319
|
-
* Maps reasoning_effort values to Anthropic's effort parameter values
|
|
320
|
-
*/
|
|
321
|
-
const EFFORT_MAP = {
|
|
322
|
-
none: 'low',
|
|
323
|
-
minimal: 'low',
|
|
324
|
-
low: 'medium',
|
|
325
|
-
medium: 'high',
|
|
326
|
-
high: 'xhigh',
|
|
327
|
-
max: 'max',
|
|
328
|
-
};
|
|
329
|
-
|
|
330
333
|
/**
|
|
331
334
|
* Custom error class for Anthropic provider errors
|
|
332
335
|
*/
|
|
@@ -716,16 +719,17 @@ export const anthropicProvider = {
|
|
|
716
719
|
|
|
717
720
|
// Add effort parameter for models that support it (uses output_config)
|
|
718
721
|
if (modelConfig.supportsEffort && reasoning_effort) {
|
|
719
|
-
const effortValue =
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
722
|
+
const effortValue = clampReasoningEffort(
|
|
723
|
+
reasoning_effort,
|
|
724
|
+
modelConfig.effortTiers,
|
|
725
|
+
);
|
|
726
|
+
requestPayload.output_config = {
|
|
727
|
+
...requestPayload.output_config,
|
|
728
|
+
effort: effortValue,
|
|
729
|
+
};
|
|
730
|
+
debugLog(
|
|
731
|
+
`[Anthropic] Effort parameter set to "${effortValue}" for ${resolvedModel} (from reasoning_effort: ${reasoning_effort})`,
|
|
732
|
+
);
|
|
729
733
|
}
|
|
730
734
|
|
|
731
735
|
// If streaming is requested and model doesn't support it, fall back to non-streaming
|
package/src/providers/codex.js
CHANGED
|
@@ -16,17 +16,10 @@
|
|
|
16
16
|
import { debugLog, debugError } from '../utils/console.js';
|
|
17
17
|
import { ProviderError, ErrorCodes, StopReasons } from './interface.js';
|
|
18
18
|
import { normalizeExtendedPath } from '../utils/pathUtils.js';
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
*
|
|
24
|
-
* Codex also exposes 'ultra' above 'max', but that tier turns on automatic
|
|
25
|
-
* sub-agent delegation — a change in how the run executes, not just how deep
|
|
26
|
-
* it reasons — so nothing at the tool level maps to it and it is kept off the
|
|
27
|
-
* ladder so the clamp can never select it.
|
|
28
|
-
*/
|
|
29
|
-
const EFFORT_LADDER = ['none', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'];
|
|
19
|
+
import {
|
|
20
|
+
EFFORT_LADDER,
|
|
21
|
+
clampReasoningEffort,
|
|
22
|
+
} from '../utils/reasoningEffort.js';
|
|
30
23
|
|
|
31
24
|
/**
|
|
32
25
|
* Backend models Codex can run, keyed by the slug passed to the CLI as
|
|
@@ -35,6 +28,11 @@ const EFFORT_LADDER = ['none', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max
|
|
|
35
28
|
* are: 'low', 'medium', 'high', 'xhigh', and 'max'"). The SDK's
|
|
36
29
|
* ModelReasoningEffort type is the union across models, so the backend is the
|
|
37
30
|
* authority and requests are clamped per model.
|
|
31
|
+
*
|
|
32
|
+
* Codex also exposes 'ultra' above 'max', but that tier turns on automatic
|
|
33
|
+
* sub-agent delegation — a change in how the run executes, not just how deep
|
|
34
|
+
* it reasons — so it is deliberately absent from every supportedEfforts list
|
|
35
|
+
* and nothing at the tool level can select it.
|
|
38
36
|
*/
|
|
39
37
|
const CODEX_BACKEND_MODELS = {
|
|
40
38
|
'gpt-6-astra': {
|
|
@@ -307,50 +305,6 @@ async function getThreadIdFromContinuation(
|
|
|
307
305
|
}
|
|
308
306
|
}
|
|
309
307
|
|
|
310
|
-
/**
|
|
311
|
-
* Tool-level reasoning_effort values translated to their Codex equivalent.
|
|
312
|
-
* Tool enum: 'none' | 'minimal' | 'low' | 'medium' | 'high' | 'max'
|
|
313
|
-
*/
|
|
314
|
-
const EFFORT_ALIASES = {
|
|
315
|
-
none: 'none',
|
|
316
|
-
minimal: 'minimal',
|
|
317
|
-
low: 'low',
|
|
318
|
-
medium: 'medium',
|
|
319
|
-
high: 'high',
|
|
320
|
-
max: 'max',
|
|
321
|
-
};
|
|
322
|
-
|
|
323
|
-
/**
|
|
324
|
-
* Map a tool-level reasoning_effort onto a tier the target model accepts.
|
|
325
|
-
*
|
|
326
|
-
* When the requested tier isn't in the model's supported set, the nearest
|
|
327
|
-
* *stronger* tier wins: nudging 'minimal' up to 'low' keeps reasoning on,
|
|
328
|
-
* where falling back to 'none' would silently switch it off.
|
|
329
|
-
*
|
|
330
|
-
* @param {string} effort - Tool-level reasoning_effort value
|
|
331
|
-
* @param {string[]} [supported] - Tiers the model accepts
|
|
332
|
-
* @returns {string} A tier from `supported`
|
|
333
|
-
*/
|
|
334
|
-
export function mapReasoningEffort(effort, supported = EFFORT_LADDER) {
|
|
335
|
-
const desired = EFFORT_ALIASES[effort] || 'medium';
|
|
336
|
-
if (supported.includes(desired)) {
|
|
337
|
-
return desired;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
const rank = EFFORT_LADDER.indexOf(desired);
|
|
341
|
-
const stronger = EFFORT_LADDER.slice(rank + 1).find((tier) =>
|
|
342
|
-
supported.includes(tier),
|
|
343
|
-
);
|
|
344
|
-
if (stronger) {
|
|
345
|
-
return stronger;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
const weaker = EFFORT_LADDER.slice(0, rank)
|
|
349
|
-
.reverse()
|
|
350
|
-
.find((tier) => supported.includes(tier));
|
|
351
|
-
return weaker || 'medium';
|
|
352
|
-
}
|
|
353
|
-
|
|
354
308
|
/**
|
|
355
309
|
* Resolve a user-facing model name to its entry in SUPPORTED_MODELS.
|
|
356
310
|
* @param {string} modelName
|
|
@@ -500,9 +454,9 @@ export const codexProvider = {
|
|
|
500
454
|
if (reasoning_effort) {
|
|
501
455
|
const supportedEfforts =
|
|
502
456
|
getBackendModelConfig(backendModel)?.supportedEfforts || EFFORT_LADDER;
|
|
503
|
-
const mappedEffort =
|
|
457
|
+
const mappedEffort = clampReasoningEffort(reasoning_effort, supportedEfforts);
|
|
504
458
|
threadOptions.modelReasoningEffort = mappedEffort;
|
|
505
|
-
if (mappedEffort !==
|
|
459
|
+
if (mappedEffort !== reasoning_effort) {
|
|
506
460
|
debugLog(
|
|
507
461
|
`[Codex] reasoning_effort "${reasoning_effort}" not supported by ${backendModel} — using "${mappedEffort}"`,
|
|
508
462
|
);
|
package/src/providers/copilot.js
CHANGED
|
@@ -17,6 +17,7 @@ import { delimiter, dirname, join } from 'node:path';
|
|
|
17
17
|
import { fileURLToPath } from 'node:url';
|
|
18
18
|
import { debugLog, debugError } from '../utils/console.js';
|
|
19
19
|
import { ProviderError, ErrorCodes, StopReasons } from './interface.js';
|
|
20
|
+
import { clampReasoningEffort } from '../utils/reasoningEffort.js';
|
|
20
21
|
|
|
21
22
|
const SUPPORTED_MODELS = {
|
|
22
23
|
copilot: {
|
|
@@ -517,20 +518,14 @@ function resolveSessionModel(requestModel, config) {
|
|
|
517
518
|
* - session.error → { data: { errorType, message } }
|
|
518
519
|
*/
|
|
519
520
|
/**
|
|
520
|
-
*
|
|
521
|
-
*
|
|
522
|
-
*
|
|
521
|
+
* Values the Copilot SDK's ReasoningEffort type accepts. The SDK tops out at
|
|
522
|
+
* 'xhigh' and cannot switch reasoning off, so 'max' clamps down and
|
|
523
|
+
* 'none'/'minimal' clamp up.
|
|
523
524
|
*/
|
|
525
|
+
const COPILOT_EFFORT_TIERS = ['low', 'medium', 'high', 'xhigh'];
|
|
526
|
+
|
|
524
527
|
function mapReasoningEffort(effort) {
|
|
525
|
-
|
|
526
|
-
none: 'low',
|
|
527
|
-
minimal: 'low',
|
|
528
|
-
low: 'low',
|
|
529
|
-
medium: 'medium',
|
|
530
|
-
high: 'high',
|
|
531
|
-
max: 'xhigh',
|
|
532
|
-
};
|
|
533
|
-
return mapping[effort] || undefined;
|
|
528
|
+
return clampReasoningEffort(effort, COPILOT_EFFORT_TIERS);
|
|
534
529
|
}
|
|
535
530
|
|
|
536
531
|
/**
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import { createOpenAICompatibleProvider } from './openai-compatible.js';
|
|
9
9
|
import { debugLog } from '../utils/console.js';
|
|
10
|
+
import { clampReasoningEffort } from '../utils/reasoningEffort.js';
|
|
10
11
|
|
|
11
12
|
// Define supported DeepSeek models with their capabilities.
|
|
12
13
|
// V4 unified the catalog: both tiers share a 1M context window and a 384K output
|
|
@@ -65,13 +66,19 @@ function validateApiKey(apiKey) {
|
|
|
65
66
|
return apiKey.length >= 32;
|
|
66
67
|
}
|
|
67
68
|
|
|
69
|
+
/**
|
|
70
|
+
* Values DeepSeek's `reasoning_effort` field accepts once thinking is on.
|
|
71
|
+
* There is no lower documented tier, so every enabled level below high clamps
|
|
72
|
+
* up to "high" (the default `medium` runs thinking-on rather than silently
|
|
73
|
+
* disabling it) and `xhigh` clamps up to "max".
|
|
74
|
+
*/
|
|
75
|
+
const DEEPSEEK_EFFORT_TIERS = ['high', 'max'];
|
|
76
|
+
|
|
68
77
|
/**
|
|
69
78
|
* Map a Converse reasoning_effort level to DeepSeek's thinking-mode request
|
|
70
|
-
* fields. DeepSeek exposes
|
|
71
|
-
* ({type:"enabled"|"disabled"}) and
|
|
72
|
-
*
|
|
73
|
-
* no lower documented tier), preserving enabled-reasoning intent so the default
|
|
74
|
-
* `medium` runs thinking-on rather than silently disabling it.
|
|
79
|
+
* fields. DeepSeek exposes two independent controls: a `thinking` toggle
|
|
80
|
+
* ({type:"enabled"|"disabled"}) and the `reasoning_effort` tier. Only `none`
|
|
81
|
+
* flips the toggle off.
|
|
75
82
|
*/
|
|
76
83
|
function applyReasoning(requestPayload, reasoningEffort) {
|
|
77
84
|
if (reasoningEffort === 'none') {
|
|
@@ -81,7 +88,10 @@ function applyReasoning(requestPayload, reasoningEffort) {
|
|
|
81
88
|
}
|
|
82
89
|
|
|
83
90
|
requestPayload.thinking = { type: 'enabled' };
|
|
84
|
-
requestPayload.reasoning_effort =
|
|
91
|
+
requestPayload.reasoning_effort = clampReasoningEffort(
|
|
92
|
+
reasoningEffort,
|
|
93
|
+
DEEPSEEK_EFFORT_TIERS,
|
|
94
|
+
);
|
|
85
95
|
}
|
|
86
96
|
|
|
87
97
|
/**
|
|
@@ -31,6 +31,7 @@ import { join, delimiter } from 'node:path';
|
|
|
31
31
|
import { randomUUID } from 'node:crypto';
|
|
32
32
|
import { debugLog, debugError } from '../utils/console.js';
|
|
33
33
|
import { ProviderError, ErrorCodes, StopReasons } from './interface.js';
|
|
34
|
+
import { clampReasoningEffort } from '../utils/reasoningEffort.js';
|
|
34
35
|
|
|
35
36
|
// Prompts at or below this length pass directly as the -p argv value (fast
|
|
36
37
|
// path). Larger prompts are written to a file and -p carries a bootstrap
|
|
@@ -174,32 +175,25 @@ export function findAgyBinary() {
|
|
|
174
175
|
return _cachedAgyPath;
|
|
175
176
|
}
|
|
176
177
|
|
|
178
|
+
// agy variant tiers per model base. Flash offers Low/Medium/High; Pro has no
|
|
179
|
+
// Medium variant.
|
|
180
|
+
const FLASH_EFFORT_TIERS = ['low', 'medium', 'high'];
|
|
181
|
+
const PRO_EFFORT_TIERS = ['low', 'high'];
|
|
182
|
+
|
|
177
183
|
/**
|
|
178
184
|
* Map a reasoning_effort value to the agy parenthesized variant suffix.
|
|
179
|
-
*
|
|
185
|
+
* Unset effort selects High, agy's own default.
|
|
180
186
|
* @param {string} base - agy model base ('Gemini 3.8 Flash' / 'Gemini 3.1 Pro')
|
|
181
187
|
* @param {string} [reasoningEffort]
|
|
182
188
|
* @returns {string} e.g. '(Low)', '(Medium)', '(High)'
|
|
183
189
|
*/
|
|
184
190
|
function effortSuffix(base, reasoningEffort) {
|
|
185
|
-
|
|
186
|
-
const effort = (reasoningEffort || '').toLowerCase();
|
|
187
|
-
|
|
188
|
-
switch (effort) {
|
|
189
|
-
case 'none':
|
|
190
|
-
case 'minimal':
|
|
191
|
-
case 'low':
|
|
192
|
-
return '(Low)';
|
|
193
|
-
case 'medium':
|
|
194
|
-
// Pro has no Medium variant — fall back to High
|
|
195
|
-
return isPro ? '(High)' : '(Medium)';
|
|
196
|
-
case 'high':
|
|
197
|
-
case 'max':
|
|
198
|
-
return '(High)';
|
|
199
|
-
default:
|
|
200
|
-
// unset → High
|
|
191
|
+
if (!reasoningEffort) {
|
|
201
192
|
return '(High)';
|
|
202
193
|
}
|
|
194
|
+
const tiers = /pro/i.test(base) ? PRO_EFFORT_TIERS : FLASH_EFFORT_TIERS;
|
|
195
|
+
const tier = clampReasoningEffort(reasoningEffort.toLowerCase(), tiers);
|
|
196
|
+
return `(${tier[0].toUpperCase()}${tier.slice(1)})`;
|
|
203
197
|
}
|
|
204
198
|
|
|
205
199
|
/**
|
package/src/providers/google.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import { GoogleGenAI } from '@google/genai';
|
|
9
9
|
import { debugLog, debugError } from '../utils/console.js';
|
|
10
|
+
import { clampReasoningEffort } from '../utils/reasoningEffort.js';
|
|
10
11
|
|
|
11
12
|
// Define supported Gemini models with their capabilities
|
|
12
13
|
const SUPPORTED_MODELS = {
|
|
@@ -161,9 +162,14 @@ const THINKING_BUDGETS = {
|
|
|
161
162
|
low: 0.08, // 8% of max - light reasoning tasks
|
|
162
163
|
medium: 0.33, // 33% of max - balanced reasoning (default)
|
|
163
164
|
high: 0.67, // 67% of max - complex analysis
|
|
165
|
+
xhigh: 0.85, // 85% of max - near-full budget
|
|
164
166
|
max: 1.0, // 100% of max - full thinking budget
|
|
165
167
|
};
|
|
166
168
|
|
|
169
|
+
// thinkingLevel values for models without an explicit thinkingLevels list
|
|
170
|
+
// (Gemini 3.0 Pro exposes only low/high).
|
|
171
|
+
const BINARY_THINKING_LEVELS = ['low', 'high'];
|
|
172
|
+
|
|
167
173
|
/**
|
|
168
174
|
* Custom error class for Google provider errors
|
|
169
175
|
*/
|
|
@@ -527,37 +533,13 @@ export const googleProvider = {
|
|
|
527
533
|
// Add thinking configuration for models that support it
|
|
528
534
|
if (modelConfig.supportsThinking && reasoning_effort) {
|
|
529
535
|
if (modelConfig.thinkingMode === 'level') {
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
medium: 'medium',
|
|
538
|
-
high: 'high',
|
|
539
|
-
max: 'high',
|
|
540
|
-
};
|
|
541
|
-
thinkingLevel = levelMap[reasoning_effort] || 'high';
|
|
542
|
-
if (!modelConfig.thinkingLevels.includes(thinkingLevel)) {
|
|
543
|
-
// Clamp to the nearest supported level: a request below the
|
|
544
|
-
// model's floor (e.g. minimal on 3.8 Flash) takes the lowest
|
|
545
|
-
// level, anything else the highest.
|
|
546
|
-
const rank = ['minimal', 'low', 'medium', 'high'];
|
|
547
|
-
const [lowest] = modelConfig.thinkingLevels;
|
|
548
|
-
thinkingLevel =
|
|
549
|
-
rank.indexOf(thinkingLevel) < rank.indexOf(lowest)
|
|
550
|
-
? lowest
|
|
551
|
-
: modelConfig.thinkingLevels[
|
|
552
|
-
modelConfig.thinkingLevels.length - 1
|
|
553
|
-
];
|
|
554
|
-
}
|
|
555
|
-
} else {
|
|
556
|
-
// Binary levels only (Gemini 3.0 Pro: low/high)
|
|
557
|
-
thinkingLevel = ['minimal', 'low'].includes(reasoning_effort)
|
|
558
|
-
? 'low'
|
|
559
|
-
: 'high';
|
|
560
|
-
}
|
|
536
|
+
// Gemini 3.x thinkingLevel names are tool-level tiers, so the request
|
|
537
|
+
// clamps straight onto the levels the model lists (none → minimal or
|
|
538
|
+
// the floor, xhigh/max → the ceiling).
|
|
539
|
+
const thinkingLevel = clampReasoningEffort(
|
|
540
|
+
reasoning_effort,
|
|
541
|
+
modelConfig.thinkingLevels || BINARY_THINKING_LEVELS,
|
|
542
|
+
);
|
|
561
543
|
generationConfig.thinkingConfig = { thinkingLevel };
|
|
562
544
|
} else {
|
|
563
545
|
// Gemini 2.5: Use thinking budget (token count)
|
package/src/providers/mistral.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import { debugLog, debugError } from '../utils/console.js';
|
|
9
9
|
import { ProviderError, ErrorCodes, StopReasons } from './interface.js';
|
|
10
|
+
import { clampReasoningEffort } from '../utils/reasoningEffort.js';
|
|
10
11
|
|
|
11
12
|
// Define supported Mistral models with their capabilities.
|
|
12
13
|
// Reasoning (`reasoning_effort`) is supported only on Medium 3.5 and Small 4;
|
|
@@ -61,19 +62,22 @@ const SUPPORTED_MODELS = {
|
|
|
61
62
|
},
|
|
62
63
|
};
|
|
63
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Values Mistral documents for `reasoning_effort`. Every enabled level clamps
|
|
67
|
+
* to "high" so the default `medium` runs thinking-on; only `none` disables.
|
|
68
|
+
*/
|
|
69
|
+
const MISTRAL_EFFORT_TIERS = ['none', 'high'];
|
|
70
|
+
|
|
64
71
|
/**
|
|
65
72
|
* Map a Converse reasoning_effort level to Mistral's documented request value.
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
* thinking-on); only `none` disables. Returns null when reasoning must not be
|
|
69
|
-
* forwarded — i.e. the model does not support it (Large 3) or is an unknown
|
|
70
|
-
* pass-through ID (capability-gated).
|
|
73
|
+
* Returns null when reasoning must not be forwarded — i.e. the model does not
|
|
74
|
+
* support it (Large 3) or is an unknown pass-through ID (capability-gated).
|
|
71
75
|
*/
|
|
72
76
|
function resolveReasoningEffort(level, modelConfig) {
|
|
73
77
|
if (!modelConfig?.supportsReasoning) {
|
|
74
78
|
return null;
|
|
75
79
|
}
|
|
76
|
-
return level
|
|
80
|
+
return clampReasoningEffort(level, MISTRAL_EFFORT_TIERS);
|
|
77
81
|
}
|
|
78
82
|
|
|
79
83
|
/**
|
package/src/providers/openai.js
CHANGED
|
@@ -7,6 +7,21 @@
|
|
|
7
7
|
|
|
8
8
|
import OpenAI from 'openai';
|
|
9
9
|
import { debugLog, debugError } from '../utils/console.js';
|
|
10
|
+
import { clampReasoningEffort } from '../utils/reasoningEffort.js';
|
|
11
|
+
|
|
12
|
+
// Values each family accepts for reasoning effort, per the model pages at
|
|
13
|
+
// developers.openai.com/api/docs/models. GPT-5.6 is the only family with
|
|
14
|
+
// 'max'; the GPT-5.4 tier stops at 'xhigh'; the original GPT-5 minis kept
|
|
15
|
+
// 'minimal' but never gained 'xhigh'; the o-series predates both ends of the
|
|
16
|
+
// ladder; GPT-5.4 Pro starts at 'medium'. Models without a list are passed
|
|
17
|
+
// the requested value unchanged, except uncatalogued GPT-5 Pro snapshots,
|
|
18
|
+
// which are only known to accept 'high'.
|
|
19
|
+
const GPT_56_EFFORT_TIERS = ['none', 'low', 'medium', 'high', 'xhigh', 'max'];
|
|
20
|
+
const GPT_54_EFFORT_TIERS = ['none', 'low', 'medium', 'high', 'xhigh'];
|
|
21
|
+
const GPT_54_PRO_EFFORT_TIERS = ['medium', 'high', 'xhigh'];
|
|
22
|
+
const GPT_5_EFFORT_TIERS = ['minimal', 'low', 'medium', 'high'];
|
|
23
|
+
const O_SERIES_EFFORT_TIERS = ['low', 'medium', 'high'];
|
|
24
|
+
const PRO_PASSTHROUGH_EFFORT_TIERS = ['high'];
|
|
10
25
|
|
|
11
26
|
// Define supported models with their capabilities
|
|
12
27
|
const SUPPORTED_MODELS = {
|
|
@@ -19,7 +34,7 @@ const SUPPORTED_MODELS = {
|
|
|
19
34
|
supportsImages: true,
|
|
20
35
|
supportsWebSearch: true,
|
|
21
36
|
supportsResponsesAPI: true,
|
|
22
|
-
|
|
37
|
+
supportedEfforts: GPT_56_EFFORT_TIERS,
|
|
23
38
|
timeout: 10800000, // 3 hours
|
|
24
39
|
description:
|
|
25
40
|
'Flagship GPT-5.6 model (1M context, 128K output) - Frontier reasoning, coding, agentic workflows. Most token-efficient flagship',
|
|
@@ -44,7 +59,7 @@ const SUPPORTED_MODELS = {
|
|
|
44
59
|
supportsImages: true,
|
|
45
60
|
supportsWebSearch: true,
|
|
46
61
|
supportsResponsesAPI: true,
|
|
47
|
-
|
|
62
|
+
supportedEfforts: GPT_56_EFFORT_TIERS,
|
|
48
63
|
timeout: 5400000, // 90 minutes
|
|
49
64
|
description:
|
|
50
65
|
'Lower-cost GPT-5.6 (400K context, 128K output) - Performance competitive with GPT-5.5 at half the flagship price',
|
|
@@ -59,7 +74,7 @@ const SUPPORTED_MODELS = {
|
|
|
59
74
|
supportsImages: true,
|
|
60
75
|
supportsWebSearch: true,
|
|
61
76
|
supportsResponsesAPI: true,
|
|
62
|
-
|
|
77
|
+
supportedEfforts: GPT_56_EFFORT_TIERS,
|
|
63
78
|
timeout: 1800000, // 30 minutes
|
|
64
79
|
description:
|
|
65
80
|
'Fastest, most affordable GPT-5.6 (400K context, 128K output) - High-volume, latency-sensitive workloads',
|
|
@@ -74,7 +89,7 @@ const SUPPORTED_MODELS = {
|
|
|
74
89
|
supportsImages: true,
|
|
75
90
|
supportsWebSearch: true,
|
|
76
91
|
supportsResponsesAPI: true,
|
|
77
|
-
|
|
92
|
+
supportedEfforts: GPT_54_EFFORT_TIERS,
|
|
78
93
|
timeout: 10800000, // 3 hours
|
|
79
94
|
description:
|
|
80
95
|
'Latest flagship model (1M context, 128K output) - Superior reasoning, coding, agentic workflows, computer use. Most token-efficient reasoning model',
|
|
@@ -92,6 +107,7 @@ const SUPPORTED_MODELS = {
|
|
|
92
107
|
supportsImages: true,
|
|
93
108
|
supportsWebSearch: true,
|
|
94
109
|
supportsResponsesAPI: true,
|
|
110
|
+
supportedEfforts: GPT_5_EFFORT_TIERS,
|
|
95
111
|
timeout: 5400000, // 90 minutes
|
|
96
112
|
description:
|
|
97
113
|
'Faster, cost-efficient GPT-5 (400K context, 128K output) - Well-defined tasks, precise prompts',
|
|
@@ -106,6 +122,7 @@ const SUPPORTED_MODELS = {
|
|
|
106
122
|
supportsImages: true,
|
|
107
123
|
supportsWebSearch: false, // GPT-5-nano doesn't support web search
|
|
108
124
|
supportsResponsesAPI: true,
|
|
125
|
+
supportedEfforts: GPT_5_EFFORT_TIERS,
|
|
109
126
|
timeout: 1800000, // 30 minutes
|
|
110
127
|
description:
|
|
111
128
|
'Fastest, most cost-efficient GPT-5 (400K context, 128K output) - Summarization, classification',
|
|
@@ -120,6 +137,7 @@ const SUPPORTED_MODELS = {
|
|
|
120
137
|
supportsImages: true,
|
|
121
138
|
supportsWebSearch: true,
|
|
122
139
|
supportsResponsesAPI: true,
|
|
140
|
+
supportedEfforts: GPT_54_EFFORT_TIERS,
|
|
123
141
|
timeout: 5400000, // 90 minutes
|
|
124
142
|
description:
|
|
125
143
|
'Fast, efficient GPT-5.4 (400K context, 128K output) - Coding, subagents, computer use, tool use. 2x faster than GPT-5 mini',
|
|
@@ -139,6 +157,7 @@ const SUPPORTED_MODELS = {
|
|
|
139
157
|
supportsImages: true,
|
|
140
158
|
supportsWebSearch: false,
|
|
141
159
|
supportsResponsesAPI: true,
|
|
160
|
+
supportedEfforts: GPT_54_EFFORT_TIERS,
|
|
142
161
|
timeout: 1800000, // 30 minutes
|
|
143
162
|
description:
|
|
144
163
|
'Smallest, cheapest GPT-5.4 (400K context, 128K output) - Classification, data extraction, ranking, coding subagents',
|
|
@@ -159,6 +178,7 @@ const SUPPORTED_MODELS = {
|
|
|
159
178
|
supportsWebSearch: true,
|
|
160
179
|
supportsResponsesAPI: true,
|
|
161
180
|
supportsDeepResearch: false,
|
|
181
|
+
supportedEfforts: GPT_54_PRO_EFFORT_TIERS,
|
|
162
182
|
timeout: 10800000, // 180 minutes
|
|
163
183
|
description:
|
|
164
184
|
'Maximum performance reasoning model (1M context, 272K output) - Most complex tasks, extended compute time (EXPENSIVE)',
|
|
@@ -179,6 +199,7 @@ const SUPPORTED_MODELS = {
|
|
|
179
199
|
supportsImages: true,
|
|
180
200
|
supportsWebSearch: true,
|
|
181
201
|
supportsResponsesAPI: true,
|
|
202
|
+
supportedEfforts: O_SERIES_EFFORT_TIERS,
|
|
182
203
|
timeout: 1800000, // 30 minutes
|
|
183
204
|
description:
|
|
184
205
|
'Strong reasoning (200K context) - Logical problems, code generation, systematic analysis',
|
|
@@ -193,6 +214,7 @@ const SUPPORTED_MODELS = {
|
|
|
193
214
|
supportsImages: true,
|
|
194
215
|
supportsWebSearch: true,
|
|
195
216
|
supportsResponsesAPI: true,
|
|
217
|
+
supportedEfforts: O_SERIES_EFFORT_TIERS,
|
|
196
218
|
timeout: 10800000, // 180 minutes
|
|
197
219
|
description:
|
|
198
220
|
'Professional-grade reasoning (200K context) - EXTREMELY EXPENSIVE: Only for the most complex problems',
|
|
@@ -207,6 +229,7 @@ const SUPPORTED_MODELS = {
|
|
|
207
229
|
supportsImages: true,
|
|
208
230
|
supportsWebSearch: true,
|
|
209
231
|
supportsResponsesAPI: true,
|
|
232
|
+
supportedEfforts: O_SERIES_EFFORT_TIERS,
|
|
210
233
|
timeout: 540000, // 9 minutes
|
|
211
234
|
description:
|
|
212
235
|
'Latest reasoning model (200K context) - Optimized for shorter contexts, rapid reasoning',
|
|
@@ -311,17 +334,32 @@ function resolveModelName(modelName) {
|
|
|
311
334
|
}
|
|
312
335
|
|
|
313
336
|
/**
|
|
314
|
-
*
|
|
315
|
-
*
|
|
316
|
-
*
|
|
317
|
-
* maps to the closest supported value.
|
|
337
|
+
* Whether a request to this model should carry a reasoning effort at all.
|
|
338
|
+
* Catalogued models say so through their declared tiers; pass-through IDs
|
|
339
|
+
* fall back to the o-series / GPT-5 name heuristic.
|
|
318
340
|
*/
|
|
319
|
-
function
|
|
341
|
+
function acceptsReasoningEffort(resolvedModel, modelConfig) {
|
|
342
|
+
if (modelConfig.supportedEfforts) {
|
|
343
|
+
return true;
|
|
344
|
+
}
|
|
345
|
+
return resolvedModel.startsWith('o3') || resolvedModel.startsWith('gpt-5');
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Resolve the reasoning effort actually sent to the API. Catalogued models
|
|
350
|
+
* clamp onto their declared tiers. Pass-through IDs are matched by family
|
|
351
|
+
* where the tiers are known (GPT-5 Pro snapshots, GPT-5.6 snapshots) and
|
|
352
|
+
* otherwise keep the requested value.
|
|
353
|
+
*/
|
|
354
|
+
function resolveReasoningEffort(resolvedModel, modelConfig, reasoningEffort) {
|
|
355
|
+
if (modelConfig.supportedEfforts) {
|
|
356
|
+
return clampReasoningEffort(reasoningEffort, modelConfig.supportedEfforts);
|
|
357
|
+
}
|
|
320
358
|
if (resolvedModel.endsWith('-pro') && resolvedModel.startsWith('gpt-5')) {
|
|
321
|
-
return
|
|
359
|
+
return clampReasoningEffort(reasoningEffort, PRO_PASSTHROUGH_EFFORT_TIERS);
|
|
322
360
|
}
|
|
323
|
-
if (resolvedModel.startsWith('gpt-5.6')
|
|
324
|
-
return
|
|
361
|
+
if (resolvedModel.startsWith('gpt-5.6')) {
|
|
362
|
+
return clampReasoningEffort(reasoningEffort, GPT_56_EFFORT_TIERS);
|
|
325
363
|
}
|
|
326
364
|
return reasoningEffort;
|
|
327
365
|
}
|
|
@@ -511,13 +549,13 @@ export const openaiProvider = {
|
|
|
511
549
|
requestPayload.tools = [{ type: 'web_search_preview' }];
|
|
512
550
|
}
|
|
513
551
|
|
|
514
|
-
|
|
515
|
-
if (
|
|
516
|
-
(resolvedModel.startsWith('o3') || resolvedModel.startsWith('gpt-5')) &&
|
|
517
|
-
reasoning_effort
|
|
518
|
-
) {
|
|
552
|
+
if (acceptsReasoningEffort(resolvedModel, modelConfig) && reasoning_effort) {
|
|
519
553
|
requestPayload.reasoning = {
|
|
520
|
-
effort: resolveReasoningEffort(
|
|
554
|
+
effort: resolveReasoningEffort(
|
|
555
|
+
resolvedModel,
|
|
556
|
+
modelConfig,
|
|
557
|
+
reasoning_effort,
|
|
558
|
+
),
|
|
521
559
|
summary: 'auto', // Enable reasoning summaries
|
|
522
560
|
};
|
|
523
561
|
}
|
|
@@ -531,13 +569,10 @@ export const openaiProvider = {
|
|
|
531
569
|
...cleanOptions,
|
|
532
570
|
};
|
|
533
571
|
|
|
534
|
-
|
|
535
|
-
if (
|
|
536
|
-
(resolvedModel.startsWith('o3') || resolvedModel.startsWith('gpt-5')) &&
|
|
537
|
-
reasoning_effort
|
|
538
|
-
) {
|
|
572
|
+
if (acceptsReasoningEffort(resolvedModel, modelConfig) && reasoning_effort) {
|
|
539
573
|
requestPayload.reasoning_effort = resolveReasoningEffort(
|
|
540
574
|
resolvedModel,
|
|
575
|
+
modelConfig,
|
|
541
576
|
reasoning_effort,
|
|
542
577
|
);
|
|
543
578
|
}
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
lookupOpenRouterModel,
|
|
27
27
|
DiscoveryStatus,
|
|
28
28
|
} from './openrouter-discovery.js';
|
|
29
|
+
import { clampReasoningEffort } from '../utils/reasoningEffort.js';
|
|
29
30
|
|
|
30
31
|
// Curated static catalog (verified live 2026-07-11). getSupportedModels() must
|
|
31
32
|
// return exactly these 8 slugs even after a discovery call has run — dynamic
|
|
@@ -255,7 +256,7 @@ function extractReasoningText(details) {
|
|
|
255
256
|
* 1. passthrough (openrouter/auto) → null (router decides)
|
|
256
257
|
* 2. mandatory (kimi-k2.7-code) → { enabled: true } (cannot disable)
|
|
257
258
|
* 3. effort-tiered (glm/deepseek) → clamp into supported_efforts
|
|
258
|
-
* (
|
|
259
|
+
* (nearest stronger tier; none→disabled)
|
|
259
260
|
* 4. enable/disable-only (qwen/kimi) → { enabled: false } for none, else true
|
|
260
261
|
* 5. unavailable metadata → null (omit conservatively)
|
|
261
262
|
*
|
|
@@ -280,14 +281,9 @@ function buildOpenRouterReasoning(modelConfig, reasoningEffort) {
|
|
|
280
281
|
reasoning.supported_efforts.length > 0
|
|
281
282
|
) {
|
|
282
283
|
if (level === 'none') return { enabled: false };
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
? wanted
|
|
287
|
-
: efforts.includes('high')
|
|
288
|
-
? 'high'
|
|
289
|
-
: efforts[0];
|
|
290
|
-
return { effort };
|
|
284
|
+
return {
|
|
285
|
+
effort: clampReasoningEffort(level, reasoning.supported_efforts),
|
|
286
|
+
};
|
|
291
287
|
}
|
|
292
288
|
|
|
293
289
|
// Enable/disable-only.
|
package/src/providers/xai.js
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
import OpenAI from 'openai';
|
|
16
16
|
import { debugLog, debugError } from '../utils/console.js';
|
|
17
|
+
import { clampReasoningEffort } from '../utils/reasoningEffort.js';
|
|
17
18
|
|
|
18
19
|
// Curated catalog: grok-4.5 only (verified live 2026-07-11 against
|
|
19
20
|
// GET https://api.x.ai/v1/models/grok-4.5 — id, aliases, 500k context).
|
|
@@ -85,27 +86,15 @@ function resolveModelName(modelName) {
|
|
|
85
86
|
}
|
|
86
87
|
|
|
87
88
|
/**
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
* (e.g. `none`/`minimal`/`max`) returns HTTP 400, so unsupported Converse
|
|
93
|
-
* levels are clamped into {low, medium, high} rather than forwarded.
|
|
89
|
+
* Tiers grok-4.5 accepts (default high). It cannot disable reasoning — there
|
|
90
|
+
* is no `none`/off value — and sending an unsupported value (`none`,
|
|
91
|
+
* `minimal`, `xhigh`, `max`) returns HTTP 400, so requests are clamped into
|
|
92
|
+
* this set rather than forwarded.
|
|
94
93
|
*/
|
|
94
|
+
const GROK_EFFORT_TIERS = ['low', 'medium', 'high'];
|
|
95
|
+
|
|
95
96
|
function resolveReasoningEffort(reasoningEffort) {
|
|
96
|
-
|
|
97
|
-
case 'none':
|
|
98
|
-
case 'minimal':
|
|
99
|
-
case 'low':
|
|
100
|
-
return 'low';
|
|
101
|
-
case 'medium':
|
|
102
|
-
return 'medium';
|
|
103
|
-
case 'high':
|
|
104
|
-
case 'max':
|
|
105
|
-
return 'high';
|
|
106
|
-
default:
|
|
107
|
-
return 'high';
|
|
108
|
-
}
|
|
97
|
+
return clampReasoningEffort(reasoningEffort, GROK_EFFORT_TIERS);
|
|
109
98
|
}
|
|
110
99
|
|
|
111
100
|
/**
|
package/src/tools/chat.js
CHANGED
|
@@ -27,6 +27,7 @@ import { applyTokenLimit, getTokenLimit } from '../utils/tokenLimiter.js';
|
|
|
27
27
|
import { validateAllPaths } from '../utils/fileValidator.js';
|
|
28
28
|
import { SummarizationService } from '../services/summarizationService.js';
|
|
29
29
|
import { exportConversation } from '../utils/conversationExporter.js';
|
|
30
|
+
import { EFFORT_LADDER } from '../utils/reasoningEffort.js';
|
|
30
31
|
import {
|
|
31
32
|
getDefaultModelForProvider,
|
|
32
33
|
getProviderUnavailableMessage,
|
|
@@ -1187,9 +1188,9 @@ chatTool.inputSchema = {
|
|
|
1187
1188
|
},
|
|
1188
1189
|
reasoning_effort: {
|
|
1189
1190
|
type: 'string',
|
|
1190
|
-
enum:
|
|
1191
|
+
enum: EFFORT_LADDER,
|
|
1191
1192
|
description:
|
|
1192
|
-
'Reasoning depth for thinking models
|
|
1193
|
+
'Reasoning depth for thinking models, weakest to strongest: "none" (reasoning off, where the model allows it), "minimal", "low", "medium" (balanced), "high", "xhigh", "max". Passed through by name when the model accepts it, otherwise clamped to the nearest tier it does. Default: "medium"',
|
|
1193
1194
|
},
|
|
1194
1195
|
async: {
|
|
1195
1196
|
type: 'boolean',
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reasoning Effort Ladder
|
|
3
|
+
*
|
|
4
|
+
* The tool-level `reasoning_effort` vocabulary is the union of the tiers the
|
|
5
|
+
* providers expose, ordered weakest to strongest. Every provider declares the
|
|
6
|
+
* subset its model accepts and clamps the request onto it here, so the same
|
|
7
|
+
* word means the same tier everywhere and a provider never re-derives the
|
|
8
|
+
* ordering on its own.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Every tool-level reasoning_effort value, weakest first. Doubles as the
|
|
13
|
+
* `enum` of the chat tool's `reasoning_effort` parameter.
|
|
14
|
+
* @type {string[]}
|
|
15
|
+
*/
|
|
16
|
+
export const EFFORT_LADDER = [
|
|
17
|
+
'none',
|
|
18
|
+
'minimal',
|
|
19
|
+
'low',
|
|
20
|
+
'medium',
|
|
21
|
+
'high',
|
|
22
|
+
'xhigh',
|
|
23
|
+
'max',
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Effort applied when the caller leaves reasoning_effort unset or sends a
|
|
28
|
+
* value outside the ladder.
|
|
29
|
+
*/
|
|
30
|
+
export const DEFAULT_EFFORT = 'medium';
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Clamp a requested effort onto the tiers a model accepts.
|
|
34
|
+
*
|
|
35
|
+
* The requested tier wins when the model accepts it. Otherwise the nearest
|
|
36
|
+
* *stronger* accepted tier wins: nudging 'minimal' up to 'low' keeps reasoning
|
|
37
|
+
* on, where falling back to 'none' would silently switch it off. Only when
|
|
38
|
+
* nothing stronger exists does the nearest weaker tier apply (e.g. 'max' on a
|
|
39
|
+
* model that tops out at 'xhigh').
|
|
40
|
+
*
|
|
41
|
+
* Tiers in `supported` that are not on the ladder are ignored for ranking but
|
|
42
|
+
* the first of them is used as a last resort when nothing on the ladder
|
|
43
|
+
* matches, so a provider-declared list is never answered with a tier it
|
|
44
|
+
* doesn't contain. An empty list is invalid capability data and throws rather
|
|
45
|
+
* than inventing a tier the model may reject.
|
|
46
|
+
*
|
|
47
|
+
* @param {string|undefined} effort - Tool-level reasoning_effort value
|
|
48
|
+
* @param {string[]} supported - Tiers the model accepts (any order)
|
|
49
|
+
* @returns {string} A tier from `supported`
|
|
50
|
+
*/
|
|
51
|
+
export function clampReasoningEffort(effort, supported) {
|
|
52
|
+
if (!Array.isArray(supported) || supported.length === 0) {
|
|
53
|
+
throw new TypeError(
|
|
54
|
+
'clampReasoningEffort requires a non-empty list of supported tiers',
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const desired = EFFORT_LADDER.includes(effort) ? effort : DEFAULT_EFFORT;
|
|
59
|
+
if (supported.includes(desired)) {
|
|
60
|
+
return desired;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const rank = EFFORT_LADDER.indexOf(desired);
|
|
64
|
+
const stronger = EFFORT_LADDER.slice(rank + 1).find((tier) =>
|
|
65
|
+
supported.includes(tier),
|
|
66
|
+
);
|
|
67
|
+
if (stronger) {
|
|
68
|
+
return stronger;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const weaker = EFFORT_LADDER.slice(0, rank)
|
|
72
|
+
.reverse()
|
|
73
|
+
.find((tier) => supported.includes(tier));
|
|
74
|
+
return weaker || supported[0];
|
|
75
|
+
}
|