@@ -472,9 +472,132 @@ describe('ToolRunner', () => {
472472 for await ( const event of stream ) {
473473 events . push ( event ) ;
474474 }
475- // Verify we get the expected number of events (with chunked JSON, we'll get more deltas), but we
476- // should get at least 6 including
477- expect ( events . length ) . toBeGreaterThanOrEqual ( 6 ) ;
475+
476+ // 1. Initial chunk with role only (no content, no tool_calls)
477+ expect ( events [ 0 ] ) . toMatchObject ( {
478+ choices : [
479+ {
480+ delta : {
481+ content : null ,
482+ refusal : null ,
483+ role : 'assistant' ,
484+ } ,
485+ finish_reason : null ,
486+ index : 0 ,
487+ } ,
488+ ] ,
489+ object : 'chat.completion.chunk' ,
490+ } ) ;
491+
492+ // 2. Tool call chunk with function name
493+ expect ( events [ 1 ] ) . toMatchObject ( {
494+ choices : [
495+ {
496+ delta : {
497+ tool_calls : [
498+ {
499+ index : 0 ,
500+ id : 'tool_1' ,
501+ type : 'function' ,
502+ function : {
503+ name : 'getWeather' ,
504+ } ,
505+ } ,
506+ ] ,
507+ content : null ,
508+ refusal : null ,
509+ } ,
510+ finish_reason : null ,
511+ } ,
512+ ] ,
513+ object : 'chat.completion.chunk' ,
514+ } ) ;
515+
516+ // 3-5. Argument chunks (3 chunks for the JSON string)
517+ expect ( events [ 2 ] ) . toMatchObject ( {
518+ choices : [
519+ {
520+ delta : {
521+ tool_calls : [
522+ {
523+ index : 0 ,
524+ function : {
525+ arguments : expect . any ( String ) ,
526+ } ,
527+ } ,
528+ ] ,
529+ content : null ,
530+ refusal : null ,
531+ } ,
532+ finish_reason : null ,
533+ } ,
534+ ] ,
535+ object : 'chat.completion.chunk' ,
536+ } ) ;
537+
538+ expect ( events [ 3 ] ) . toMatchObject ( {
539+ choices : [
540+ {
541+ delta : {
542+ tool_calls : [
543+ {
544+ index : 0 ,
545+ function : {
546+ arguments : expect . any ( String ) ,
547+ } ,
548+ } ,
549+ ] ,
550+ content : null ,
551+ refusal : null ,
552+ } ,
553+ finish_reason : null ,
554+ } ,
555+ ] ,
556+ object : 'chat.completion.chunk' ,
557+ } ) ;
558+
559+ expect ( events [ 4 ] ) . toMatchObject ( {
560+ choices : [
561+ {
562+ delta : {
563+ tool_calls : [
564+ {
565+ index : 0 ,
566+ function : {
567+ arguments : expect . any ( String ) ,
568+ } ,
569+ } ,
570+ ] ,
571+ content : null ,
572+ refusal : null ,
573+ } ,
574+ finish_reason : null ,
575+ } ,
576+ ] ,
577+ object : 'chat.completion.chunk' ,
578+ } ) ;
579+
580+ // 6. Final chunk with finish_reason
581+ expect ( events [ 5 ] ) . toMatchObject ( {
582+ choices : [
583+ {
584+ delta : {
585+ content : null ,
586+ role : 'assistant' ,
587+ refusal : null ,
588+ } ,
589+ finish_reason : 'tool_calls' ,
590+ } ,
591+ ] ,
592+ object : 'chat.completion.chunk' ,
593+ usage : {
594+ prompt_tokens : 10 ,
595+ completion_tokens : 20 ,
596+ total_tokens : 30 ,
597+ } ,
598+ } ) ;
599+
600+ expect ( events . length ) . toBe ( 6 ) ;
478601 } ) ;
479602
480603 // Second iteration: assistant provides final response
@@ -487,11 +610,75 @@ describe('ToolRunner', () => {
487610 for await ( const event of stream2 ) {
488611 events2 . push ( event ) ;
489612 }
613+ // Assert the expected structure of events2
614+ expect ( events2 ) . toHaveLength ( 4 ) ;
490615
491- // With chunked text, we'll get multiple text_delta events
492- // expect(events2.length).toBeGreaterThanOrEqual(6);
493- // const textDeltas = events2.filter((e) => e.type === 'content_block_delta');
494- // expect(textDeltas.length).toBeGreaterThanOrEqual(1);
616+ // 1. Initial chunk with role only
617+ expect ( events2 [ 0 ] ) . toMatchObject ( {
618+ choices : [
619+ {
620+ delta : {
621+ content : null ,
622+ refusal : null ,
623+ role : 'assistant' ,
624+ } ,
625+ finish_reason : null ,
626+ index : 0 ,
627+ } ,
628+ ] ,
629+ object : 'chat.completion.chunk' ,
630+ } ) ;
631+
632+ // 2. First text content delta
633+ expect ( events2 [ 1 ] ) . toMatchObject ( {
634+ choices : [
635+ {
636+ delta : {
637+ content : 'Some text ' ,
638+ refusal : null ,
639+ } ,
640+ index : 0 ,
641+ finish_reason : null ,
642+ } ,
643+ ] ,
644+ object : 'chat.completion.chunk' ,
645+ } ) ;
646+
647+ // 3. Second text content delta
648+ expect ( events2 [ 2 ] ) . toMatchObject ( {
649+ choices : [
650+ {
651+ delta : {
652+ content : 'content' ,
653+ refusal : null ,
654+ } ,
655+ index : 0 ,
656+ finish_reason : null ,
657+ } ,
658+ ] ,
659+ object : 'chat.completion.chunk' ,
660+ } ) ;
661+
662+ // 4. Final chunk with finish_reason and usage
663+ expect ( events2 [ 3 ] ) . toMatchObject ( {
664+ choices : [
665+ {
666+ delta : {
667+ content : null ,
668+ role : 'assistant' ,
669+ refusal : null ,
670+ } ,
671+ index : 0 ,
672+ finish_reason : 'stop' ,
673+ } ,
674+ ] ,
675+ object : 'chat.completion.chunk' ,
676+ usage : {
677+ prompt_tokens : 10 ,
678+ completion_tokens : 20 ,
679+ total_tokens : 30 ,
680+ } ,
681+ } ) ;
495682
496683 await expectDone ( iterator ) ;
497684 } ) ;
@@ -692,7 +879,7 @@ describe('ToolRunner', () => {
692879
693880 it ( 'respects max_iterations parameter' , async ( ) => {
694881 const { runner, handleAssistantMessage } = setupTest ( {
695- messages : [ { role : 'user' , content : 'Use tools repeatedly' } ] ,
882+ messages : [ { role : 'user' , content : 'Use tools repeatedly, one at a time ' } ] ,
696883 max_iterations : 2 , // Limit to 2 iterations
697884 } ) ;
698885
0 commit comments