Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support .NET Standard 2.0 #84

Closed
wants to merge 2 commits into from
Closed
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
4 changes: 2 additions & 2 deletions OpenAI.SDK/Extensions/HttpclientExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public static async Task<TResponse> PostAndReadAsAsync<TResponse>(this HttpClien
return await response.Content.ReadFromJsonAsync<TResponse>() ?? throw new InvalidOperationException();
}

public static HttpResponseMessage PostAsStreamAsync(this HttpClient client, string uri, object requestModel)
public static async Task<HttpResponseMessage> PostAsStreamAsync(this HttpClient client, string uri, object requestModel)
{
var settings = new JsonSerializerOptions()
{
Expand All @@ -35,7 +35,7 @@ public static HttpResponseMessage PostAsStreamAsync(this HttpClient client, stri
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("text/event-stream"));
request.Content = content;

return client.Send(request, HttpCompletionOption.ResponseHeadersRead);
return await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
}

public static async Task<TResponse> PostFileAndReadAsAsync<TResponse>(this HttpClient client, string uri, HttpContent content)
Expand Down
2 changes: 1 addition & 1 deletion OpenAI.SDK/Extensions/StringExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public static class StringExtensions
public static string RemoveIfStartWith(this string text, string search)
{
var pos = text.IndexOf(search, StringComparison.Ordinal);
return pos != 0 ? text : text[search.Length..];
return pos != 0 ? text : text.Substring(search.Length);
}
}
}
9 changes: 7 additions & 2 deletions OpenAI.SDK/Interfaces/ICompletionService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,19 @@ public interface ICompletionService
/// <returns></returns>
IAsyncEnumerable<CompletionCreateResponse> CreateCompletionAsStream(CompletionCreateRequest createCompletionModel, string? modelId = null);

}

public static class ICompletionServiceExtension
{
/// <summary>
/// Creates a new completion for the provided prompt and parameters
/// </summary>
/// <param name="service"></param>
/// <param name="createCompletionModel"></param>
/// <param name="modelId">The ID of the engine to use for this request</param>
/// <returns></returns>
Task<CompletionCreateResponse> Create(CompletionCreateRequest createCompletionModel, Models.Model modelId)
public static Task<CompletionCreateResponse> Create(this ICompletionService service,CompletionCreateRequest createCompletionModel, Models.Model modelId)
{
return CreateCompletion(createCompletionModel, modelId.EnumToString());
return service.CreateCompletion(createCompletionModel, modelId.EnumToString());
}
}
9 changes: 7 additions & 2 deletions OpenAI.SDK/Interfaces/IEditService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,19 @@ public interface IEditService
/// <returns></returns>
Task<EditCreateResponse> CreateEdit(EditCreateRequest editCreate, string? engineId = null);

}

public static class IEditServiceExtension
{
/// <summary>
/// Creates a new edit for the provided input, instruction, and parameters
/// </summary>
/// <param name="service"></param>
/// <param name="editCreate"></param>
/// <param name="engineId">The ID of the engine to use for this request</param>
/// <returns></returns>
Task<EditCreateResponse> Edit(EditCreateRequest editCreate, Models.Model engineId)
public static Task<EditCreateResponse> Edit(this IEditService service, EditCreateRequest editCreate, Models.Model engineId)
{
return CreateEdit(editCreate, engineId.EnumToString());
return service.CreateEdit(editCreate, engineId.EnumToString());
}
}
33 changes: 18 additions & 15 deletions OpenAI.SDK/Interfaces/IFileService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,21 +32,6 @@ public interface IFileService
/// <returns></returns>
Task<FileUploadResponse> UploadFile(string purpose, byte[] file, string fileName);

Task<FileUploadResponse> FileUpload(string purpose, Stream file, string fileName)
{
return UploadFile(purpose, file.ToByteArray(), fileName);
}

Task<FileUploadResponse> FileUpload(UploadFilePurposes.UploadFilePurpose purpose, Stream file, string fileName)
{
return UploadFile(purpose.EnumToString(), file.ToByteArray(), fileName);
}

Task<FileUploadResponse> FileUpload(UploadFilePurposes.UploadFilePurpose purpose, byte[] file, string fileName)
{
return UploadFile(purpose.EnumToString(), file, fileName);
}

/// <summary>
/// Delete a file.
/// </summary>
Expand All @@ -67,4 +52,22 @@ Task<FileUploadResponse> FileUpload(UploadFilePurposes.UploadFilePurpose purpose
/// <param name="fileId">The ID of the file to use for this request</param>
/// <returns></returns>
Task RetrieveFileContent(string fileId);
}

