Skip to content

Commit 99f09b4

Browse files
.Net: Add HttpClient parameter to AddAzureOpenAITextToImage method (#12925)
### Motivation and Context <!-- Thank you for your contribution to the semantic-kernel repo! Please help reviewers and future users, providing the following information: 1. Why is this change required? 2. What problem does it solve? 3. What scenario does it contribute to? 4. If it fixes an open issue, please link to the issue here. --> There is an issue, if we want to use custom `httpClinet` without `ApiKey` in Azure Open AI Text2Image services, it's not possible. I added the new parameter in the same approach like other services, e. g. `AddAzureOpenAIChatCompletion`. [Referenced issue](#12908) ### Description <!-- Describe your changes, the overall approach, the underlying design. These notes will help understanding how your code works. Thanks! --> I added one new option parameter for Azure OpenAI Text2Image connector like other services: e. g. AddAzureOpenAIChatCompletion. The change is too small to keep review as easy as possible. ### Contribution Checklist <!-- Before submitting this PR, please make sure: --> - [x] The code builds clean without any errors or warnings - [x] The PR follows the [SK Contribution Guidelines](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md) and the [pre-submission formatting script](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md#development-scripts) raises no violations - [x] All unit tests pass, and I have added new tests where possible - [x] I didn't break anyone 😄 --------- Co-authored-by: Roger Barreto <[email protected]>
1 parent 049dbc1 commit 99f09b4

File tree

4 files changed

+122
-23
lines changed

4 files changed

+122
-23
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!-- https://learn.microsoft.com/dotnet/fundamentals/package-validation/diagnostic-ids -->
3+
<Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
4+
<Suppression>
5+
<DiagnosticId>CP0002</DiagnosticId>
6+
<Target>M:Microsoft.SemanticKernel.AzureOpenAIKernelBuilderExtensions.AddAzureOpenAITextToImage(Microsoft.SemanticKernel.IKernelBuilder,System.String,System.String,Azure.Core.TokenCredential,System.String,System.String,System.String)</Target>
7+
<Left>lib/net8.0/Microsoft.SemanticKernel.Connectors.AzureOpenAI.dll</Left>
8+
<Right>lib/net8.0/Microsoft.SemanticKernel.Connectors.AzureOpenAI.dll</Right>
9+
<IsBaselineSuppression>true</IsBaselineSuppression>
10+
</Suppression>
11+
<Suppression>
12+
<DiagnosticId>CP0002</DiagnosticId>
13+
<Target>M:Microsoft.SemanticKernel.AzureOpenAIServiceCollectionExtensions.AddAzureOpenAITextToImage(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.String,System.String,Azure.Core.TokenCredential,System.String,System.String,System.String)</Target>
14+
<Left>lib/net8.0/Microsoft.SemanticKernel.Connectors.AzureOpenAI.dll</Left>
15+
<Right>lib/net8.0/Microsoft.SemanticKernel.Connectors.AzureOpenAI.dll</Right>
16+
<IsBaselineSuppression>true</IsBaselineSuppression>
17+
</Suppression>
18+
<Suppression>
19+
<DiagnosticId>CP0002</DiagnosticId>
20+
<Target>M:Microsoft.SemanticKernel.AzureOpenAIServiceCollectionExtensions.AddAzureOpenAITextToImage(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.String,System.String,System.String,System.String,System.String,System.Int32,System.String)</Target>
21+
<Left>lib/net8.0/Microsoft.SemanticKernel.Connectors.AzureOpenAI.dll</Left>
22+
<Right>lib/net8.0/Microsoft.SemanticKernel.Connectors.AzureOpenAI.dll</Right>
23+
<IsBaselineSuppression>true</IsBaselineSuppression>
24+
</Suppression>
25+
<Suppression>
26+
<DiagnosticId>CP0002</DiagnosticId>
27+
<Target>M:Microsoft.SemanticKernel.AzureOpenAIKernelBuilderExtensions.AddAzureOpenAITextToImage(Microsoft.SemanticKernel.IKernelBuilder,System.String,System.String,Azure.Core.TokenCredential,System.String,System.String,System.String)</Target>
28+
<Left>lib/netstandard2.0/Microsoft.SemanticKernel.Connectors.AzureOpenAI.dll</Left>
29+
<Right>lib/netstandard2.0/Microsoft.SemanticKernel.Connectors.AzureOpenAI.dll</Right>
30+
<IsBaselineSuppression>true</IsBaselineSuppression>
31+
</Suppression>
32+
<Suppression>
33+
<DiagnosticId>CP0002</DiagnosticId>
34+
<Target>M:Microsoft.SemanticKernel.AzureOpenAIServiceCollectionExtensions.AddAzureOpenAITextToImage(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.String,System.String,Azure.Core.TokenCredential,System.String,System.String,System.String)</Target>
35+
<Left>lib/netstandard2.0/Microsoft.SemanticKernel.Connectors.AzureOpenAI.dll</Left>
36+
<Right>lib/netstandard2.0/Microsoft.SemanticKernel.Connectors.AzureOpenAI.dll</Right>
37+
<IsBaselineSuppression>true</IsBaselineSuppression>
38+
</Suppression>
39+
<Suppression>
40+
<DiagnosticId>CP0002</DiagnosticId>
41+
<Target>M:Microsoft.SemanticKernel.AzureOpenAIServiceCollectionExtensions.AddAzureOpenAITextToImage(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.String,System.String,System.String,System.String,System.String,System.Int32,System.String)</Target>
42+
<Left>lib/netstandard2.0/Microsoft.SemanticKernel.Connectors.AzureOpenAI.dll</Left>
43+
<Right>lib/netstandard2.0/Microsoft.SemanticKernel.Connectors.AzureOpenAI.dll</Right>
44+
<IsBaselineSuppression>true</IsBaselineSuppression>
45+
</Suppression>
46+
</Suppressions>

dotnet/src/Connectors/Connectors.AzureOpenAI/Extensions/AzureOpenAIKernelBuilderExtensions.cs

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -612,6 +612,7 @@ public static IKernelBuilder AddAzureOpenAITextToAudio(
612612
/// <param name="modelId">Model identifier, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart</param>
613613
/// <param name="serviceId">A local identifier for the given AI service</param>
614614
/// <param name="apiVersion">Azure OpenAI API version</param>
615+
/// <param name="httpClient">The HttpClient to use with this service.</param>
615616
/// <returns>The same instance as <paramref name="builder"/>.</returns>
616617
[Experimental("SKEXP0010")]
617618
public static IKernelBuilder AddAzureOpenAITextToImage(
@@ -621,21 +622,21 @@ public static IKernelBuilder AddAzureOpenAITextToImage(
621622
TokenCredential credentials,
622623
string? modelId = null,
623624
string? serviceId = null,
624-
string? apiVersion = null)
625+
string? apiVersion = null,
626+
HttpClient? httpClient = null)
625627
{
626628
Verify.NotNull(builder);
627629
Verify.NotNullOrWhiteSpace(endpoint);
628630
Verify.NotNull(credentials);
629631

630-
builder.Services.AddKeyedSingleton<ITextToImageService>(serviceId, (serviceProvider, _) =>
631-
new AzureOpenAITextToImageService(
632-
deploymentName,
633-
endpoint,
634-
credentials,
635-
modelId,
636-
HttpClientProvider.GetHttpClient(serviceProvider),
637-
serviceProvider.GetService<ILoggerFactory>(),
638-
apiVersion));
632+
builder.Services.AddAzureOpenAITextToImage(
633+
deploymentName,
634+
endpoint,
635+
credentials,
636+
modelId,
637+
serviceId,
638+
apiVersion,
639+
httpClient);
639640

640641
return builder;
641642
}
@@ -667,15 +668,14 @@ public static IKernelBuilder AddAzureOpenAITextToImage(
667668
Verify.NotNullOrWhiteSpace(endpoint);
668669
Verify.NotNullOrWhiteSpace(apiKey);
669670

670-
builder.Services.AddKeyedSingleton<ITextToImageService>(serviceId, (serviceProvider, _) =>
671-
new AzureOpenAITextToImageService(
672-
deploymentName,
673-
endpoint,
674-
apiKey,
675-
modelId,
676-
HttpClientProvider.GetHttpClient(httpClient, serviceProvider),
677-
serviceProvider.GetService<ILoggerFactory>(),
678-
apiVersion));
671+
builder.Services.AddAzureOpenAITextToImage(
672+
deploymentName: deploymentName,
673+
endpoint: endpoint,
674+
apiKey: apiKey,
675+
serviceId: serviceId,
676+
modelId: modelId,
677+
apiVersion: apiVersion,
678+
httpClient: httpClient);
679679

680680
return builder;
681681
}

dotnet/src/Connectors/Connectors.AzureOpenAI/Extensions/AzureOpenAIServiceCollectionExtensions.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,7 @@ public static IServiceCollection AddAzureOpenAITextToAudio(
315315
/// <param name="modelId">Model identifier, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart</param>
316316
/// <param name="serviceId">A local identifier for the given AI service</param>
317317
/// <param name="apiVersion">Optional Azure OpenAI API version, see available here <see cref="AzureOpenAIClientOptions.ServiceVersion"/></param>
318+
/// <param name="httpClient">The HttpClient to use with this service.</param>
318319
/// <returns>The same instance as <paramref name="services"/>.</returns>
319320
[Experimental("SKEXP0010")]
320321
public static IServiceCollection AddAzureOpenAITextToImage(
@@ -324,7 +325,8 @@ public static IServiceCollection AddAzureOpenAITextToImage(
324325
TokenCredential credentials,
325326
string? modelId = null,
326327
string? serviceId = null,
327-
string? apiVersion = null)
328+
string? apiVersion = null,
329+
HttpClient? httpClient = null)
328330
{
329331
Verify.NotNull(services);
330332
Verify.NotNullOrWhiteSpace(endpoint);
@@ -336,7 +338,7 @@ public static IServiceCollection AddAzureOpenAITextToImage(
336338
endpoint,
337339
credentials,
338340
modelId,
339-
HttpClientProvider.GetHttpClient(serviceProvider),
341+
HttpClientProvider.GetHttpClient(httpClient, serviceProvider),
340342
serviceProvider.GetService<ILoggerFactory>(),
341343
apiVersion));
342344
}
@@ -352,6 +354,7 @@ public static IServiceCollection AddAzureOpenAITextToImage(
352354
/// <param name="modelId">Model identifier, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart</param>
353355
/// <param name="maxRetryCount">Maximum number of attempts to retrieve the text to image operation result.</param>
354356
/// <param name="apiVersion">Optional Azure OpenAI API version, see available here <see cref="AzureOpenAIClientOptions.ServiceVersion"/></param>
357+
/// <param name="httpClient">The HttpClient to use with this service.</param>
355358
/// <returns>The same instance as <paramref name="services"/>.</returns>
356359
[Experimental("SKEXP0010")]
357360
public static IServiceCollection AddAzureOpenAITextToImage(
@@ -362,7 +365,8 @@ public static IServiceCollection AddAzureOpenAITextToImage(
362365
string? serviceId = null,
363366
string? modelId = null,
364367
int maxRetryCount = 5,
365-
string? apiVersion = null)
368+
string? apiVersion = null,
369+
HttpClient? httpClient = null)
366370
{
367371
Verify.NotNull(services);
368372
Verify.NotNullOrWhiteSpace(endpoint);
@@ -374,7 +378,7 @@ public static IServiceCollection AddAzureOpenAITextToImage(
374378
endpoint,
375379
apiKey,
376380
modelId,
377-
HttpClientProvider.GetHttpClient(serviceProvider),
381+
HttpClientProvider.GetHttpClient(httpClient, serviceProvider),
378382
serviceProvider.GetService<ILoggerFactory>(),
379383
apiVersion));
380384
}

dotnet/src/IntegrationTests/Connectors/AzureOpenAI/AzureOpenAITextToImageTests.cs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
// Copyright (c) Microsoft. All rights reserved.
22

3+
using System.Net.Http;
4+
using System.Threading;
35
using System.Threading.Tasks;
46
using Azure.Identity;
57
using Microsoft.Extensions.Configuration;
@@ -71,4 +73,51 @@ public async Task GetImageContentsCanReturnImageUrlAsync()
7173
Assert.NotEmpty(result[0].Uri!.ToString());
7274
Assert.StartsWith("https://", result[0].Uri!.ToString());
7375
}
76+
77+
[Fact]
78+
public async Task SemanticKernelVersionHeaderIsSentAsync()
79+
{
80+
// Arrange
81+
using var defaultHandler = new HttpClientHandler();
82+
using var httpHeaderHandler = new HttpHeaderHandler(defaultHandler);
83+
using var httpClient = new HttpClient(httpHeaderHandler);
84+
AzureOpenAIConfiguration? configuration = this._configuration.GetSection("AzureOpenAITextToImage").Get<AzureOpenAIConfiguration>();
85+
Assert.NotNull(configuration);
86+
87+
var kernel = Kernel.CreateBuilder()
88+
.AddAzureOpenAITextToImage(
89+
deploymentName: configuration.DeploymentName,
90+
endpoint: configuration.Endpoint,
91+
credentials: new AzureCliCredential(),
92+
httpClient: httpClient)
93+
.Build();
94+
95+
var service = kernel.GetRequiredService<ITextToImageService>();
96+
97+
// Act
98+
var result = await service.GetImageContentsAsync("The sun rises in the east and sets in the west.", new OpenAITextToImageExecutionSettings { Size = (1024, 1024) });
99+
100+
// Assert
101+
Assert.NotNull(result);
102+
Assert.NotEmpty(result);
103+
Assert.NotEmpty(result[0].Uri!.ToString());
104+
Assert.StartsWith("https://", result[0].Uri!.ToString());
105+
Assert.NotNull(httpHeaderHandler.RequestHeaders);
106+
Assert.True(httpHeaderHandler.RequestHeaders.TryGetValues("Semantic-Kernel-Version", out var values));
107+
}
108+
109+
#region internals
110+
111+
private sealed class HttpHeaderHandler(HttpMessageHandler innerHandler) : DelegatingHandler(innerHandler)
112+
{
113+
public System.Net.Http.Headers.HttpRequestHeaders? RequestHeaders { get; private set; }
114+
115+
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
116+
{
117+
this.RequestHeaders = request.Headers;
118+
return await base.SendAsync(request, cancellationToken);
119+
}
120+
}
121+
122+
#endregion
74123
}

0 commit comments

Comments
 (0)