diff --git a/contrib/spring-ai/src/main/java/com/google/adk/models/springai/MessageConverter.java b/contrib/spring-ai/src/main/java/com/google/adk/models/springai/MessageConverter.java index 442997be0..2c5f6e413 100644 --- a/contrib/spring-ai/src/main/java/com/google/adk/models/springai/MessageConverter.java +++ b/contrib/spring-ai/src/main/java/com/google/adk/models/springai/MessageConverter.java @@ -22,6 +22,7 @@ import com.google.adk.models.LlmResponse; import com.google.genai.types.Content; import com.google.genai.types.FunctionCall; +import com.google.genai.types.FunctionResponse; import com.google.genai.types.GenerateContentResponseUsageMetadata; import com.google.genai.types.Part; import java.net.URI; @@ -261,10 +262,15 @@ private List handleUserContent(Content content) { if (part.text().isPresent()) { textBuilder.append(part.text().get()); } else if (part.functionResponse().isPresent()) { - // TODO: Spring AI 1.1.0 ToolResponseMessage constructors are protected - // For now, we skip tool responses in user messages - // This will need to be addressed in a future update when Spring AI provides - // a public API for creating ToolResponseMessage + FunctionResponse functionResponse = part.functionResponse().get(); + String id = functionResponse.id().orElse(""); + String name = functionResponse.name().orElse(""); + String responseData = toJson(functionResponse.response().orElse(Map.of())); + + ToolResponseMessage.ToolResponse toolResponse = + new ToolResponseMessage.ToolResponse(id, name, responseData); + toolResponseMessages.add( + ToolResponseMessage.builder().responses(List.of(toolResponse)).build()); } else if (part.inlineData().isPresent()) { // Handle inline media data (images, audio, video, etc.) com.google.genai.types.Blob blob = part.inlineData().get(); @@ -298,7 +304,9 @@ private List handleUserContent(Content content) { } List messages = new ArrayList<>(); - messages.add(UserMessage.builder().text(textBuilder.toString()).media(mediaList).build()); + if (textBuilder.length() > 0 || !mediaList.isEmpty() || toolResponseMessages.isEmpty()) { + messages.add(UserMessage.builder().text(textBuilder.toString()).media(mediaList).build()); + } messages.addAll(toolResponseMessages); return messages; diff --git a/contrib/spring-ai/src/test/java/com/google/adk/models/springai/MessageConverterTest.java b/contrib/spring-ai/src/test/java/com/google/adk/models/springai/MessageConverterTest.java index bb529e104..7728d78e6 100644 --- a/contrib/spring-ai/src/test/java/com/google/adk/models/springai/MessageConverterTest.java +++ b/contrib/spring-ai/src/test/java/com/google/adk/models/springai/MessageConverterTest.java @@ -37,6 +37,7 @@ import org.springframework.ai.chat.messages.AssistantMessage; import org.springframework.ai.chat.messages.Message; import org.springframework.ai.chat.messages.SystemMessage; +import org.springframework.ai.chat.messages.ToolResponseMessage; import org.springframework.ai.chat.messages.UserMessage; import org.springframework.ai.chat.metadata.ChatResponseMetadata; import org.springframework.ai.chat.metadata.DefaultUsage; @@ -184,19 +185,18 @@ void testToLlmPromptWithFunctionResponse() { Prompt prompt = messageConverter.toLlmPrompt(request); // Currently only UserMessage is created (function response is skipped) - assertThat(prompt.getInstructions()).hasSize(1); + assertThat(prompt.getInstructions()).hasSize(2); Message userMessage = prompt.getInstructions().get(0); assertThat(userMessage).isInstanceOf(UserMessage.class); assertThat(((UserMessage) userMessage).getText()).isEqualTo("What's the weather?"); - // When Spring AI provides public API for ToolResponseMessage, uncomment: - // Message toolResponseMessage = prompt.getInstructions().get(1); - // assertThat(toolResponseMessage).isInstanceOf(ToolResponseMessage.class); - // ToolResponseMessage toolResponse = (ToolResponseMessage) toolResponseMessage; - // assertThat(toolResponse.getResponses()).hasSize(1); - // ToolResponseMessage.ToolResponse response = toolResponse.getResponses().get(0); - // assertThat(response.name()).isEqualTo("get_weather"); + Message toolResponseMessage = prompt.getInstructions().get(1); + assertThat(toolResponseMessage).isInstanceOf(ToolResponseMessage.class); + ToolResponseMessage toolResponse = (ToolResponseMessage) toolResponseMessage; + assertThat(toolResponse.getResponses()).hasSize(1); + ToolResponseMessage.ToolResponse response = toolResponse.getResponses().get(0); + assertThat(response.name()).isEqualTo("get_weather"); } @Test