@@ -672,5 +672,87 @@ def encode_message(message)
672672 expect ( structured_output . read_buffered_property ( :key ) ) . to eq ( "Hello!\n There" )
673673 end
674674 end
675+
676+ it "works with JSON schema array types" do
677+ schema = {
678+ type : "json_schema" ,
679+ json_schema : {
680+ name : "reply" ,
681+ schema : {
682+ type : "object" ,
683+ properties : {
684+ plain : {
685+ type : "string" ,
686+ } ,
687+ key : {
688+ type : "array" ,
689+ items : {
690+ type : "string" ,
691+ } ,
692+ } ,
693+ } ,
694+ required : %w[ plain key ] ,
695+ additionalProperties : false ,
696+ } ,
697+ strict : true ,
698+ } ,
699+ }
700+
701+ messages =
702+ [
703+ { type : "message_start" , message : { usage : { input_tokens : 9 } } } ,
704+ { type : "content_block_delta" , delta : { text : "\" " } } ,
705+ { type : "content_block_delta" , delta : { text : "key" } } ,
706+ { type : "content_block_delta" , delta : { text : "\" :" } } ,
707+ { type : "content_block_delta" , delta : { text : " [\" " } } ,
708+ { type : "content_block_delta" , delta : { text : "Hello!" } } ,
709+ { type : "content_block_delta" , delta : { text : " I am" } } ,
710+ { type : "content_block_delta" , delta : { text : " a " } } ,
711+ { type : "content_block_delta" , delta : { text : "chunk\" ," } } ,
712+ { type : "content_block_delta" , delta : { text : "\" There" } } ,
713+ { type : "content_block_delta" , delta : { text : "\" ]," } } ,
714+ { type : "content_block_delta" , delta : { text : " \" plain" } } ,
715+ { type : "content_block_delta" , delta : { text : "\" :\" " } } ,
716+ { type : "content_block_delta" , delta : { text : "I'm here" } } ,
717+ { type : "content_block_delta" , delta : { text : " too\" }" } } ,
718+ { type : "message_delta" , delta : { usage : { output_tokens : 25 } } } ,
719+ ] . map { |message | encode_message ( message ) }
720+
721+ proxy = DiscourseAi ::Completions ::Llm . proxy ( "custom:#{ model . id } " )
722+ request = nil
723+ bedrock_mock . with_chunk_array_support do
724+ stub_request (
725+ :post ,
726+ "https://bedrock-runtime.us-east-1.amazonaws.com/model/anthropic.claude-3-sonnet-20240229-v1:0/invoke-with-response-stream" ,
727+ )
728+ . with do |inner_request |
729+ request = inner_request
730+ true
731+ end
732+ . to_return ( status : 200 , body : messages )
733+
734+ structured_output = nil
735+ proxy . generate ( "hello world" , response_format : schema , user : user ) do |partial |
736+ structured_output = partial
737+ end
738+
739+ expected = {
740+ "max_tokens" => 4096 ,
741+ "anthropic_version" => "bedrock-2023-05-31" ,
742+ "messages" => [
743+ { "role" => "user" , "content" => "hello world" } ,
744+ { "role" => "assistant" , "content" => "{" } ,
745+ ] ,
746+ "system" => "You are a helpful bot" ,
747+ }
748+ expect ( JSON . parse ( request . body ) ) . to eq ( expected )
749+
750+ expect ( structured_output . read_buffered_property ( :key ) ) . to contain_exactly (
751+ "Hello! I am a chunk" ,
752+ "There" ,
753+ )
754+ expect ( structured_output . read_buffered_property ( :plain ) ) . to eq ( "I'm here too" )
755+ end
756+ end
675757 end
676758end
0 commit comments