public static class IFileServiceExtension
{
public static Task<FileUploadResponse> FileUpload(this IFileService service,string purpose, Stream file, string fileName)
{
return service.UploadFile(purpose, file.ToByteArray(), fileName);
}

public static Task<FileUploadResponse> FileUpload(this IFileService service, UploadFilePurposes.UploadFilePurpose purpose, Stream file, string fileName)
{
return service.UploadFile(purpose.EnumToString(), file.ToByteArray(), fileName);
}

public static Task<FileUploadResponse> FileUpload(this IFileService service, UploadFilePurposes.UploadFilePurpose purpose, byte[] file, string fileName)
{
return service.UploadFile(purpose.EnumToString(), file, fileName);
}
}
24 changes: 14 additions & 10 deletions OpenAI.SDK/Interfaces/IImageService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,6 @@ public interface IImageService
/// <returns></returns>
Task<ImageCreateResponse> CreateImage(ImageCreateRequest imageCreate);

/// <summary>
/// Creates an image given a prompt.
/// </summary>
/// <param name="prompt"></param>
/// <returns></returns>
Task<ImageCreateResponse> CreateImage(string prompt)
{
return CreateImage(new ImageCreateRequest(prompt));
}

/// <summary>
/// Creates an edited or extended image given an original image and a prompt.
/// </summary>
Expand All @@ -39,4 +29,18 @@ Task<ImageCreateResponse> CreateImage(string prompt)
/// <param name="imageEditCreateRequest"></param>
/// <returns></returns>
Task<ImageCreateResponse> CreateImageVariation(ImageVariationCreateRequest imageEditCreateRequest);
}

public static class IImageServiceExtension
{
/// <summary>
/// Creates an image given a prompt.
/// </summary>
/// <param name="service"></param>
/// <param name="prompt"></param>
/// <returns></returns>
public static Task<ImageCreateResponse> CreateImage(this IImageService service, string prompt)
{
return service.CreateImage(new ImageCreateRequest(prompt));
}
}
9 changes: 7 additions & 2 deletions OpenAI.SDK/Interfaces/IModerationService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,14 @@ public interface IModerationService
/// <returns></returns>
Task<CreateModerationResponse> CreateModeration(CreateModerationRequest createModerationRequest);

}

