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
Expand Up @@ -37,17 +37,26 @@ internal async Task<string> GenerateImageAsync(
var imageOptions = new ImageGenerationOptions()
{
Size = size,
ResponseFormat = GeneratedImageFormat.Uri
};

// The model is not required by the OpenAI API and defaults to the DALL-E 2 server-side - https://platform.openai.com/docs/api-reference/images/create#images-create-model.
// However, considering that the model is required by the OpenAI SDK and the ModelId property is optional, it defaults to DALL-E 2 in the line below.
targetModel = string.IsNullOrEmpty(targetModel) ? "dall-e-2" : targetModel!;
// However, considering that the model is required by the OpenAI SDK and the ModelId property is optional, it defaults to gpt-image-1 in the line below.
targetModel = string.IsNullOrEmpty(targetModel) ? "gpt-image-1" : targetModel!;

ClientResult<GeneratedImage> response = await RunRequestAsync(() => this.Client!.GetImageClient(targetModel).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");
if (generatedImage.ImageUri is not null)
{
return generatedImage.ImageUri.ToString();
}

if (generatedImage.ImageBytes is not null)
{
return $"data:image/png;base64,{Convert.ToBase64String(generatedImage.ImageBytes.ToArray())}";
}

throw new KernelException("The generated image has no valid content.");
}

/// <summary>
Expand Down Expand Up @@ -113,6 +122,9 @@ internal async Task<IReadOnlyList<ImageContent>> GetImageContentsAsync(
{
"STANDARD" => GeneratedImageQuality.Standard,
"HIGH" or "HD" => GeneratedImageQuality.High,
"MEDIUM" => GeneratedImageQuality.Medium,
"LOW" => GeneratedImageQuality.Low,
"AUTO" => GeneratedImageQuality.Auto,
_ => throw new NotSupportedException($"The provided quality '{quality}' is not supported.")
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public OpenAITextToImageService(
HttpClient? httpClient = null,
ILoggerFactory? loggerFactory = null)
{
this._client = new(modelId ?? "dall-e-2", apiKey, organization, null, httpClient, loggerFactory?.CreateLogger(this.GetType()));
this._client = new(modelId ?? "gpt-image-1", apiKey, organization, null, httpClient, loggerFactory?.CreateLogger(this.GetType()));
}

/// <inheritdoc/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ public sealed class OpenAITextToImageExecutionSettings : PromptExecutionSettings
/// </summary>
/// <remarks>
/// <list type="bullet">
/// <item>Must be one of <c>256x256, 512x512, or 1024x1024</c> for <c>dall-e-2</c> model.</item>
/// <item>Must be one of <c>1024x1024, 1792x1024, 1024x1792</c> for <c>dall-e-3</c> model.</item>
/// <item>Must be one of <c>1024x1024, 1536x1024, 1024x1536, auto</c> for <c>gpt-image-1</c> model.</item>
/// </list>
/// </remarks>
public (int Width, int Height)? Size
Expand All @@ -38,12 +37,12 @@ public sealed class OpenAITextToImageExecutionSettings : PromptExecutionSettings
/// The quality of the image that will be generated.
/// </summary>
/// <remarks>
/// Must be one of <c>standard</c> or <c>hd</c> or <c>high</c>.
/// <list type="bullet">
/// <item><c>standard</c>: creates images with standard quality. This is the default.</item>
/// <item><c>hd</c> OR <c>high</c>: creates images with finer details and greater consistency.</item>
/// <item><c>medium</c>: creates images with medium quality (supported by <c>gpt-image-1</c>).</item>
/// <item><c>low</c>: creates images with lower quality for faster generation (supported by <c>gpt-image-1</c>).</item>
/// </list>
/// This param is only supported for <c>dall-e-3</c> model.
/// </remarks>
[JsonPropertyName("quality")]
public string? Quality
Expand All @@ -66,7 +65,7 @@ public string? Quality
/// <item><c>vivid</c>: causes the model to lean towards generating hyper-real and dramatic images.</item>
/// <item><c>natural</c>: causes the model to produce more natural, less hyper-real looking images.</item>
/// </list>
/// This param is only supported for <c>dall-e-3</c> model.
/// This param is not supported for <c>gpt-image-1</c> model.
/// </remarks>
[JsonPropertyName("style")]
public string? Style
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public sealed class AzureOpenAITextToImageTests
.Build();

[Fact(Skip = "This test is for manual verification.")]
public async Task ItCanReturnImageUrlAsync()
public async Task ItCanReturnImageContentAsync()
{
// Arrange
AzureOpenAIConfiguration? configuration = this._configuration.GetSection("AzureOpenAITextToImage").Get<AzureOpenAIConfiguration>();
Expand All @@ -35,7 +35,8 @@ public async Task ItCanReturnImageUrlAsync()
.AddAzureOpenAITextToImage(
deploymentName: configuration.DeploymentName,
endpoint: configuration.Endpoint,
credentials: new AzureCliCredential())
credentials: new AzureCliCredential(),
apiVersion: "2025-04-01-preview")
.Build();

var service = kernel.GetRequiredService<ITextToImageService>();
Expand All @@ -45,11 +46,11 @@ public async Task ItCanReturnImageUrlAsync()

// Assert
Assert.NotNull(result);
Assert.StartsWith("https://", result);
Assert.NotEmpty(result);
}

[Fact]
public async Task GetImageContentsCanReturnImageUrlAsync()
public async Task GetImageContentsCanReturnImageAsync()
{
// Arrange
AzureOpenAIConfiguration? configuration = this._configuration.GetSection("AzureOpenAITextToImage").Get<AzureOpenAIConfiguration>();
Expand All @@ -59,7 +60,8 @@ public async Task GetImageContentsCanReturnImageUrlAsync()
.AddAzureOpenAITextToImage(
deploymentName: configuration.DeploymentName,
endpoint: configuration.Endpoint,
credentials: new AzureCliCredential())
credentials: new AzureCliCredential(),
apiVersion: "2025-04-01-preview")
.Build();

var service = kernel.GetRequiredService<ITextToImageService>();
Expand All @@ -70,8 +72,8 @@ public async Task GetImageContentsCanReturnImageUrlAsync()
// Assert
Assert.NotNull(result);
Assert.NotEmpty(result);
Assert.NotEmpty(result[0].Uri!.ToString());
Assert.StartsWith("https://", result[0].Uri!.ToString());
var imageContent = result[0];
Assert.True(imageContent.Uri is not null || imageContent.Data is not null, "Image content should have either a URI or binary data.");
}

[Fact]
Expand All @@ -89,6 +91,7 @@ public async Task SemanticKernelVersionHeaderIsSentAsync()
deploymentName: configuration.DeploymentName,
endpoint: configuration.Endpoint,
credentials: new AzureCliCredential(),
apiVersion: "2025-04-01-preview",
httpClient: httpClient)
.Build();

