Skip to content

Commit 411bfb7

Browse files
committed
feat(core): expose Gemini retry options
1 parent 97f22a2 commit 411bfb7

2 files changed

Lines changed: 94 additions & 11 deletions

File tree

‎core/src/main/java/com/google/adk/models/Gemini.java‎

Lines changed: 68 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import com.google.genai.types.GenerateContentConfig;
3535
import com.google.genai.types.GenerateContentResponse;
3636
import com.google.genai.types.HttpOptions;
37+
import com.google.genai.types.HttpRetryOptions;
3738
import com.google.genai.types.LiveConnectConfig;
3839
import com.google.genai.types.Part;
3940
import com.google.genai.types.PartialArg;
@@ -76,20 +77,24 @@ private static OkHttpClient prepareHttpClient(@Nullable ExecutorService executor
7677
}
7778

7879
private static Client buildApiKeyClient(
79-
String apiKey, @Nullable ExecutorService executorService) {
80+
String apiKey,
81+
@Nullable ExecutorService executorService,
82+
@Nullable HttpRetryOptions retryOptions) {
8083
return Client.builder()
8184
.apiKey(apiKey)
82-
.httpOptions(HttpOptions.builder().headers(TRACKING_HEADERS).build())
85+
.httpOptions(buildHttpOptions(retryOptions))
8386
.clientOptions(
8487
ClientOptions.builder().customHttpClient(prepareHttpClient(executorService)).build())
8588
.build();
8689
}
8790

