Skip to content

Commit 8d88751

Browse files
damianmomotgooglecopybara-github
authored andcommitted
fix: Allow BuiltInCodeExecutor for Gemini 3 models
PiperOrigin-RevId: 897673367
1 parent 69680bb commit 8d88751

4 files changed

Lines changed: 161 additions & 1 deletion

File tree

core/src/main/java/com/google/adk/codeexecutors/BuiltInCodeExecutor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public CodeExecutionResult executeCode(
4343
/** Pre-process the LLM request for Gemini 2.0+ models to use the code execution tool. */
4444
public void processLlmRequest(LlmRequest.Builder llmRequestBuilder) {
4545
LlmRequest llmRequest = llmRequestBuilder.build();
46-
if (ModelNameUtils.isGemini2Model(llmRequest.model().orElse(null))) {
46+
if (llmRequest.model().map(ModelNameUtils::isGemini2OrAbove).orElse(false)) {
4747
GenerateContentConfig.Builder configBuilder =
4848
llmRequest.config().map(c -> c.toBuilder()).orElseGet(GenerateContentConfig::builder);
4949
ImmutableList.Builder<Tool> toolsBuilder = ImmutableList.<Tool>builder();

core/src/main/java/com/google/adk/utils/ModelNameUtils.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,14 @@
2020
import java.util.Objects;
2121
import java.util.regex.Matcher;
2222
import java.util.regex.Pattern;
23+
import org.jspecify.annotations.Nullable;
2324

2425
/** Utility class for model names. */
2526
public final class ModelNameUtils {
2627
private static final String GEMINI_PREFIX = "gemini-";
2728
private static final Pattern GEMINI_2_PATTERN = Pattern.compile("^gemini-2\\..*");
29+
private static final Pattern GEMINI_VERSION_PATTERN =
30+
Pattern.compile("^gemini-(\\d+)(?:\\.(\\d+))?.*");
2831
private static final String GEMINI_CLASS = "com.google.adk.models.Gemini";
2932
private static final Pattern PATH_PATTERN =
3033
Pattern.compile("^projects/[^/]+/locations/[^/]+/publishers/[^/]+/models/(.+)$");
@@ -39,6 +42,28 @@ public static boolean isGemini2Model(String modelString) {
3942
return matchesModelPattern(modelString, GEMINI_2_PATTERN);
4043
}
4144

45+
public static boolean isGemini2OrAbove(@Nullable String modelString) {
46+
return isGeminiVersionOrAbove(modelString, 2, 0);
47+
}
48+
49+
private static boolean isGeminiVersionOrAbove(
50+
@Nullable String modelString, int minMajor, int minMinor) {
51+
if (modelString == null) {
52+
return false;
53+
}
54+
String modelName = extractModelName(modelString);
55+
Matcher matcher = GEMINI_VERSION_PATTERN.matcher(modelName);
56+
if (matcher.matches()) {
57+
int major = Integer.parseInt(matcher.group(1));
58+
int minor = matcher.group(2) != null ? Integer.parseInt(matcher.group(2)) : 0;
59+
if (major > minMajor) {
60+
return true;
61+
}
62+
return major == minMajor && minor >= minMinor;
63+
}
64+
return false;
65+
}
66+
4267
private static boolean matchesModelPattern(String modelString, Pattern pattern) {
4368
if (modelString == null) {
4469
return false;
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package com.google.adk.codeexecutors;
2+
3+
import static com.google.common.truth.Truth.assertThat;
4+
import static org.junit.Assert.assertThrows;
5+
6+
import com.google.adk.models.LlmRequest;
7+
import com.google.genai.types.Tool;
8+
import java.util.List;
9+
import org.junit.Test;
10+
import org.junit.runner.RunWith;
11+
import org.junit.runners.JUnit4;
12+
13+
@RunWith(JUnit4.class)
14+
public class BuiltInCodeExecutorTest {
15+
16+
@Test
17+
public void executeCode_throwsUnsupportedOperationException() {
18+
BuiltInCodeExecutor executor = new BuiltInCodeExecutor();
19+
assertThrows(UnsupportedOperationException.class, () -> executor.executeCode(null, null));
20+
}
21+
22+
@Test
23+
public void processLlmRequest_withGemini2_addsCodeExecutionTool() {
24+
BuiltInCodeExecutor executor = new BuiltInCodeExecutor();
25+
LlmRequest.Builder requestBuilder = LlmRequest.builder().model("gemini-2.5-flash");
26+
27+
executor.processLlmRequest(requestBuilder);
28+
29+
List<Tool> tools = requestBuilder.build().config().get().tools().get();
30+
assertThat(tools).hasSize(1);
31+
assertThat(tools.get(0).codeExecution()).isPresent();
32+
}
33+
34+
@Test
35+
public void processLlmRequest_withGemini3_addsCodeExecutionTool() {
36+
BuiltInCodeExecutor executor = new BuiltInCodeExecutor();
37+
LlmRequest.Builder requestBuilder = LlmRequest.builder().model("gemini-3.0-pro");
38+
39+
executor.processLlmRequest(requestBuilder);
40+
41+
List<Tool> tools = requestBuilder.build().config().get().tools().get();
42+
assertThat(tools).hasSize(1);
43+
assertThat(tools.get(0).codeExecution()).isPresent();
44+
}
45+
46+
@Test
47+
public void processLlmRequest_withGemini1_throwsException() {
48+
BuiltInCodeExecutor executor = new BuiltInCodeExecutor();
49+
LlmRequest.Builder requestBuilder = LlmRequest.builder().model("gemini-1.5-pro");
50+
51+
IllegalArgumentException exception =
52+
assertThrows(
53+
IllegalArgumentException.class, () -> executor.processLlmRequest(requestBuilder));
54+
55+
assertThat(exception)
56+
.hasMessageThat()
57+
.contains("Gemini code execution tool is not supported for model gemini-1.5-pro");
58+
}
59+
60+
@Test
61+
public void processLlmRequest_withoutModel_throwsException() {
62+
BuiltInCodeExecutor executor = new BuiltInCodeExecutor();
63+
LlmRequest.Builder requestBuilder = LlmRequest.builder();
64+
65+
IllegalArgumentException exception =
66+
assertThrows(
67+
IllegalArgumentException.class, () -> executor.processLlmRequest(requestBuilder));
68+
69+
assertThat(exception)
70+
.hasMessageThat()
71+
.contains("Gemini code execution tool is not supported for model");
72+
}
73+
}

core/src/test/java/com/google/adk/utils/ModelNameUtilsTest.java

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,68 @@ public void isGemini2Model_withNullModel_returnsFalse() {
7171
assertThat(ModelNameUtils.isGemini2Model(null)).isFalse();
7272
}
7373

74+
@Test
75+
public void isGemini2OrAbove_withGemini3Model_returnsTrue() {
76+
assertThat(ModelNameUtils.isGemini2OrAbove("gemini-3.0-pro")).isTrue();
77+
}
78+
79+
@Test
80+
public void isGemini2OrAbove_withGemini2Model_returnsTrue() {
81+
assertThat(ModelNameUtils.isGemini2OrAbove("gemini-2.0-pro")).isTrue();
82+
}
83+
84+
@Test
85+
public void isGemini2OrAbove_withGemini25Model_returnsTrue() {
86+
assertThat(ModelNameUtils.isGemini2OrAbove("gemini-2.5-flash")).isTrue();
87+
}
88+
89+
@Test
90+
public void isGemini2OrAbove_withGemini1Model_returnsFalse() {
91+
assertThat(ModelNameUtils.isGemini2OrAbove("gemini-1.5-pro")).isFalse();
92+
}
93+
94+
@Test
95+
public void isGemini2OrAbove_withInvalid_returnsFalse() {
96+
assertThat(ModelNameUtils.isGemini2OrAbove("???")).isFalse();
97+
}
98+
99+
@Test
100+
public void isGemini2OrAbove_withInvalidGemini1Version_returnsFalse() {
101+
assertThat(ModelNameUtils.isGemini2OrAbove("gemini-01")).isFalse();
102+
}
103+
104+
@Test
105+
public void isGemini2OrAbove_withPathBasedGemini3Model_returnsTrue() {
106+
assertThat(
107+
ModelNameUtils.isGemini2OrAbove(
108+
"projects/test-project/locations/us-central1/publishers/google/models/gemini-3.0-flash"))
109+
.isTrue();
110+
}
111+
112+
@Test
113+
public void isGemini2OrAbove_withPathBasedGemini1Model_returnsFalse() {
114+
assertThat(
115+
ModelNameUtils.isGemini2OrAbove(
116+
"projects/test-project/locations/us-central1/publishers/google/models/gemini-1.5-pro"))
117+
.isFalse();
118+
}
119+
120+
@Test
121+
public void isGemini2OrAbove_withApigeeGemini3Model_returnsTrue() {
122+
assertThat(ModelNameUtils.isGemini2OrAbove("apigee/gemini-3.0-flash")).isTrue();
123+
}
124+
125+
@Test
126+
public void isGemini2OrAbove_withApigeeProviderV1BetaGemini3Model_returnsTrue() {
127+
assertThat(ModelNameUtils.isGemini2OrAbove("apigee/vertex_ai/v1beta/gemini-3.0-flash"))
128+
.isTrue();
129+
}
130+
131+
@Test
132+
public void isGemini2OrAbove_withNullModel_returnsFalse() {
133+
assertThat(ModelNameUtils.isGemini2OrAbove(null)).isFalse();
134+
}
135+
74136
@Test
75137
public void isGeminiModel_withGeminiModel_returnsTrue() {
76138
assertThat(ModelNameUtils.isGeminiModel("gemini-1.5-flash")).isTrue();

0 commit comments

Comments
 (0)