diff --git a/examples/src/main/java/com/google/genai/examples/RequestLevelHttpOptions.java b/examples/src/main/java/com/google/genai/examples/RequestLevelHttpOptions.java
new file mode 100644
index 00000000000..1473a9ee9d7
--- /dev/null
+++ b/examples/src/main/java/com/google/genai/examples/RequestLevelHttpOptions.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2025 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Usage:
+ *
+ *
1a. If you are using Vertex AI, setup ADC to get credentials:
+ * https://cloud.google.com/docs/authentication/provide-credentials-adc#google-idp
+ *
+ *
Then set Project, Location, and USE_VERTEXAI flag as environment variables:
+ *
+ *
export GOOGLE_CLOUD_PROJECT=YOUR_PROJECT
+ *
+ *
export GOOGLE_CLOUD_LOCATION=YOUR_LOCATION
+ *
+ *
1b. If you are using Gemini Developer AI, set an API key environment variable. You can find a
+ * list of available API keys here: https://aistudio.google.com/app/apikey
+ *
+ *
export GOOGLE_API_KEY=YOUR_API_KEY
+ *
+ *
2. Compile the java package and run the sample code.
+ *
+ *
mvn clean compile
+ *
+ *
mvn exec:java -Dexec.mainClass="com.google.genai.examples.RequestLevelHttpOptions"
+ */
+package com.google.genai.examples;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.genai.Client;
+import com.google.genai.types.GenerateContentConfig;
+import com.google.genai.types.GenerateContentResponse;
+import com.google.genai.types.HttpOptions;
+
+/** An example of setting http options at request level. */
+public class RequestLevelHttpOptions {
+ public static void main(String[] args) {
+ // Instantiate the client. The client by default uses the Gemini Developer API. It gets the API
+ // key from the environment variable `GOOGLE_API_KEY`. Vertex AI API can be used by setting the
+ // environment variables `GOOGLE_CLOUD_LOCATION` and `GOOGLE_CLOUD_PROJECT`, as well as setting
+ // `GOOGLE_GENAI_USE_VERTEXAI` to "true".
+ //
+ // Do not set http options at client level.
+ Client client = new Client();
+
+ if (client.vertexAI()) {
+ System.out.println("Using Vertex AI");
+ } else {
+ System.out.println("Using Gemini Developer API");
+ }
+
+ // Set a customized header per request config.
+ GenerateContentConfig config =
+ GenerateContentConfig.builder()
+ .httpOptions(
+ HttpOptions.builder().headers(ImmutableMap.of("my-header", "my-value")).build())
+ .build();
+
+ GenerateContentResponse response =
+ client.models.generateContent("gemini-2.0-flash-001", "Tell me the history of LLM", config);
+
+ System.out.println("Response: " + response.text());
+ }
+}
diff --git a/src/main/java/com/google/genai/ApiClient.java b/src/main/java/com/google/genai/ApiClient.java
index 4e9c7c72a70..761e690b1e4 100644
--- a/src/main/java/com/google/genai/ApiClient.java
+++ b/src/main/java/com/google/genai/ApiClient.java
@@ -64,7 +64,7 @@ abstract class ApiClient {
this.httpOptions = defaultHttpOptions(/* vertexAI= */ false, this.location);
if (customHttpOptions.isPresent()) {
- applyHttpOptions(customHttpOptions.get());
+ this.httpOptions = applyHttpOptions(customHttpOptions.get());
}
this.httpClient = createHttpClient(httpOptions.timeout());
@@ -109,7 +109,7 @@ abstract class ApiClient {
this.httpOptions = defaultHttpOptions(/* vertexAI= */ true, this.location);
if (customHttpOptions.isPresent()) {
- applyHttpOptions(customHttpOptions.get());
+ this.httpOptions = applyHttpOptions(customHttpOptions.get());
}
this.apiKey = Optional.empty();
this.vertexAI = true;
@@ -128,7 +128,8 @@ private CloseableHttpClient createHttpClient(Optional timeout) {
}
/** Sends a Http request given the http method, path, and request json string. */
- public abstract ApiResponse request(String httpMethod, String path, String requestJson);
+ public abstract ApiResponse request(
+ String httpMethod, String path, String requestJson, HttpOptions httpOptions);
/** Returns the library version. */
static String libraryVersion() {
@@ -172,7 +173,16 @@ private Optional> getTimeoutHeader(HttpOptions httpOptionsTo
return Optional.empty();
}
- private void applyHttpOptions(HttpOptions httpOptionsToApply) {
+ /**
+ * Applies the http options to the client's http options.
+ *
+ * @param httpOptionsToApply the http options to apply
+ * @return the merged http options
+ */
+ HttpOptions applyHttpOptions(HttpOptions httpOptionsToApply) {
+ if (httpOptionsToApply == null) {
+ return this.httpOptions;
+ }
HttpOptions.Builder mergedHttpOptionsBuilder = this.httpOptions.toBuilder();
if (httpOptionsToApply.baseUrl().isPresent()) {
mergedHttpOptionsBuilder.baseUrl(httpOptionsToApply.baseUrl().get());
@@ -192,7 +202,7 @@ private void applyHttpOptions(HttpOptions httpOptionsToApply) {
.build();
mergedHttpOptionsBuilder.headers(mergedHeaders);
}
- this.httpOptions = mergedHttpOptionsBuilder.build();
+ return mergedHttpOptionsBuilder.build();
}
static HttpOptions defaultHttpOptions(boolean vertexAI, Optional location) {
diff --git a/src/main/java/com/google/genai/HttpApiClient.java b/src/main/java/com/google/genai/HttpApiClient.java
index f1363f05650..d4ac3be8817 100644
--- a/src/main/java/com/google/genai/HttpApiClient.java
+++ b/src/main/java/com/google/genai/HttpApiClient.java
@@ -49,9 +49,11 @@ public class HttpApiClient extends ApiClient {
super(project, location, credentials, httpOptions);
}
- /** Sends a Http request given the http method, path, and request json string. */
+ /** Sends a Http request given the http method, path, request json string, and http options. */
@Override
- public HttpApiResponse request(String httpMethod, String path, String requestJson) {
+ public HttpApiResponse request(
+ String httpMethod, String path, String requestJson, HttpOptions httpOptions) {
+ HttpOptions requestHttpOptions = applyHttpOptions(httpOptions);
boolean queryBaseModel =
httpMethod.equalsIgnoreCase("GET") && path.startsWith("publishers/google/models/");
if (this.vertexAI() && !path.startsWith("projects/") && !queryBaseModel) {
@@ -61,20 +63,21 @@ public HttpApiResponse request(String httpMethod, String path, String requestJso
}
String requestUrl =
String.format(
- "%s/%s/%s", httpOptions.baseUrl().get(), httpOptions.apiVersion().get(), path);
+ "%s/%s/%s",
+ requestHttpOptions.baseUrl().get(), requestHttpOptions.apiVersion().get(), path);
if (httpMethod.equalsIgnoreCase("POST")) {
HttpPost httpPost = new HttpPost(requestUrl);
- setHeaders(httpPost);
+ setHeaders(httpPost, requestHttpOptions);
httpPost.setEntity(new StringEntity(requestJson, ContentType.APPLICATION_JSON));
return executeRequest(httpPost);
} else if (httpMethod.equalsIgnoreCase("GET")) {
HttpGet httpGet = new HttpGet(requestUrl);
- setHeaders(httpGet);
+ setHeaders(httpGet, requestHttpOptions);
return executeRequest(httpGet);
} else if (httpMethod.equalsIgnoreCase("DELETE")) {
HttpDelete httpDelete = new HttpDelete(requestUrl);
- setHeaders(httpDelete);
+ setHeaders(httpDelete, requestHttpOptions);
return executeRequest(httpDelete);
} else {
throw new IllegalArgumentException("Unsupported HTTP method: " + httpMethod);
@@ -82,9 +85,9 @@ public HttpApiResponse request(String httpMethod, String path, String requestJso
}
/** Sets the required headers (including auth) on the request object. */
- private void setHeaders(HttpRequestBase request) {
+ private void setHeaders(HttpRequestBase request, HttpOptions requestHttpOptions) {
for (Map.Entry header :
- httpOptions.headers().orElse(ImmutableMap.of()).entrySet()) {
+ requestHttpOptions.headers().orElse(ImmutableMap.of()).entrySet()) {
request.setHeader(header.getKey(), header.getValue());
}
diff --git a/src/main/java/com/google/genai/Models.java b/src/main/java/com/google/genai/Models.java
index 82066b3adeb..5c094ddce56 100644
--- a/src/main/java/com/google/genai/Models.java
+++ b/src/main/java/com/google/genai/Models.java
@@ -47,6 +47,7 @@
import com.google.genai.types.GenerateVideosOperation;
import com.google.genai.types.GenerateVideosParameters;
import com.google.genai.types.GeneratedImage;
+import com.google.genai.types.HttpOptions;
import com.google.genai.types.Image;
import com.google.genai.types.Part;
import com.google.genai.types.ReferenceImage;
@@ -4116,8 +4117,13 @@ private GenerateContentResponse privateGenerateContent(
// TODO: Remove the hack that removes config.
body.remove("config");
+ HttpOptions httpOptions = null;
+ if (config != null) {
+ httpOptions = config.httpOptions().orElse(null);
+ }
+
try (ApiResponse response =
- this.apiClient.request("post", path, JsonSerializable.toJsonString(body))) {
+ this.apiClient.request("post", path, JsonSerializable.toJsonString(body), httpOptions)) {
HttpEntity entity = response.getEntity();
String responseString;
try {
@@ -4167,8 +4173,13 @@ private ResponseStream privateGenerateContentStream(
// TODO: Remove the hack that removes config.
body.remove("config");
+ HttpOptions httpOptions = null;
+ if (config != null) {
+ httpOptions = config.httpOptions().orElse(null);
+ }
+
ApiResponse response =
- this.apiClient.request("post", path, JsonSerializable.toJsonString(body));
+ this.apiClient.request("post", path, JsonSerializable.toJsonString(body), httpOptions);
String converterName;
if (this.apiClient.vertexAI()) {
converterName = "generateContentResponseFromVertex";
@@ -4211,8 +4222,13 @@ private EmbedContentResponse privateEmbedContent(
// TODO: Remove the hack that removes config.
body.remove("config");
+ HttpOptions httpOptions = null;
+ if (config != null) {
+ httpOptions = config.httpOptions().orElse(null);
+ }
+
try (ApiResponse response =
- this.apiClient.request("post", path, JsonSerializable.toJsonString(body))) {
+ this.apiClient.request("post", path, JsonSerializable.toJsonString(body), httpOptions)) {
HttpEntity entity = response.getEntity();
String responseString;
try {
@@ -4262,8 +4278,13 @@ private GenerateImagesResponse privateGenerateImages(
// TODO: Remove the hack that removes config.
body.remove("config");
+ HttpOptions httpOptions = null;
+ if (config != null) {
+ httpOptions = config.httpOptions().orElse(null);
+ }
+
try (ApiResponse response =
- this.apiClient.request("post", path, JsonSerializable.toJsonString(body))) {
+ this.apiClient.request("post", path, JsonSerializable.toJsonString(body), httpOptions)) {
HttpEntity entity = response.getEntity();
String responseString;
try {
@@ -4319,8 +4340,13 @@ private EditImageResponse privateEditImage(
// TODO: Remove the hack that removes config.
body.remove("config");
+ HttpOptions httpOptions = null;
+ if (config != null) {
+ httpOptions = config.httpOptions().orElse(null);
+ }
+
try (ApiResponse response =
- this.apiClient.request("post", path, JsonSerializable.toJsonString(body))) {
+ this.apiClient.request("post", path, JsonSerializable.toJsonString(body), httpOptions)) {
HttpEntity entity = response.getEntity();
String responseString;
try {
@@ -4374,8 +4400,13 @@ private UpscaleImageResponse privateUpscaleImage(
// TODO: Remove the hack that removes config.
body.remove("config");
+ HttpOptions httpOptions = null;
+ if (config != null) {
+ httpOptions = config.httpOptions().orElse(null);
+ }
+
try (ApiResponse response =
- this.apiClient.request("post", path, JsonSerializable.toJsonString(body))) {
+ this.apiClient.request("post", path, JsonSerializable.toJsonString(body), httpOptions)) {
HttpEntity entity = response.getEntity();
String responseString;
try {
@@ -4426,8 +4457,13 @@ public CountTokensResponse countTokens(
// TODO: Remove the hack that removes config.
body.remove("config");
+ HttpOptions httpOptions = null;
+ if (config != null) {
+ httpOptions = config.httpOptions().orElse(null);
+ }
+
try (ApiResponse response =
- this.apiClient.request("post", path, JsonSerializable.toJsonString(body))) {
+ this.apiClient.request("post", path, JsonSerializable.toJsonString(body), httpOptions)) {
HttpEntity entity = response.getEntity();
String responseString;
try {
@@ -4477,8 +4513,13 @@ public ComputeTokensResponse computeTokens(
// TODO: Remove the hack that removes config.
body.remove("config");
+ HttpOptions httpOptions = null;
+ if (config != null) {
+ httpOptions = config.httpOptions().orElse(null);
+ }
+
try (ApiResponse response =
- this.apiClient.request("post", path, JsonSerializable.toJsonString(body))) {
+ this.apiClient.request("post", path, JsonSerializable.toJsonString(body), httpOptions)) {
HttpEntity entity = response.getEntity();
String responseString;
try {
@@ -4545,8 +4586,13 @@ public GenerateVideosOperation generateVideos(
// TODO: Remove the hack that removes config.
body.remove("config");
+ HttpOptions httpOptions = null;
+ if (config != null) {
+ httpOptions = config.httpOptions().orElse(null);
+ }
+
try (ApiResponse response =
- this.apiClient.request("post", path, JsonSerializable.toJsonString(body))) {
+ this.apiClient.request("post", path, JsonSerializable.toJsonString(body), httpOptions)) {
HttpEntity entity = response.getEntity();
String responseString;
try {
diff --git a/src/main/java/com/google/genai/Operations.java b/src/main/java/com/google/genai/Operations.java
index 9cdaef0ad04..e739eaf9311 100644
--- a/src/main/java/com/google/genai/Operations.java
+++ b/src/main/java/com/google/genai/Operations.java
@@ -28,6 +28,7 @@
import com.google.genai.types.GenerateVideosOperation;
import com.google.genai.types.GetOperationConfig;
import com.google.genai.types.GetOperationParameters;
+import com.google.genai.types.HttpOptions;
import java.io.IOException;
import org.apache.http.HttpEntity;
import org.apache.http.util.EntityUtils;
@@ -402,8 +403,13 @@ private GenerateVideosOperation privateGetVideosOperation(
// TODO: Remove the hack that removes config.
body.remove("config");
+ HttpOptions httpOptions = null;
+ if (config != null) {
+ httpOptions = config.httpOptions().orElse(null);
+ }
+
try (ApiResponse response =
- this.apiClient.request("get", path, JsonSerializable.toJsonString(body))) {
+ this.apiClient.request("get", path, JsonSerializable.toJsonString(body), httpOptions)) {
HttpEntity entity = response.getEntity();
String responseString;
try {
@@ -454,8 +460,13 @@ private GenerateVideosOperation privateFetchPredictVideosOperation(
// TODO: Remove the hack that removes config.
body.remove("config");
+ HttpOptions httpOptions = null;
+ if (config != null) {
+ httpOptions = config.httpOptions().orElse(null);
+ }
+
try (ApiResponse response =
- this.apiClient.request("post", path, JsonSerializable.toJsonString(body))) {
+ this.apiClient.request("post", path, JsonSerializable.toJsonString(body), httpOptions)) {
HttpEntity entity = response.getEntity();
String responseString;
try {
diff --git a/src/main/java/com/google/genai/ReplayApiClient.java b/src/main/java/com/google/genai/ReplayApiClient.java
index db8b39dbb2b..bb5ddee8151 100644
--- a/src/main/java/com/google/genai/ReplayApiClient.java
+++ b/src/main/java/com/google/genai/ReplayApiClient.java
@@ -121,7 +121,8 @@ void initializeReplaySession(String replayId) {
/** Sends a Http Post request given the path and request json string. */
@SuppressWarnings("unchecked")
@Override
- public ApiResponse request(String httpMethod, String path, String requestJson) {
+ public ApiResponse request(
+ String httpMethod, String path, String requestJson, HttpOptions httpOptions) {
if (this.clientMode.equals("replay") || this.clientMode.equals("auto")) {
System.out.println(" === Using replay for ID: " + this.replayId);
List interactions = Arrays.asList(this.replaySession.get("interactions"));
diff --git a/src/main/java/com/google/genai/types/ComputeTokensConfig.java b/src/main/java/com/google/genai/types/ComputeTokensConfig.java
index 8f9aebf57f6..9b6826bd4ad 100644
--- a/src/main/java/com/google/genai/types/ComputeTokensConfig.java
+++ b/src/main/java/com/google/genai/types/ComputeTokensConfig.java
@@ -19,14 +19,20 @@
package com.google.genai.types;
import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.google.auto.value.AutoValue;
import com.google.genai.JsonSerializable;
+import java.util.Optional;
/** Optional parameters for computing tokens. */
@AutoValue
@JsonDeserialize(builder = ComputeTokensConfig.Builder.class)
public abstract class ComputeTokensConfig extends JsonSerializable {
+ /** Used to override HTTP request options. */
+ @JsonProperty("httpOptions")
+ public abstract Optional httpOptions();
+
/** Instantiates a builder for ComputeTokensConfig. */
public static Builder builder() {
return new AutoValue_ComputeTokensConfig.Builder();
@@ -44,6 +50,9 @@ private static Builder create() {
return new AutoValue_ComputeTokensConfig.Builder();
}
+ @JsonProperty("httpOptions")
+ public abstract Builder httpOptions(HttpOptions httpOptions);
+
public abstract ComputeTokensConfig build();
}
diff --git a/src/main/java/com/google/genai/types/CountTokensConfig.java b/src/main/java/com/google/genai/types/CountTokensConfig.java
index 671780f25d2..0dd8fe93f6e 100644
--- a/src/main/java/com/google/genai/types/CountTokensConfig.java
+++ b/src/main/java/com/google/genai/types/CountTokensConfig.java
@@ -30,6 +30,10 @@
@AutoValue
@JsonDeserialize(builder = CountTokensConfig.Builder.class)
public abstract class CountTokensConfig extends JsonSerializable {
+ /** Used to override HTTP request options. */
+ @JsonProperty("httpOptions")
+ public abstract Optional httpOptions();
+
/** Instructions for the model to steer it toward better performance. */
@JsonProperty("systemInstruction")
public abstract Optional systemInstruction();
@@ -65,6 +69,9 @@ private static Builder create() {
return new AutoValue_CountTokensConfig.Builder();
}
+ @JsonProperty("httpOptions")
+ public abstract Builder httpOptions(HttpOptions httpOptions);
+
@JsonProperty("systemInstruction")
public abstract Builder systemInstruction(Content systemInstruction);
diff --git a/src/main/java/com/google/genai/types/DownloadFileConfig.java b/src/main/java/com/google/genai/types/DownloadFileConfig.java
new file mode 100644
index 00000000000..8ecf6384d16
--- /dev/null
+++ b/src/main/java/com/google/genai/types/DownloadFileConfig.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2025 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Auto-generated code. Do not edit.
+
+package com.google.genai.types;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.google.auto.value.AutoValue;
+import com.google.genai.JsonSerializable;
+import java.util.Optional;
+
+/** Used to override the default configuration. */
+@AutoValue
+@JsonDeserialize(builder = DownloadFileConfig.Builder.class)
+public abstract class DownloadFileConfig extends JsonSerializable {
+ /** Used to override HTTP request options. */
+ @JsonProperty("httpOptions")
+ public abstract Optional httpOptions();
+
+ /** Instantiates a builder for DownloadFileConfig. */
+ public static Builder builder() {
+ return new AutoValue_DownloadFileConfig.Builder();
+ }
+
+ /** Creates a builder with the same values as this instance. */
+ public abstract Builder toBuilder();
+
+ /** Builder for DownloadFileConfig. */
+ @AutoValue.Builder
+ public abstract static class Builder {
+ /** For internal usage. Please use `DownloadFileConfig.builder()` for instantiation. */
+ @JsonCreator
+ private static Builder create() {
+ return new AutoValue_DownloadFileConfig.Builder();
+ }
+
+ @JsonProperty("httpOptions")
+ public abstract Builder httpOptions(HttpOptions httpOptions);
+
+ public abstract DownloadFileConfig build();
+ }
+
+ /** Deserializes a JSON string to a DownloadFileConfig object. */
+ public static DownloadFileConfig fromJson(String jsonString) {
+ return JsonSerializable.fromJsonString(jsonString, DownloadFileConfig.class);
+ }
+}
diff --git a/src/main/java/com/google/genai/types/EditImageConfig.java b/src/main/java/com/google/genai/types/EditImageConfig.java
index 3085aba9de7..d468ab17601 100644
--- a/src/main/java/com/google/genai/types/EditImageConfig.java
+++ b/src/main/java/com/google/genai/types/EditImageConfig.java
@@ -30,6 +30,10 @@
@AutoValue
@JsonDeserialize(builder = EditImageConfig.Builder.class)
public abstract class EditImageConfig extends JsonSerializable {
+ /** Used to override HTTP request options. */
+ @JsonProperty("httpOptions")
+ public abstract Optional httpOptions();
+
/** Cloud Storage URI used to store the generated images. */
@JsonProperty("outputGcsUri")
public abstract Optional outputGcsUri();
@@ -121,6 +125,9 @@ private static Builder create() {
return new AutoValue_EditImageConfig.Builder();
}
+ @JsonProperty("httpOptions")
+ public abstract Builder httpOptions(HttpOptions httpOptions);
+
@JsonProperty("outputGcsUri")
public abstract Builder outputGcsUri(String outputGcsUri);
diff --git a/src/main/java/com/google/genai/types/EmbedContentConfig.java b/src/main/java/com/google/genai/types/EmbedContentConfig.java
index eec61c958b1..76d8fab5fbb 100644
--- a/src/main/java/com/google/genai/types/EmbedContentConfig.java
+++ b/src/main/java/com/google/genai/types/EmbedContentConfig.java
@@ -29,6 +29,10 @@
@AutoValue
@JsonDeserialize(builder = EmbedContentConfig.Builder.class)
public abstract class EmbedContentConfig extends JsonSerializable {
+ /** Used to override HTTP request options. */
+ @JsonProperty("httpOptions")
+ public abstract Optional httpOptions();
+
/** Type of task for which the embedding will be used. */
@JsonProperty("taskType")
public abstract Optional taskType();
@@ -74,6 +78,9 @@ private static Builder create() {
return new AutoValue_EmbedContentConfig.Builder();
}
+ @JsonProperty("httpOptions")
+ public abstract Builder httpOptions(HttpOptions httpOptions);
+
@JsonProperty("taskType")
public abstract Builder taskType(String taskType);
diff --git a/src/main/java/com/google/genai/types/FetchPredictOperationConfig.java b/src/main/java/com/google/genai/types/FetchPredictOperationConfig.java
index 10b9dafb8c9..66c1f5235c3 100644
--- a/src/main/java/com/google/genai/types/FetchPredictOperationConfig.java
+++ b/src/main/java/com/google/genai/types/FetchPredictOperationConfig.java
@@ -19,14 +19,20 @@
package com.google.genai.types;
import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.google.auto.value.AutoValue;
import com.google.genai.JsonSerializable;
+import java.util.Optional;
/** None */
@AutoValue
@JsonDeserialize(builder = FetchPredictOperationConfig.Builder.class)
public abstract class FetchPredictOperationConfig extends JsonSerializable {
+ /** Used to override HTTP request options. */
+ @JsonProperty("httpOptions")
+ public abstract Optional httpOptions();
+
/** Instantiates a builder for FetchPredictOperationConfig. */
public static Builder builder() {
return new AutoValue_FetchPredictOperationConfig.Builder();
@@ -44,6 +50,9 @@ private static Builder create() {
return new AutoValue_FetchPredictOperationConfig.Builder();
}
+ @JsonProperty("httpOptions")
+ public abstract Builder httpOptions(HttpOptions httpOptions);
+
public abstract FetchPredictOperationConfig build();
}
diff --git a/src/main/java/com/google/genai/types/GenerateContentConfig.java b/src/main/java/com/google/genai/types/GenerateContentConfig.java
index 89fba741bab..d5e96dfac56 100644
--- a/src/main/java/com/google/genai/types/GenerateContentConfig.java
+++ b/src/main/java/com/google/genai/types/GenerateContentConfig.java
@@ -37,6 +37,10 @@
@AutoValue
@JsonDeserialize(builder = GenerateContentConfig.Builder.class)
public abstract class GenerateContentConfig extends JsonSerializable {
+ /** Used to override HTTP request options. */
+ @JsonProperty("httpOptions")
+ public abstract Optional httpOptions();
+
/**
* Instructions for the model to steer it toward better performance. For example, "Answer as
* concisely as possible" or "Don't use technical terms in your response".
@@ -205,6 +209,9 @@ private static Builder create() {
return new AutoValue_GenerateContentConfig.Builder();
}
+ @JsonProperty("httpOptions")
+ public abstract Builder httpOptions(HttpOptions httpOptions);
+
@JsonProperty("systemInstruction")
public abstract Builder systemInstruction(Content systemInstruction);
diff --git a/src/main/java/com/google/genai/types/GenerateImagesConfig.java b/src/main/java/com/google/genai/types/GenerateImagesConfig.java
index 7eddb35bc71..849a60e4c12 100644
--- a/src/main/java/com/google/genai/types/GenerateImagesConfig.java
+++ b/src/main/java/com/google/genai/types/GenerateImagesConfig.java
@@ -30,6 +30,10 @@
@AutoValue
@JsonDeserialize(builder = GenerateImagesConfig.Builder.class)
public abstract class GenerateImagesConfig extends JsonSerializable {
+ /** Used to override HTTP request options. */
+ @JsonProperty("httpOptions")
+ public abstract Optional httpOptions();
+
/** Cloud Storage URI used to store the generated images. */
@JsonProperty("outputGcsUri")
public abstract Optional outputGcsUri();
@@ -118,6 +122,9 @@ private static Builder create() {
return new AutoValue_GenerateImagesConfig.Builder();
}
+ @JsonProperty("httpOptions")
+ public abstract Builder httpOptions(HttpOptions httpOptions);
+
@JsonProperty("outputGcsUri")
public abstract Builder outputGcsUri(String outputGcsUri);
diff --git a/src/main/java/com/google/genai/types/GenerateVideosConfig.java b/src/main/java/com/google/genai/types/GenerateVideosConfig.java
index afb4f9c4933..a536fe3fcae 100644
--- a/src/main/java/com/google/genai/types/GenerateVideosConfig.java
+++ b/src/main/java/com/google/genai/types/GenerateVideosConfig.java
@@ -29,6 +29,10 @@
@AutoValue
@JsonDeserialize(builder = GenerateVideosConfig.Builder.class)
public abstract class GenerateVideosConfig extends JsonSerializable {
+ /** Used to override HTTP request options. */
+ @JsonProperty("httpOptions")
+ public abstract Optional httpOptions();
+
/** Number of output videos. */
@JsonProperty("numberOfVideos")
public abstract Optional numberOfVideos();
@@ -102,6 +106,9 @@ private static Builder create() {
return new AutoValue_GenerateVideosConfig.Builder();
}
+ @JsonProperty("httpOptions")
+ public abstract Builder httpOptions(HttpOptions httpOptions);
+
@JsonProperty("numberOfVideos")
public abstract Builder numberOfVideos(Integer numberOfVideos);
diff --git a/src/main/java/com/google/genai/types/GetOperationConfig.java b/src/main/java/com/google/genai/types/GetOperationConfig.java
index 21a24b75e33..aa62b6e429c 100644
--- a/src/main/java/com/google/genai/types/GetOperationConfig.java
+++ b/src/main/java/com/google/genai/types/GetOperationConfig.java
@@ -19,14 +19,20 @@
package com.google.genai.types;
import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.google.auto.value.AutoValue;
import com.google.genai.JsonSerializable;
+import java.util.Optional;
/** None */
@AutoValue
@JsonDeserialize(builder = GetOperationConfig.Builder.class)
public abstract class GetOperationConfig extends JsonSerializable {
+ /** Used to override HTTP request options. */
+ @JsonProperty("httpOptions")
+ public abstract Optional httpOptions();
+
/** Instantiates a builder for GetOperationConfig. */
public static Builder builder() {
return new AutoValue_GetOperationConfig.Builder();
@@ -44,6 +50,9 @@ private static Builder create() {
return new AutoValue_GetOperationConfig.Builder();
}
+ @JsonProperty("httpOptions")
+ public abstract Builder httpOptions(HttpOptions httpOptions);
+
public abstract GetOperationConfig build();
}
diff --git a/src/main/java/com/google/genai/types/LiveConnectConfig.java b/src/main/java/com/google/genai/types/LiveConnectConfig.java
index 4cd49b2b001..7542f0afdbd 100644
--- a/src/main/java/com/google/genai/types/LiveConnectConfig.java
+++ b/src/main/java/com/google/genai/types/LiveConnectConfig.java
@@ -34,6 +34,10 @@
@AutoValue
@JsonDeserialize(builder = LiveConnectConfig.Builder.class)
public abstract class LiveConnectConfig extends JsonSerializable {
+ /** Used to override HTTP request options. */
+ @JsonProperty("httpOptions")
+ public abstract Optional httpOptions();
+
/**
* The requested modalities of the response. Represents the set of modalities that the model can
* return. Defaults to AUDIO if not specified.
@@ -148,6 +152,9 @@ private static Builder create() {
return new AutoValue_LiveConnectConfig.Builder();
}
+ @JsonProperty("httpOptions")
+ public abstract Builder httpOptions(HttpOptions httpOptions);
+
@JsonProperty("responseModalities")
public abstract Builder responseModalities(List responseModalities);
diff --git a/src/main/java/com/google/genai/types/UploadFileConfig.java b/src/main/java/com/google/genai/types/UploadFileConfig.java
index 20a68aa4cca..2a5b67eb187 100644
--- a/src/main/java/com/google/genai/types/UploadFileConfig.java
+++ b/src/main/java/com/google/genai/types/UploadFileConfig.java
@@ -29,6 +29,10 @@
@AutoValue
@JsonDeserialize(builder = UploadFileConfig.Builder.class)
public abstract class UploadFileConfig extends JsonSerializable {
+ /** Used to override HTTP request options. */
+ @JsonProperty("httpOptions")
+ public abstract Optional httpOptions();
+
/**
* The name of the file in the destination (e.g., 'files/sample-image'. If not provided one will
* be generated.
@@ -64,6 +68,9 @@ private static Builder create() {
return new AutoValue_UploadFileConfig.Builder();
}
+ @JsonProperty("httpOptions")
+ public abstract Builder httpOptions(HttpOptions httpOptions);
+
@JsonProperty("name")
public abstract Builder name(String name);
diff --git a/src/main/java/com/google/genai/types/UpscaleImageAPIConfig.java b/src/main/java/com/google/genai/types/UpscaleImageAPIConfig.java
index eabd4671f28..d6e6cc587db 100644
--- a/src/main/java/com/google/genai/types/UpscaleImageAPIConfig.java
+++ b/src/main/java/com/google/genai/types/UpscaleImageAPIConfig.java
@@ -36,6 +36,10 @@
@InternalApi
@JsonDeserialize(builder = UpscaleImageAPIConfig.Builder.class)
public abstract class UpscaleImageAPIConfig extends JsonSerializable {
+ /** Used to override HTTP request options. */
+ @JsonProperty("httpOptions")
+ public abstract Optional httpOptions();
+
/** Whether to include a reason for filtered-out images in the response. */
@JsonProperty("includeRaiReason")
public abstract Optional includeRaiReason();
@@ -73,6 +77,9 @@ private static Builder create() {
return new AutoValue_UpscaleImageAPIConfig.Builder();
}
+ @JsonProperty("httpOptions")
+ public abstract Builder httpOptions(HttpOptions httpOptions);
+
@JsonProperty("includeRaiReason")
public abstract Builder includeRaiReason(boolean includeRaiReason);
diff --git a/src/main/java/com/google/genai/types/UpscaleImageConfig.java b/src/main/java/com/google/genai/types/UpscaleImageConfig.java
index ec40ffa5efe..c7e5396f8f6 100644
--- a/src/main/java/com/google/genai/types/UpscaleImageConfig.java
+++ b/src/main/java/com/google/genai/types/UpscaleImageConfig.java
@@ -34,6 +34,10 @@
@AutoValue
@JsonDeserialize(builder = UpscaleImageConfig.Builder.class)
public abstract class UpscaleImageConfig extends JsonSerializable {
+ /** Used to override HTTP request options. */
+ @JsonProperty("httpOptions")
+ public abstract Optional httpOptions();
+
/** Whether to include a reason for filtered-out images in the response. */
@JsonProperty("includeRaiReason")
public abstract Optional includeRaiReason();
@@ -63,6 +67,9 @@ private static Builder create() {
return new AutoValue_UpscaleImageConfig.Builder();
}
+ @JsonProperty("httpOptions")
+ public abstract Builder httpOptions(HttpOptions httpOptions);
+
@JsonProperty("includeRaiReason")
public abstract Builder includeRaiReason(boolean includeRaiReason);
diff --git a/src/test/java/com/google/genai/ChatTest.java b/src/test/java/com/google/genai/ChatTest.java
index c995b2f5511..020ff1ed1f4 100644
--- a/src/test/java/com/google/genai/ChatTest.java
+++ b/src/test/java/com/google/genai/ChatTest.java
@@ -18,6 +18,7 @@
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;
@@ -142,7 +143,8 @@ public class ChatTest {
void setUp() {
mockedClient = Mockito.mock(ApiClient.class);
mockedResponse = Mockito.mock(ApiResponse.class);
- when(mockedClient.request(anyString(), anyString(), anyString())).thenReturn(mockedResponse);
+ when(mockedClient.request(anyString(), anyString(), anyString(), any()))
+ .thenReturn(mockedResponse);
mockedEntity = Mockito.mock(HttpEntity.class);
client = Client.builder().build();
@@ -433,7 +435,7 @@ public void testIterateOverResponseStream() throws Exception {
when(mockedEntity1.getContent()).thenReturn(inputStream1);
when(mockedEntity2.getContent()).thenReturn(inputStream2);
when(mockedEntity3.getContent()).thenReturn(inputStream3);
- when(mockedClient.request(anyString(), anyString(), anyString()))
+ when(mockedClient.request(anyString(), anyString(), anyString(), any()))
.thenReturn(mockedResponse1, mockedResponse2, mockedResponse3);
assert chatSession.getHistory(false).size() == 0;
@@ -519,7 +521,7 @@ public void testThrowsIfStreamResponseIsNotConsumed() throws Exception {
when(mockedResponse2.getEntity()).thenReturn(mockedEntity2);
when(mockedEntity1.getContent()).thenReturn(inputStream1);
when(mockedEntity2.getContent()).thenReturn(inputStream2);
- when(mockedClient.request(anyString(), anyString(), anyString()))
+ when(mockedClient.request(anyString(), anyString(), anyString(), any()))
.thenReturn(mockedResponse1, mockedResponse2);
assert chatSession.getHistory(false).size() == 0;
diff --git a/src/test/java/com/google/genai/DefaultValuesTest.java b/src/test/java/com/google/genai/DefaultValuesTest.java
index f52827fc737..7e5a0e7a980 100644
--- a/src/test/java/com/google/genai/DefaultValuesTest.java
+++ b/src/test/java/com/google/genai/DefaultValuesTest.java
@@ -17,6 +17,7 @@
package com.google.genai;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -37,7 +38,8 @@ public void testDefaultValues() throws Exception {
// Mocks and test setup.
ApiClient httpClientSpy = Mockito.spy(Mockito.mock(ApiClient.class));
ApiResponse mockedResponse = Mockito.mock(ApiResponse.class);
- when(httpClientSpy.request(anyString(), anyString(), anyString())).thenReturn(mockedResponse);
+ when(httpClientSpy.request(anyString(), anyString(), anyString(), any()))
+ .thenReturn(mockedResponse);
HttpEntity mockedEntity = Mockito.mock(HttpEntity.class);
GenerateContentResponse returnResponse = GenerateContentResponse.builder().build();
StringEntity content = new StringEntity(returnResponse.toJson());
@@ -55,7 +57,8 @@ public void testDefaultValues() throws Exception {
ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(String.class);
verify(httpClientSpy)
- .request(argumentCaptor.capture(), argumentCaptor.capture(), argumentCaptor.capture());
+ .request(
+ argumentCaptor.capture(), argumentCaptor.capture(), argumentCaptor.capture(), any());
GenerateContentConfig spiedConfig =
GenerateContentConfig.fromJson(argumentCaptor.getAllValues().get(2));
diff --git a/src/test/java/com/google/genai/ForwardCompatibilityTest.java b/src/test/java/com/google/genai/ForwardCompatibilityTest.java
index 95887f57fb0..329897349fd 100644
--- a/src/test/java/com/google/genai/ForwardCompatibilityTest.java
+++ b/src/test/java/com/google/genai/ForwardCompatibilityTest.java
@@ -18,6 +18,7 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -57,7 +58,8 @@ public class ForwardCompatibilityTest {
void setUp() {
mockedClient = Mockito.mock(ApiClient.class);
mockedResponse = Mockito.mock(ApiResponse.class);
- when(mockedClient.request(anyString(), anyString(), anyString())).thenReturn(mockedResponse);
+ when(mockedClient.request(anyString(), anyString(), anyString(), any()))
+ .thenReturn(mockedResponse);
mockedEntity = Mockito.mock(HttpEntity.class);
returnResponse = GenerateContentResponse.builder().build();
@@ -69,13 +71,6 @@ void setUp() {
@Test
public void testForwardCompatibility() throws Exception {
// Mocks and test setup.
- ApiClient mockedClient = Mockito.mock(ApiClient.class);
- ApiResponse mockedResponse = Mockito.mock(ApiResponse.class);
- when(mockedClient.request(anyString(), anyString(), anyString())).thenReturn(mockedResponse);
- HttpEntity mockedEntity = Mockito.mock(HttpEntity.class);
- GenerateContentResponse returnResponse = GenerateContentResponse.builder().build();
-
- ObjectMapper objectMapper = new ObjectMapper();
JsonNode rootNode = objectMapper.readTree(returnResponse.toJson());
ObjectNode objectNode = (ObjectNode) rootNode;
objectNode.put("unknownFieldToTestForwardCompatibility", "Hello World!");
@@ -84,7 +79,6 @@ public void testForwardCompatibility() throws Exception {
StringEntity content = new StringEntity(jsonString);
when(mockedResponse.getEntity()).thenReturn(content);
- Client client = Client.builder().build();
// Make the apiClient field public so that it can be spied on in the tests. This is a
// workaround for the fact that the ApiClient is a final class and cannot be spied on directly.
Field apiClientField = Models.class.getDeclaredField("apiClient");
@@ -251,7 +245,7 @@ public void testEnumSendingUnknownSafetysettings() throws Exception {
client.models.generateContent("gemini-2.0-flash-exp", "What is your name?", config);
ArgumentCaptor stringCaptor = ArgumentCaptor.forClass(String.class);
- verify(mockedClient).request(anyString(), anyString(), stringCaptor.capture());
+ verify(mockedClient).request(anyString(), anyString(), stringCaptor.capture(), any());
assertEquals(
"{\"contents\":[{\"parts\":[{\"text\":\"What is your name?\"}],\"role\":\"user\"}],"
+ "\"safetySettings\":[{\"threshold\":\"NEW_UNKNOWN_VALUE\"}],\"generationConfig\":{}}",
diff --git a/src/test/java/com/google/genai/HttpApiClientTest.java b/src/test/java/com/google/genai/HttpApiClientTest.java
index c36d69ff26c..7a9ce4ef876 100644
--- a/src/test/java/com/google/genai/HttpApiClientTest.java
+++ b/src/test/java/com/google/genai/HttpApiClientTest.java
@@ -67,6 +67,12 @@ public class HttpApiClientTest {
HttpApiClient.defaultHttpOptions(false, Optional.empty());
private static final HttpOptions defaultHttpOptionsVertex =
HttpApiClient.defaultHttpOptions(true, Optional.of(LOCATION));
+ private static final HttpOptions REQUEST_HTTP_OPTIONS =
+ HttpOptions.builder()
+ .baseUrl("test-url")
+ .apiVersion("test-api-version")
+ .headers(ImmutableMap.of("test", "header"))
+ .build();
private static final String TEST_PATH = "test-path";
private static final String TEST_REQUEST_JSON = "{\"test\": \"request-json\"}";
@@ -94,7 +100,7 @@ public void testRequestPostMethodWithVertexAI() throws Exception {
setMockClient(client);
// Act
- client.request("POST", TEST_PATH, TEST_REQUEST_JSON);
+ client.request("POST", TEST_PATH, TEST_REQUEST_JSON, null);
// Assert
ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(HttpRequestBase.class);
@@ -127,7 +133,7 @@ public void testRequestGetMethodWithMldev() throws Exception {
setMockClient(client);
// Act
- client.request("GET", TEST_PATH, null);
+ client.request("GET", TEST_PATH, null, null);
// Assert
ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(HttpRequestBase.class);
@@ -152,7 +158,7 @@ public void testRequestDeleteMethodWithMldev() throws Exception {
setMockClient(client);
// Act
- client.request("DELETE", TEST_PATH, null);
+ client.request("DELETE", TEST_PATH, null, null);
// Assert
ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(HttpRequestBase.class);
@@ -170,6 +176,29 @@ public void testRequestDeleteMethodWithMldev() throws Exception {
assertNull(capturedRequest.getFirstHeader("Authorization"));
}
+ @Test
+ public void testRequestWithHttpOptions() throws Exception {
+ // Arrange
+ HttpApiClient client = new HttpApiClient(Optional.of(API_KEY), Optional.empty());
+ setMockClient(client);
+
+ // Act
+ client.request("POST", TEST_PATH, TEST_REQUEST_JSON, REQUEST_HTTP_OPTIONS);
+
+ // Assert
+ ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(HttpRequestBase.class);
+ verify(mockHttpClient).execute(requestCaptor.capture());
+ HttpRequestBase capturedRequest = requestCaptor.getValue();
+
+ assertTrue(capturedRequest instanceof HttpPost);
+ assertEquals("POST", capturedRequest.getMethod());
+ // The request URL is set by the request-level http options.
+ assertEquals("test-url/test-api-version/" + TEST_PATH, capturedRequest.getURI().toString());
+ Header authHeader = capturedRequest.getFirstHeader("Authorization");
+ // Request should have the header set by the request-level http options.
+ assertEquals("header", capturedRequest.getFirstHeader("test").getValue());
+ }
+
@Test
public void testInitHttpClientMldev() throws Exception {
HttpOptions httpOptions =