8891
private static Client buildVertexClient(
89-
VertexCredentials vertexCredentials, @Nullable ExecutorService executorService) {
92+
VertexCredentials vertexCredentials,
93+
@Nullable ExecutorService executorService,
94+
@Nullable HttpRetryOptions retryOptions) {
9095
Client.Builder apiClientBuilder =
9196
Client.builder()
92-
.httpOptions(HttpOptions.builder().headers(TRACKING_HEADERS).build())
97+
.httpOptions(buildHttpOptions(retryOptions))
9398
.clientOptions(
9499
ClientOptions.builder()
95100
.customHttpClient(prepareHttpClient(executorService))
@@ -100,14 +105,24 @@ private static Client buildVertexClient(
100105
return apiClientBuilder.build();
101106
}
102107

103-
private static Client buildDefaultClient(@Nullable ExecutorService executorService) {
108+
private static Client buildDefaultClient(
109+
@Nullable ExecutorService executorService, @Nullable HttpRetryOptions retryOptions) {
104110
return Client.builder()
105-
.httpOptions(HttpOptions.builder().headers(TRACKING_HEADERS).build())
111+
.httpOptions(buildHttpOptions(retryOptions))
106112
.clientOptions(
107113
ClientOptions.builder().customHttpClient(prepareHttpClient(executorService)).build())
108114
.build();
109115
}
110116

117+
@VisibleForTesting
118+
static HttpOptions buildHttpOptions(@Nullable HttpRetryOptions retryOptions) {
119+
HttpOptions.Builder httpOptionsBuilder = HttpOptions.builder().headers(TRACKING_HEADERS);
120+
if (retryOptions != null) {
121+
httpOptionsBuilder.retryOptions(retryOptions);
122+
}
123+
return httpOptionsBuilder.build();
124+
}
125+
111126
static {
112127
String frameworkLabel = "google-adk/" + Version.JAVA_ADK_VERSION;
113128
String languageLabel = "gl-java/" + JAVA_VERSION.value();
@@ -139,9 +154,20 @@ public Gemini(String modelName, Client apiClient) {
139154
* @param apiKey The Google Gemini API key.
140155
*/
141156
public Gemini(String modelName, String apiKey) {
157+
this(modelName, apiKey, /* retryOptions= */ null);
158+
}
159+
160+
/**
161+
* Constructs a new Gemini instance with a Google Gemini API key and optional retry options.
162+
*
163+
* @param modelName The name of the Gemini model to use (e.g., "gemini-2.0-flash").
164+
* @param apiKey The Google Gemini API key.
165+
* @param retryOptions Options for retrying transient HTTP failures.
166+
*/
167+
public Gemini(String modelName, String apiKey, @Nullable HttpRetryOptions retryOptions) {
142168
super(modelName);
143169
Objects.requireNonNull(apiKey, "apiKey cannot be null");
144-
this.apiClient = buildApiKeyClient(apiKey, null);
170+
this.apiClient = buildApiKeyClient(apiKey, null, retryOptions);
145171
}
146172

147173
/**
@@ -151,9 +177,23 @@ public Gemini(String modelName, String apiKey) {
151177
* @param vertexCredentials The Vertex AI credentials to access the Gemini model.
152178
*/
153179
public Gemini(String modelName, VertexCredentials vertexCredentials) {
180+
this(modelName, vertexCredentials, /* retryOptions= */ null);
181+
}
182+
183+
/**
184+
* Constructs a new Gemini instance with Vertex AI credentials and optional retry options.
185+
*
186+
* @param modelName The name of the Gemini model to use (e.g., "gemini-2.0-flash").
187+
* @param vertexCredentials The Vertex AI credentials to access the Gemini model.
188+
* @param retryOptions Options for retrying transient HTTP failures.
189+
*/
190+
public Gemini(
191+
String modelName,
192+
VertexCredentials vertexCredentials,
193+
@Nullable HttpRetryOptions retryOptions) {
154194
super(modelName);
155195
Objects.requireNonNull(vertexCredentials, "vertexCredentials cannot be null");
156-
this.apiClient = buildVertexClient(vertexCredentials, null);
196+
this.apiClient = buildVertexClient(vertexCredentials, null, retryOptions);
157197
}
158198

159199
/**
@@ -174,6 +214,7 @@ public static class Builder {
174214
private String apiKey;
175215
private VertexCredentials vertexCredentials;
176216
private ExecutorService httpExecutorService;
217+
private HttpRetryOptions retryOptions;
177218

178219
private Builder() {}
179220

@@ -245,6 +286,21 @@ public Builder httpExecutorService(ExecutorService httpExecutorService) {
245286
return this;
246287
}
247288

289+
/**
290+
* Sets options for retrying transient HTTP failures through the Gemini client.
291+
*
292+
* <p>If an explicit {@link #apiClient(Client)} is supplied, its existing HTTP options take
293+
* precedence and these retry options are ignored.
294+
*
295+
* @param retryOptions The retry options to pass to the Google Gen AI client.
296+
* @return This builder.
297+
*/
298+
@CanIgnoreReturnValue
299+
public Builder retryOptions(HttpRetryOptions retryOptions) {
300+
this.retryOptions = retryOptions;
301+
return this;
302+
}
303+
248304
/**
249305
* Builds the {@link Gemini} instance.
250306
*
@@ -257,11 +313,12 @@ public Gemini build() {
257313
if (apiClient != null) {
258314
return new Gemini(modelName, apiClient);
259315
} else if (apiKey != null) {
260-
return new Gemini(modelName, buildApiKeyClient(apiKey, httpExecutorService));
316+
return new Gemini(modelName, buildApiKeyClient(apiKey, httpExecutorService, retryOptions));
261317
} else if (vertexCredentials != null) {
262-
return new Gemini(modelName, buildVertexClient(vertexCredentials, httpExecutorService));
318+
return new Gemini(
319+
modelName, buildVertexClient(vertexCredentials, httpExecutorService, retryOptions));
263320
} else {
264-
return new Gemini(modelName, buildDefaultClient(httpExecutorService));
321+
return new Gemini(modelName, buildDefaultClient(httpExecutorService, retryOptions));
265322
}
266323
}
267324
}

‎core/src/test/java/com/google/adk/models/GeminiTest.java‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
import com.google.genai.types.FunctionCall;
2929
import com.google.genai.types.GenerateContentResponse;
3030
import com.google.genai.types.GenerateContentResponseUsageMetadata;
31+
import com.google.genai.types.HttpOptions;
32+
import com.google.genai.types.HttpRetryOptions;
3133
import com.google.genai.types.Part;
3234
import com.google.genai.types.PartialArg;
3335
import com.google.genai.types.ToolCall;
@@ -42,6 +44,30 @@
4244
@RunWith(JUnit4.class)
4345
public final class GeminiTest {
4446

47+
@Test
48+
public void buildHttpOptions_withRetryOptions_setsRetryOptions() {
49+
HttpRetryOptions retryOptions =
50+
HttpRetryOptions.builder()
51+
.attempts(3)
52+
.initialDelay(1.0)
53+
.maxDelay(8.0)
54+
.expBase(2.0)
55+
.jitter(0.2)
56+
.httpStatusCodes(429, 500, 503)
57+
.build();
58+
59+
HttpOptions httpOptions = Gemini.buildHttpOptions(retryOptions);
60+
61+
assertThat(httpOptions.retryOptions()).hasValue(retryOptions);
62+
}
63+
64+
@Test
65+
public void buildHttpOptions_withoutRetryOptions_leavesRetriesDisabled() {
66+
HttpOptions httpOptions = Gemini.buildHttpOptions(null);
67+
68+
assertThat(httpOptions.retryOptions()).isEmpty();
69+
}
70+
4571
// Test cases for processRawResponses static method
4672
@Test
4773
public void processRawResponses_withTextChunks_emitsPartialResponses() {

0 commit comments

Comments
 (0)