Expand All @@ -100,8 +103,8 @@ public async Task SemanticKernelVersionHeaderIsSentAsync()
// Assert
Assert.NotNull(result);
Assert.NotEmpty(result);
Assert.NotEmpty(result[0].Uri!.ToString());
Assert.StartsWith("https://", result[0].Uri!.ToString());
var imageContent = result[0];
Assert.True(imageContent.Uri is not null || imageContent.Data is not null, "Image content should have either a URI or binary data.");
Assert.NotNull(httpHeaderHandler.RequestHeaders);
Assert.True(httpHeaderHandler.RequestHeaders.TryGetValues("Semantic-Kernel-Version", out var values));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ public sealed class OpenAITextToImageTests
.Build();

[Theory(Skip = "This test is for manual verification.")]
[InlineData("dall-e-2", 512, 512)]
[InlineData("dall-e-3", 1024, 1024)]
[InlineData("gpt-image-1", 1024, 1024)]
public async Task OpenAITextToImageByModelTestAsync(string modelId, int width, int height)
{
// Arrange
Expand All @@ -45,7 +44,7 @@ public async Task OpenAITextToImageByModelTestAsync(string modelId, int width, i
}

[Fact(Skip = "Failing in integration tests pipeline with - HTTP 400 (invalid_request_error: billing_hard_limit_reached) error.")]
public async Task OpenAITextToImageUseDallE2ByDefaultAsync()
public async Task OpenAITextToImageUseDefaultModelAsync()
{
// Arrange
OpenAIConfiguration? openAIConfiguration = this._configuration.GetSection("OpenAITextToImage").Get<OpenAIConfiguration>();
Expand All @@ -58,22 +57,22 @@ public async Task OpenAITextToImageUseDallE2ByDefaultAsync()
var service = kernel.GetRequiredService<ITextToImageService>();

// Act
var result = await service.GenerateImageAsync("The sun rises in the east and sets in the west.", 256, 256);
var result = await service.GenerateImageAsync("The sun rises in the east and sets in the west.", 1024, 1024);

// Assert
Assert.NotNull(result);
Assert.NotEmpty(result);
}

[Fact(Skip = "Failing in integration tests pipeline with - HTTP 400 (invalid_request_error: billing_hard_limit_reached) error.")]
public async Task OpenAITextToImageDalle3GetImagesTestAsync()
public async Task OpenAITextToImageGetImagesTestAsync()
{
// Arrange
OpenAIConfiguration? openAIConfiguration = this._configuration.GetSection("OpenAITextToImage").Get<OpenAIConfiguration>();
Assert.NotNull(openAIConfiguration);

var kernel = Kernel.CreateBuilder()
.AddOpenAITextToImage(apiKey: openAIConfiguration.ApiKey, modelId: "dall-e-3")
.AddOpenAITextToImage(apiKey: openAIConfiguration.ApiKey, modelId: "gpt-image-1")
.Build();

var service = kernel.GetRequiredService<ITextToImageService>();
Expand All @@ -84,6 +83,7 @@ public async Task OpenAITextToImageDalle3GetImagesTestAsync()
// Assert
Assert.NotNull(result);
Assert.NotEmpty(result);
Assert.NotEmpty(result[0].Uri!.ToString());
var imageContent = result[0];
Assert.True(imageContent.Uri is not null || imageContent.Data is not null, "Image content should have either a URI or binary data.");
}
}
8 changes: 4 additions & 4 deletions dotnet/src/IntegrationTests/testsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,13 @@
"Endpoint": ""
},
"OpenAITextToImage": {
"ServiceId": "dall-e-2",
"ModelId": "dall-e-2",
"ServiceId": "gpt-image-1",
"ModelId": "gpt-image-1",
"ApiKey": ""
},
"AzureOpenAITextToImage": {
"ServiceId": "azure-dalle3",
"DeploymentName": "Dalle3",
"ServiceId": "azure-gpt-image-1",
"DeploymentName": "gpt-image-1",
"Endpoint": ""
},
"HuggingFace": {
Expand Down
Loading