@@ -2,6 +2,7 @@ import { Anthropic } from "@anthropic-ai/sdk"
22import OpenAI from "openai"
33
44import { UnboundHandler } from "../unbound"
5+ import { asyncStreamFrom , collectStream } from "../../../test-utils/stream"
56
67vi . mock ( "openai" , ( ) => {
78 const createMock = vi . fn ( )
@@ -54,61 +55,59 @@ describe("UnboundHandler", () => {
5455
5556 it ( "streams reasoning chunks from delta.reasoning_content" , async ( ) => {
5657 const mockCreate = ( OpenAI as unknown as any ) ( ) . chat . completions . create
57- mockCreate . mockResolvedValue ( {
58- async * [ Symbol . asyncIterator ] ( ) {
59- yield { choices : [ { delta : { reasoning_content : "thinking..." } } ] }
60- yield { choices : [ { delta : { content : "answer" } } ] }
61- yield { choices : [ { delta : { } } ] , usage : { prompt_tokens : 1 , completion_tokens : 1 } }
62- } ,
63- } )
58+ mockCreate . mockResolvedValue (
59+ asyncStreamFrom ( [
60+ { choices : [ { delta : { reasoning_content : "thinking..." } } ] } ,
61+ { choices : [ { delta : { content : "answer" } } ] } ,
62+ { choices : [ { delta : { } } ] , usage : { prompt_tokens : 1 , completion_tokens : 1 } } ,
63+ ] ) ,
64+ )
6465
6566 const handler = new UnboundHandler ( {
6667 unboundApiKey : "test-key" ,
6768 unboundModelId : "openai/gpt-4o" ,
6869 } )
6970
70- const chunks : any [ ] = [ ]
71- for await ( const chunk of handler . createMessage ( "system" , [ { role : "user" , content : "hi" } ] , {
72- taskId : "t" ,
73- tools : [ ] ,
74- } ) ) {
75- chunks . push ( chunk )
76- }
71+ const chunks = await collectStream (
72+ handler . createMessage ( "system" , [ { role : "user" , content : "hi" } ] , {
73+ taskId : "t" ,
74+ tools : [ ] ,
75+ } ) ,
76+ )
7777
7878 expect ( chunks ) . toContainEqual ( { type : "reasoning" , text : "thinking..." } )
7979 } )
8080
8181 it ( "falls back to delta.reasoning when reasoning_content is absent" , async ( ) => {
8282 const mockCreate = ( OpenAI as unknown as any ) ( ) . chat . completions . create
83- mockCreate . mockResolvedValue ( {
84- async * [ Symbol . asyncIterator ] ( ) {
85- yield { choices : [ { delta : { reasoning : "router-style thought" } } ] }
86- yield { choices : [ { delta : { } } ] , usage : { prompt_tokens : 1 , completion_tokens : 1 } }
87- } ,
88- } )
83+ mockCreate . mockResolvedValue (
84+ asyncStreamFrom ( [
85+ { choices : [ { delta : { reasoning : "router-style thought" } } ] } ,
86+ { choices : [ { delta : { } } ] , usage : { prompt_tokens : 1 , completion_tokens : 1 } } ,
87+ ] ) ,
88+ )
8989
9090 const handler = new UnboundHandler ( {
9191 unboundApiKey : "test-key" ,
9292 unboundModelId : "openai/gpt-4o" ,
9393 } )
9494
95- const chunks : any [ ] = [ ]
96- for await ( const chunk of handler . createMessage ( "system" , [ { role : "user" , content : "hi" } ] , {
97- taskId : "t" ,
98- tools : [ ] ,
99- } ) ) {
100- chunks . push ( chunk )
101- }
95+ const chunks = await collectStream (
96+ handler . createMessage ( "system" , [ { role : "user" , content : "hi" } ] , {
97+ taskId : "t" ,
98+ tools : [ ] ,
99+ } ) ,
100+ )
102101
103102 expect ( chunks ) . toContainEqual ( { type : "reasoning" , text : "router-style thought" } )
104103 } )
105104
106105 it ( "prefers delta.reasoning_content over delta.reasoning when both are present" , async ( ) => {
107106 const mockCreate = ( OpenAI as unknown as any ) ( ) . chat . completions . create
108107
109- mockCreate . mockResolvedValue ( {
110- async * [ Symbol . asyncIterator ] ( ) {
111- yield {
108+ mockCreate . mockResolvedValue (
109+ asyncStreamFrom ( [
110+ {
112111 choices : [
113112 {
114113 delta : {
@@ -117,24 +116,22 @@ describe("UnboundHandler", () => {
117116 } ,
118117 } ,
119118 ] ,
120- }
121- yield { choices : [ { delta : { } } ] , usage : { prompt_tokens : 1 , completion_tokens : 1 } }
122- } ,
123- } )
119+ } ,
120+ { choices : [ { delta : { } } ] , usage : { prompt_tokens : 1 , completion_tokens : 1 } } ,
121+ ] ) ,
122+ )
124123
125124 const handler = new UnboundHandler ( {
126125 unboundApiKey : "test-key" ,
127126 unboundModelId : "openai/gpt-4o" ,
128127 } )
129128
130- const chunks : any [ ] = [ ]
131-
132- for await ( const chunk of handler . createMessage ( "system" , [ { role : "user" , content : "hi" } ] , {
133- taskId : "t" ,
134- tools : [ ] ,
135- } ) ) {
136- chunks . push ( chunk )
137- }
129+ const chunks = await collectStream (
130+ handler . createMessage ( "system" , [ { role : "user" , content : "hi" } ] , {
131+ taskId : "t" ,
132+ tools : [ ] ,
133+ } ) ,
134+ )
138135
139136 const reasoningChunks = chunks . filter ( ( chunk ) => chunk . type === "reasoning" )
140137
@@ -143,17 +140,17 @@ describe("UnboundHandler", () => {
143140
144141 it ( "identifies itself as Zoo Code in per-request Unbound metadata" , async ( ) => {
145142 const mockCreate = ( OpenAI as unknown as any ) ( ) . chat . completions . create
146- mockCreate . mockResolvedValue ( {
147- async * [ Symbol . asyncIterator ] ( ) {
148- yield {
143+ mockCreate . mockResolvedValue (
144+ asyncStreamFrom ( [
145+ {
149146 choices : [ { delta : { content : "ok" } } ] ,
150- }
151- yield {
147+ } ,
148+ {
152149 choices : [ { delta : { } } ] ,
153150 usage : { prompt_tokens : 1 , completion_tokens : 1 } ,
154- }
155- } ,
156- } )
151+ } ,
152+ ] ) ,
153+ )
157154
158155 const handler = new UnboundHandler ( {
159156 unboundApiKey : "test-key" ,
@@ -167,9 +164,7 @@ describe("UnboundHandler", () => {
167164 tools : [ ] ,
168165 } )
169166
170- for await ( const _chunk of stream ) {
171- // drain stream
172- }
167+ await collectStream ( stream )
173168
174169 expect ( mockCreate ) . toHaveBeenCalledWith (
175170 expect . objectContaining ( {
0 commit comments