3131import com .google .genai .types .GenerateContentConfig ;
3232import com .google .genai .types .GenerateContentResponse ;
3333import com .google .genai .types .HttpOptions ;
34+ import com .google .genai .types .HttpRetryOptions ;
3435import com .google .genai .types .LiveConnectConfig ;
3536import com .google .genai .types .Part ;
3637import com .google .genai .types .PartialArg ;
@@ -72,35 +73,69 @@ private static OkHttpClient prepareHttpClient(@Nullable ExecutorService executor
7273 }
7374
7475 // Every client ADK builds starts here, so each sends ADK's tracking tag.
75- private static Client .Builder adkClientBuilder (@ Nullable ExecutorService executorService ) {
76+ private static Client .Builder adkClientBuilder (
77+ @ Nullable ExecutorService executorService , @ Nullable HttpRetryOptions retryOptions ) {
7678 return Client .builder ()
77- .httpOptions (HttpOptions . builder (). headers ( TrackingHeaders . GENAI_SDK_HEADERS ). build ( ))
79+ .httpOptions (buildHttpOptions ( retryOptions ))
7880 .clientOptions (
7981 ClientOptions .builder ().customHttpClient (prepareHttpClient (executorService )).build ());
8082 }
8183
8284 private static Client buildApiKeyClient (
83- String apiKey , @ Nullable ExecutorService executorService ) {
84- return adkClientBuilder (executorService ).apiKey (apiKey ).build ();
85+ String apiKey ,
86+ @ Nullable ExecutorService executorService ,
87+ @ Nullable HttpRetryOptions retryOptions ) {
88+ return adkClientBuilder (executorService , retryOptions ).apiKey (apiKey ).build ();
8589 }
8690
8791 private static Client buildVertexClient (
88- VertexCredentials vertexCredentials , @ Nullable ExecutorService executorService ) {
89- Client .Builder apiClientBuilder = adkClientBuilder (executorService );
92+ VertexCredentials vertexCredentials ,
93+ @ Nullable ExecutorService executorService ,
94+ @ Nullable HttpRetryOptions retryOptions ) {
95+ Client .Builder apiClientBuilder = adkClientBuilder (executorService , retryOptions );
9096 vertexCredentials .project ().ifPresent (apiClientBuilder ::project );
9197 vertexCredentials .location ().ifPresent (apiClientBuilder ::location );
9298 vertexCredentials .credentials ().ifPresent (apiClientBuilder ::credentials );
9399 return apiClientBuilder .build ();
94100 }
95101
96- private static Client buildDefaultClient (@ Nullable ExecutorService executorService ) {
97- return adkClientBuilder (executorService ).build ();
102+ private static Client buildDefaultClient (
103+ @ Nullable ExecutorService executorService , @ Nullable HttpRetryOptions retryOptions ) {
104+ return adkClientBuilder (executorService , retryOptions ).build ();
105+ }
106+
107+ @ VisibleForTesting
108+ static HttpOptions buildHttpOptions (@ Nullable HttpRetryOptions retryOptions ) {
109+ HttpOptions .Builder httpOptionsBuilder =
110+ HttpOptions .builder ().headers (TrackingHeaders .GENAI_SDK_HEADERS );
111+ if (retryOptions != null ) {
112+ httpOptionsBuilder .retryOptions (retryOptions );
113+ }
114+ return httpOptionsBuilder .build ();
98115 }
99116
100117 private final Client apiClient ;
101118 // True when apiClient already sends ADK's tracking tag.
102119 private final boolean clientSendsTrackingHeaders ;
103120
121+ private Gemini (Builder builder ) {
122+ super (builder .modelName );
123+ if (builder .apiClient != null ) {
124+ this .apiClient = builder .apiClient ;
125+ } else if (builder .apiKey != null ) {
126+ this .apiClient =
127+ buildApiKeyClient (builder .apiKey , builder .httpExecutorService , builder .retryOptions );
128+ } else if (builder .vertexCredentials != null ) {
129+ this .apiClient =
130+ buildVertexClient (
131+ builder .vertexCredentials , builder .httpExecutorService , builder .retryOptions );
132+ } else {
133+ this .apiClient = buildDefaultClient (builder .httpExecutorService , builder .retryOptions );
134+ }
135+ this .clientSendsTrackingHeaders =
136+ builder .apiClient == null || builder .clientSendsTrackingHeaders ;
137+ }
138+
104139 /**
105140 * Constructs a new Gemini instance. Each generate request sent through {@code apiClient} carries
106141 * ADK's tracking tag, which the genai SDK appends to the client's tracking headers. If the
@@ -111,13 +146,18 @@ private static Client buildDefaultClient(@Nullable ExecutorService executorServi
111146 * @param apiClient The genai {@link com.google.genai.Client} instance for making API calls.
112147 */
113148 public Gemini (String modelName , Client apiClient ) {
114- this (modelName , apiClient , /* clientSendsTrackingHeaders= */ false );
149+ this (
150+ builder ()
151+ .modelName (modelName )
152+ .apiClient (Objects .requireNonNull (apiClient , "apiClient cannot be null" )));
115153 }
116154
117155 Gemini (String modelName , Client apiClient , boolean clientSendsTrackingHeaders ) {
118- super (modelName );
119- this .apiClient = Objects .requireNonNull (apiClient , "apiClient cannot be null" );
120- this .clientSendsTrackingHeaders = clientSendsTrackingHeaders ;
156+ this (
157+ builder ()
158+ .modelName (modelName )
159+ .apiClient (Objects .requireNonNull (apiClient , "apiClient cannot be null" ))
160+ .clientSendsTrackingHeaders (clientSendsTrackingHeaders ));
121161 }
122162
123163 /**
@@ -128,29 +168,28 @@ public Gemini(String modelName, Client apiClient) {
128168 */
129169 public Gemini (String modelName , String apiKey ) {
130170 this (
131- modelName ,
132- buildApiKeyClient ( Objects . requireNonNull ( apiKey , "apiKey cannot be null" ), null ),
133- /* clientSendsTrackingHeaders= */ true );
171+ builder ()
172+ . modelName ( modelName )
173+ . apiKey ( Objects . requireNonNull ( apiKey , "apiKey cannot be null" )) );
134174 }
135175
136176 /**
137- * Constructs a new Gemini instance with a Google Gemini API key .
177+ * Constructs a new Gemini instance with Vertex AI credentials .
138178 *
139179 * @param modelName The name of the Gemini model to use (e.g., "gemini-2.0-flash").
140180 * @param vertexCredentials The Vertex AI credentials to access the Gemini model.
141181 */
142182 public Gemini (String modelName , VertexCredentials vertexCredentials ) {
143183 this (
144- modelName ,
145- buildVertexClient (
146- Objects . requireNonNull ( vertexCredentials , "vertexCredentials cannot be null" ), null ),
147- /* clientSendsTrackingHeaders= */ true );
184+ builder ()
185+ . modelName ( modelName )
186+ . vertexCredentials (
187+ Objects . requireNonNull ( vertexCredentials , "vertexCredentials cannot be null" )) );
148188 }
149189
150190 /**
151- * Returns a new Builder instance for constructing Gemini objects. Note that when building a
152- * Gemini object, at least one of apiKey, vertexCredentials, or an explicit apiClient must be set.
153- * If multiple are set, the explicit apiClient will take precedence.
191+ * Returns a builder for configuring Gemini. An explicit client takes precedence over an API key
192+ * or Vertex credentials. If none are supplied, the client uses its default configuration.
154193 *
155194 * @return A new {@link Builder}.
156195 */
@@ -165,9 +204,16 @@ public static class Builder {
165204 private String apiKey ;
166205 private VertexCredentials vertexCredentials ;
167206 private ExecutorService httpExecutorService ;
207+ private HttpRetryOptions retryOptions ;
208+ private boolean clientSendsTrackingHeaders ;
168209
169210 private Builder () {}
170211
212+ private Builder clientSendsTrackingHeaders (boolean clientSendsTrackingHeaders ) {
213+ this .clientSendsTrackingHeaders = clientSendsTrackingHeaders ;
214+ return this ;
215+ }
216+
171217 /**
172218 * Sets the name of the Gemini model to use.
173219 *
@@ -236,6 +282,21 @@ public Builder httpExecutorService(ExecutorService httpExecutorService) {
236282 return this ;
237283 }
238284
285+ /**
286+ * Sets options for retrying transient HTTP failures through the Gemini client.
287+ *
288+ * <p>If an explicit {@link #apiClient(Client)} is supplied, its existing HTTP options take
289+ * precedence and these retry options are ignored.
290+ *
291+ * @param retryOptions The retry options to pass to the Google Gen AI client.
292+ * @return This builder.
293+ */
294+ @ CanIgnoreReturnValue
295+ public Builder retryOptions (HttpRetryOptions retryOptions ) {
296+ this .retryOptions = retryOptions ;
297+ return this ;
298+ }
299+
239300 /**
240301 * Builds the {@link Gemini} instance.
241302 *
@@ -244,19 +305,7 @@ public Builder httpExecutorService(ExecutorService httpExecutorService) {
244305 */
245306 public Gemini build () {
246307 Objects .requireNonNull (modelName , "modelName must be set." );
247-
248- if (apiClient != null ) {
249- return new Gemini (modelName , apiClient );
250- }
251- Client client ;
252- if (apiKey != null ) {
253- client = buildApiKeyClient (apiKey , httpExecutorService );
254- } else if (vertexCredentials != null ) {
255- client = buildVertexClient (vertexCredentials , httpExecutorService );
256- } else {
257- client = buildDefaultClient (httpExecutorService );
258- }
259- return new Gemini (modelName , client , /* clientSendsTrackingHeaders= */ true );
308+ return new Gemini (this );
260309 }
261310 }
262311
0 commit comments