Claude: map Reasoning Effort to adaptive thinking config (#5219) Supersedes #5105
Signed| @@ -2182,6 +2182,9 @@ | ||
| 2182 | 2182 | <div class="toggle-description justifyLeft marginBot5" data-i18n="Constrains the verbosity of the model's response."> |
| 2183 | 2183 | Constrains the verbosity of the model's response. |
| 2184 | 2184 | </div> |
| 2185 | + <strong class="toggle-description justifyLeft marginBot5" data-source="claude"> | |
| 2186 | + On Opus 4.6 / Sonnet 4.6, a non-automatic Reasoning Effort takes precedence over Verbosity. | |
| 2187 | + </strong> | |
| 2185 | 2188 | </div> |
| 2186 | 2189 | </div> |
| 2187 | 2190 | <div class="range-block" data-source="claude"> |
| @@ -228,6 +228,7 @@ async function sendClaudeRequest(request, response) { | ||
| 228 | 228 | const isLimitedSampling = /^claude-(opus-4-1|sonnet-4-5|haiku-4-5|opus-4-5|opus-4-6|sonnet-4-6)/.test(request.body.model); |
| 229 | 229 | const useVerbosity = /^claude-(opus-4-5|opus-4-6|sonnet-4-6)/.test(request.body.model); |
| 230 | 230 | const noPrefillModel = /^claude-(opus-4-6|sonnet-4-6)/.test(request.body.model); |
| 231 | + const isAdaptiveModel = /^claude-(opus-4-6|sonnet-4-6)/.test(request.body.model); | |
| 231 | 232 | let fixThinkingPrefill = false; |
| 232 | 233 | // Add custom stop sequences |
| 233 | 234 | const stopSequences = []; |
| @@ -305,10 +306,18 @@ async function sendClaudeRequest(request, response) { | ||
| 305 | 306 | } |
| 306 | 307 | |
| 307 | 308 | const reasoningEffort = request.body.reasoning_effort; |
| 308 | 309 | const budgetTokens = calculateClaudeBudgetTokens(requestBody.max_tokens, reasoningEffort, requestBody.stream, isAdaptiveModel); |
| 309 | 310 | |
| 310 | - if (useThinking && Number.isInteger(budgetTokens)) { | |
| 311 | + // Adaptive thinking: returns a string effort level (like Gemini 3) | |
| 311 | - // No prefill when thinking | |
| 312 | + if (useThinking && typeof budgetTokens === 'string') { | |
| 313 | + fixThinkingPrefill = true; | |
| 314 | + requestBody.thinking = { type: 'adaptive' }; | |
| 315 | + requestBody.output_config ??= {}; | |
| 316 | + requestBody.output_config.effort = budgetTokens; | |
| 317 | + // top_k is not allowed in adaptive mode | |
| 318 | + delete requestBody.top_k; | |
| 319 | + } else if (useThinking && Number.isInteger(budgetTokens)) { | |
| 320 | + // Traditional thinking: returns a numeric budget | |
| 312 | 321 | fixThinkingPrefill = true; |
| 313 | 322 | const minThinkTokens = 1024; |
| 314 | 323 | if (requestBody.max_tokens <= minThinkTokens) { |
| @@ -332,8 +341,8 @@ async function sendClaudeRequest(request, response) { | ||
| 332 | 341 | convertedPrompt.messages[convertedPrompt.messages.length - 1].role = 'user'; |
| 333 | 342 | } |
| 334 | 343 | |
| 335 | 344 | // Verbosity = 'effort' (same values as OpenAI) - only if not already set by adaptive thinking |
| 336 | 345 | if (useVerbosity && request.body.verbosity && !requestBody.output_config?.effort) { |
| 337 | 346 | betaHeaders.push('effort-2025-11-24'); |
| 338 | 347 | requestBody.output_config ??= {}; |
| 339 | 348 | requestBody.output_config.effort = request.body.verbosity; |
| @@ -1110,12 +1110,33 @@ export function cachingSystemPromptForOpenRouter(messages, ttl = undefined) { | ||
| 1110 | 1110 | |
| 1111 | 1111 | /** |
| 1112 | 1112 | * Calculate the Claude budget tokens for a given reasoning effort. |
| 1113 | + * Returns a string effort level for adaptive thinking (Opus 4.6+), a number for traditional thinking, or null for auto. | |
| 1113 | 1114 | * @param {number} maxTokens Maximum tokens |
| 1114 | 1115 | * @param {string} reasoningEffort Reasoning effort |
| 1115 | 1116 | * @param {boolean} stream If streaming is enabled |
| 1116 | - * @returns {number?} Budget tokens | |
| 1117 | + * @param {boolean} isAdaptiveModel If the model supports adaptive thinking (Opus 4.6+) | |
| 1118 | + * @returns {number|string|null} Budget tokens, effort string, or null | |
| 1117 | 1119 | */ |
| 1118 | 1120 | export function calculateClaudeBudgetTokens(maxTokens, reasoningEffort, stream, isAdaptiveModel) { |
| 1121 | + // Adaptive thinking for Opus 4.6+: return effort string (like Gemini 3) | |
| 1122 | + if (isAdaptiveModel) { | |
| 1123 | + switch (reasoningEffort) { | |
| 1124 | + case REASONING_EFFORT.auto: | |
| 1125 | + return null; | |
| 1126 | + case REASONING_EFFORT.min: | |
| 1127 | + return 'low'; | |
| 1128 | + case REASONING_EFFORT.low: | |
| 1129 | + return 'low'; | |
| 1130 | + case REASONING_EFFORT.medium: | |
| 1131 | + return 'medium'; | |
| 1132 | + case REASONING_EFFORT.high: | |
| 1133 | + return 'high'; | |
| 1134 | + case REASONING_EFFORT.max: | |
| 1135 | + return 'max'; | |
| 1136 | + } | |
| 1137 | + return null; | |
| 1138 | + } | |
| 1139 | + | |
| 1119 | 1140 | let budgetTokens = 0; |
| 1120 | 1141 | |
| 1121 | 1142 | switch (reasoningEffort) { |