@@ -950,4 +950,179 @@ describe("MimoHandler", () => {
950950 expect ( params . model ) . toBe ( "mimo-v2.5" )
951951 } )
952952 } )
953+
954+ describe ( "advanced streaming scenarios" , ( ) => {
955+ it ( "should handle stream with multiple text chunks concatenated" , async ( ) => {
956+ mockCreate . mockImplementationOnce ( async ( ) => ( {
957+ [ Symbol . asyncIterator ] : async function * ( ) {
958+ yield {
959+ choices : [ { delta : { content : "Hello" } , index : 0 } ] ,
960+ usage : null ,
961+ }
962+ yield {
963+ choices : [ { delta : { content : " world" } , index : 0 } ] ,
964+ usage : null ,
965+ }
966+ yield {
967+ choices : [ { delta : { content : "!" } , index : 0 } ] ,
968+ usage : null ,
969+ }
970+ yield {
971+ choices : [ { delta : { } , index : 0 , finish_reason : "stop" } ] ,
972+ usage : { prompt_tokens : 10 , completion_tokens : 3 , total_tokens : 13 } ,
973+ }
974+ } ,
975+ } ) )
976+
977+ const messages : Anthropic . Messages . MessageParam [ ] = [
978+ { role : "user" , content : [ { type : "text" , text : "Hi" } ] } ,
979+ ]
980+
981+ const chunks : any [ ] = [ ]
982+ const stream = handler . createMessage ( "System prompt" , messages )
983+ for await ( const chunk of stream ) {
984+ chunks . push ( chunk )
985+ }
986+
987+ const textChunks = chunks . filter ( ( c ) => c . type === "text" )
988+ expect ( textChunks ) . toHaveLength ( 3 )
989+ expect ( textChunks . map ( ( c : any ) => c . text ) . join ( "" ) ) . toBe ( "Hello world!" )
990+ } )
991+
992+ it ( "should handle stream with both reasoning and tool calls" , async ( ) => {
993+ mockCreate . mockImplementationOnce ( async ( ) => ( {
994+ [ Symbol . asyncIterator ] : async function * ( ) {
995+ yield {
996+ choices : [ { delta : { reasoning_content : "Let me think" } , index : 0 } ] ,
997+ usage : null ,
998+ }
999+ yield {
1000+ choices : [ { delta : { reasoning_content : " about this" } , index : 0 } ] ,
1001+ usage : null ,
1002+ }
1003+ yield {
1004+ choices : [ { delta : { content : "I'll read it" } , index : 0 } ] ,
1005+ usage : null ,
1006+ }
1007+ yield {
1008+ choices : [
1009+ {
1010+ delta : {
1011+ tool_calls : [
1012+ {
1013+ index : 0 ,
1014+ id : "call_read" ,
1015+ function : { name : "read_file" , arguments : '{"path":"test.ts"}' } ,
1016+ } ,
1017+ ] ,
1018+ } ,
1019+ index : 0 ,
1020+ } ,
1021+ ] ,
1022+ usage : null ,
1023+ }
1024+ yield {
1025+ choices : [ { delta : { } , index : 0 , finish_reason : "tool_calls" } ] ,
1026+ usage : { prompt_tokens : 20 , completion_tokens : 15 , total_tokens : 35 } ,
1027+ }
1028+ } ,
1029+ } ) )
1030+
1031+ const messages : Anthropic . Messages . MessageParam [ ] = [
1032+ { role : "user" , content : [ { type : "text" , text : "Read test.ts" } ] } ,
1033+ ]
1034+
1035+ const chunks : any [ ] = [ ]
1036+ const stream = handler . createMessage ( "System prompt" , messages )
1037+ for await ( const chunk of stream ) {
1038+ chunks . push ( chunk )
1039+ }
1040+
1041+ const reasoningChunks = chunks . filter ( ( c ) => c . type === "reasoning" )
1042+ expect ( reasoningChunks ) . toHaveLength ( 2 )
1043+ expect ( reasoningChunks . map ( ( c : any ) => c . text ) . join ( "" ) ) . toBe ( "Let me think about this" )
1044+
1045+ const textChunks = chunks . filter ( ( c ) => c . type === "text" )
1046+ expect ( textChunks ) . toHaveLength ( 1 )
1047+ expect ( textChunks [ 0 ] . text ) . toBe ( "I'll read it" )
1048+
1049+ const toolChunks = chunks . filter ( ( c ) => c . type === "tool_call_partial" )
1050+ expect ( toolChunks ) . toHaveLength ( 1 )
1051+ expect ( toolChunks [ 0 ] . id ) . toBe ( "call_read" )
1052+ expect ( toolChunks [ 0 ] . name ) . toBe ( "read_file" )
1053+ } )
1054+
1055+ it ( "should handle stream with no usage in final chunk" , async ( ) => {
1056+ mockCreate . mockImplementationOnce ( async ( ) => ( {
1057+ [ Symbol . asyncIterator ] : async function * ( ) {
1058+ yield {
1059+ choices : [ { delta : { content : "Done" } , index : 0 } ] ,
1060+ usage : null ,
1061+ }
1062+ yield {
1063+ choices : [ { delta : { } , index : 0 , finish_reason : "stop" } ] ,
1064+ usage : null ,
1065+ }
1066+ } ,
1067+ } ) )
1068+
1069+ const messages : Anthropic . Messages . MessageParam [ ] = [
1070+ { role : "user" , content : [ { type : "text" , text : "Hello" } ] } ,
1071+ ]
1072+
1073+ const chunks : any [ ] = [ ]
1074+ const stream = handler . createMessage ( "System prompt" , messages )
1075+ for await ( const chunk of stream ) {
1076+ chunks . push ( chunk )
1077+ }
1078+
1079+ const usageChunks = chunks . filter ( ( c ) => c . type === "usage" )
1080+ expect ( usageChunks ) . toHaveLength ( 0 )
1081+
1082+ const textChunks = chunks . filter ( ( c ) => c . type === "text" )
1083+ expect ( textChunks ) . toHaveLength ( 1 )
1084+ expect ( textChunks [ 0 ] . text ) . toBe ( "Done" )
1085+ } )
1086+
1087+ it ( "should handle stream with zero cache tokens in usage" , async ( ) => {
1088+ mockCreate . mockImplementationOnce ( async ( ) => ( {
1089+ [ Symbol . asyncIterator ] : async function * ( ) {
1090+ yield {
1091+ choices : [ { delta : { content : "Hi" } , index : 0 } ] ,
1092+ usage : null ,
1093+ }
1094+ yield {
1095+ choices : [ { delta : { } , index : 0 , finish_reason : "stop" } ] ,
1096+ usage : {
1097+ prompt_tokens : 50 ,
1098+ completion_tokens : 10 ,
1099+ total_tokens : 60 ,
1100+ prompt_tokens_details : {
1101+ cache_write_tokens : 0 ,
1102+ cached_tokens : 0 ,
1103+ } ,
1104+ } ,
1105+ }
1106+ } ,
1107+ } ) )
1108+
1109+ const messages : Anthropic . Messages . MessageParam [ ] = [
1110+ { role : "user" , content : [ { type : "text" , text : "Hello" } ] } ,
1111+ ]
1112+
1113+ const chunks : any [ ] = [ ]
1114+ const stream = handler . createMessage ( "System prompt" , messages )
1115+ for await ( const chunk of stream ) {
1116+ chunks . push ( chunk )
1117+ }
1118+
1119+ const usageChunks = chunks . filter ( ( c ) => c . type === "usage" )
1120+ expect ( usageChunks ) . toHaveLength ( 1 )
1121+ expect ( usageChunks [ 0 ] . inputTokens ) . toBe ( 50 )
1122+ expect ( usageChunks [ 0 ] . outputTokens ) . toBe ( 10 )
1123+ // Handler uses `|| undefined` so zero-valued cache tokens are omitted
1124+ expect ( usageChunks [ 0 ] . cacheWriteTokens ) . toBeUndefined ( )
1125+ expect ( usageChunks [ 0 ] . cacheReadTokens ) . toBeUndefined ( )
1126+ } )
1127+ } )
9531128} )
0 commit comments