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
11 changes: 11 additions & 0 deletions Google.GenAI.Tests/ClientTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/

using System;
using System.Net.Http;

using Google.Apis.Auth.OAuth2;
using Google.GenAI;
Expand Down Expand Up @@ -666,6 +667,16 @@ public void Constructor_HttpOptionsProvided_Timeout() {
Assert.AreEqual(1000, client._apiClient.HttpOptions.Timeout);
}

[TestMethod]
public void Constructor_HttpOptionsProvided_HttpClientFactory() {
Func<HttpClient> factory = () => new HttpClient();
var options = new HttpOptions { HttpClientFactory = factory };

var client = new Client(vertexAI: false, apiKey: "key", httpOptions: options);

Assert.AreSame(factory, client._apiClient.HttpOptions.HttpClientFactory);
}

#endregion

#region Successful Instantiation(all modules)
Expand Down
14 changes: 12 additions & 2 deletions Google.GenAI.Tests/HttpApiClientTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ public void GeminiConstructor_WithApiKey_SetsPropertiesCorrectly() {
Assert.AreEqual("https://generativelanguage.googleapis.com", client.HttpOptions.BaseUrl);
Assert.AreEqual("v1beta", client.HttpOptions.ApiVersion);
Assert.IsNull(client.HttpOptions.Timeout);
Assert.IsNull(client.HttpOptions.HttpClientFactory);
}

[TestMethod]
Expand All @@ -70,13 +71,16 @@ public void GeminiConstructor_WithApiKeyFromEnvironment_SetsPropertiesCorrectly(
Assert.AreEqual("https://generativelanguage.googleapis.com", client.HttpOptions.BaseUrl);
Assert.AreEqual("v1beta", client.HttpOptions.ApiVersion);
Assert.IsNull(client.HttpOptions.Timeout);
Assert.IsNull(client.HttpOptions.HttpClientFactory);
}

[TestMethod]
public void GeminiConstructor_WithHttpOptionsProvided_SetsPropertiesCorrectly() {
System.Environment.SetEnvironmentVariable(EnvApiKeyName, TestApiKey);
var httpClientFactory = () => new HttpClient();
var customHttpOptions = new Types.HttpOptions { BaseUrl = "https://custom-url.com",
ApiVersion = "v2", Timeout = 6000 };
ApiVersion = "v2", Timeout = 6000,
HttpClientFactory = httpClientFactory };

var client = new HttpApiClient(vertexAI: false, httpOptions: customHttpOptions);

Expand All @@ -88,6 +92,7 @@ public void GeminiConstructor_WithHttpOptionsProvided_SetsPropertiesCorrectly()
Assert.AreEqual("https://custom-url.com", client.HttpOptions.BaseUrl);
Assert.AreEqual("v2", client.HttpOptions.ApiVersion);
Assert.AreEqual(6000, client.HttpOptions.Timeout);
Assert.AreSame(httpClientFactory, client.HttpOptions.HttpClientFactory);
}

[TestMethod]
Expand Down Expand Up @@ -127,6 +132,7 @@ public void VertexConstructor_WithProjectLocationCredentials_SetsPropertiesCorre
client.HttpOptions.BaseUrl);
Assert.AreEqual("v1beta1", client.HttpOptions.ApiVersion);
Assert.IsNull(client.HttpOptions.Timeout);
Assert.IsNull(client.HttpOptions.HttpClientFactory);
}

[TestMethod]
Expand All @@ -150,6 +156,7 @@ public void VertexConstructor_WithProjectLocationFromEnvironment_SetsPropertiesC
client.HttpOptions.BaseUrl);
Assert.AreEqual("v1beta1", client.HttpOptions.ApiVersion);
Assert.IsNull(client.HttpOptions.Timeout);
Assert.IsNull(client.HttpOptions.HttpClientFactory);
}

[TestMethod]
Expand Down Expand Up @@ -213,8 +220,10 @@ public void VertexConstructor_WithCustomHttpOptions_OverridesDefaults() {
.Setup(c => c.GetAccessTokenForRequestAsync(
It.IsAny<string?>(), It.IsAny<System.Threading.CancellationToken>()))
.ReturnsAsync("mock-access-token");
var httpClientFactory = () => new HttpClient();
var customOptions = new Types.HttpOptions { BaseUrl = "https://custom.vertex.ai",
ApiVersion = "v2alpha", Timeout = 8000 };
ApiVersion = "v2alpha", Timeout = 8000,
HttpClientFactory = httpClientFactory };

var client =
new HttpApiClient(vertexAI: true, project: TestProject, location: TestLocation, credentials: mockCredential.Object, httpOptions: customOptions);
Expand All @@ -227,6 +236,7 @@ public void VertexConstructor_WithCustomHttpOptions_OverridesDefaults() {
Assert.AreEqual("https://custom.vertex.ai", client.HttpOptions.BaseUrl);
Assert.AreEqual("v2alpha", client.HttpOptions.ApiVersion);
Assert.AreEqual(8000, client.HttpOptions.Timeout);
Assert.AreSame(httpClientFactory, client.HttpOptions.HttpClientFactory);
}

[TestMethod]
Expand Down
6 changes: 5 additions & 1 deletion Google.GenAI/ApiClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ protected ApiClient(

private static HttpClient CreateHttpClient(HttpOptions httpOptions)
{
var client = new HttpClient();
var client = httpOptions.HttpClientFactory?.Invoke() ?? new HttpClient();
if (httpOptions.Timeout != null)
{
client.Timeout = System.TimeSpan.FromMilliseconds(httpOptions.Timeout.Value);
Expand Down Expand Up @@ -263,6 +263,10 @@ protected HttpOptions MergeHttpOptions(HttpOptions? optionsToApply)
{
mergedOptions.BaseUrlResourceScope = optionsToApply?.BaseUrlResourceScope;
}
if (optionsToApply?.HttpClientFactory != null)
{
mergedOptions.HttpClientFactory = optionsToApply?.HttpClientFactory;
}

var currentHeaders = this.HttpOptions.Headers ?? new Dictionary<string, string>();
var newHeaders = optionsToApply?.Headers ?? new Dictionary<string, string>();
Expand Down
3 changes: 2 additions & 1 deletion Google.GenAI/UploadClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ public static HttpOptions BuildResumableUploadHttpOptions(
ApiVersion = "",
Headers = mergedHeaders,
BaseUrl = userOptions?.BaseUrl,
Timeout = userOptions?.Timeout
Timeout = userOptions?.Timeout,
HttpClientFactory = userOptions?.HttpClientFactory
};
}

Expand Down
10 changes: 10 additions & 0 deletions Google.GenAI/types/HttpOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,16 @@ public int
get; set;
}

/// <summary>
/// A factory function to create HttpClient instances.
/// This allows for custom configuration of HttpClient, such as setting default headers, timeouts, or using a custom message handler.
/// </summary>
[JsonIgnore]
public Func<HttpClient>
? HttpClientFactory {
get; set;
}

/// <summary>
/// Deserializes a JSON string to a HttpOptions object.
/// </summary>
Expand Down