From adb2ed2b38e36c9f34b63c17428659f7854a3f61 Mon Sep 17 00:00:00 2001 From: multipleof4 Date: Mon, 30 Mar 2026 23:44:08 -0700 Subject: [PATCH] Feat: Wire up Claude adaptive thinking + effort Co-authored-by: Opus 4.6 --- providers.js | 84 ++++++++++++++++------------------------------------ 1 file changed, 26 insertions(+), 58 deletions(-) diff --git a/providers.js b/providers.js index 230974e..623a217 100644 --- a/providers.js +++ b/providers.js @@ -143,10 +143,6 @@ export async function streamClaude({ apiKey, body, signal, onDelta, isRunning }) const online = (body.model ?? '').endsWith(':online') const model = online ? body.model.slice(0, -7) : body.model - const includeThoughts = body.reasoning?.exclude !== true - const effort = body.reasoning?.effort - const useAdaptive = effort && effort !== 'default' - const system = body.messages .filter(m => m.role === 'system') .map(extractText) @@ -165,18 +161,17 @@ export async function streamClaude({ apiKey, body, signal, onDelta, isRunning }) }).filter(Boolean), })).filter(m => m.content.length), max_tokens: body.max_tokens || 64000, - stream: true, } - - if (useAdaptive) { - payload.thinking = { type: 'adaptive' } - if (!includeThoughts) payload.thinking.display = 'omitted' - payload.output_config = { effort } - } - if (system) payload.system = system - if (Number.isFinite(+body.temperature)) payload.temperature = +body.temperature - if (Number.isFinite(+body.top_p)) payload.top_p = +body.top_p + + const effort = body.reasoning?.effort + if (effort && effort !== 'default') { + payload.thinking = { type: 'adaptive' } + payload.output_config = { effort } + } else { + if (Number.isFinite(+body.temperature)) payload.temperature = +body.temperature + if (Number.isFinite(+body.top_p)) payload.top_p = +body.top_p + } if (online) { payload.tools = [ @@ -185,53 +180,26 @@ export async function streamClaude({ apiKey, body, signal, onDelta, isRunning }) ] } - const resp = await fetch('https://api.anthropic.com/v1/messages', { - method: 'POST', - headers: { - 'x-api-key': apiKey, - 'anthropic-version': '2023-06-01', - 'content-type': 'application/json', - }, - body: JSON.stringify(payload), - signal, - }) - if (!resp.ok) throw new Error(`Claude API error: ${resp.status} ${await resp.text()}`) + const includeThoughts = body.reasoning?.exclude !== true + let hasThinking = false, hasContent = false - const reader = resp.body.getReader() - const dec = new TextDecoder() - let buf = '', hasThinking = false, hasContent = false - - while (isRunning()) { - const { done, value } = await reader.read() - if (done) break - buf += dec.decode(value, { stream: true }) - const lines = buf.split('\n') - buf = lines.pop() - for (const line of lines) { - if (!line.startsWith('data: ')) continue - const data = line.substring(6).trim() - if (!data) continue - try { - const event = JSON.parse(data) - if (event.type === 'content_block_delta') { - const delta = event.delta - if (delta?.type === 'thinking_delta' && delta.thinking && includeThoughts) { - onDelta(delta.thinking) - hasThinking = true - } else if (delta?.type === 'text_delta' && delta.text) { - if (hasThinking && !hasContent) onDelta('\n') - onDelta(delta.text) - hasContent = true - } - } else if (event.type === 'message_delta' && event.delta?.stop_reason) { - break - } else if (event.type === 'error') { - throw new Error(event.error?.message || 'Claude stream error') - } - } catch (e) { - if (e.message?.includes('Claude')) throw e + const stream = client.messages.stream(payload) + try { + for await (const event of stream) { + if (!isRunning()) break + if (event.type !== 'content_block_delta') continue + const delta = event.delta + if (delta.type === 'thinking_delta' && includeThoughts) { + onDelta(delta.thinking) + hasThinking = true + } else if (delta.type === 'text_delta') { + if (hasThinking && !hasContent) onDelta('\n') + onDelta(delta.text) + hasContent = true } } + } finally { + try { stream.controller?.abort() } catch {} } }