Claude: map Reasoning Effort to adaptive thinking config (#5219) Supersedes #5105

63fa9c1d0778b171c0650331de1d9f73d3a9cb4e

Cohee <18619528+Cohee1207@users.noreply.github.com>

Signed
3 files changed, +40 -7Ignore whitespace
public/index.html+3 -0
@@ -2182,6 +2182,9 @@
21822182 <div class="toggle-description justifyLeft marginBot5" data-i18n="Constrains the verbosity of the model's response.">
21832183 Constrains the verbosity of the model's response.
21842184 </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>
21852188 </div>
21862189 </div>
21872190 <div class="range-block" data-source="claude">
src/endpoints/backends/chat-completions.js+14 -5
@@ -228,6 +228,7 @@ async function sendClaudeRequest(request, response) {
228228 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);
229229 const useVerbosity = /^claude-(opus-4-5|opus-4-6|sonnet-4-6)/.test(request.body.model);
230230 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);
231232 let fixThinkingPrefill = false;
232233 // Add custom stop sequences
233234 const stopSequences = [];
@@ -305,10 +306,18 @@ async function sendClaudeRequest(request, response) {
305306 }
306307
307308 const reasoningEffort = request.body.reasoning_effort;
308309 const budgetTokens = calculateClaudeBudgetTokens(requestBody.max_tokens, reasoningEffort, requestBody.stream, isAdaptiveModel);
309310
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
312321 fixThinkingPrefill = true;
313322 const minThinkTokens = 1024;
314323 if (requestBody.max_tokens <= minThinkTokens) {
@@ -332,8 +341,8 @@ async function sendClaudeRequest(request, response) {
332341 convertedPrompt.messages[convertedPrompt.messages.length - 1].role = 'user';
333342 }
334343
335344 // Verbosity = 'effort' (same values as OpenAI) - only if not already set by adaptive thinking
336345 if (useVerbosity && request.body.verbosity && !requestBody.output_config?.effort) {
337346 betaHeaders.push('effort-2025-11-24');
338347 requestBody.output_config ??= {};
339348 requestBody.output_config.effort = request.body.verbosity;
src/prompt-converters.js+23 -2
@@ -1110,12 +1110,33 @@ export function cachingSystemPromptForOpenRouter(messages, ttl = undefined) {
11101110
11111111/**
11121112 * 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.
11131114 * @param {number} maxTokens Maximum tokens
11141115 * @param {string} reasoningEffort Reasoning effort
11151116 * @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
11171119 */
11181120export 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+
11191140 let budgetTokens = 0;
11201141
11211142 switch (reasoningEffort) {