File tree Expand file tree Collapse file tree 2 files changed +39
-8
lines changed
spring-ai-client-chat/src
main/java/org/springframework/ai/chat/client
test/java/org/springframework/ai/chat/client Expand file tree Collapse file tree 2 files changed +39
-8
lines changed Original file line number Diff line number Diff line change @@ -519,14 +519,23 @@ private ChatClientResponse doGetObservableChatClientResponse(ChatClientRequest c
519519 return chatClientResponse != null ? chatClientResponse : ChatClientResponse .builder ().build ();
520520 }
521521
522- @ Nullable
523- private static String getContentFromChatResponse (@ Nullable ChatResponse chatResponse ) {
524- return Optional .ofNullable (chatResponse )
525- .map (ChatResponse ::getResult )
526- .map (Generation ::getOutput )
527- .map (AbstractMessage ::getText )
528- .orElse (null );
529- }
522+ @ Nullable
523+ private static String getContentFromChatResponse (@ Nullable ChatResponse chatResponse ) {
524+ if (chatResponse == null || CollectionUtils .isEmpty (chatResponse .getResults ())) {
525+ return null ;
526+ }
527+ // Iterate through all generations to find the first one with non-null content
528+ // This handles cases where models return multiple generations (e.g., Bedrock Converse API
529+ // with openai.gpt-oss models may return reasoning output first with null content,
530+ // followed by the actual response)
531+ return chatResponse .getResults ()
532+ .stream ()
533+ .map (Generation ::getOutput )
534+ .map (AbstractMessage ::getText )
535+ .filter (text -> text != null )
536+ .findFirst ()
537+ .orElse (null );
538+ }
530539
531540 }
532541
Original file line number Diff line number Diff line change @@ -969,6 +969,28 @@ void whenChatResponseContentIsNull() {
969969 assertThat (content ).isNull ();
970970 }
971971
972+ @ Test
973+ void whenMultipleGenerationsWithFirstContentNull () {
974+ // Test case for Bedrock Converse API with openai.gpt-oss models
975+ // which return multiple generations where the first one has null content (reasoning output)
976+ // and the second one contains the actual response
977+ ChatModel chatModel = mock (ChatModel .class );
978+ ArgumentCaptor <Prompt > promptCaptor = ArgumentCaptor .forClass (Prompt .class );
979+ given (chatModel .call (promptCaptor .capture ())).willReturn (new ChatResponse (List .of (
980+ new Generation (new AssistantMessage (null )), // First generation with null content
981+ new Generation (new AssistantMessage ("Hello! How can I help you today?" )) // Second generation with actual content
982+ )));
983+
984+ ChatClient chatClient = new DefaultChatClientBuilder (chatModel ).build ();
985+ DefaultChatClient .DefaultChatClientRequestSpec chatClientRequestSpec = (DefaultChatClient .DefaultChatClientRequestSpec ) chatClient
986+ .prompt ("Hello" );
987+ DefaultChatClient .DefaultCallResponseSpec spec = (DefaultChatClient .DefaultCallResponseSpec ) chatClientRequestSpec
988+ .call ();
989+
990+ String content = spec .content ();
991+ assertThat (content ).isEqualTo ("Hello! How can I help you today?" );
992+ }
993+
972994 @ Test
973995 void whenResponseEntityWithParameterizedTypeIsNull () {
974996 ChatClient chatClient = new DefaultChatClientBuilder (mock (ChatModel .class )).build ();
You can’t perform that action at this time.
0 commit comments