Skip to content

Commit 1ecea39

Browse files
authored
Improve content type detection from responses (#49)
And also default format to Uri if not provided via request options.
1 parent 59ecb02 commit 1ecea39

File tree

2 files changed

+41
-11
lines changed

2 files changed

+41
-11
lines changed

src/xAI.Tests/ImageGeneratorTests.cs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ public async Task GenerateImage_WithEditsToPreviousImage()
7474
public async Task GenerateImage_WithBase64Response_ReturnsDataContent()
7575
{
7676
var imageGenerator = new GrokClient(Configuration["XAI_API_KEY"]!)
77-
.AsIImageGenerator("grok-2-image");
77+
.AsIImageGenerator("grok-imagine-image-beta");
7878

7979
var request = new ImageGenerationRequest("A sunset over mountains");
8080
var options = new ImageGenerationOptions
@@ -99,6 +99,22 @@ public async Task GenerateImage_WithBase64Response_ReturnsDataContent()
9999
output.WriteLine($"Generated image size: {dataContent.Data.Length} bytes");
100100
}
101101

102+
[SecretsFact("XAI_API_KEY")]
103+
public async Task GenerateImage_DefaultsToUriContent()
104+
{
105+
var imageGenerator = new GrokClient(Configuration["XAI_API_KEY"]!)
106+
.AsIImageGenerator("grok-imagine-image-beta");
107+
108+
var request = new ImageGenerationRequest("A sunset over mountains");
109+
var response = await imageGenerator.GenerateAsync(request);
110+
111+
Assert.NotNull(response);
112+
Assert.NotEmpty(response.Contents);
113+
Assert.Single(response.Contents);
114+
115+
Assert.IsType<UriContent>(response.Contents.First());
116+
}
117+
102118
[SecretsFact("XAI_API_KEY")]
103119
public async Task GenerateMultipleImages_ReturnsCorrectCount()
104120
{

src/xAI/GrokImageGenerator.cs

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,18 @@ namespace xAI;
1010
/// </summary>
1111
sealed class GrokImageGenerator : IImageGenerator
1212
{
13+
// add inverted dictionary for extension to mime type if needed in future
14+
static readonly Dictionary<string, string> extensionToMimeType = new(StringComparer.OrdinalIgnoreCase)
15+
{
16+
[".png"] = "image/png",
17+
[".jpg"] = "image/jpeg",
18+
[".jpeg"] = "image/jpeg",
19+
[".webp"] = "image/webp",
20+
[".gif"] = "image/gif",
21+
[".bmp"] = "image/bmp",
22+
[".tiff"] = "image/tiff",
23+
};
24+
1325
const string DefaultInputContentType = "image/png";
1426
const string DefaultOutputContentType = "image/jpeg";
1527

@@ -50,15 +62,12 @@ public async Task<ImageGenerationResponse> GenerateAsync(
5062
if (options?.Count is { } count)
5163
protocolRequest.N = count;
5264

53-
if (options?.ResponseFormat is { } responseFormat)
65+
protocolRequest.Format = (options?.ResponseFormat ?? ImageGenerationResponseFormat.Uri) switch
5466
{
55-
protocolRequest.Format = responseFormat switch
56-
{
57-
ImageGenerationResponseFormat.Uri => ImageFormat.ImgFormatUrl,
58-
ImageGenerationResponseFormat.Data => ImageFormat.ImgFormatBase64,
59-
_ => throw new ArgumentException($"Unsupported response format: {responseFormat}", nameof(options))
60-
};
61-
}
67+
ImageGenerationResponseFormat.Uri => ImageFormat.ImgFormatUrl,
68+
ImageGenerationResponseFormat.Data => ImageFormat.ImgFormatBase64,
69+
_ => throw new ArgumentException($"Unsupported response format: {options?.ResponseFormat}", nameof(options))
70+
};
6271

6372
// Handle image editing if original images are provided
6473
if (request.OriginalImages?.FirstOrDefault() is { } originalImage)
@@ -108,20 +117,25 @@ void IDisposable.Dispose() { }
108117
static ImageGenerationResponse ToImageGenerationResponse(ImageResponse response)
109118
{
110119
var contents = new List<AIContent>();
120+
var contentType = DefaultOutputContentType;
111121

112122
foreach (var image in response.Images)
113123
{
114124
switch (image.ImageCase)
115125
{
116126
case GeneratedImage.ImageOneofCase.Base64:
117127
{
128+
// We assume JPEG since there's no way to get the actual content type.
118129
var imageBytes = Convert.FromBase64String(image.Base64);
119-
contents.Add(new DataContent(imageBytes, DefaultOutputContentType));
130+
contents.Add(new DataContent(imageBytes, contentType));
120131
break;
121132
}
122133
case GeneratedImage.ImageOneofCase.Url:
123134
{
124-
contents.Add(new UriContent(new Uri(image.Url), DefaultOutputContentType));
135+
if (Path.GetExtension(image.Url) is { } extension && extensionToMimeType.TryGetValue(extension, out var mimeType))
136+
contentType = mimeType;
137+
138+
contents.Add(new UriContent(new Uri(image.Url), contentType));
125139
break;
126140
}
127141
default:

0 commit comments

Comments
 (0)