@@ -41,7 +41,13 @@ import {
4141 convertOpenAIToolsToAnthropic ,
4242 convertOpenAIToolChoiceToAnthropic ,
4343} from "../../core/prompts/tools/native-tools/converters"
44- import { createAbortError , isRequestAborted , resolveModelWithAbort } from "./utils/abort-signal"
44+ import {
45+ createAbortError ,
46+ hasUsableTimeout ,
47+ isRequestAborted ,
48+ resolveModelWithAbort ,
49+ toSdkTimeoutMs ,
50+ } from "./utils/abort-signal"
4551
4652/**
4753 * The wire formats exposed by the Opencode Go gateway:
@@ -856,16 +862,20 @@ export class OpencodeGoHandler extends RouterProvider implements SingleCompletio
856862 reasoningEffort,
857863 maxTokens,
858864 } = await resolveModelWithAbort ( ( ) => this . resolveModel ( ) , options ?. abortSignal , "Opencode Go" )
865+ const timeoutMs = options ?. timeoutMs
859866
860867 if ( format === "anthropic" ) {
861868 try {
862869 // Build request options with abortSignal and/or timeout handling.
863- // timeoutMs <= 0 means "no explicit timeout": omit the SDK timeout
864- // option entirely — the SDKs treat timeout: 0 as an immediate
865- // abort, which would cancel the request right away.
870+ // hasUsableTimeout/toSdkTimeoutMs own the non-positive and non-finite
871+ // semantics: the SDKs treat timeout: 0 as an immediate abort and
872+ // throw on negative/non-finite values, so only a usable budget is
873+ // forwarded.
866874 const requestOptions : Anthropic . RequestOptions = {
867875 ...( options ?. abortSignal && { signal : options . abortSignal } ) ,
868- ...( options ?. timeoutMs !== undefined && options . timeoutMs > 0 && { timeout : options . timeoutMs } ) ,
876+ }
877+ if ( hasUsableTimeout ( timeoutMs ) ) {
878+ requestOptions . timeout = toSdkTimeoutMs ( timeoutMs )
869879 }
870880
871881 const message = await this . anthropicClient . messages . create (
@@ -911,12 +921,14 @@ export class OpencodeGoHandler extends RouterProvider implements SingleCompletio
911921 if ( format === "responses" ) {
912922 try {
913923 // Build request options with abortSignal and/or timeout (series
914- // standard): timeoutMs <= 0 means "no explicit timeout" — the
915- // OpenAI SDK treats timeout: 0 as an immediate abort, so the SDK
916- // timeout option is omitted unless the value is positive .
924+ // standard): hasUsableTimeout/toSdkTimeoutMs own the disabled and
925+ // non-finite semantics, so the SDK option is only set for a usable
926+ // budget .
917927 const createOptions : OpenAI . RequestOptions = {
918928 ...( options ?. abortSignal && { signal : options . abortSignal } ) ,
919- ...( options ?. timeoutMs !== undefined && options . timeoutMs > 0 && { timeout : options . timeoutMs } ) ,
929+ }
930+ if ( hasUsableTimeout ( timeoutMs ) ) {
931+ createOptions . timeout = toSdkTimeoutMs ( timeoutMs )
920932 }
921933
922934 const response = await this . client . responses . create (
@@ -987,13 +999,14 @@ export class OpencodeGoHandler extends RouterProvider implements SingleCompletio
987999 reasoningEffort as OpenAI . Chat . ChatCompletionCreateParams [ "reasoning_effort" ]
9881000 }
9891001
990- // Build request options with abortSignal and/or timeout for OpenAI path.
991- // timeoutMs <= 0 means "no explicit timeout": omit the SDK timeout
992- // option entirely — the OpenAI SDK treats timeout: 0 as an immediate
993- // abort, which would cancel the request right away.
1002+ // Build request options with abortSignal and/or timeout for the OpenAI
1003+ // path: hasUsableTimeout/toSdkTimeoutMs own the disabled and non-finite
1004+ // semantics.
9941005 const createOptions : OpenAI . RequestOptions = {
9951006 ...( options ?. abortSignal && { signal : options . abortSignal } ) ,
996- ...( options ?. timeoutMs !== undefined && options . timeoutMs > 0 && { timeout : options . timeoutMs } ) ,
1007+ }
1008+ if ( hasUsableTimeout ( timeoutMs ) ) {
1009+ createOptions . timeout = toSdkTimeoutMs ( timeoutMs )
9971010 }
9981011
9991012 const response = await this . client . chat . completions . create ( requestOptions , {
0 commit comments