diff --git a/agentscope-extensions/agentscope-extensions-model/agentscope-extensions-model-gemini/src/main/java/io/agentscope/extensions/model/gemini/formatter/GeminiMessageConverter.java b/agentscope-extensions/agentscope-extensions-model/agentscope-extensions-model-gemini/src/main/java/io/agentscope/extensions/model/gemini/formatter/GeminiMessageConverter.java
index 4ca629ed45..a20e8fa662 100644
--- a/agentscope-extensions/agentscope-extensions-model/agentscope-extensions-model-gemini/src/main/java/io/agentscope/extensions/model/gemini/formatter/GeminiMessageConverter.java
+++ b/agentscope-extensions/agentscope-extensions-model/agentscope-extensions-model-gemini/src/main/java/io/agentscope/extensions/model/gemini/formatter/GeminiMessageConverter.java
@@ -53,13 +53,13 @@
*
* - Text blocks
* - Tool use blocks (function_call)
- * - Tool result blocks (function_response as independent Content)
+ * - Tool result blocks (function_response grouped into one user Content per message)
* - Multimodal content (image, audio, video)
*
*
* Important Conversion Behaviors:
*
- * - Tool result blocks are converted to independent "user" role Content
+ * - Tool result blocks from one message are converted to one "user" role Content
* - Multiple tool outputs are formatted with "- " prefix per line
* - System messages are treated as "user" role (Gemini API requirement)
*
@@ -88,6 +88,7 @@ public List convertMessages(List msgs) {
for (Msg msg : msgs) {
List parts = new ArrayList<>();
+ List toolResultParts = new ArrayList<>();
for (ContentBlock block : msg.getContent()) {
if (block instanceof TextBlock tb) {
@@ -137,7 +138,7 @@ public List convertMessages(List msgs) {
parts.add(partBuilder.build());
} else if (block instanceof ToolResultBlock trb) {
- // IMPORTANT: Tool result as independent Content with "user" role
+ // Tool results from the same message share one user Content.
String textOutput = convertToolResultToString(trb.getOutput());
// Create response map with "output" key
@@ -154,14 +155,7 @@ public List convertMessages(List msgs) {
Part functionResponsePart =
Part.builder().functionResponse(functionResponse).build();
- Content toolResultContent =
- Content.builder()
- .role("user")
- .parts(List.of(functionResponsePart))
- .build();
-
- result.add(toolResultContent);
- // Skip adding to current message parts
+ toolResultParts.add(functionResponsePart);
continue;
} else if (block instanceof ImageBlock ib) {
@@ -190,6 +184,12 @@ public List convertMessages(List msgs) {
}
}
+ if (!toolResultParts.isEmpty()) {
+ Content toolResultContent =
+ Content.builder().role("user").parts(toolResultParts).build();
+ result.add(toolResultContent);
+ }
+
// Add message if there are parts
if (!parts.isEmpty()) {
String role = convertRole(msg.getRole());
diff --git a/agentscope-extensions/agentscope-extensions-model/agentscope-extensions-model-gemini/src/test/java/io/agentscope/extensions/model/gemini/formatter/GeminiMessageConverterTest.java b/agentscope-extensions/agentscope-extensions-model/agentscope-extensions-model-gemini/src/test/java/io/agentscope/extensions/model/gemini/formatter/GeminiMessageConverterTest.java
index 6cad792f02..2b33991964 100644
--- a/agentscope-extensions/agentscope-extensions-model/agentscope-extensions-model-gemini/src/test/java/io/agentscope/extensions/model/gemini/formatter/GeminiMessageConverterTest.java
+++ b/agentscope-extensions/agentscope-extensions-model/agentscope-extensions-model-gemini/src/test/java/io/agentscope/extensions/model/gemini/formatter/GeminiMessageConverterTest.java
@@ -223,6 +223,38 @@ void testConvertToolResultBlock() {
assertEquals("Result text", part.functionResponse().get().response().get().get("output"));
}
+ @Test
+ @DisplayName("Should group tool results from one message into one user Content")
+ void testConvertMultipleToolResultBlocks() {
+ ToolResultBlock firstResult =
+ ToolResultBlock.builder()
+ .id("call_123")
+ .name("search")
+ .output(List.of(TextBlock.builder().text("First result").build()))
+ .build();
+ ToolResultBlock secondResult =
+ ToolResultBlock.builder()
+ .id("call_456")
+ .name("lookup")
+ .output(List.of(TextBlock.builder().text("Second result").build()))
+ .build();
+ Msg msg =
+ Msg.builder()
+ .name("system")
+ .content(List.of(firstResult, secondResult))
+ .role(MsgRole.TOOL)
+ .build();
+
+ List result = converter.convertMessages(List.of(msg));
+
+ assertEquals(1, result.size());
+ Content content = result.get(0);
+ assertEquals("user", content.role().get());
+ assertEquals(2, content.parts().get().size());
+ assertEquals("call_123", content.parts().get().get(0).functionResponse().get().id().get());
+ assertEquals("call_456", content.parts().get().get(1).functionResponse().get().id().get());
+ }
+
@Test
@DisplayName("Should format tool result with single output")
void testToolResultSingleOutput() {