From 09b4ad8f01fe845813e1e37098f2c33104655e5b Mon Sep 17 00:00:00 2001 From: Kfir Amar Date: Tue, 3 Feb 2026 23:24:07 +0200 Subject: [PATCH] feat(request): add thinking block extraction and mapping - Implement low/medium/high/max variants mapping to token budgets - Add support for explicit thinkingConfig budget overrides - Include unit tests for thinking configuration resolution Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus --- src/core/request/thinking.ts | 56 ++++++++++++++++++++++++++++++++++++ test/thinking.test.js | 30 +++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 src/core/request/thinking.ts create mode 100644 test/thinking.test.js diff --git a/src/core/request/thinking.ts b/src/core/request/thinking.ts new file mode 100644 index 0000000..c9dbe1e --- /dev/null +++ b/src/core/request/thinking.ts @@ -0,0 +1,56 @@ +type RawBody = { + variant?: unknown + providerOptions?: { + variant?: unknown + modelVariant?: unknown + thinkingConfig?: { + thinkingBudget?: unknown + } + } +} + +export type ThinkingVariant = 'low' | 'medium' | 'high' | 'max' + +export function resolveThinkingConfig( + model: string, + body: unknown, + defaults: { budget: number } = { budget: 20000 } +): { enabled: boolean; budget: number; variant?: string } { + const b = (body || {}) as RawBody + + const rawVariant = + (typeof b.variant === 'string' && b.variant) || + (typeof b.providerOptions?.variant === 'string' && b.providerOptions.variant) || + (typeof b.providerOptions?.modelVariant === 'string' && b.providerOptions.modelVariant) || + undefined + + const explicitBudget = b.providerOptions?.thinkingConfig?.thinkingBudget + const budgetFromBody = + typeof explicitBudget === 'number' && Number.isFinite(explicitBudget) && explicitBudget > 0 + ? explicitBudget + : undefined + + const budgetFromVariant = variantToBudget(rawVariant) + const enabled = + model.endsWith('-thinking') || + budgetFromBody !== undefined || + b.providerOptions?.thinkingConfig !== undefined || + budgetFromVariant !== undefined + + return { + enabled, + budget: budgetFromBody ?? budgetFromVariant ?? defaults.budget, + variant: rawVariant + } +} + +function variantToBudget(rawVariant: string | undefined): number | undefined { + if (!rawVariant) return undefined + + const v = rawVariant.toLowerCase() + if (v === 'low') return 8192 + if (v === 'medium') return 16384 + // "high" is the documented name, but "max" is kept as backward compat + if (v === 'high' || v === 'max') return 32768 + return undefined +} diff --git a/test/thinking.test.js b/test/thinking.test.js new file mode 100644 index 0000000..f20c8d5 --- /dev/null +++ b/test/thinking.test.js @@ -0,0 +1,30 @@ +import assert from 'node:assert/strict' +import { test } from 'node:test' + +import { resolveThinkingConfig } from '../dist/core/request/thinking.js' + +test('resolveThinkingConfig: model suffix enables thinking with default budget', () => { + const r = resolveThinkingConfig('claude-sonnet-4-5-thinking', {}) + assert.equal(r.enabled, true) + assert.equal(r.budget, 20000) +}) + +test('resolveThinkingConfig: explicit thinkingConfig takes precedence', () => { + const r = resolveThinkingConfig('claude-sonnet-4-5', { + providerOptions: { thinkingConfig: { thinkingBudget: 12345 } } + }) + assert.equal(r.enabled, true) + assert.equal(r.budget, 12345) +}) + +test('resolveThinkingConfig: variant low/medium/high maps to budgets', () => { + assert.equal(resolveThinkingConfig('claude-sonnet-4-5', { variant: 'low' }).budget, 8192) + assert.equal(resolveThinkingConfig('claude-sonnet-4-5', { variant: 'medium' }).budget, 16384) + assert.equal(resolveThinkingConfig('claude-sonnet-4-5', { variant: 'high' }).budget, 32768) +}) + +test('resolveThinkingConfig: max is accepted as backward-compatible alias of high', () => { + const r = resolveThinkingConfig('claude-sonnet-4-5', { providerOptions: { variant: 'max' } }) + assert.equal(r.enabled, true) + assert.equal(r.budget, 32768) +})