@@ -291,6 +291,159 @@ func TestChatCompletionStreamAPIError(t *testing.T) {
291291 }
292292}
293293
294+ func TestChatCompletionStreamAutoInjectsStreamOptions (t * testing.T ) {
295+ srv := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
296+ var req ChatRequest
297+ if err := json .NewDecoder (r .Body ).Decode (& req ); err != nil {
298+ t .Errorf ("decode request: %v" , err )
299+ }
300+
301+ if req .StreamOptions == nil {
302+ t .Error ("StreamOptions = nil, want non-nil" )
303+ } else if ! req .StreamOptions .IncludeUsage {
304+ t .Error ("StreamOptions.IncludeUsage = false, want true" )
305+ }
306+
307+ w .Header ().Set ("Content-Type" , "text/event-stream" )
308+ flusher , _ := w .(http.Flusher )
309+ _ , _ = fmt .Fprint (w , "data: [DONE]\n \n " )
310+ flusher .Flush ()
311+ }))
312+ defer srv .Close ()
313+
314+ c , err := NewClient (WithAPIKey ("sk-test" ), WithBaseURL (srv .URL ))
315+ if err != nil {
316+ t .Fatalf ("NewClient: %v" , err )
317+ }
318+
319+ // Caller does NOT set StreamOptions.
320+ stream , err := c .ChatCompletionStream (context .Background (), & ChatRequest {
321+ Model : ModelOpenaiGPT4o ,
322+ Messages : []Message {{Role : RoleUser , Content : NewTextContent ("Hi" )}},
323+ })
324+ if err != nil {
325+ t .Fatalf ("ChatCompletionStream: %v" , err )
326+ }
327+ defer func () { _ = stream .Close () }()
328+
329+ // Drain the stream.
330+ for {
331+ _ , err := stream .Recv ()
332+ if errors .Is (err , io .EOF ) {
333+ break
334+ }
335+ if err != nil {
336+ t .Fatalf ("Recv: %v" , err )
337+ }
338+ }
339+ }
340+
341+ func TestChatCompletionStreamPreservesExplicitStreamOptions (t * testing.T ) {
342+ srv := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
343+ var req ChatRequest
344+ if err := json .NewDecoder (r .Body ).Decode (& req ); err != nil {
345+ t .Errorf ("decode request: %v" , err )
346+ }
347+
348+ if req .StreamOptions == nil {
349+ t .Error ("StreamOptions = nil, want non-nil" )
350+ } else if req .StreamOptions .IncludeUsage {
351+ t .Error ("StreamOptions.IncludeUsage = true, want false (caller explicitly set false)" )
352+ }
353+
354+ w .Header ().Set ("Content-Type" , "text/event-stream" )
355+ flusher , _ := w .(http.Flusher )
356+ _ , _ = fmt .Fprint (w , "data: [DONE]\n \n " )
357+ flusher .Flush ()
358+ }))
359+ defer srv .Close ()
360+
361+ c , err := NewClient (WithAPIKey ("sk-test" ), WithBaseURL (srv .URL ))
362+ if err != nil {
363+ t .Fatalf ("NewClient: %v" , err )
364+ }
365+
366+ // Caller explicitly sets IncludeUsage: false.
367+ stream , err := c .ChatCompletionStream (context .Background (), & ChatRequest {
368+ Model : ModelOpenaiGPT4o ,
369+ Messages : []Message {{Role : RoleUser , Content : NewTextContent ("Hi" )}},
370+ StreamOptions : & StreamOptions {IncludeUsage : false },
371+ })
372+ if err != nil {
373+ t .Fatalf ("ChatCompletionStream: %v" , err )
374+ }
375+ defer func () { _ = stream .Close () }()
376+
377+ for {
378+ _ , err := stream .Recv ()
379+ if errors .Is (err , io .EOF ) {
380+ break
381+ }
382+ if err != nil {
383+ t .Fatalf ("Recv: %v" , err )
384+ }
385+ }
386+ }
387+
388+ func TestChatCompletionStreamUsage (t * testing.T ) {
389+ srv := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
390+ w .Header ().Set ("Content-Type" , "text/event-stream" )
391+ flusher , _ := w .(http.Flusher )
392+
393+ chunks := []string {
394+ `{"id":"1","object":"chat.completion.chunk","created":1,"model":"gpt-4o","choices":[{"index":0,"delta":{"role":"assistant","content":"Hello"},"finish_reason":null}]}` ,
395+ `{"id":"1","object":"chat.completion.chunk","created":1,"model":"gpt-4o","choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"usage":{"prompt_tokens":8,"completion_tokens":16,"total_tokens":24}}` ,
396+ }
397+
398+ for _ , chunk := range chunks {
399+ _ , _ = fmt .Fprintf (w , "data: %s\n \n " , chunk )
400+ flusher .Flush ()
401+ }
402+
403+ _ , _ = fmt .Fprint (w , "data: [DONE]\n \n " )
404+ flusher .Flush ()
405+ }))
406+ defer srv .Close ()
407+
408+ c , err := NewClient (WithAPIKey ("sk-test" ), WithBaseURL (srv .URL ))
409+ if err != nil {
410+ t .Fatalf ("NewClient: %v" , err )
411+ }
412+
413+ stream , err := c .ChatCompletionStream (context .Background (), & ChatRequest {
414+ Model : ModelOpenaiGPT4o ,
415+ Messages : []Message {{Role : RoleUser , Content : NewTextContent ("Hi" )}},
416+ })
417+ if err != nil {
418+ t .Fatalf ("ChatCompletionStream: %v" , err )
419+ }
420+ defer func () { _ = stream .Close () }()
421+
422+ for {
423+ _ , err := stream .Recv ()
424+ if errors .Is (err , io .EOF ) {
425+ break
426+ }
427+ if err != nil {
428+ t .Fatalf ("Recv: %v" , err )
429+ }
430+ }
431+
432+ usage := stream .Usage ()
433+ if usage == nil {
434+ t .Fatal ("Usage() = nil, want non-nil" )
435+ }
436+ if usage .PromptTokens != 8 {
437+ t .Errorf ("prompt_tokens = %d, want 8" , usage .PromptTokens )
438+ }
439+ if usage .CompletionTokens != 16 {
440+ t .Errorf ("completion_tokens = %d, want 16" , usage .CompletionTokens )
441+ }
442+ if usage .TotalTokens != 24 {
443+ t .Errorf ("total_tokens = %d, want 24" , usage .TotalTokens )
444+ }
445+ }
446+
294447func TestChatCompletionWithTools (t * testing.T ) {
295448 srv := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
296449 var req ChatRequest
0 commit comments