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
6 changes: 6 additions & 0 deletions src/main/java/com/google/genai/ApiClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ private OkHttpClient createHttpClient(
builder.connectTimeout(Duration.ofMillis(0));
builder.readTimeout(Duration.ofMillis(0));
builder.writeTimeout(Duration.ofMillis(0));
builder.callTimeout(Duration.ofMillis(0));

timeout.ifPresent(connectTimeout -> builder.connectTimeout(Duration.ofMillis(connectTimeout)));

Expand All @@ -232,6 +233,11 @@ private OkHttpClient createHttpClient(
options.maxConnections().ifPresent(dispatcher::setMaxRequests);
options.maxConnectionsPerHost().ifPresent(dispatcher::setMaxRequestsPerHost);
builder.dispatcher(dispatcher);

// Apply timeout configurations from ClientOptions
options.readTimeout().ifPresent(rt -> builder.readTimeout(Duration.ofMillis(rt)));
options.writeTimeout().ifPresent(wt -> builder.writeTimeout(Duration.ofMillis(wt)));
options.callTimeout().ifPresent(ct -> builder.callTimeout(Duration.ofMillis(ct)));
});

return builder.build();
Expand Down
36 changes: 36 additions & 0 deletions src/main/java/com/google/genai/types/ClientOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,18 @@ public abstract class ClientOptions extends JsonSerializable {
@JsonProperty("maxConnectionsPerHost")
public abstract Optional<Integer> maxConnectionsPerHost();

/** Read timeout in milliseconds. */
@JsonProperty("readTimeout")
public abstract Optional<Integer> readTimeout();

/** Write timeout in milliseconds. */
@JsonProperty("writeTimeout")
public abstract Optional<Integer> writeTimeout();

/** Call timeout in milliseconds. */
@JsonProperty("callTimeout")
public abstract Optional<Integer> callTimeout();

/** Instantiates a builder for ClientOptions. */
@ExcludeFromGeneratedCoverageReport
public static Builder builder() {
Expand Down Expand Up @@ -71,6 +83,30 @@ private static Builder create() {
@JsonProperty("maxConnectionsPerHost")
public abstract Builder maxConnectionsPerHost(Integer maxConnectionsPerHost);

/**
* Setter for readTimeout.
*
* <p>readTimeout: Read timeout in milliseconds.
*/
@JsonProperty("readTimeout")
public abstract Builder readTimeout(Integer readTimeout);

/**
* Setter for writeTimeout.
*
* <p>writeTimeout: Write timeout in milliseconds.
*/
@JsonProperty("writeTimeout")
public abstract Builder writeTimeout(Integer writeTimeout);

/**
* Setter for callTimeout.
*
* <p>callTimeout: Call timeout in milliseconds.
*/
@JsonProperty("callTimeout")
public abstract Builder callTimeout(Integer callTimeout);

public abstract ClientOptions build();
}

Expand Down
93 changes: 93 additions & 0 deletions src/test/java/com/google/genai/HttpApiClientTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -1013,6 +1013,99 @@ public void testHttpClientMldevDefaultClientOptions() throws Exception {
assertFalse(client.vertexAI());
}

@Test
public void testHttpClientMldevWithTimeoutSettings() throws Exception {
ClientOptions clientOptions =
ClientOptions.builder()
.readTimeout(5000)
.writeTimeout(10000)
.callTimeout(15000)
.build();
HttpApiClient client =
new HttpApiClient(Optional.of(API_KEY), Optional.empty(), Optional.of(clientOptions));

OkHttpClient httpClient = client.httpClient();

assertEquals(API_KEY, client.apiKey());
assertFalse(client.vertexAI());
assertEquals(5000, httpClient.readTimeoutMillis());
assertEquals(10000, httpClient.writeTimeoutMillis());
assertEquals(15000, httpClient.callTimeoutMillis());
}

@Test
public void testHttpClientVertexWithTimeoutSettings() throws Exception {
ClientOptions clientOptions =
ClientOptions.builder()
.readTimeout(3000)
.writeTimeout(6000)
.callTimeout(9000)
.build();
HttpApiClient client =
new HttpApiClient(
Optional.empty(),
Optional.of(PROJECT),
Optional.of(LOCATION),
Optional.of(CREDENTIALS),
Optional.empty(),
Optional.of(clientOptions));

OkHttpClient httpClient = client.httpClient();

assertEquals(PROJECT, client.project());
assertEquals(LOCATION, client.location());
assertTrue(client.vertexAI());
assertEquals(3000, httpClient.readTimeoutMillis());
assertEquals(6000, httpClient.writeTimeoutMillis());
assertEquals(9000, httpClient.callTimeoutMillis());
}

@Test
public void testHttpClientWithPartialTimeoutSettings() throws Exception {
ClientOptions clientOptions =
ClientOptions.builder()
.readTimeout(5000)
.build();
HttpApiClient client =
new HttpApiClient(Optional.of(API_KEY), Optional.empty(), Optional.of(clientOptions));

OkHttpClient httpClient = client.httpClient();

assertEquals(API_KEY, client.apiKey());
assertFalse(client.vertexAI());
assertEquals(5000, httpClient.readTimeoutMillis());
// When not set, timeouts should remain at default 0 (no timeout)
assertEquals(0, httpClient.writeTimeoutMillis());
assertEquals(0, httpClient.callTimeoutMillis());
}

@Test
public void testHttpClientWithAllClientOptionsIncludingTimeouts() throws Exception {
ClientOptions clientOptions =
ClientOptions.builder()
.maxConnections(32)
.maxConnectionsPerHost(8)
.readTimeout(2000)
.writeTimeout(4000)
.callTimeout(8000)
.build();
HttpApiClient client =
new HttpApiClient(Optional.of(API_KEY), Optional.empty(), Optional.of(clientOptions));

OkHttpClient httpClient = client.httpClient();
Dispatcher dispatcher = httpClient.dispatcher();

assertEquals(API_KEY, client.apiKey());
assertFalse(client.vertexAI());
// Test dispatcher settings
assertEquals(32, dispatcher.getMaxRequests());
assertEquals(8, dispatcher.getMaxRequestsPerHost());
// Test timeout settings
assertEquals(2000, httpClient.readTimeoutMillis());
assertEquals(4000, httpClient.writeTimeoutMillis());
assertEquals(8000, httpClient.callTimeoutMillis());
}

@Test
public void testHttpClientWithCustomCredentials() throws Exception {
HttpApiClient client =
Expand Down