diff --git a/.dotnet/src/Custom/Audio/AudioClient.cs b/.dotnet/src/Custom/Audio/AudioClient.cs index 1eaaeb708..4fa2267a5 100644 --- a/.dotnet/src/Custom/Audio/AudioClient.cs +++ b/.dotnet/src/Custom/Audio/AudioClient.cs @@ -154,7 +154,7 @@ public virtual ClientResult TranscribeAudio(BinaryData audio public virtual async Task> TranscribeAudioAsync(BinaryData audioBytes, string filename, AudioTranscriptionOptions options = null) { PipelineMessage message = CreateInternalTranscriptionRequestMessage(audioBytes, filename, options); - await Shim.Pipeline.SendAsync(message); + await Shim.Pipeline.SendAsync(message).ConfigureAwait(false); return GetTranscriptionResultFromResponse(message.Response); } @@ -168,7 +168,7 @@ public virtual ClientResult TranslateAudio(BinaryData audioByt public virtual async Task> TranslateAudioAsync(BinaryData audioBytes, string filename, AudioTranslationOptions options = null) { PipelineMessage message = CreateInternalTranslationRequestMessage(audioBytes, filename, options); - await Shim.Pipeline.SendAsync(message); + await Shim.Pipeline.SendAsync(message).ConfigureAwait(false); return GetTranslationResultFromResponse(message.Response); } diff --git a/.dotnet/src/Custom/Chat/ChatClient.cs b/.dotnet/src/Custom/Chat/ChatClient.cs index 2a029bbf1..9fdc022ae 100644 --- a/.dotnet/src/Custom/Chat/ChatClient.cs +++ b/.dotnet/src/Custom/Chat/ChatClient.cs @@ -298,7 +298,7 @@ public virtual async Task> CompleteCh { PipelineMessage requestMessage = CreateCustomRequestMessage(messages, choiceCount, options); requestMessage.BufferResponse = false; - await Shim.Pipeline.SendAsync(requestMessage); + await Shim.Pipeline.SendAsync(requestMessage).ConfigureAwait(false); PipelineResponse response = requestMessage.ExtractResponse(); if (response.IsError) diff --git a/.dotnet/src/Custom/Files/FileClient.cs b/.dotnet/src/Custom/Files/FileClient.cs index b57ebefc8..a1cdb9ea8 100644 --- a/.dotnet/src/Custom/Files/FileClient.cs +++ b/.dotnet/src/Custom/Files/FileClient.cs @@ -108,7 +108,7 @@ public virtual async Task> UploadFileAsync(BinaryDa if (string.IsNullOrWhiteSpace(filename)) throw new ArgumentException(nameof(filename)); PipelineMessage uploadMessage = CreateInternalUploadMessage(file, filename, purpose); - await Shim.Pipeline.SendAsync(uploadMessage); + await Shim.Pipeline.SendAsync(uploadMessage).ConfigureAwait(false); return GetUploadResultFromResponse(uploadMessage.Response); } @@ -193,7 +193,7 @@ public virtual async Task> DownloadFileAsync(string fil request.Uri = uriBuilder.Uri; request.Headers.Set("content-type", "multipart/form-data"); - await Shim.Pipeline.SendAsync(message); + await Shim.Pipeline.SendAsync(message).ConfigureAwait(false); if (message.Response.IsError) { diff --git a/.dotnet/src/Custom/Images/ImageGenerationCollection.cs b/.dotnet/src/Custom/Images/GeneratedImageCollection.cs similarity index 60% rename from .dotnet/src/Custom/Images/ImageGenerationCollection.cs rename to .dotnet/src/Custom/Images/GeneratedImageCollection.cs index 7774309b5..78d217f1a 100644 --- a/.dotnet/src/Custom/Images/ImageGenerationCollection.cs +++ b/.dotnet/src/Custom/Images/GeneratedImageCollection.cs @@ -6,7 +6,7 @@ namespace OpenAI.Images; /// /// Represents an image generation response payload that contains information for multiple generated images. /// -public class ImageGenerationCollection : ReadOnlyCollection +public class GeneratedImageCollection : ReadOnlyCollection { - internal ImageGenerationCollection(IList list) : base(list) { } + internal GeneratedImageCollection(IList list) : base(list) { } } \ No newline at end of file diff --git a/.dotnet/src/Custom/Images/ImageClient.cs b/.dotnet/src/Custom/Images/ImageClient.cs index 6a303045e..624fb2410 100644 --- a/.dotnet/src/Custom/Images/ImageClient.cs +++ b/.dotnet/src/Custom/Images/ImageClient.cs @@ -1,6 +1,12 @@ +using OpenAI.Audio; +using OpenAI.ClientShared.Internal; using System; using System.ClientModel; +using System.ClientModel.Primitives; using System.Collections.Generic; +using System.Runtime.InteropServices.ComTypes; +using System.Text; +using System.Text.Json; using System.Threading.Tasks; namespace OpenAI.Images; @@ -99,9 +105,11 @@ public ImageClient(string model, OpenAIClientOptions options = null) /// Additional options for the image generation request. /// The cancellation token for the operation. /// A result for a single image generation. - public virtual ClientResult GenerateImage(string prompt, ImageGenerationOptions options = null) + public virtual ClientResult GenerateImage( + string prompt, + ImageGenerationOptions options = null) { - ClientResult multiResult = GenerateImages(prompt, imageCount: null, options); + ClientResult multiResult = GenerateImages(prompt, imageCount: null, options); return ClientResult.FromValue(multiResult.Value[0], multiResult.GetRawResponse()); } @@ -112,9 +120,11 @@ public virtual ClientResult GenerateImage(string prompt, ImageGe /// Additional options for the image generation request. /// The cancellation token for the operation. /// A result for a single image generation. - public virtual async Task> GenerateImageAsync(string prompt, ImageGenerationOptions options = null) + public virtual async Task> GenerateImageAsync( + string prompt, + ImageGenerationOptions options = null) { - ClientResult multiResult = await GenerateImagesAsync(prompt, imageCount: null, options).ConfigureAwait(false); + ClientResult multiResult = await GenerateImagesAsync(prompt, imageCount: null, options).ConfigureAwait(false); return ClientResult.FromValue(multiResult.Value[0], multiResult.GetRawResponse()); } @@ -128,19 +138,21 @@ public virtual async Task> GenerateImageAsync(strin /// Additional options for the image generation request. /// The cancellation token for the operation. /// A result for a single image generation. - public virtual ClientResult GenerateImages( + public virtual ClientResult GenerateImages( string prompt, int? imageCount = null, ImageGenerationOptions options = null) { - Internal.Models.CreateImageRequest request = CreateInternalRequest(prompt, imageCount, options); + Internal.Models.CreateImageRequest request = CreateInternalImageRequest(prompt, imageCount, options); ClientResult response = Shim.CreateImage(request); - List ImageGenerations = []; + + List images = []; for (int i = 0; i < response.Value.Data.Count; i++) { - ImageGenerations.Add(new(response.Value, i)); + images.Add(new GeneratedImage(response.Value, i)); } - return ClientResult.FromValue(new ImageGenerationCollection(ImageGenerations), response.GetRawResponse()); + + return ClientResult.FromValue(new GeneratedImageCollection(images), response.GetRawResponse()); } /// @@ -153,22 +165,126 @@ public virtual ClientResult GenerateImages( /// Additional options for the image generation request. /// The cancellation token for the operation. /// A result for a single image generation. - public virtual async Task> GenerateImagesAsync( + public virtual async Task> GenerateImagesAsync( string prompt, int? imageCount = null, ImageGenerationOptions options = null) { - Internal.Models.CreateImageRequest request = CreateInternalRequest(prompt, imageCount, options); + Internal.Models.CreateImageRequest request = CreateInternalImageRequest(prompt, imageCount, options); ClientResult response = await Shim.CreateImageAsync(request).ConfigureAwait(false); - List ImageGenerations = []; + + List images = []; for (int i = 0; i < response.Value.Data.Count; i++) { - ImageGenerations.Add(new(response.Value, i)); + images.Add(new GeneratedImage(response.Value, i)); + } + + return ClientResult.FromValue(new GeneratedImageCollection(images), response.GetRawResponse()); + } + + public virtual ClientResult GenerateImageEdits( + BinaryData imageBytes, + string prompt, + int? imageCount = null, + ImageEditOptions options = null) + { + PipelineMessage message = CreateInternalImageEditsPipelineMessage(imageBytes, prompt, imageCount, options); + Shim.Pipeline.Send(message); + + if (message.Response.IsError) + { + throw new ClientResultException(message.Response); } - return ClientResult.FromValue(new ImageGenerationCollection(ImageGenerations), response.GetRawResponse()); + + using JsonDocument responseDocument = JsonDocument.Parse(message.Response.Content); + Internal.Models.ImagesResponse response = Internal.Models.ImagesResponse.DeserializeImagesResponse(responseDocument.RootElement); + + List images = []; + for (int i = 0; i < response.Data.Count; i++) + { + images.Add(new GeneratedImage(response, i)); + } + + return ClientResult.FromValue(new GeneratedImageCollection(images), message.Response); } - private Internal.Models.CreateImageRequest CreateInternalRequest( + public virtual async Task> GenerateImageEditsAsync( + BinaryData imageBytes, + string prompt, + int? imageCount = null, + ImageEditOptions options = null) + { + PipelineMessage message = CreateInternalImageEditsPipelineMessage(imageBytes, prompt, imageCount, options); + await Shim.Pipeline.SendAsync(message).ConfigureAwait(false); + + if (message.Response.IsError) + { + throw new ClientResultException(message.Response); + } + + using JsonDocument responseDocument = JsonDocument.Parse(message.Response.Content); + Internal.Models.ImagesResponse response = Internal.Models.ImagesResponse.DeserializeImagesResponse(responseDocument.RootElement); + + List images = []; + for (int i = 0; i < response.Data.Count; i++) + { + images.Add(new GeneratedImage(response, i)); + } + + return ClientResult.FromValue(new GeneratedImageCollection(images), message.Response); + } + + public virtual ClientResult GenerateImageVariations( + BinaryData imageBytes, + int? imageCount = null, + ImageVariationOptions options = null) + { + PipelineMessage message = CreateInternalImageVariationsPipelineMessage(imageBytes, imageCount, options); + Shim.Pipeline.Send(message); + + if (message.Response.IsError) + { + throw new ClientResultException(message.Response); + } + + using JsonDocument responseDocument = JsonDocument.Parse(message.Response.Content); + Internal.Models.ImagesResponse response = Internal.Models.ImagesResponse.DeserializeImagesResponse(responseDocument.RootElement); + + List images = []; + for (int i = 0; i < response.Data.Count; i++) + { + images.Add(new GeneratedImage(response, i)); + } + + return ClientResult.FromValue(new GeneratedImageCollection(images), message.Response); + } + + public virtual async Task> GenerateImageVariationsAsync( + BinaryData imageBytes, + int? imageCount = null, + ImageVariationOptions options = null) + { + PipelineMessage message = CreateInternalImageVariationsPipelineMessage(imageBytes, imageCount, options); + await Shim.Pipeline.SendAsync(message).ConfigureAwait(false); + + if (message.Response.IsError) + { + throw new ClientResultException(message.Response); + } + + using JsonDocument responseDocument = JsonDocument.Parse(message.Response.Content); + Internal.Models.ImagesResponse response = Internal.Models.ImagesResponse.DeserializeImagesResponse(responseDocument.RootElement); + + List images = []; + for (int i = 0; i < response.Data.Count; i++) + { + images.Add(new GeneratedImage(response, i)); + } + + return ClientResult.FromValue(new GeneratedImageCollection(images), message.Response); + } + + private Internal.Models.CreateImageRequest CreateInternalImageRequest( string prompt, int? imageCount = null, ImageGenerationOptions options = null) @@ -184,6 +300,7 @@ private Internal.Models.CreateImageRequest CreateInternalRequest( _ => throw new ArgumentException(nameof(options.Quality)), }; } + Internal.Models.CreateImageRequestResponseFormat? internalFormat = null; if (options.ResponseFormat != null) { @@ -194,19 +311,21 @@ private Internal.Models.CreateImageRequest CreateInternalRequest( _ => throw new ArgumentException(nameof(options.ResponseFormat)), }; } + Internal.Models.CreateImageRequestSize? internalSize = null; if (options.Size != null) { internalSize = options.Size switch { + ImageSize.Size256x256 => Internal.Models.CreateImageRequestSize._256x256, + ImageSize.Size512x512 => Internal.Models.CreateImageRequestSize._512x512, ImageSize.Size1024x1024 => Internal.Models.CreateImageRequestSize._1024x1024, ImageSize.Size1024x1792 => Internal.Models.CreateImageRequestSize._1024x1792, ImageSize.Size1792x1024 => Internal.Models.CreateImageRequestSize._1792x1024, - ImageSize.Size256x256 => Internal.Models.CreateImageRequestSize._256x256, - ImageSize.Size512x512 => Internal.Models.CreateImageRequestSize._512x512, _ => throw new ArgumentException(nameof(options.Size)), }; } + Internal.Models.CreateImageRequestStyle? internalStyle = null; if (options.Style != null) { @@ -217,6 +336,7 @@ private Internal.Models.CreateImageRequest CreateInternalRequest( _ => throw new ArgumentException(nameof(options.Style)), }; } + return new Internal.Models.CreateImageRequest( prompt, _clientConnector.Model, @@ -228,4 +348,241 @@ private Internal.Models.CreateImageRequest CreateInternalRequest( options.User, serializedAdditionalRawData: null); } + + private PipelineMessage CreateInternalImageEditsPipelineMessage( + BinaryData imageBytes, + string prompt, + int? imageCount = null, + ImageEditOptions options = null) + { + PipelineMessage message = Shim.Pipeline.CreateMessage(); + message.ResponseClassifier = ResponseErrorClassifier200; + PipelineRequest request = message.Request; + request.Method = "POST"; + UriBuilder uriBuilder = new(_clientConnector.Endpoint.AbsoluteUri); + StringBuilder path = new(); + path.Append("/images/edits"); + uriBuilder.Path += path.ToString(); + request.Uri = uriBuilder.Uri; + + options ??= new(); + MultipartFormDataContent requestContent = CreateInternalImageEditsMultipartFormDataContent( + imageBytes, + prompt, + options.MaskBytes, + imageCount, + options.ResponseFormat, + options.Size, + options.User); + requestContent.ApplyToRequest(request); + + return message; + } + + private MultipartFormDataContent CreateInternalImageEditsMultipartFormDataContent( + BinaryData imageBytes, + string prompt, + BinaryData maskBytes, + int? imageCount, + ImageResponseFormat? imageResponseFormat, + ImageSize? imageSize, + string user) + { + MultipartFormDataContent content = new(); + + content.Add(MultipartContent.Create(imageBytes), name: "image", fileName: "image.png", headers: []); + + content.Add(MultipartContent.Create(BinaryData.FromString(prompt)), name: "prompt", headers: []); + + content.Add(MultipartContent.Create(BinaryData.FromString(_clientConnector.Model)), name: "model", headers: []); + + if (OptionalProperty.IsDefined(maskBytes)) + { + content.Add(MultipartContent.Create(maskBytes), name: "mask", fileName: "mask.png", headers: []); + } + + if (OptionalProperty.IsDefined(imageCount)) + { + content.Add(MultipartContent.Create(BinaryData.FromString(imageCount.ToString())), name: "n", headers: []); + } + + if (OptionalProperty.IsDefined(imageResponseFormat)) + { + content.Add(MultipartContent.Create( + BinaryData.FromString( + imageResponseFormat switch + { + ImageResponseFormat.Uri => "url", + ImageResponseFormat.Bytes => "b64_json", + _ => throw new ArgumentException(nameof(imageResponseFormat)), + }) + ), + name: "response_format", + headers: []); + } + + if (OptionalProperty.IsDefined(imageSize)) + { + content.Add(MultipartContent.Create( + BinaryData.FromString( + imageSize switch + { + ImageSize.Size256x256 => "256x256", + ImageSize.Size512x512 => "512x512", + ImageSize.Size1024x1024 => "1024x1024", + // TODO: 1024x1792 and 1792x1024 are currently not supported in image edits. + ImageSize.Size1024x1792 => "1024x1792", + ImageSize.Size1792x1024 => "1792x1024", + _ => throw new ArgumentException(nameof(imageSize)) + }) + ), + name: "size", + headers: []); + } + + if (OptionalProperty.IsDefined(user)) + { + content.Add(MultipartContent.Create(BinaryData.FromString(user)), "user", []); + } + + return content; + } + + private PipelineMessage CreateInternalImageVariationsPipelineMessage( + BinaryData imageBytes, + int? imageCount = null, + ImageVariationOptions options = null) + { + PipelineMessage message = Shim.Pipeline.CreateMessage(); + message.ResponseClassifier = ResponseErrorClassifier200; + PipelineRequest request = message.Request; + request.Method = "POST"; + UriBuilder uriBuilder = new(_clientConnector.Endpoint.AbsoluteUri); + StringBuilder path = new(); + path.Append("/images/variations"); + uriBuilder.Path += path.ToString(); + request.Uri = uriBuilder.Uri; + + options ??= new(); + MultipartFormDataContent requestContent = CreateInternalImageVariationsMultipartFormDataContent( + imageBytes, + imageCount, + options.ResponseFormat, + options.Size, + options.User); + requestContent.ApplyToRequest(request); + + return message; + } + + private MultipartFormDataContent CreateInternalImageVariationsMultipartFormDataContent( + BinaryData imageBytes, + int? imageCount, + ImageResponseFormat? imageResponseFormat, + ImageSize? imageSize, + string user) + { + MultipartFormDataContent content = new(); + + content.Add(MultipartContent.Create(imageBytes), name: "image", fileName: "image.png", headers: []); + + content.Add(MultipartContent.Create(BinaryData.FromString(_clientConnector.Model)), name: "model", headers: []); + + if (OptionalProperty.IsDefined(imageCount)) + { + content.Add(MultipartContent.Create(BinaryData.FromString(imageCount.ToString())), name: "n", headers: []); + } + + if (OptionalProperty.IsDefined(imageResponseFormat)) + { + content.Add(MultipartContent.Create( + BinaryData.FromString( + imageResponseFormat switch + { + ImageResponseFormat.Uri => "url", + ImageResponseFormat.Bytes => "b64_json", + _ => throw new ArgumentException(nameof(imageResponseFormat)), + }) + ), + name: "response_format", + headers: []); + } + + if (OptionalProperty.IsDefined(imageSize)) + { + content.Add(MultipartContent.Create( + BinaryData.FromString( + imageSize switch + { + ImageSize.Size256x256 => "256x256", + ImageSize.Size512x512 => "512x512", + ImageSize.Size1024x1024 => "1024x1024", + // TODO: 1024x1792 and 1792x1024 are currently not supported in image variations. + ImageSize.Size1024x1792 => "1024x1792", + ImageSize.Size1792x1024 => "1792x1024", + _ => throw new ArgumentException(nameof(imageSize)) + }) + ), + name: "size", + headers: []); + } + + if (OptionalProperty.IsDefined(user)) + { + content.Add(MultipartContent.Create(BinaryData.FromString(user)), "user", []); + } + + return content; + } + + private static PipelineMessageClassifier _responseErrorClassifier200; + private static PipelineMessageClassifier ResponseErrorClassifier200 => _responseErrorClassifier200 ??= PipelineMessageClassifier.Create(stackalloc ushort[] { 200 }); + + private Internal.Models.CreateImageEditRequest CreateInternalImageEditRequest( + BinaryData imageBytes, + string prompt, + int? imageCount = null, + ImageEditOptions options = null) + { + options ??= new(); + + + Internal.Models.CreateImageEditRequestSize? internalSize = null; + if (options.Size != null) + { + internalSize = options.Size switch + { + + ImageSize.Size256x256 => Internal.Models.CreateImageEditRequestSize._256x256, + ImageSize.Size512x512 => Internal.Models.CreateImageEditRequestSize._512x512, + ImageSize.Size1024x1024 => Internal.Models.CreateImageEditRequestSize._1024x1024, + // TODO: 1024x1792 and 1792x1024 are currently not supported in image edits. + ImageSize.Size1024x1792 => new Internal.Models.CreateImageEditRequestSize("1024x1792"), + ImageSize.Size1792x1024 => new Internal.Models.CreateImageEditRequestSize("1792x1024"), + _ => throw new ArgumentException(nameof(options.Size)), + }; + } + + Internal.Models.CreateImageEditRequestResponseFormat? internalFormat = null; + if (options.ResponseFormat != null) + { + internalFormat = options.ResponseFormat switch + { + ImageResponseFormat.Bytes => Internal.Models.CreateImageEditRequestResponseFormat.B64Json, + ImageResponseFormat.Uri => Internal.Models.CreateImageEditRequestResponseFormat.Url, + _ => throw new ArgumentException(nameof(options.ResponseFormat)), + }; + } + + return new Internal.Models.CreateImageEditRequest( + imageBytes, + prompt, + options.MaskBytes, + _clientConnector.Model, + imageCount, + internalSize, + internalFormat, + options.User, + serializedAdditionalRawData: null); + } } diff --git a/.dotnet/src/Custom/Images/ImageEditOptions.cs b/.dotnet/src/Custom/Images/ImageEditOptions.cs new file mode 100644 index 000000000..8412a42ce --- /dev/null +++ b/.dotnet/src/Custom/Images/ImageEditOptions.cs @@ -0,0 +1,21 @@ +using System; + +namespace OpenAI.Images; + +/// +/// Represents additional options available to control the behavior of an image generation operation. +/// +public partial class ImageEditOptions +{ + /// + public BinaryData MaskBytes { get; set; } + + /// + public ImageSize? Size { get; set; } + + /// + public ImageResponseFormat? ResponseFormat { get; set; } + + /// + public string User { get; set; } +} \ No newline at end of file diff --git a/.dotnet/src/Custom/Images/ImageSize.cs b/.dotnet/src/Custom/Images/ImageSize.cs index e92dbf99d..c857509d1 100644 --- a/.dotnet/src/Custom/Images/ImageSize.cs +++ b/.dotnet/src/Custom/Images/ImageSize.cs @@ -5,6 +5,20 @@ namespace OpenAI.Images; /// public enum ImageSize { + /// + /// A small, square image with 256 pixels of both width and height. + /// + /// Supported only for the older dall-e-2 model. + /// + /// + Size256x256, + /// + /// A medium-small, square image with 512 pixels of both width and height. + /// + /// Supported only for the older dall-e-2 model. + /// + /// + Size512x512, /// /// A square image with 1024 pixels of both width and height. /// @@ -26,18 +40,4 @@ public enum ImageSize /// /// Size1792x1024, - /// - /// A small, square image with 256 pixels of both width and height. - /// - /// Supported only for the older dall-e-2 model. - /// - /// - Size256x256, - /// - /// A medium-small, square image with 512 pixels of both width and height. - /// - /// Supported only for the older dall-e-2 model. - /// - /// - Size512x512, } \ No newline at end of file diff --git a/.dotnet/src/Custom/Images/ImageVariationOptions.cs b/.dotnet/src/Custom/Images/ImageVariationOptions.cs new file mode 100644 index 000000000..129c14cb7 --- /dev/null +++ b/.dotnet/src/Custom/Images/ImageVariationOptions.cs @@ -0,0 +1,18 @@ +using System; + +namespace OpenAI.Images; + +/// +/// Represents additional options available to control the behavior of an image generation operation. +/// +public partial class ImageVariationOptions +{ + /// + public ImageSize? Size { get; set; } + + /// + public ImageResponseFormat? ResponseFormat { get; set; } + + /// + public string User { get; set; } +} \ No newline at end of file diff --git a/.dotnet/tests/Assets/edit_sample_image.png b/.dotnet/tests/Assets/edit_sample_image.png new file mode 100644 index 000000000..869bb1e04 Binary files /dev/null and b/.dotnet/tests/Assets/edit_sample_image.png differ diff --git a/.dotnet/tests/Assets/edit_sample_mask.png b/.dotnet/tests/Assets/edit_sample_mask.png new file mode 100644 index 000000000..98b9c237c Binary files /dev/null and b/.dotnet/tests/Assets/edit_sample_mask.png differ diff --git a/.dotnet/tests/data/hello_world.m4a b/.dotnet/tests/Assets/hello_world.m4a similarity index 100% rename from .dotnet/tests/data/hello_world.m4a rename to .dotnet/tests/Assets/hello_world.m4a diff --git a/.dotnet/tests/data/hola_mundo.m4a b/.dotnet/tests/Assets/hola_mundo.m4a similarity index 100% rename from .dotnet/tests/data/hola_mundo.m4a rename to .dotnet/tests/Assets/hola_mundo.m4a diff --git a/.dotnet/tests/data/stop_sign.png b/.dotnet/tests/Assets/stop_sign.png similarity index 100% rename from .dotnet/tests/data/stop_sign.png rename to .dotnet/tests/Assets/stop_sign.png diff --git a/.dotnet/tests/Assets/variation_sample_image.png b/.dotnet/tests/Assets/variation_sample_image.png new file mode 100644 index 000000000..119a13e8f Binary files /dev/null and b/.dotnet/tests/Assets/variation_sample_image.png differ diff --git a/.dotnet/tests/OpenAI.Tests.csproj b/.dotnet/tests/OpenAI.Tests.csproj index aaf8385b2..9bb493c12 100644 --- a/.dotnet/tests/OpenAI.Tests.csproj +++ b/.dotnet/tests/OpenAI.Tests.csproj @@ -16,4 +16,27 @@ + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + \ No newline at end of file diff --git a/.dotnet/tests/Samples/Images/Sample02_SimpleImageEdit.cs b/.dotnet/tests/Samples/Images/Sample02_SimpleImageEdit.cs new file mode 100644 index 000000000..90c41104a --- /dev/null +++ b/.dotnet/tests/Samples/Images/Sample02_SimpleImageEdit.cs @@ -0,0 +1,38 @@ +using NUnit.Framework; +using OpenAI.Images; +using System; +using System.IO; + +namespace OpenAI.Samples +{ + public partial class ImageSamples + { + [Test] + // [Ignore("Compilation validation only")] + public void Sample02_SimpleImageEdit() + { + ImageClient client = new("dall-e-2", Environment.GetEnvironmentVariable("OpenAIClient_KEY")); + + string imagePath = Path.Combine("Assets", "edit_sample_image.png"); + BinaryData imageBytes = BinaryData.FromBytes(File.ReadAllBytes(imagePath)); + + string prompt = "An inflatable flamingo float in a pool"; + + string maskPath = Path.Combine("Assets", "edit_sample_mask.png"); + BinaryData maskBytes = BinaryData.FromBytes(File.ReadAllBytes(maskPath)); + + ImageEditOptions options = new() + { + MaskBytes = maskBytes, + Size = ImageSize.Size1024x1024, + ResponseFormat = ImageResponseFormat.Bytes + }; + + GeneratedImageCollection image = client.GenerateImageEdits(imageBytes, prompt, 1, options); + BinaryData bytes = image[0].ImageBytes; + + using FileStream stream = File.OpenWrite($"{Guid.NewGuid()}.png"); + bytes.ToStream().CopyTo(stream); + } + } +} diff --git a/.dotnet/tests/Samples/Images/Sample02_SimpleImageEditAsync.cs b/.dotnet/tests/Samples/Images/Sample02_SimpleImageEditAsync.cs new file mode 100644 index 000000000..3ff68efcc --- /dev/null +++ b/.dotnet/tests/Samples/Images/Sample02_SimpleImageEditAsync.cs @@ -0,0 +1,39 @@ +using NUnit.Framework; +using OpenAI.Images; +using System; +using System.IO; +using System.Threading.Tasks; + +namespace OpenAI.Samples +{ + public partial class ImageSamples + { + [Test] + // [Ignore("Compilation validation only")] + public async Task Sample02_SimpleImageEditAsync() + { + ImageClient client = new("dall-e-2", Environment.GetEnvironmentVariable("OpenAIClient_KEY")); + + string imagePath = Path.Combine("Assets", "edit_sample_image.png"); + BinaryData imageBytes = BinaryData.FromBytes(File.ReadAllBytes(imagePath)); + + string prompt = "An inflatable flamingo float in a pool"; + + string maskPath = Path.Combine("Assets", "edit_sample_mask.png"); + BinaryData maskBytes = BinaryData.FromBytes(File.ReadAllBytes(maskPath)); + + ImageEditOptions options = new() + { + MaskBytes = maskBytes, + Size = ImageSize.Size1024x1024, + ResponseFormat = ImageResponseFormat.Bytes + }; + + GeneratedImageCollection image = await client.GenerateImageEditsAsync(imageBytes, prompt, 1, options); + BinaryData bytes = image[0].ImageBytes; + + using FileStream stream = File.OpenWrite($"{Guid.NewGuid()}.png"); + bytes.ToStream().CopyTo(stream); + } + } +} diff --git a/.dotnet/tests/Samples/Images/Sample03_SimpleImageVariation.cs b/.dotnet/tests/Samples/Images/Sample03_SimpleImageVariation.cs new file mode 100644 index 000000000..1ec803cd4 --- /dev/null +++ b/.dotnet/tests/Samples/Images/Sample03_SimpleImageVariation.cs @@ -0,0 +1,32 @@ +using NUnit.Framework; +using OpenAI.Images; +using System; +using System.IO; + +namespace OpenAI.Samples +{ + public partial class ImageSamples + { + [Test] + [Ignore("Compilation validation only")] + public void Sample03_SimpleImageVariation() + { + ImageClient client = new("dall-e-2", Environment.GetEnvironmentVariable("OpenAIClient_KEY")); + + string imagePath = Path.Combine("Assets", "variation_sample_image.png"); + BinaryData imageBytes = BinaryData.FromBytes(File.ReadAllBytes(imagePath)); + + ImageVariationOptions options = new() + { + Size = ImageSize.Size1024x1024, + ResponseFormat = ImageResponseFormat.Bytes + }; + + GeneratedImageCollection image = client.GenerateImageVariations(imageBytes, 1, options); + BinaryData bytes = image[0].ImageBytes; + + using FileStream stream = File.OpenWrite($"{Guid.NewGuid()}.png"); + bytes.ToStream().CopyTo(stream); + } + } +} diff --git a/.dotnet/tests/Samples/Images/Sample03_SimpleImageVariationAsync.cs b/.dotnet/tests/Samples/Images/Sample03_SimpleImageVariationAsync.cs new file mode 100644 index 000000000..6f2aba398 --- /dev/null +++ b/.dotnet/tests/Samples/Images/Sample03_SimpleImageVariationAsync.cs @@ -0,0 +1,33 @@ +using NUnit.Framework; +using OpenAI.Images; +using System; +using System.IO; +using System.Threading.Tasks; + +namespace OpenAI.Samples +{ + public partial class ImageSamples + { + [Test] + [Ignore("Compilation validation only")] + public async Task Sample03_SimpleImageVariationAsync() + { + ImageClient client = new("dall-e-2", Environment.GetEnvironmentVariable("OpenAIClient_KEY")); + + string imagePath = Path.Combine("Assets", "variation_sample_image.png"); + BinaryData imageBytes = BinaryData.FromBytes(File.ReadAllBytes(imagePath)); + + ImageVariationOptions options = new() + { + Size = ImageSize.Size1024x1024, + ResponseFormat = ImageResponseFormat.Bytes + }; + + GeneratedImageCollection image = await client.GenerateImageVariationsAsync(imageBytes, 1, options); + BinaryData bytes = image[0].ImageBytes; + + using FileStream stream = File.OpenWrite($"{Guid.NewGuid()}.png"); + bytes.ToStream().CopyTo(stream); + } + } +} diff --git a/.dotnet/tests/TestScenarios/ChatWithVision.cs b/.dotnet/tests/TestScenarios/ChatWithVision.cs index 4b37ea727..fe6318bf0 100644 --- a/.dotnet/tests/TestScenarios/ChatWithVision.cs +++ b/.dotnet/tests/TestScenarios/ChatWithVision.cs @@ -13,7 +13,7 @@ public partial class ChatWithVision [Test] public void DescribeAnImage() { - var stopSignPath = Path.Combine("data", "stop_sign.png"); + var stopSignPath = Path.Combine("Assets", "stop_sign.png"); var stopSignData = BinaryData.FromBytes(File.ReadAllBytes(stopSignPath)); ChatClient client = GetTestClient(TestScenario.VisionChat); diff --git a/.dotnet/tests/TestScenarios/TranscriptionTests.cs b/.dotnet/tests/TestScenarios/TranscriptionTests.cs index 64813c2c3..70753d47d 100644 --- a/.dotnet/tests/TestScenarios/TranscriptionTests.cs +++ b/.dotnet/tests/TestScenarios/TranscriptionTests.cs @@ -13,7 +13,7 @@ public partial class TranscriptionTests public void BasicTranscriptionWorks() { AudioClient client = GetTestClient(); - using FileStream inputStream = File.OpenRead(Path.Combine("data", "hello_world.m4a")); + using FileStream inputStream = File.OpenRead(Path.Combine("Assets", "hello_world.m4a")); BinaryData inputData = BinaryData.FromStream(inputStream); ClientResult transcriptionResult = client.TranscribeAudio(inputData, "hello_world.m4a"); Assert.That(transcriptionResult.Value, Is.Not.Null); @@ -24,7 +24,7 @@ public void BasicTranscriptionWorks() public void WordTimestampsWork() { AudioClient client = GetTestClient(); - using FileStream inputStream = File.OpenRead(Path.Combine("data", "hello_world.m4a")); + using FileStream inputStream = File.OpenRead(Path.Combine("Assets", "hello_world.m4a")); BinaryData inputData = BinaryData.FromStream(inputStream); ClientResult transcriptionResult = client.TranscribeAudio(inputData, "hello_world.m4a", new AudioTranscriptionOptions() { diff --git a/.dotnet/tests/TestScenarios/TranslationTests.cs b/.dotnet/tests/TestScenarios/TranslationTests.cs index e3310bd0d..f035a87e5 100644 --- a/.dotnet/tests/TestScenarios/TranslationTests.cs +++ b/.dotnet/tests/TestScenarios/TranslationTests.cs @@ -13,7 +13,7 @@ public partial class TranslationTests public void BasicTranslationWorks() { AudioClient client = GetTestClient(); - using FileStream inputStream = File.OpenRead(Path.Combine("data", "hola_mundo.m4a")); + using FileStream inputStream = File.OpenRead(Path.Combine("Assets", "hola_mundo.m4a")); BinaryData inputData = BinaryData.FromStream(inputStream); ClientResult translationResult = client.TranslateAudio(inputData, "hola_mundo.m4a"); Assert.That(translationResult.Value, Is.Not.Null);