Skip to content

Commit

Permalink
OpenAI-DotNet 4.4.1 (RageAgainstThePixel#22)
Browse files Browse the repository at this point in the history
- hotfix to CompletionsEndpoint to use `IEnumerable<string>`
- hotfix to cleanup Images endpoints
  • Loading branch information
StephenHodgson committed Feb 4, 2023
1 parent 33b2b95 commit d8e239d
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 57 deletions.
4 changes: 2 additions & 2 deletions OpenAI-DotNet/Completions/CompletionsEndpoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ public async Task StreamCompletionAsync(CompletionRequest completionRequest, Act
/// for more details on how to consume an async enumerable.</returns>
public IAsyncEnumerable<CompletionResult> StreamCompletionEnumerableAsync(
string prompt = null,
string[] prompts = null,
IEnumerable<string> prompts = null,
string suffix = null,
int? maxTokens = null,
double? temperature = null,
Expand All @@ -275,7 +275,7 @@ public async Task StreamCompletionAsync(CompletionRequest completionRequest, Act
double? frequencyPenalty = null,
int? logProbabilities = null,
bool? echo = null,
string[] stopSequences = null,
IEnumerable<string> stopSequences = null,
Model model = null,
CancellationToken cancellationToken = default)
{
Expand Down
74 changes: 34 additions & 40 deletions OpenAI-DotNet/Images/ImagesEndpoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Linq;
using System.Net.Http;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;

namespace OpenAI.Images
Expand All @@ -26,25 +27,23 @@ public sealed class ImagesEndpoint : BaseEndPoint
/// <param name="numberOfResults">The number of images to generate. Must be between 1 and 10.</param>
/// <param name="size">The size of the generated images. Must be one of 256x256, 512x512, or 1024x1024.</param>
/// <param name="user">A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse.</param>
/// <returns>An array of generated textures.</returns>
public async Task<IReadOnlyList<string>> GenerateImageAsync(string prompt, int numberOfResults = 1, ImageSize size = ImageSize.Large, string user = null)
=> await GenerateImageAsync(new ImageGenerationRequest(prompt, numberOfResults, size, user)).ConfigureAwait(false);
/// <param name="cancellationToken">Optional, <see cref="CancellationToken"/>.</param>
/// <returns>A list of generated texture urls to download.</returns>
public async Task<IReadOnlyList<string>> GenerateImageAsync(string prompt, int numberOfResults = 1, ImageSize size = ImageSize.Large, string user = null, CancellationToken cancellationToken = default)
=> await GenerateImageAsync(new ImageGenerationRequest(prompt, numberOfResults, size, user), cancellationToken).ConfigureAwait(false);

/// <summary>
/// Creates an image given a prompt.
/// </summary>
/// <param name="request"><see cref="ImageGenerationRequest"/></param>
/// <returns>An array of generated textures.</returns>
/// <param name="cancellationToken">Optional, <see cref="CancellationToken"/>.</param>
/// <returns>A list of generated texture urls to download.</returns>
/// <exception cref="HttpRequestException"></exception>
public async Task<IReadOnlyList<string>> GenerateImageAsync(ImageGenerationRequest request)
public async Task<IReadOnlyList<string>> GenerateImageAsync(ImageGenerationRequest request, CancellationToken cancellationToken = default)
{
var jsonContent = JsonSerializer.Serialize(request, Api.JsonSerializationOptions);
var response = await Api.Client.PostAsync($"{GetEndpoint()}generations", jsonContent.ToJsonStringContent()).ConfigureAwait(false);

return response.IsSuccessStatusCode
? await DeserializeResponseAsync(response).ConfigureAwait(false)
: throw new HttpRequestException(
$"{nameof(GenerateImageAsync)} Failed! HTTP status code: {response.StatusCode}. Request body: {jsonContent}");
var response = await Api.Client.PostAsync($"{GetEndpoint()}generations", jsonContent.ToJsonStringContent(), cancellationToken).ConfigureAwait(false);
return await DeserializeResponseAsync(response, cancellationToken).ConfigureAwait(false);
}

/// <summary>
Expand All @@ -68,25 +67,27 @@ public async Task<IReadOnlyList<string>> GenerateImageAsync(ImageGenerationReque
/// The size of the generated images. Must be one of 256x256, 512x512, or 1024x1024.
/// </param>
/// <param name="user">A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse.</param>
/// <returns>An array of generated textures.</returns>
/// <param name="cancellationToken">Optional, <see cref="CancellationToken"/>.</param>
/// <returns>A list of generated texture urls to download.</returns>
/// <exception cref="HttpRequestException"></exception>
public async Task<IReadOnlyList<string>> CreateImageEditAsync(string image, string mask, string prompt, int numberOfResults = 1, ImageSize size = ImageSize.Large, string user = null)
=> await CreateImageEditAsync(new ImageEditRequest(image, mask, prompt, numberOfResults, size, user)).ConfigureAwait(false);
public async Task<IReadOnlyList<string>> CreateImageEditAsync(string image, string mask, string prompt, int numberOfResults = 1, ImageSize size = ImageSize.Large, string user = null, CancellationToken cancellationToken = default)
=> await CreateImageEditAsync(new ImageEditRequest(image, mask, prompt, numberOfResults, size, user), cancellationToken).ConfigureAwait(false);

/// <summary>
/// Creates an edited or extended image given an original image and a prompt.
/// </summary>
/// <param name="request"><see cref="ImageEditRequest"/></param>
/// <returns>An array of generated textures.</returns>
/// <param name="cancellationToken">Optional, <see cref="CancellationToken"/>.</param>
/// <returns>A list of generated texture urls to download.</returns>
/// <exception cref="HttpRequestException"></exception>
public async Task<IReadOnlyList<string>> CreateImageEditAsync(ImageEditRequest request)
public async Task<IReadOnlyList<string>> CreateImageEditAsync(ImageEditRequest request, CancellationToken cancellationToken = default)
{
using var content = new MultipartFormDataContent();
using var imageData = new MemoryStream();
await request.Image.CopyToAsync(imageData).ConfigureAwait(false);
await request.Image.CopyToAsync(imageData, cancellationToken).ConfigureAwait(false);
content.Add(new ByteArrayContent(imageData.ToArray()), "image", request.ImageName);
using var maskData = new MemoryStream();
await request.Mask.CopyToAsync(maskData).ConfigureAwait(false);
await request.Mask.CopyToAsync(maskData, cancellationToken).ConfigureAwait(false);
content.Add(new ByteArrayContent(maskData.ToArray()), "mask", request.MaskName);
content.Add(new StringContent(request.Prompt), "prompt");
content.Add(new StringContent(request.Number.ToString()), "n");
Expand All @@ -99,13 +100,8 @@ public async Task<IReadOnlyList<string>> CreateImageEditAsync(ImageEditRequest r

request.Dispose();

var response = await Api.Client.PostAsync($"{GetEndpoint()}edits", content).ConfigureAwait(false);
var responseAsString = await response.ReadAsStringAsync().ConfigureAwait(false);

return response.IsSuccessStatusCode
? await DeserializeResponseAsync(response).ConfigureAwait(false)
: throw new HttpRequestException(
$"{nameof(CreateImageEditAsync)} Failed! HTTP status code: {response.StatusCode}. Response: {responseAsString}");
var response = await Api.Client.PostAsync($"{GetEndpoint()}edits", content, cancellationToken).ConfigureAwait(false);
return await DeserializeResponseAsync(response, cancellationToken).ConfigureAwait(false);
}

/// <summary>
Expand All @@ -124,21 +120,24 @@ public async Task<IReadOnlyList<string>> CreateImageEditAsync(ImageEditRequest r
/// <param name="user">
/// A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse.
/// </param>
/// <returns></returns>
public async Task<IReadOnlyList<string>> CreateImageVariationAsync(string imagePath, int numberOfResults = 1, ImageSize size = ImageSize.Large, string user = null)
=> await CreateImageVariationAsync(new ImageVariationRequest(imagePath, numberOfResults, size, user)).ConfigureAwait(false);
/// <param name="cancellationToken">Optional, <see cref="CancellationToken"/>.</param>
/// <returns>A list of generated texture urls to download.</returns>
/// <exception cref="HttpRequestException"></exception>
public async Task<IReadOnlyList<string>> CreateImageVariationAsync(string imagePath, int numberOfResults = 1, ImageSize size = ImageSize.Large, string user = null, CancellationToken cancellationToken = default)
=> await CreateImageVariationAsync(new ImageVariationRequest(imagePath, numberOfResults, size, user), cancellationToken).ConfigureAwait(false);

/// <summary>
/// Creates a variation of a given image.
/// </summary>
/// <param name="request"><see cref="ImageVariationRequest"/></param>
/// <returns>An array of generated textures.</returns>
/// <param name="cancellationToken">Optional, <see cref="CancellationToken"/>.</param>
/// <returns>A list of generated texture urls to download.</returns>
/// <exception cref="HttpRequestException"></exception>
public async Task<IReadOnlyList<string>> CreateImageVariationAsync(ImageVariationRequest request)
public async Task<IReadOnlyList<string>> CreateImageVariationAsync(ImageVariationRequest request, CancellationToken cancellationToken = default)
{
using var content = new MultipartFormDataContent();
using var imageData = new MemoryStream();
await request.Image.CopyToAsync(imageData).ConfigureAwait(false);
await request.Image.CopyToAsync(imageData, cancellationToken).ConfigureAwait(false);
content.Add(new ByteArrayContent(imageData.ToArray()), "image", request.ImageName);
content.Add(new StringContent(request.Number.ToString()), "n");
content.Add(new StringContent(request.Size), "size");
Expand All @@ -150,19 +149,14 @@ public async Task<IReadOnlyList<string>> CreateImageVariationAsync(ImageVariatio

request.Dispose();

var response = await Api.Client.PostAsync($"{GetEndpoint()}variations", content).ConfigureAwait(false);
var responseAsString = await response.ReadAsStringAsync().ConfigureAwait(false);

return response.IsSuccessStatusCode
? await DeserializeResponseAsync(response).ConfigureAwait(false)
: throw new HttpRequestException(
$"{nameof(CreateImageVariationAsync)} Failed! HTTP status code: {response.StatusCode}. Response: {responseAsString}");
var response = await Api.Client.PostAsync($"{GetEndpoint()}variations", content, cancellationToken).ConfigureAwait(false);
return await DeserializeResponseAsync(response, cancellationToken).ConfigureAwait(false);
}

private async Task<IReadOnlyList<string>> DeserializeResponseAsync(HttpResponseMessage response)
private async Task<IReadOnlyList<string>> DeserializeResponseAsync(HttpResponseMessage response, CancellationToken cancellationToken = default)
{
Debug.Assert(response.IsSuccessStatusCode);
var resultAsString = await response.ReadAsStringAsync().ConfigureAwait(false);
var resultAsString = await response.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
var imagesResponse = JsonSerializer.Deserialize<ImagesResponse>(resultAsString, Api.JsonSerializationOptions);

if (imagesResponse?.Data == null || imagesResponse.Data.Count == 0)
Expand Down
19 changes: 6 additions & 13 deletions OpenAI-DotNet/Moderations/ModerationsEndpoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,23 +56,16 @@ public async Task<ModerationsResponse> CreateModerationAsync(ModerationsRequest
{
var jsonContent = JsonSerializer.Serialize(request, Api.JsonSerializationOptions);
var response = await Api.Client.PostAsync(GetEndpoint(), jsonContent.ToJsonStringContent()).ConfigureAwait(false);
var resultAsString = await response.ReadAsStringAsync().ConfigureAwait(false);
var moderationResponse = JsonSerializer.Deserialize<ModerationsResponse>(resultAsString, Api.JsonSerializationOptions);

if (response.IsSuccessStatusCode)
if (moderationResponse == null)
{
var resultAsString = await response.ReadAsStringAsync().ConfigureAwait(false);
var moderationResponse = JsonSerializer.Deserialize<ModerationsResponse>(resultAsString, Api.JsonSerializationOptions);

if (moderationResponse == null)
{
throw new HttpRequestException($"{nameof(CreateModerationAsync)} returned no results! HTTP status code: {response.StatusCode}. Response body: {resultAsString}");
}

moderationResponse.SetResponseData(response.Headers);

return moderationResponse;
throw new HttpRequestException($"{nameof(CreateModerationAsync)} returned no results! HTTP status code: {response.StatusCode}. Response body: {resultAsString}");
}

throw new HttpRequestException($"{nameof(CreateModerationAsync)} Failed! HTTP status code: {response.StatusCode}. Request body: {jsonContent}");
moderationResponse.SetResponseData(response.Headers);
return moderationResponse;
}
}
}
7 changes: 5 additions & 2 deletions OpenAI-DotNet/OpenAI-DotNet.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,15 @@ Based on OpenAI-API by OKGoDoIt (Roger Pincombe)</Description>
- `top_p` -&gt; `topP`
- Updated `CompletionRequest` to accept `IEnumerable&lt;string&gt;` values for `prompts` and `stopSequences`
- Refactored all endpoints to use new response validation extension
- Added `CancellationToken` to most endpoints that had long running operations</PackageReleaseNotes>
- Added `CancellationToken` to most endpoints that had long running operations
Bump version to 4.4.1
- hotfix to CompletionsEndpoint to use `IEnumerable&lt;string&gt;`
- hotfix to cleanup Images endpoints</PackageReleaseNotes>
<SignAssembly>false</SignAssembly>
<AssemblyOriginatorKeyFile>OpenAI-DotNet.pfx</AssemblyOriginatorKeyFile>
<DelaySign>true</DelaySign>
<PackageId>OpenAI-DotNet</PackageId>
<Version>4.4.0</Version>
<Version>4.4.1</Version>
<AssemblyVersion>4.3.0.0</AssemblyVersion>
<FileVersion>4.3.0.0</FileVersion>
<Company>RageAgainstThePixel</Company>
Expand Down

0 comments on commit d8e239d

Please sign in to comment.