Skip to content

Commit 3efff42

Browse files
google-genai-botcopybara-github
authored andcommitted
fix: preserve non-client function call IDs in GeminiUtil
Fix unconditional function call ID stripping in GeminiUtil. Previously, the Java ADK cleared both model-generated IDs (like call_123) and client-generated IDs, which broke compatibility with Gemini 3.5 Flash's Strict Response Matching convention. PiperOrigin-RevId: 947448471
1 parent 6bae658 commit 3efff42

2 files changed

Lines changed: 47 additions & 4 deletions

File tree

core/src/main/java/com/google/adk/models/GeminiUtil.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ public final class GeminiUtil {
3838
"Continue output. DO NOT look at this line. ONLY look at the content before this line and"
3939
+ " system instruction.";
4040

41+
private static final String AF_FUNCTION_CALL_ID_PREFIX = "adk-";
42+
4143
private GeminiUtil() {}
4244

4345
/**
@@ -170,12 +172,16 @@ public static LlmRequest removeClientFunctionCallId(LlmRequest llmRequest) {
170172
}
171173

172174
private static Part removeClientFunctionCallIdFromPart(Part part) {
173-
if (part.functionCall().isPresent() && part.functionCall().get().id().isPresent()) {
175+
if (part.functionCall().isPresent()
176+
&& part.functionCall().get().id().isPresent()
177+
&& part.functionCall().get().id().get().startsWith(AF_FUNCTION_CALL_ID_PREFIX)) {
174178
return part.toBuilder()
175179
.functionCall(part.functionCall().get().toBuilder().clearId().build())
176180
.build();
177181
}
178-
if (part.functionResponse().isPresent() && part.functionResponse().get().id().isPresent()) {
182+
if (part.functionResponse().isPresent()
183+
&& part.functionResponse().get().id().isPresent()
184+
&& part.functionResponse().get().id().get().startsWith(AF_FUNCTION_CALL_ID_PREFIX)) {
179185
return part.toBuilder()
180186
.functionResponse(part.functionResponse().get().toBuilder().clearId().build())
181187
.build();

core/src/test/java/com/google/adk/models/GeminiUtilTest.java

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -460,7 +460,7 @@ public void removeClientFunctionCallId_stripsIds() {
460460
.functionCall(
461461
FunctionCall.builder()
462462
.name("foo")
463-
.id("id1")
463+
.id("adk-id1")
464464
.args(ImmutableMap.of("key", "value"))
465465
.build())
466466
.build();
@@ -469,7 +469,7 @@ public void removeClientFunctionCallId_stripsIds() {
469469
.functionResponse(
470470
FunctionResponse.builder()
471471
.name("bar")
472-
.id("id2")
472+
.id("adk-id2")
473473
.response(ImmutableMap.of("key", "value"))
474474
.build())
475475
.build();
@@ -490,6 +490,43 @@ public void removeClientFunctionCallId_stripsIds() {
490490
assertThat(resultPart2.functionResponse().get().name()).hasValue("bar");
491491
}
492492

493+
@Test
494+
public void removeClientFunctionCallId_preservesNonClientIds() {
495+
Part partWithFunctionCall =
496+
Part.builder()
497+
.functionCall(
498+
FunctionCall.builder()
499+
.name("foo")
500+
.id("call_123")
501+
.args(ImmutableMap.of("key", "value"))
502+
.build())
503+
.build();
504+
Part partWithFunctionResponse =
505+
Part.builder()
506+
.functionResponse(
507+
FunctionResponse.builder()
508+
.name("bar")
509+
.id("call_456")
510+
.response(ImmutableMap.of("key", "value"))
511+
.build())
512+
.build();
513+
LlmRequest request = toRequest(partWithFunctionCall, partWithFunctionResponse);
514+
515+
LlmRequest result = GeminiUtil.removeClientFunctionCallId(request);
516+
517+
assertThat(result.contents()).hasSize(1);
518+
assertThat(result.contents().get(0).parts()).isPresent();
519+
assertThat(result.contents().get(0).parts().get()).hasSize(2);
520+
Part resultPart1 = result.contents().get(0).parts().get().get(0);
521+
assertThat(resultPart1.functionCall()).isPresent();
522+
assertThat(resultPart1.functionCall().get().id()).hasValue("call_123");
523+
assertThat(resultPart1.functionCall().get().name()).hasValue("foo");
524+
Part resultPart2 = result.contents().get(0).parts().get().get(1);
525+
assertThat(resultPart2.functionResponse()).isPresent();
526+
assertThat(resultPart2.functionResponse().get().id()).hasValue("call_456");
527+
assertThat(resultPart2.functionResponse().get().name()).hasValue("bar");
528+
}
529+
493530
private static Content toContent(Part... parts) {
494531
return Content.builder().parts(ImmutableList.copyOf(parts)).build();
495532
}

0 commit comments

Comments
 (0)