@@ -231,6 +231,7 @@ export type AnthropicThinkingDisplay = "summarized" | "omitted";
231231
232232const FINE_GRAINED_TOOL_STREAMING_BETA = "fine-grained-tool-streaming-2025-05-14" ;
233233const INTERLEAVED_THINKING_BETA = "interleaved-thinking-2025-05-14" ;
234+ const ANTHROPIC_MIN_THINKING_BUDGET_TOKENS = 1024 ;
234235
235236function getAnthropicCompat ( model : Model < "anthropic-messages" > ) : Required < AnthropicMessagesCompat > {
236237 // Auto-detect session affinity and cache control support from provider
@@ -504,6 +505,7 @@ export const streamAnthropic: StreamFunction<"anthropic-messages", AnthropicOpti
504505) => {
505506 const stream = new AssistantMessageEventStream ( ) ;
506507 const requestContext = prepareClaudeSonnet5RequestContext ( model , context ) ;
508+ const requestOptions = normalizeAnthropicThinkingOptions ( model , options ) ;
507509
508510 void ( async ( ) => {
509511 const output : AssistantMessage = {
@@ -527,7 +529,7 @@ export const streamAnthropic: StreamFunction<"anthropic-messages", AnthropicOpti
527529 // to expose until the terminal stop reason is known.
528530 const refusalBuffer = usesClaudeStreamingRefusalContract ( model )
529531 ? createDeferredEventBuffer < AssistantMessageEvent > ( stream , ( ) =>
530- notifyLlmRequestActivity ( options ?. signal ) ,
532+ notifyLlmRequestActivity ( requestOptions ?. signal ) ,
531533 )
532534 : undefined ;
533535 const eventSink = refusalBuffer ?? stream ;
@@ -544,11 +546,11 @@ export const streamAnthropic: StreamFunction<"anthropic-messages", AnthropicOpti
544546 // caller-owned headers.
545547 let serverSideFallback = false ;
546548
547- if ( options ?. client ) {
548- client = options . client ;
549+ if ( requestOptions ?. client ) {
550+ client = requestOptions . client ;
549551 isOAuth = false ;
550552 } else {
551- const apiKey = options ?. apiKey ?? getEnvApiKey ( model . provider ) ?? "" ;
553+ const apiKey = requestOptions ?. apiKey ?? getEnvApiKey ( model . provider ) ?? "" ;
552554
553555 let copilotDynamicHeaders : Record < string , string > | undefined ;
554556 if ( model . provider === "github-copilot" ) {
@@ -559,40 +561,48 @@ export const streamAnthropic: StreamFunction<"anthropic-messages", AnthropicOpti
559561 } ) ;
560562 }
561563
562- const cacheRetention = options ?. cacheRetention ?? resolveCacheRetention ( ) ;
563- const cacheSessionId = cacheRetention === "none" ? undefined : options ?. sessionId ;
564+ const cacheRetention = requestOptions ?. cacheRetention ?? resolveCacheRetention ( ) ;
565+ const cacheSessionId = cacheRetention === "none" ? undefined : requestOptions ?. sessionId ;
564566
565567 const created = createClient (
566568 model ,
567569 apiKey ,
568- options ?. thinkingEnabled === true ,
569- options ?. interleavedThinking ?? true ,
570+ requestOptions ?. thinkingEnabled === true ,
571+ requestOptions ?. interleavedThinking ?? true ,
570572 shouldUseFineGrainedToolStreamingBeta ( model , requestContext ) ,
571- options ?. headers ,
573+ requestOptions ?. headers ,
572574 copilotDynamicHeaders ,
573575 cacheSessionId ,
574576 ) ;
575577 client = created . client ;
576578 isOAuth = created . isOAuthToken ;
577579 serverSideFallback = created . serverSideFallback ;
578580 }
579- const builtParams = buildParams ( model , requestContext , isOAuth , options , serverSideFallback ) ;
581+ const builtParams = buildParams (
582+ model ,
583+ requestContext ,
584+ isOAuth ,
585+ requestOptions ,
586+ serverSideFallback ,
587+ ) ;
580588 let params = builtParams . params ;
581589 const toolProjection = builtParams . toolProjection ;
582- const nextParams = await options ?. onPayload ?.( params , model ) ;
590+ const nextParams = await requestOptions ?. onPayload ?.( params , model ) ;
583591 if ( nextParams !== undefined ) {
584592 params = nextParams as MessageCreateParamsStreaming ;
585593 }
586594 applyClaudeRequestContract ( params as unknown as Record < string , unknown > , model ) ;
587- const requestOptions = {
588- ...( options ?. signal ? { signal : options . signal } : { } ) ,
589- ...( options ?. timeoutMs !== undefined ? { timeout : options . timeoutMs } : { } ) ,
590- ...( options ?. maxRetries !== undefined ? { maxRetries : options . maxRetries } : { } ) ,
595+ const sdkRequestOptions = {
596+ ...( requestOptions ?. signal ? { signal : requestOptions . signal } : { } ) ,
597+ ...( requestOptions ?. timeoutMs !== undefined ? { timeout : requestOptions . timeoutMs } : { } ) ,
598+ ...( requestOptions ?. maxRetries !== undefined
599+ ? { maxRetries : requestOptions . maxRetries }
600+ : { } ) ,
591601 } ;
592602 const response = await client . messages
593- . create ( { ...params , stream : true } , requestOptions )
603+ . create ( { ...params , stream : true } , sdkRequestOptions )
594604 . asResponse ( ) ;
595- await options ?. onResponse ?.(
605+ await requestOptions ?. onResponse ?.(
596606 { status : response . status , headers : headersToRecord ( response . headers ) } ,
597607 model ,
598608 ) ;
@@ -605,7 +615,7 @@ export const streamAnthropic: StreamFunction<"anthropic-messages", AnthropicOpti
605615
606616 for await ( const event of iterateAnthropicEvents (
607617 response ,
608- options ?. signal ,
618+ requestOptions ?. signal ,
609619 refusalBuffer !== undefined ,
610620 ) ) {
611621 if ( event . type === "message_start" ) {
@@ -904,7 +914,7 @@ export const streamAnthropic: StreamFunction<"anthropic-messages", AnthropicOpti
904914 }
905915 }
906916
907- if ( options ?. signal ?. aborted ) {
917+ if ( requestOptions ?. signal ?. aborted ) {
908918 throw new Error ( "Request was aborted" ) ;
909919 }
910920
@@ -925,7 +935,7 @@ export const streamAnthropic: StreamFunction<"anthropic-messages", AnthropicOpti
925935 refusalBuffer . discard ( ) ;
926936 output . content = [ ] ;
927937 }
928- output . stopReason = options ?. signal ?. aborted ? "aborted" : "error" ;
938+ output . stopReason = requestOptions ?. signal ?. aborted ? "aborted" : "error" ;
929939 output . errorMessage = error instanceof Error ? error . message : JSON . stringify ( error ) ;
930940 stream . push ( { type : "error" , reason : output . stopReason , error : output } ) ;
931941 stream . end ( ) ;
@@ -955,6 +965,25 @@ function supportsAdaptiveThinking(model: Model<"anthropic-messages">): boolean {
955965 return supportsClaudeAdaptiveThinking ( model ) ;
956966}
957967
968+ function normalizeAnthropicThinkingOptions (
969+ model : Model < "anthropic-messages" > ,
970+ options : AnthropicOptions | undefined ,
971+ ) : AnthropicOptions | undefined {
972+ if ( options ?. thinkingEnabled !== true || supportsAdaptiveThinking ( model ) ) {
973+ return options ;
974+ }
975+
976+ const budgetTokens = options . thinkingBudgetTokens ?? ANTHROPIC_MIN_THINKING_BUDGET_TOKENS ;
977+ const maxTokens = options . maxTokens ?? model . maxTokens ;
978+ if ( budgetTokens >= ANTHROPIC_MIN_THINKING_BUDGET_TOKENS && budgetTokens < maxTokens ) {
979+ return options ;
980+ }
981+
982+ // Manual thinking is one request-wide mode: replay, sampling, tool choice,
983+ // headers, and payload construction must all observe the disabled state.
984+ return { ...options , thinkingEnabled : false , thinkingBudgetTokens : undefined } ;
985+ }
986+
958987function supportsNativeXhighEffort ( model : Model < "anthropic-messages" > ) : boolean {
959988 return supportsClaudeNativeXhighEffort ( model ) ;
960989}
@@ -1353,19 +1382,11 @@ function buildParams(
13531382 }
13541383 } else {
13551384 // Budget-based thinking for older models.
1356- // Anthropic SDK requires budget_tokens >= 1024. Sub-minimum budgets
1357- // (including explicit zero) skip the thinking block instead of
1358- // producing a request the API would reject. Option resolution already
1359- // normalizes these for simple/transport paths; this guard protects
1360- // direct streamAnthropic and bundled-plugin callers.
1361- const budgetTokens = options ?. thinkingBudgetTokens ?? 1024 ;
1362- if ( budgetTokens >= 1024 ) {
1363- params . thinking = {
1364- type : "enabled" ,
1365- budget_tokens : budgetTokens ,
1366- display,
1367- } ;
1368- }
1385+ params . thinking = {
1386+ type : "enabled" ,
1387+ budget_tokens : options ?. thinkingBudgetTokens ?? ANTHROPIC_MIN_THINKING_BUDGET_TOKENS ,
1388+ display,
1389+ } ;
13691390 }
13701391 } else if ( options ?. thinkingEnabled === false ) {
13711392 params . thinking = { type : "disabled" } ;
0 commit comments