@@ -428,20 +428,77 @@ describe("AnthropicHandler", () => {
428428 expect ( requestBody ?. max_tokens ) . toBe ( 32768 )
429429 expect ( requestOptions ?. headers ?. [ "anthropic-beta" ] ) . toContain ( "prompt-caching-2024-07-31" )
430430 } )
431+
432+ it ( "should pass signal for models outside the prompt-caching list" , async ( ) => {
433+ // Mock getModel to return an ID not in the createMessage outer switch,
434+ // so it hits the default (non-caching) path which passes { signal }.
435+ const modelInfo = handler . getModel ( )
436+ vitest . spyOn ( handler , "getModel" ) . mockReturnValue ( {
437+ ...modelInfo ,
438+ id : "non-cached-model" ,
439+ } as any )
440+
441+ const stream = handler . createMessage ( systemPrompt , [
442+ {
443+ role : "user" ,
444+ content : [ { type : "text" as const , text : "Hello" } ] ,
445+ } ,
446+ ] )
447+
448+ for await ( const _chunk of stream ) {
449+ // Consume stream
450+ }
451+
452+ // Verify messages.create was called with { signal } (no prompt-caching headers)
453+ const requestOptions = mockCreate . mock . calls [ mockCreate . mock . calls . length - 1 ] ?. [ 1 ]
454+ expect ( requestOptions ) . toEqual ( expect . objectContaining ( { signal : expect . any ( AbortSignal ) } ) )
455+ expect ( requestOptions ?. headers ) . toBeUndefined ( )
456+ } )
457+
458+ it ( "should pass signal when inner prompt-caching switch hits default" , async ( ) => {
459+ // Mock getModel to return the coverage-inner-default model, which
460+ // is in the outer prompt-caching switch but NOT in the inner
461+ // prompt-caching beta switch, so the inner default returns { signal }.
462+ const modelInfo = handler . getModel ( )
463+ vitest . spyOn ( handler , "getModel" ) . mockReturnValue ( {
464+ ...modelInfo ,
465+ id : "coverage-inner-default" ,
466+ } as any )
467+
468+ const stream = handler . createMessage ( systemPrompt , [
469+ {
470+ role : "user" ,
471+ content : [ { type : "text" as const , text : "Hello" } ] ,
472+ } ,
473+ ] )
474+
475+ for await ( const _chunk of stream ) {
476+ // Consume stream
477+ }
478+
479+ // Verify messages.create was called with { signal } and no
480+ // prompt-caching beta header (the inner switch default path).
481+ const requestOptions = mockCreate . mock . calls [ mockCreate . mock . calls . length - 1 ] ?. [ 1 ]
482+ expect ( requestOptions ) . toEqual ( expect . objectContaining ( { signal : expect . any ( AbortSignal ) } ) )
483+ expect ( requestOptions ?. headers ) . toBeUndefined ( )
484+ } )
431485 } )
432486
433487 describe ( "completePrompt" , ( ) => {
434488 it ( "should complete prompt successfully" , async ( ) => {
435489 const result = await handler . completePrompt ( "Test prompt" )
436490 expect ( result ) . toBe ( "Test response" )
437- expect ( mockCreate ) . toHaveBeenCalledWith ( {
438- model : mockOptions . apiModelId ,
439- messages : [ { role : "user" , content : "Test prompt" } ] ,
440- max_tokens : 8192 ,
441- temperature : 0 ,
442- thinking : undefined ,
443- stream : false ,
444- } )
491+ expect ( mockCreate ) . toHaveBeenCalledWith (
492+ {
493+ model : mockOptions . apiModelId ,
494+ messages : [ { role : "user" , content : "Test prompt" } ] ,
495+ max_tokens : 8192 ,
496+ temperature : 0 ,
497+ thinking : undefined ,
498+ stream : false ,
499+ } ,
500+ expect . objectContaining ( { signal : expect . any ( AbortSignal ) } ) ,
501+ )
445502 } )
446503
447504 it ( "should handle API errors" , async ( ) => {
@@ -1057,4 +1114,76 @@ describe("AnthropicHandler", () => {
10571114 } )
10581115 } )
10591116 } )
1117+
1118+ describe ( "content_block_start and content_block_delta coverage" , ( ) => {
1119+ it ( "should handle thinking content_block_start" , async ( ) => {
1120+ mockCreate . mockImplementationOnce ( async ( ) => ( {
1121+ async * [ Symbol . asyncIterator ] ( ) {
1122+ yield { type : "message_start" , message : { usage : { input_tokens : 1 , output_tokens : 1 } } }
1123+ yield {
1124+ type : "content_block_start" ,
1125+ index : 0 ,
1126+ content_block : { type : "thinking" , thinking : "let me think" } ,
1127+ }
1128+ yield { type : "message_stop" }
1129+ } ,
1130+ } ) )
1131+
1132+ const stream = handler . createMessage ( "sys" , [ { role : "user" , content : "hi" } ] )
1133+ const chunks : any [ ] = [ ]
1134+ for await ( const c of stream ) {
1135+ chunks . push ( c )
1136+ }
1137+ const reasoning = chunks . filter ( ( c ) => c . type === "reasoning" )
1138+ expect ( reasoning . some ( ( c ) => c . text === "let me think" ) ) . toBe ( true )
1139+ } )
1140+
1141+ it ( "should insert newline for second text content_block_start" , async ( ) => {
1142+ mockCreate . mockImplementationOnce ( async ( ) => ( {
1143+ async * [ Symbol . asyncIterator ] ( ) {
1144+ yield { type : "message_start" , message : { usage : { input_tokens : 1 , output_tokens : 1 } } }
1145+ yield {
1146+ type : "content_block_start" ,
1147+ index : 0 ,
1148+ content_block : { type : "text" , text : "first" } ,
1149+ }
1150+ yield {
1151+ type : "content_block_start" ,
1152+ index : 1 ,
1153+ content_block : { type : "text" , text : "" } ,
1154+ }
1155+ yield { type : "message_stop" }
1156+ } ,
1157+ } ) )
1158+
1159+ const stream = handler . createMessage ( "sys" , [ { role : "user" , content : "hi" } ] )
1160+ const chunks : any [ ] = [ ]
1161+ for await ( const c of stream ) {
1162+ chunks . push ( c )
1163+ }
1164+ const textChunks = chunks . filter ( ( c ) => c . type === "text" )
1165+ expect ( textChunks . some ( ( c ) => c . text === "\n" ) ) . toBe ( true )
1166+ } )
1167+
1168+ it ( "should handle thinking_delta" , async ( ) => {
1169+ mockCreate . mockImplementationOnce ( async ( ) => ( {
1170+ async * [ Symbol . asyncIterator ] ( ) {
1171+ yield { type : "message_start" , message : { usage : { input_tokens : 1 , output_tokens : 1 } } }
1172+ yield {
1173+ type : "content_block_delta" ,
1174+ delta : { type : "thinking_delta" , thinking : "hmm" } ,
1175+ }
1176+ yield { type : "message_stop" }
1177+ } ,
1178+ } ) )
1179+
1180+ const stream = handler . createMessage ( "sys" , [ { role : "user" , content : "hi" } ] )
1181+ const chunks : any [ ] = [ ]
1182+ for await ( const c of stream ) {
1183+ chunks . push ( c )
1184+ }
1185+ const reasoning = chunks . filter ( ( c ) => c . type === "reasoning" )
1186+ expect ( reasoning . some ( ( c ) => c . text === "hmm" ) ) . toBe ( true )
1187+ } )
1188+ } )
10601189} )
0 commit comments