Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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:
*
* <p>1a. If you are using Vertex AI, setup ADC to get credentials:
* https://cloud.google.com/docs/authentication/provide-credentials-adc#google-idp
*
* <p>Then set Project, Location, and USE_VERTEXAI flag as environment variables:
*
* <p>export GOOGLE_CLOUD_PROJECT=YOUR_PROJECT
*
* <p>export GOOGLE_CLOUD_LOCATION=YOUR_LOCATION
*
* <p>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
*
* <p>export GOOGLE_API_KEY=YOUR_API_KEY
*
* <p>2. Compile the java package and run the sample code.
*
* <p>mvn clean compile
*
* <p>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());
}
}
20 changes: 15 additions & 5 deletions src/main/java/com/google/genai/ApiClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down Expand Up @@ -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;
Expand All @@ -128,7 +128,8 @@ private CloseableHttpClient createHttpClient(Optional<Integer> 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() {
Expand Down Expand Up @@ -172,7 +173,16 @@ private Optional<Map<String, String>> 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());
Expand All @@ -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<String> location) {
Expand Down
19 changes: 11 additions & 8 deletions src/main/java/com/google/genai/HttpApiClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -61,30 +63,31 @@ 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);
}
}

/** 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<String, String> header :
httpOptions.headers().orElse(ImmutableMap.of()).entrySet()) {
requestHttpOptions.headers().orElse(ImmutableMap.of()).entrySet()) {
request.setHeader(header.getKey(), header.getValue());
}

Expand Down
64 changes: 55 additions & 9 deletions src/main/java/com/google/genai/Models.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -4167,8 +4173,13 @@ private ResponseStream<GenerateContentResponse> 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";
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down
15 changes: 13 additions & 2 deletions src/main/java/com/google/genai/Operations.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/com/google/genai/ReplayApiClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<Object> interactions = Arrays.asList(this.replaySession.get("interactions"));
Expand Down
Loading