Skip to content
Merged
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: 5 additions & 1 deletion src/xAI.Tests/ImageGeneratorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ public async Task GenerateImage_WithPrompt_ReturnsImageContent()
var options = new ImageGenerationOptions
{
ResponseFormat = ImageGenerationResponseFormat.Uri,
Count = 1
};

var response = await imageGenerator.GenerateAsync(request, options);
Expand All @@ -44,6 +43,7 @@ public async Task GenerateImage_WithEditsToPreviousImage()
var request = new ImageGenerationRequest("A cat sitting on a tree branch");
var options = new ImageGenerationOptions
{
MediaType = "image/png",
ResponseFormat = ImageGenerationResponseFormat.Uri,
Count = 1
};
Expand All @@ -54,6 +54,8 @@ public async Task GenerateImage_WithEditsToPreviousImage()
Assert.NotEmpty(response.Contents);
Assert.Single(response.Contents);
var image = Assert.IsType<UriContent>(response.Contents.First());
// media type in options is ignored and you always get the same jpg
Assert.Equal("image/jpg", image.MediaType);
output.WriteLine($"Generated image URL: {image.Uri}");

var edit = await imageGenerator.GenerateAsync(new ImageGenerationRequest("Edit provided image by adding a batman mask", [image]), options);
Expand All @@ -62,6 +64,8 @@ public async Task GenerateImage_WithEditsToPreviousImage()
Assert.NotEmpty(edit.Contents);
Assert.Single(edit.Contents);
image = Assert.IsType<UriContent>(edit.Contents.First());
// media type in options is ignored and you always get the same jpg
Assert.Equal("image/jpg", image.MediaType);

output.WriteLine($"Edited image URL: {image.Uri}");
}
Expand Down
19 changes: 6 additions & 13 deletions src/xAI/GrokImageGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ sealed class GrokImageGenerator : IImageGenerator
readonly ImageGeneratorMetadata metadata;
readonly ImageClient imageClient;
readonly string defaultModelId;
readonly GrokClientOptions clientOptions;

internal GrokImageGenerator(GrpcChannel channel, GrokClientOptions clientOptions, string defaultModelId)
: this(new ImageClient(channel), clientOptions, defaultModelId)
Expand All @@ -32,7 +31,6 @@ internal GrokImageGenerator(ImageClient imageClient, string defaultModelId)
GrokImageGenerator(ImageClient imageClient, GrokClientOptions clientOptions, string defaultModelId)
{
this.imageClient = imageClient;
this.clientOptions = clientOptions;
this.defaultModelId = defaultModelId;
metadata = new ImageGeneratorMetadata("xai", clientOptions.Endpoint, defaultModelId);
}
Expand Down Expand Up @@ -63,9 +61,8 @@ public async Task<ImageGenerationResponse> GenerateAsync(
}

// Handle image editing if original images are provided
if (request.OriginalImages is not null && request.OriginalImages.Any())
if (request.OriginalImages?.FirstOrDefault() is { } originalImage)
{
var originalImage = request.OriginalImages.FirstOrDefault();
if (originalImage is DataContent dataContent)
{
var imageUrl = dataContent.Uri?.ToString();
Expand All @@ -91,7 +88,7 @@ public async Task<ImageGenerationResponse> GenerateAsync(

var response = await imageClient.GenerateImageAsync(protocolRequest, cancellationToken: cancellationToken).ConfigureAwait(false);

return ToImageGenerationResponse(response, options?.MediaType);
return ToImageGenerationResponse(response);
}

/// <inheritdoc />
Expand All @@ -103,18 +100,14 @@ public async Task<ImageGenerationResponse> GenerateAsync(
};

/// <inheritdoc />
void IDisposable.Dispose()
{
// Nothing to dispose. Implementation required for the IImageGenerator interface.
}
void IDisposable.Dispose() { }

/// <summary>
/// Converts an xAI <see cref="ImageResponse"/> to a <see cref="ImageGenerationResponse"/>.
/// </summary>
static ImageGenerationResponse ToImageGenerationResponse(ImageResponse response, string? mediaType)
static ImageGenerationResponse ToImageGenerationResponse(ImageResponse response)
{
var contents = new List<AIContent>();
var contentType = mediaType ?? DefaultOutputContentType; // xAI returns JPG by default

foreach (var image in response.Images)
{
Expand All @@ -123,12 +116,12 @@ static ImageGenerationResponse ToImageGenerationResponse(ImageResponse response,
case GeneratedImage.ImageOneofCase.Base64:
{
var imageBytes = Convert.FromBase64String(image.Base64);
contents.Add(new DataContent(imageBytes, contentType));
contents.Add(new DataContent(imageBytes, DefaultOutputContentType));
break;
}
case GeneratedImage.ImageOneofCase.Url:
{
contents.Add(new UriContent(new Uri(image.Url), contentType));
contents.Add(new UriContent(new Uri(image.Url), DefaultOutputContentType));
break;
}
default:
Expand Down