Skip to content

Commit 1cd6900

Browse files
google-genai-botcopybara-github
authored andcommitted
fix: remove client-side function call IDs from LlmRequest
PiperOrigin-RevId: 871928882
1 parent 6fa3439 commit 1cd6900

File tree

2 files changed

+88
-2
lines changed

2 files changed

+88
-2
lines changed

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

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,9 @@ public static LlmRequest prepareGenenerateContentRequest(
5959
* Prepares an {@link LlmRequest} for the GenerateContent API.
6060
*
6161
* <p>This method can optionally sanitize the request and ensures that the last content part is
62-
* from the user to prompt a model response. It also strips out any parts marked as "thoughts".
62+
* from the user to prompt a model response. It also strips out any parts marked as "thoughts" and
63+
* removes client-side function call IDs as some LLM APIs reject requests with client-side
64+
* function call IDs.
6365
*
6466
* @param llmRequest The original {@link LlmRequest}.
6567
* @param sanitize Whether to sanitize the request to be compatible with the Gemini API backend.
@@ -70,6 +72,7 @@ public static LlmRequest prepareGenenerateContentRequest(
7072
if (sanitize) {
7173
llmRequest = sanitizeRequestForGeminiApi(llmRequest);
7274
}
75+
llmRequest = removeClientFunctionCallId(llmRequest);
7376
List<Content> contents = ensureModelResponse(llmRequest.contents());
7477
if (stripThoughts) {
7578
contents = stripThoughts(contents);
@@ -136,6 +139,50 @@ public static LlmRequest sanitizeRequestForGeminiApi(LlmRequest llmRequest) {
136139
return requestBuilder.contents(updatedContents).build();
137140
}
138141

142+
/**
143+
* Removes client-side function call IDs from the request.
144+
*
145+
* <p>Client-side function call IDs are internal to the ADK and should not be sent to the model.
146+
* This method iterates through the contents and parts, removing the ID from any {@link
147+
* com.google.genai.types.FunctionCall} or {@link com.google.genai.types.FunctionResponse} parts.
148+
*
149+
* @param llmRequest The request to process.
150+
* @return A new {@link LlmRequest} with function call IDs removed.
151+
*/
152+
public static LlmRequest removeClientFunctionCallId(LlmRequest llmRequest) {
153+
if (llmRequest.contents().isEmpty()) {
154+
return llmRequest;
155+
}
156+
157+
ImmutableList<Content> updatedContents =
158+
llmRequest.contents().stream()
159+
.map(
160+
content ->
161+
content.toBuilder()
162+
.parts(
163+
content.parts().orElse(ImmutableList.of()).stream()
164+
.map(GeminiUtil::removeClientFunctionCallIdFromPart)
165+
.collect(toImmutableList()))
166+
.build())
167+
.collect(toImmutableList());
168+
169+
return llmRequest.toBuilder().contents(updatedContents).build();
170+
}
171+
172+
private static Part removeClientFunctionCallIdFromPart(Part part) {
173+
if (part.functionCall().isPresent() && part.functionCall().get().id().isPresent()) {
174+
return part.toBuilder()
175+
.functionCall(part.functionCall().get().toBuilder().clearId().build())
176+
.build();
177+
}
178+
if (part.functionResponse().isPresent() && part.functionResponse().get().id().isPresent()) {
179+
return part.toBuilder()
180+
.functionResponse(part.functionResponse().get().toBuilder().clearId().build())
181+
.build();
182+
}
183+
return part;
184+
}
185+
139186
/**
140187
* Ensures that the content is conducive to prompting a model response by ensuring the last
141188
* content part is from the user.
@@ -213,7 +260,7 @@ public static boolean shouldEmitAccumulatedText(LlmResponse currentLlmResponse)
213260
}
214261

215262
/** Removes any `Part` that contains only a `thought` from the content list. */
216-
public static List<Content> stripThoughts(List<Content> originalContents) {
263+
public static ImmutableList<Content> stripThoughts(List<Content> originalContents) {
217264
return originalContents.stream()
218265
.map(
219266
content -> {

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

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
import com.google.genai.types.Blob;
2525
import com.google.genai.types.Content;
2626
import com.google.genai.types.FileData;
27+
import com.google.genai.types.FunctionCall;
28+
import com.google.genai.types.FunctionResponse;
2729
import com.google.genai.types.GenerateContentConfig;
2830
import com.google.genai.types.Part;
2931
import java.util.Arrays;
@@ -451,6 +453,43 @@ public void prepareGenenerateContentRequest_emptyRequest_returnsRequestWithConti
451453
.inOrder();
452454
}
453455

456+
@Test
457+
public void removeClientFunctionCallId_stripsIds() {
458+
Part partWithFunctionCall =
459+
Part.builder()
460+
.functionCall(
461+
FunctionCall.builder()
462+
.name("foo")
463+
.id("id1")
464+
.args(ImmutableMap.of("key", "value"))
465+
.build())
466+
.build();
467+
Part partWithFunctionResponse =
468+
Part.builder()
469+
.functionResponse(
470+
FunctionResponse.builder()
471+
.name("bar")
472+
.id("id2")
473+
.response(ImmutableMap.of("key", "value"))
474+
.build())
475+
.build();
476+
LlmRequest request = toRequest(partWithFunctionCall, partWithFunctionResponse);
477+
478+
LlmRequest result = GeminiUtil.removeClientFunctionCallId(request);
479+
480+
assertThat(result.contents()).hasSize(1);
481+
assertThat(result.contents().get(0).parts()).isPresent();
482+
assertThat(result.contents().get(0).parts().get()).hasSize(2);
483+
Part resultPart1 = result.contents().get(0).parts().get().get(0);
484+
assertThat(resultPart1.functionCall()).isPresent();
485+
assertThat(resultPart1.functionCall().get().id()).isEmpty();
486+
assertThat(resultPart1.functionCall().get().name()).hasValue("foo");
487+
Part resultPart2 = result.contents().get(0).parts().get().get(1);
488+
assertThat(resultPart2.functionResponse()).isPresent();
489+
assertThat(resultPart2.functionResponse().get().id()).isEmpty();
490+
assertThat(resultPart2.functionResponse().get().name()).hasValue("bar");
491+
}
492+
454493
private static Content toContent(Part... parts) {
455494
return Content.builder().parts(ImmutableList.copyOf(parts)).build();
456495
}

0 commit comments

Comments
 (0)