@@ -5,6 +5,7 @@ package openai_test
55import (
66 "context"
77 "fmt"
8+ "io"
89 "net/http"
910 "reflect"
1011 "testing"
@@ -281,3 +282,115 @@ func TestContextDeadline(t *testing.T) {
281282 }
282283 }
283284}
285+
286+ func TestContextDeadlineStreaming (t * testing.T ) {
287+ testTimeout := time .After (3 * time .Second )
288+ testDone := make (chan struct {})
289+
290+ deadline := time .Now ().Add (100 * time .Millisecond )
291+ deadlineCtx , cancel := context .WithDeadline (context .Background (), deadline )
292+ defer cancel ()
293+
294+ go func () {
295+ client := openai .NewClient (
296+ option .WithHTTPClient (& http.Client {
297+ Transport : & closureTransport {
298+ fn : func (req * http.Request ) (* http.Response , error ) {
299+ return & http.Response {
300+ StatusCode : 200 ,
301+ Status : "200 OK" ,
302+ Body : io .NopCloser (
303+ io .Reader (readerFunc (func ([]byte ) (int , error ) {
304+ <- req .Context ().Done ()
305+ return 0 , req .Context ().Err ()
306+ })),
307+ ),
308+ }, nil
309+ },
310+ },
311+ }),
312+ )
313+ stream := client .Chat .Completions .NewStreaming (deadlineCtx , openai.ChatCompletionNewParams {
314+ Messages : openai .F ([]openai.ChatCompletionMessageParamUnion {openai.ChatCompletionDeveloperMessageParam {
315+ Content : openai .F ([]openai.ChatCompletionContentPartTextParam {{Text : openai .F ("text" ), Type : openai .F (openai .ChatCompletionContentPartTextTypeText )}}),
316+ Role : openai .F (openai .ChatCompletionDeveloperMessageParamRoleDeveloper ),
317+ }}),
318+ Model : openai .F (openai .ChatModelO3Mini ),
319+ })
320+ for stream .Next () {
321+ _ = stream .Current ()
322+ }
323+ if stream .Err () == nil {
324+ t .Error ("expected there to be a deadline error" )
325+ }
326+ close (testDone )
327+ }()
328+
329+ select {
330+ case <- testTimeout :
331+ t .Fatal ("client didn't finish in time" )
332+ case <- testDone :
333+ if diff := time .Since (deadline ); diff < - 30 * time .Millisecond || 30 * time .Millisecond < diff {
334+ t .Fatalf ("client did not return within 30ms of context deadline, got %s" , diff )
335+ }
336+ }
337+ }
338+
339+ func TestContextDeadlineStreamingWithRequestTimeout (t * testing.T ) {
340+ testTimeout := time .After (3 * time .Second )
341+ testDone := make (chan struct {})
342+ deadline := time .Now ().Add (100 * time .Millisecond )
343+
344+ go func () {
345+ client := openai .NewClient (
346+ option .WithHTTPClient (& http.Client {
347+ Transport : & closureTransport {
348+ fn : func (req * http.Request ) (* http.Response , error ) {
349+ return & http.Response {
350+ StatusCode : 200 ,
351+ Status : "200 OK" ,
352+ Body : io .NopCloser (
353+ io .Reader (readerFunc (func ([]byte ) (int , error ) {
354+ <- req .Context ().Done ()
355+ return 0 , req .Context ().Err ()
356+ })),
357+ ),
358+ }, nil
359+ },
360+ },
361+ }),
362+ )
363+ stream := client .Chat .Completions .NewStreaming (
364+ context .Background (),
365+ openai.ChatCompletionNewParams {
366+ Messages : openai .F ([]openai.ChatCompletionMessageParamUnion {openai.ChatCompletionDeveloperMessageParam {
367+ Content : openai .F ([]openai.ChatCompletionContentPartTextParam {{Text : openai .F ("text" ), Type : openai .F (openai .ChatCompletionContentPartTextTypeText )}}),
368+ Role : openai .F (openai .ChatCompletionDeveloperMessageParamRoleDeveloper ),
369+ }}),
370+ Model : openai .F (openai .ChatModelO3Mini ),
371+ },
372+ option .WithRequestTimeout ((100 * time .Millisecond )),
373+ )
374+ for stream .Next () {
375+ _ = stream .Current ()
376+ }
377+ if stream .Err () == nil {
378+ t .Error ("expected there to be a deadline error" )
379+ }
380+ close (testDone )
381+ }()
382+
383+ select {
384+ case <- testTimeout :
385+ t .Fatal ("client didn't finish in time" )
386+ case <- testDone :
387+ if diff := time .Since (deadline ); diff < - 30 * time .Millisecond || 30 * time .Millisecond < diff {
388+ t .Fatalf ("client did not return within 30ms of context deadline, got %s" , diff )
389+ }
390+ }
391+ }
392+
393+ type readerFunc func ([]byte ) (int , error )
394+
395+ func (f readerFunc ) Read (p []byte ) (int , error ) { return f (p ) }
396+ func (f readerFunc ) Close () error { return nil }
0 commit comments