diff --git a/dotnet/src/Connectors/Connectors.AzureOpenAI.UnitTests/Connectors.AzureOpenAI.UnitTests.csproj b/dotnet/src/Connectors/Connectors.AzureOpenAI.UnitTests/Connectors.AzureOpenAI.UnitTests.csproj
index a0a695a6719c..056ac691dfa4 100644
--- a/dotnet/src/Connectors/Connectors.AzureOpenAI.UnitTests/Connectors.AzureOpenAI.UnitTests.csproj
+++ b/dotnet/src/Connectors/Connectors.AzureOpenAI.UnitTests/Connectors.AzureOpenAI.UnitTests.csproj
@@ -30,6 +30,14 @@
+
+
+
+
+
+
+
+
diff --git a/dotnet/src/Connectors/Connectors.AzureOpenAI.UnitTests/Services/AzureOpenAITextToImageServiceTests.cs b/dotnet/src/Connectors/Connectors.AzureOpenAI.UnitTests/Services/AzureOpenAITextToImageServiceTests.cs
new file mode 100644
index 000000000000..b0d44113febb
--- /dev/null
+++ b/dotnet/src/Connectors/Connectors.AzureOpenAI.UnitTests/Services/AzureOpenAITextToImageServiceTests.cs
@@ -0,0 +1,107 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System;
+using System.IO;
+using System.Net.Http;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
+using Microsoft.SemanticKernel.Connectors.AzureOpenAI;
+using Microsoft.SemanticKernel.Services;
+using Moq;
+using OpenAI;
+
+namespace SemanticKernel.Connectors.AzureOpenAI.UnitTests.Services;
+
+///
+/// Unit tests for class.
+///
+public sealed class AzureOpenAITextToImageServiceTests : IDisposable
+{
+ private readonly HttpMessageHandlerStub _messageHandlerStub;
+ private readonly HttpClient _httpClient;
+ private readonly Mock _mockLoggerFactory;
+
+ public AzureOpenAITextToImageServiceTests()
+ {
+ this._messageHandlerStub = new()
+ {
+ ResponseToReturn = new HttpResponseMessage(System.Net.HttpStatusCode.OK)
+ {
+ Content = new StringContent(File.ReadAllText("./TestData/text-to-image-response.txt"))
+ }
+ };
+ this._httpClient = new HttpClient(this._messageHandlerStub, false);
+ this._mockLoggerFactory = new Mock();
+ }
+
+ [Fact]
+ public void ConstructorWorksCorrectly()
+ {
+ // Arrange & Act
+ var sut = new AzureOpenAITextToImageServiceTests("model", "api-key", "organization");
+
+ // Assert
+ Assert.NotNull(sut);
+ Assert.Equal("organization", sut.Attributes[ClientCore.OrganizationKey]);
+ Assert.Equal("model", sut.Attributes[AIServiceExtensions.ModelIdKey]);
+ }
+
+ [Fact]
+ public void OpenAIClientConstructorWorksCorrectly()
+ {
+ // Arrange
+ var sut = new AzureOpenAITextToImageServiceTests("model", new OpenAIClient("apikey"));
+
+ // Assert
+ Assert.NotNull(sut);
+ Assert.Equal("model", sut.Attributes[AIServiceExtensions.ModelIdKey]);
+ }
+
+ [Theory]
+ [InlineData(256, 256, "dall-e-2")]
+ [InlineData(512, 512, "dall-e-2")]
+ [InlineData(1024, 1024, "dall-e-2")]
+ [InlineData(1024, 1024, "dall-e-3")]
+ [InlineData(1024, 1792, "dall-e-3")]
+ [InlineData(1792, 1024, "dall-e-3")]
+ [InlineData(123, 321, "custom-model-1")]
+ [InlineData(179, 124, "custom-model-2")]
+ public async Task GenerateImageWorksCorrectlyAsync(int width, int height, string modelId)
+ {
+ // Arrange
+ var sut = new AzureOpenAITextToImageServiceTests(modelId, "api-key", httpClient: this._httpClient);
+ Assert.Equal(modelId, sut.Attributes["ModelId"]);
+
+ // Act
+ var result = await sut.GenerateImageAsync("description", width, height);
+
+ // Assert
+ Assert.Equal("https://image-url/", result);
+ }
+
+ [Fact]
+ public async Task GenerateImageDoesLogActionAsync()
+ {
+ // Assert
+ var modelId = "dall-e-2";
+ var logger = new Mock>();
+ logger.Setup(l => l.IsEnabled(It.IsAny())).Returns(true);
+
+ this._mockLoggerFactory.Setup(x => x.CreateLogger(It.IsAny())).Returns(logger.Object);
+
+ // Arrange
+ var sut = new AzureOpenAITextToImageServiceTests(modelId, "apiKey", httpClient: this._httpClient, loggerFactory: this._mockLoggerFactory.Object);
+
+ // Act
+ await sut.GenerateImageAsync("description", 256, 256);
+
+ // Assert
+ logger.VerifyLog(LogLevel.Information, $"Action: {nameof(AzureOpenAITextToImageServiceTests.GenerateImageAsync)}. OpenAI Model ID: {modelId}.", Times.Once());
+ }
+
+ public void Dispose()
+ {
+ this._httpClient.Dispose();
+ this._messageHandlerStub.Dispose();
+ }
+}
diff --git a/dotnet/src/Connectors/Connectors.AzureOpenAI/Connectors.AzureOpenAI.csproj b/dotnet/src/Connectors/Connectors.AzureOpenAI/Connectors.AzureOpenAI.csproj
index 35c31788610d..720cd1cf71f5 100644
--- a/dotnet/src/Connectors/Connectors.AzureOpenAI/Connectors.AzureOpenAI.csproj
+++ b/dotnet/src/Connectors/Connectors.AzureOpenAI/Connectors.AzureOpenAI.csproj
@@ -21,10 +21,20 @@
Semantic Kernel connectors for Azure OpenAI. Contains clients for text generation, chat completion, embedding and DALL-E text to image.
+
+
+
+
+
+
+
+
+
+
diff --git a/dotnet/src/Connectors/Connectors.AzureOpenAI/Core/ClientCore.TextToImage.cs b/dotnet/src/Connectors/Connectors.AzureOpenAI/Core/ClientCore.TextToImage.cs
new file mode 100644
index 000000000000..b6490a058fb9
--- /dev/null
+++ b/dotnet/src/Connectors/Connectors.AzureOpenAI/Core/ClientCore.TextToImage.cs
@@ -0,0 +1,44 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System.ClientModel;
+using System.Threading;
+using System.Threading.Tasks;
+using OpenAI.Images;
+
+namespace Microsoft.SemanticKernel.Connectors.AzureOpenAI;
+
+///
+/// Base class for AI clients that provides common functionality for interacting with OpenAI services.
+///
+internal partial class ClientCore
+{
+ ///
+ /// Generates an image with the provided configuration.
+ ///
+ /// Prompt to generate the image
+ /// Width of the image
+ /// Height of the image
+ /// The to monitor for cancellation requests. The default is .
+ /// Url of the generated image
+ internal async Task GenerateImageAsync(
+ string prompt,
+ int width,
+ int height,
+ CancellationToken cancellationToken)
+ {
+ Verify.NotNullOrWhiteSpace(prompt);
+
+ var size = new GeneratedImageSize(width, height);
+
+ var imageOptions = new ImageGenerationOptions()
+ {
+ Size = size,
+ ResponseFormat = GeneratedImageFormat.Uri
+ };
+
+ ClientResult response = await RunRequestAsync(() => this.Client.GetImageClient(this.ModelId).GenerateImageAsync(prompt, imageOptions, cancellationToken)).ConfigureAwait(false);
+ var generatedImage = response.Value;
+
+ return generatedImage.ImageUri?.ToString() ?? throw new KernelException("The generated image is not in url format");
+ }
+}
diff --git a/dotnet/src/Connectors/Connectors.AzureOpenAI/Services/AzureOpenAITextToImageService.cs b/dotnet/src/Connectors/Connectors.AzureOpenAI/Services/AzureOpenAITextToImageService.cs
new file mode 100644
index 000000000000..a48b3177ebee
--- /dev/null
+++ b/dotnet/src/Connectors/Connectors.AzureOpenAI/Services/AzureOpenAITextToImageService.cs
@@ -0,0 +1,66 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Net.Http;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
+using Microsoft.SemanticKernel.TextToImage;
+using OpenAI;
+
+namespace Microsoft.SemanticKernel.Connectors.AzureOpenAI;
+
+///
+/// OpenAI text to image service.
+///
+[Experimental("SKEXP0010")]
+public class AzureOpenAITextToImageService : ITextToImageService
+{
+ private readonly ClientCore _client;
+
+ ///
+ public IReadOnlyDictionary Attributes => this._client.Attributes;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The model to use for image generation.
+ /// OpenAI API key, see https://platform.openai.com/account/api-keys
+ /// OpenAI organization id. This is usually optional unless your account belongs to multiple organizations.
+ /// Non-default endpoint for the OpenAI API.
+ /// Custom for HTTP requests.
+ /// The to use for logging. If null, no logging will be performed.
+ public AzureOpenAITextToImageService(
+ string modelId,
+ string? apiKey = null,
+ string? organizationId = null,
+ Uri? endpoint = null,
+ HttpClient? httpClient = null,
+ ILoggerFactory? loggerFactory = null)
+ {
+ this._client = new(modelId, apiKey, organizationId, endpoint, httpClient, loggerFactory?.CreateLogger(this.GetType()));
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Model name
+ /// Custom for HTTP requests.
+ /// The to use for logging. If null, no logging will be performed.
+ public AzureOpenAITextToImageService(
+ string modelId,
+ OpenAIClient openAIClient,
+ ILoggerFactory? loggerFactory = null)
+ {
+ this._client = new(modelId, openAIClient, loggerFactory?.CreateLogger(typeof(OpenAITextEmbeddingGenerationService)));
+ }
+
+ ///
+ public Task GenerateImageAsync(string description, int width, int height, Kernel? kernel = null, CancellationToken cancellationToken = default)
+ {
+ this._client.LogActionDetails();
+ return this._client.GenerateImageAsync(description, width, height, cancellationToken);
+ }
+}
diff --git a/dotnet/src/Connectors/Connectors.OpenAIV2/Services/OpenAIAudioToTextService.cs b/dotnet/src/Connectors/Connectors.OpenAIV2/Services/OpenAIAudioToTextService.cs
index a226d6c59040..cb37384845df 100644
--- a/dotnet/src/Connectors/Connectors.OpenAIV2/Services/OpenAIAudioToTextService.cs
+++ b/dotnet/src/Connectors/Connectors.OpenAIV2/Services/OpenAIAudioToTextService.cs
@@ -33,7 +33,7 @@ public sealed class OpenAIAudioToTextService : IAudioToTextService
public IReadOnlyDictionary Attributes => this._client.Attributes;
///
- /// Creates an instance of the with API key auth.
+ /// Creates an instance of the with API key auth.
///
/// Model name
/// OpenAI API Key
@@ -49,11 +49,11 @@ public OpenAIAudioToTextService(
HttpClient? httpClient = null,
ILoggerFactory? loggerFactory = null)
{
- this._client = new(modelId, apiKey, organization, endpoint, httpClient, loggerFactory?.CreateLogger(typeof(OpenAITextToAudioService)));
+ this._client = new(modelId, apiKey, organization, endpoint, httpClient, loggerFactory?.CreateLogger(typeof(OpenAIAudioToTextService)));
}
///
- /// Creates an instance of the with API key auth.
+ /// Creates an instance of the with API key auth.
///
/// Model name
/// Custom for HTTP requests.
diff --git a/dotnet/src/Connectors/Connectors.OpenAIV2/Services/OpenAITextToImageService.cs b/dotnet/src/Connectors/Connectors.OpenAIV2/Services/OpenAITextToImageService.cs
index 1a6038aa3f43..15ebcf049a93 100644
--- a/dotnet/src/Connectors/Connectors.OpenAIV2/Services/OpenAITextToImageService.cs
+++ b/dotnet/src/Connectors/Connectors.OpenAIV2/Services/OpenAITextToImageService.cs
@@ -64,7 +64,7 @@ public OpenAITextToImageService(
OpenAIClient openAIClient,
ILoggerFactory? loggerFactory = null)
{
- this._client = new(modelId, openAIClient, loggerFactory?.CreateLogger(typeof(OpenAITextEmbeddingGenerationService)));
+ this._client = new(modelId, openAIClient, loggerFactory?.CreateLogger(typeof(OpenAITextToImageService)));
}
///