@@ -738,53 +738,15 @@ describe('BedrockRuntime', () => {
738738 } ) ;
739739
740740 describe ( 'Response Body Type Handling' , ( ) => {
741- it ( 'handles string response body correctly' , async ( ) => {
741+ it ( 'handles normal Anthropic Claude response correctly' , async ( ) => {
742742 const modelId : string = 'anthropic.claude-3-5-sonnet-20240620-v1:0' ;
743743 const mockRequestBody : string = JSON . stringify ( {
744744 anthropic_version : 'bedrock-2023-05-31' ,
745745 max_tokens : 1000 ,
746746 messages : [ { role : 'user' , content : [ { type : 'text' , text : 'test' } ] } ] ,
747747 } ) ;
748748
749- // Mock response body as already converted string
750- const mockResponseBodyString = JSON . stringify ( {
751- stop_reason : 'end_turn' ,
752- usage : { input_tokens : 15 , output_tokens : 13 } ,
753- } ) ;
754-
755- nock ( `https://bedrock-runtime.${ region } .amazonaws.com` )
756- . post ( `/model/${ encodeURIComponent ( modelId ) } /invoke` )
757- . reply ( 200 , mockResponseBodyString ) ;
758-
759- await bedrock
760- . invokeModel ( {
761- modelId : modelId ,
762- body : mockRequestBody ,
763- } )
764- . catch ( ( err : any ) => { } ) ;
765-
766- const testSpans : ReadableSpan [ ] = getTestSpans ( ) ;
767- const invokeModelSpans : ReadableSpan [ ] = testSpans . filter ( ( s : ReadableSpan ) => {
768- return s . name === 'BedrockRuntime.InvokeModel' ;
769- } ) ;
770- expect ( invokeModelSpans . length ) . toBe ( 1 ) ;
771- const invokeModelSpan = invokeModelSpans [ 0 ] ;
772-
773- // Verify attributes are set correctly despite body being a string
774- expect ( invokeModelSpan . attributes [ AwsSpanProcessingUtil . GEN_AI_USAGE_INPUT_TOKENS ] ) . toBe ( 15 ) ;
775- expect ( invokeModelSpan . attributes [ AwsSpanProcessingUtil . GEN_AI_USAGE_OUTPUT_TOKENS ] ) . toBe ( 13 ) ;
776- expect ( invokeModelSpan . attributes [ AwsSpanProcessingUtil . GEN_AI_RESPONSE_FINISH_REASONS ] ) . toEqual ( [ 'end_turn' ] ) ;
777- } ) ;
778-
779- it ( 'handles Anthropic Claude response body correctly' , async ( ) => {
780- const modelId : string = 'anthropic.claude-3-5-sonnet-20240620-v1:0' ;
781- const mockRequestBody : string = JSON . stringify ( {
782- anthropic_version : 'bedrock-2023-05-31' ,
783- max_tokens : 1000 ,
784- messages : [ { role : 'user' , content : [ { type : 'text' , text : 'test' } ] } ] ,
785- } ) ;
786-
787- // Mock response body - use standard object format (AWS SDK will handle type conversion)
749+ // Use standard object format - AWS SDK and instrumentation will handle the conversion
788750 const mockResponseBodyObj = {
789751 stop_reason : 'end_turn' ,
790752 usage : { input_tokens : 20 , output_tokens : 15 } ,
@@ -814,24 +776,18 @@ describe('BedrockRuntime', () => {
814776 expect ( invokeModelSpan . attributes [ AwsSpanProcessingUtil . GEN_AI_RESPONSE_FINISH_REASONS ] ) . toEqual ( [ 'end_turn' ] ) ;
815777 } ) ;
816778
817- it ( 'handles Buffer response body correctly ' , async ( ) => {
779+ it ( 'handles unexpected body type gracefully ' , async ( ) => {
818780 const modelId : string = 'anthropic.claude-3-5-sonnet-20240620-v1:0' ;
819781 const mockRequestBody : string = JSON . stringify ( {
820782 anthropic_version : 'bedrock-2023-05-31' ,
821783 max_tokens : 1000 ,
822784 messages : [ { role : 'user' , content : [ { type : 'text' , text : 'test' } ] } ] ,
823785 } ) ;
824786
825- // Mock response body as Buffer
826- const mockResponseBodyObj = {
827- stop_reason : 'max_tokens' ,
828- usage : { input_tokens : 25 , output_tokens : 18 } ,
829- } ;
830- const mockResponseBodyBuffer = Buffer . from ( JSON . stringify ( mockResponseBodyObj ) , 'utf8' ) ;
831-
787+ // Mock response body as unexpected type - using reply function to return a number
832788 nock ( `https://bedrock-runtime.${ region } .amazonaws.com` )
833789 . post ( `/model/${ encodeURIComponent ( modelId ) } /invoke` )
834- . reply ( 200 , mockResponseBodyBuffer ) ;
790+ . reply ( 200 , ( ) => 12345 as any ) ;
835791
836792 await bedrock
837793 . invokeModel ( {
@@ -847,26 +803,35 @@ describe('BedrockRuntime', () => {
847803 expect ( invokeModelSpans . length ) . toBe ( 1 ) ;
848804 const invokeModelSpan = invokeModelSpans [ 0 ] ;
849805
850- // Verify attributes are set correctly when body is Buffer
851- expect ( invokeModelSpan . attributes [ AwsSpanProcessingUtil . GEN_AI_USAGE_INPUT_TOKENS ] ) . toBe ( 25 ) ;
852- expect ( invokeModelSpan . attributes [ AwsSpanProcessingUtil . GEN_AI_USAGE_OUTPUT_TOKENS ] ) . toBe ( 18 ) ;
853- expect ( invokeModelSpan . attributes [ AwsSpanProcessingUtil . GEN_AI_RESPONSE_FINISH_REASONS ] ) . toEqual ( [
854- 'max_tokens' ,
855- ] ) ;
806+ // Verify that no AI attributes are set when body type is unexpected
807+ expect ( invokeModelSpan . attributes [ AwsSpanProcessingUtil . GEN_AI_USAGE_INPUT_TOKENS ] ) . toBeUndefined ( ) ;
808+ expect ( invokeModelSpan . attributes [ AwsSpanProcessingUtil . GEN_AI_USAGE_OUTPUT_TOKENS ] ) . toBeUndefined ( ) ;
809+ expect ( invokeModelSpan . attributes [ AwsSpanProcessingUtil . GEN_AI_RESPONSE_FINISH_REASONS ] ) . toBeUndefined ( ) ;
810+
811+ // Note: We can't easily test diag.debug() output in unit tests, but the important part
812+ // is that the function returns early and doesn't crash when encountering unexpected types
813+ // Debug message will be: "Unexpected body type in Bedrock response: number for commandName InvokeModelCommand"
856814 } ) ;
857815
858- it ( 'handles unexpected body type gracefully' , async ( ) => {
816+ it ( 'handles streaming response (SmithyMessageDecoderStream) gracefully' , async ( ) => {
859817 const modelId : string = 'anthropic.claude-3-5-sonnet-20240620-v1:0' ;
860818 const mockRequestBody : string = JSON . stringify ( {
861819 anthropic_version : 'bedrock-2023-05-31' ,
862820 max_tokens : 1000 ,
863821 messages : [ { role : 'user' , content : [ { type : 'text' , text : 'test' } ] } ] ,
864822 } ) ;
865823
866- // Mock response body as unexpected type - using reply function to return a number
824+ // Mock response body as streaming object (constructor name matching)
825+ const mockStreamingBody = {
826+ constructor : { name : 'SmithyMessageDecoderStream' } ,
827+ [ Symbol . asyncIterator ] : function * ( ) {
828+ yield { chunk : { bytes : new TextEncoder ( ) . encode ( '{"type":"chunk"}' ) } } ;
829+ } ,
830+ } ;
831+
867832 nock ( `https://bedrock-runtime.${ region } .amazonaws.com` )
868833 . post ( `/model/${ encodeURIComponent ( modelId ) } /invoke` )
869- . reply ( 200 , ( ) => 12345 as any ) ;
834+ . reply ( 200 , mockStreamingBody ) ;
870835
871836 await bedrock
872837 . invokeModel ( {
@@ -882,13 +847,13 @@ describe('BedrockRuntime', () => {
882847 expect ( invokeModelSpans . length ) . toBe ( 1 ) ;
883848 const invokeModelSpan = invokeModelSpans [ 0 ] ;
884849
885- // Verify that no AI attributes are set when body type is unexpected
850+ // Verify that no AI attributes are set when body is streaming (metrics not available in initial response)
886851 expect ( invokeModelSpan . attributes [ AwsSpanProcessingUtil . GEN_AI_USAGE_INPUT_TOKENS ] ) . toBeUndefined ( ) ;
887852 expect ( invokeModelSpan . attributes [ AwsSpanProcessingUtil . GEN_AI_USAGE_OUTPUT_TOKENS ] ) . toBeUndefined ( ) ;
888853 expect ( invokeModelSpan . attributes [ AwsSpanProcessingUtil . GEN_AI_RESPONSE_FINISH_REASONS ] ) . toBeUndefined ( ) ;
889854
890- // Note: We can't easily test diag.debug() output in unit tests, but the important part
891- // is that the function returns early and doesn't crash when encountering unexpected types
855+ // Streaming responses should be skipped gracefully without crashing
856+ // TODO: support InvokeModel Streaming API and Converse APIs later
892857 } ) ;
893858 } ) ;
894859 } ) ;
0 commit comments