public static class IModerationServiceExtension
{
/// <summary>
/// Classifies if text violates OpenAI's Content Policy
/// </summary>
/// <param name="service"></param>
/// <param name="input">The input text to classify</param>
/// <param name="model">
/// Two content moderations models are available: text-moderation-stable and text-moderation-latest.
Expand All @@ -27,9 +32,9 @@ public interface IModerationService
/// the model. Accuracy of text-moderation-stable may be slightly lower than for text-moderation-latest.
/// </param>
/// <returns></returns>
Task<CreateModerationResponse> CreateModeration(string input, string? model = null)
public static Task<CreateModerationResponse> CreateModeration(this IModerationService service, string input, string? model = null)
{
return CreateModeration(new CreateModerationRequest()
return service.CreateModeration(new CreateModerationRequest()
{
Input = input,
Model = model
Expand Down
4 changes: 2 additions & 2 deletions OpenAI.SDK/Managers/OpenAICompletions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ public async IAsyncEnumerable<CompletionCreateResponse> CreateCompletionAsStream
// Send the request to the CompletionCreate endpoint
createCompletionRequest.ProcessModelId(modelId, _defaultModelId);

using var response = _httpClient.PostAsStreamAsync(_endpointProvider.CompletionCreate(), createCompletionRequest);
await using var stream = await response.Content.ReadAsStreamAsync();
using var response = await _httpClient.PostAsStreamAsync(_endpointProvider.CompletionCreate(), createCompletionRequest);
using var stream = await response.Content.ReadAsStreamAsync();
using var reader = new StreamReader(stream);
// Continuously read the stream until the end of it
while (!reader.EndOfStream)
Expand Down
10 changes: 7 additions & 3 deletions OpenAI.SDK/OpenAI.GPT3.csproj
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFrameworks>net6.0;netstandard2.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

<LangVersion>Latest</LangVersion>

<Copyright>Betalgo Up Ltd.</Copyright>
<PackageProjectUrl>https://openai.com/</PackageProjectUrl>
<PackageIcon>OpenAI-Betalgo.png</PackageIcon>
Expand Down Expand Up @@ -37,5 +38,8 @@
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="6.0.0" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="System.Net.Http.Json" Version="7.0.0" />
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
</ItemGroup>
</Project>
18 changes: 18 additions & 0 deletions OpenAI.Tests/ExtensionTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using NUnit.Framework;
using OpenAI.GPT3.Extensions;
using System;

namespace OpenAI.Tests
{
public class ExtensionTests
{
[Test]
public void Extension_RemoveIfStartWith()
{
var text = "data: text";
var result = text.RemoveIfStartWith("data: ");
Console.WriteLine(result);
Assert.That(result, Is.EqualTo("text"));
}
}
}
21 changes: 21 additions & 0 deletions OpenAI.Tests/OpenAI.Tests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net6.0;net47</TargetFrameworks>
<IsPackable>false</IsPackable>
<LangVersion>Latest</LangVersion>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
<PackageReference Include="NUnit.Analyzers" Version="3.3.0" />
<PackageReference Include="coverlet.collector" Version="3.1.2" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\OpenAI.SDK\OpenAI.GPT3.csproj" />
</ItemGroup>

</Project>
99 changes: 99 additions & 0 deletions OpenAI.Tests/OpenAITests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
using NUnit.Framework;
using OpenAI.GPT3;
using OpenAI.GPT3.Managers;
using OpenAI.GPT3.ObjectModels;
using OpenAI.GPT3.ObjectModels.RequestModels;
using System;
using System.Linq;
using System.Threading.Tasks;

namespace OpenAI.Tests
{
public class OpenAITests
{
public OpenAIService openAiService { get; set; }
[OneTimeSetUp]
public void Setup()
{
openAiService = new OpenAIService(new OpenAiOptions()
{
ApiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY")
});
}

[Test]
public async Task OpenAI_CreateCompletion()
{
var completionResult = await openAiService.Completions.CreateCompletion(new CompletionCreateRequest()
{
Prompt = "Once upon a time",
Model = Models.TextDavinciV3
});

if (completionResult.Successful)
{
Console.WriteLine(completionResult.Choices.FirstOrDefault());
}
else
{
if (completionResult.Error == null)
{
throw new Exception("Unknown Error");
}
Console.WriteLine($"{completionResult.Error.Code}: {completionResult.Error.Message}");
}

Assert.IsTrue(completionResult.Successful);
}

[Test]
public async Task OpenAI_CreateImage()
{
var imageResult = await openAiService.Image.CreateImage(new ImageCreateRequest
{
Prompt = "Laser cat eyes",
N = 2,
Size = StaticValues.ImageStatics.Size.Size256,
ResponseFormat = StaticValues.ImageStatics.ResponseFormat.Url,
User = "TestUser"
});


if (imageResult.Successful)
{
Console.WriteLine(string.Join("\n", imageResult.Results.Select(r => r.Url)));
}

Assert.IsTrue(imageResult.Successful);
Assert.That(imageResult.Results.Count, Is.EqualTo(2));
}

[Test]
public async Task OpenIA_CreateCompletionAsStream()
{
var completionResult = openAiService.Completions.CreateCompletionAsStream(new CompletionCreateRequest()
{
Prompt = "Once upon a time",
MaxTokens = 50
}, Models.Davinci);

await foreach (var completion in completionResult)
{
if (completion.Successful)
{
Console.Write(completion.Choices.FirstOrDefault()?.Text);
}
else
{
if (completion.Error == null)
{
throw new Exception("Unknown Error");
}

Console.WriteLine($"{completion.Error.Code}: {completion.Error.Message}");
}
Assert.IsTrue(completion.Successful);
}
}
}
}
7 changes: 7 additions & 0 deletions OpenAI.sln
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Documentation", "Documentat
Readme.md = Readme.md
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenAI.Tests", "OpenAI.Tests\OpenAI.Tests.csproj", "{EBCB7783-B391-4064-A6ED-22CE333FAEC1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -32,13 +34,18 @@ Global
{29A85621-10FF-4CA4-9555-D73DCA0D776E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{29A85621-10FF-4CA4-9555-D73DCA0D776E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{29A85621-10FF-4CA4-9555-D73DCA0D776E}.Release|Any CPU.Build.0 = Release|Any CPU
{EBCB7783-B391-4064-A6ED-22CE333FAEC1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EBCB7783-B391-4064-A6ED-22CE333FAEC1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EBCB7783-B391-4064-A6ED-22CE333FAEC1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EBCB7783-B391-4064-A6ED-22CE333FAEC1}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{780F7F1F-45FC-4832-9E00-5BE6F32E7271} = {B614DB37-E04E-454F-8C99-EF69BC503745}
{29A85621-10FF-4CA4-9555-D73DCA0D776E} = {ADCA5A92-4E8E-4574-8E5F-DDE4C03A4F49}
{EBCB7783-B391-4064-A6ED-22CE333FAEC1} = {C0FFCEC4-22E9-423B-B10B-DAF24446FA49}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {0A5FEB47-118A-4556-9183-C427C3082E8E}
Expand Down