diff --git a/OpenAI.Playground/Program.cs b/OpenAI.Playground/Program.cs index 5f5494d4..6a3a655c 100644 --- a/OpenAI.Playground/Program.cs +++ b/OpenAI.Playground/Program.cs @@ -30,6 +30,7 @@ await ModelTestHelper.FetchModelsTest(sdk); //await CompletionTestHelper.RunSimpleCompletionTest(sdk); +//await EmbeddingTestHelper.RunSimpleEmbeddingTest(sdk); //await FileTestHelper.RunSimpleFileTest(sdk); ////await FineTuningTestHelper.CleanUpAllFineTunings(sdk); //!!!!! will delete all fine-tunings //await FineTuningTestHelper.RunCaseStudyIsTheModelMakingUntrueStatements(sdk); diff --git a/OpenAI.Playground/TestHelpers/EmbeddingTestHelper.cs b/OpenAI.Playground/TestHelpers/EmbeddingTestHelper.cs new file mode 100644 index 00000000..ae396683 --- /dev/null +++ b/OpenAI.Playground/TestHelpers/EmbeddingTestHelper.cs @@ -0,0 +1,47 @@ +using OpenAI.GPT3.Interfaces; +using OpenAI.GPT3.ObjectModels; +using OpenAI.GPT3.ObjectModels.RequestModels; +using System.ComponentModel.DataAnnotations; +using System.Numerics; + +namespace OpenAI.Playground.TestHelpers +{ + internal static class EmbeddingTestHelper + { + public static async Task RunSimpleEmbeddingTest(IOpenAIService sdk) + { + ConsoleExtensions.WriteLine("Simple Embedding test is starting:", ConsoleColor.Cyan); + + try + { + ConsoleExtensions.WriteLine("Embedding Test:", ConsoleColor.DarkCyan); + var embeddingResult = await sdk.Embeddings.Create(new EmbeddingCreateRequest() + { + Input = new List { "The quick brown fox jumped over the lazy dog." }, + Model = Models.TextSearchAdaDocV1 + }); + + if (embeddingResult.Successful) + { + Console.WriteLine(embeddingResult.Data.FirstOrDefault()); + } + else + { + if (embeddingResult.Error == null) + { + throw new Exception("Unknown Error"); + } + + Console.WriteLine($"{embeddingResult.Error.Code}: {embeddingResult.Error.Message}"); + } + + Console.WriteLine(embeddingResult.Data.FirstOrDefault()); + } + catch (Exception e) + { + Console.WriteLine(e); + throw; + } + } + } +} \ No newline at end of file diff --git a/OpenAI.SDK/Interfaces/IEmbedding.cs b/OpenAI.SDK/Interfaces/IEmbedding.cs new file mode 100644 index 00000000..1742abba --- /dev/null +++ b/OpenAI.SDK/Interfaces/IEmbedding.cs @@ -0,0 +1,17 @@ +using OpenAI.GPT3.ObjectModels.RequestModels; +using OpenAI.GPT3.ObjectModels.ResponseModels; + +namespace OpenAI.GPT3.Interfaces; + +/// +/// Creates an embedding vector representing the input text. +/// +public interface IEmbedding +{ + /// + /// Creates a new embedding for the provided input and parameters. + /// + /// + /// + Task Create(EmbeddingCreateRequest createEmbeddingModel); +} \ No newline at end of file diff --git a/OpenAI.SDK/Interfaces/IFile.cs b/OpenAI.SDK/Interfaces/IFile.cs index 71d4d6b0..802e1b6b 100644 --- a/OpenAI.SDK/Interfaces/IFile.cs +++ b/OpenAI.SDK/Interfaces/IFile.cs @@ -26,7 +26,8 @@ public interface IFile /// Name of file /// /// The intended purpose of the uploaded documents. - /// Use "fine-tune" for Fine-tuning. This allows us to validate the format of the uploaded file. + /// Use "fine-tune" for Fine-tuning. This allows us + /// to validate the format of the uploaded file. /// /// Task FileUpload(string purpose, byte[] file, string fileName); diff --git a/OpenAI.SDK/Interfaces/IOpenAISdk.cs b/OpenAI.SDK/Interfaces/IOpenAISdk.cs index 37131275..f50dfa73 100644 --- a/OpenAI.SDK/Interfaces/IOpenAISdk.cs +++ b/OpenAI.SDK/Interfaces/IOpenAISdk.cs @@ -15,6 +15,11 @@ public interface IOpenAIService /// public ICompletion Completions { get; } + /// + /// Creates an embedding vector representing the input text. + /// + public IEmbedding Embeddings { get; } + /// /// Files are used to upload documents that can be used across features like /// diff --git a/OpenAI.SDK/Managers/OpenAIEmbedding.cs b/OpenAI.SDK/Managers/OpenAIEmbedding.cs new file mode 100644 index 00000000..d91814e1 --- /dev/null +++ b/OpenAI.SDK/Managers/OpenAIEmbedding.cs @@ -0,0 +1,14 @@ +using OpenAI.GPT3.Extensions; +using OpenAI.GPT3.Interfaces; +using OpenAI.GPT3.ObjectModels.RequestModels; +using OpenAI.GPT3.ObjectModels.ResponseModels; + +namespace OpenAI.GPT3.Managers; + +public partial class OpenAIService : IEmbedding +{ + public async Task Create(EmbeddingCreateRequest createEmbeddingRequest) + { + return await _httpClient.PostAndReadAsAsync(_endpointProvider.CreateEmbedding(), createEmbeddingRequest); + } +} \ No newline at end of file diff --git a/OpenAI.SDK/Managers/OpenAIService.cs b/OpenAI.SDK/Managers/OpenAIService.cs index 88eef1fb..62de670b 100644 --- a/OpenAI.SDK/Managers/OpenAIService.cs +++ b/OpenAI.SDK/Managers/OpenAIService.cs @@ -51,6 +51,7 @@ public OpenAIService(OpenAiOptions settings, HttpClient? httpClient = null) public IModel Models => this; public ICompletion Completions => this; + public IEmbedding Embeddings => this; public IFile Files => this; public IFineTune FineTunes => this; diff --git a/OpenAI.SDK/ObjectModels/Models.cs b/OpenAI.SDK/ObjectModels/Models.cs index 5a7ef020..fa42ce50 100644 --- a/OpenAI.SDK/ObjectModels/Models.cs +++ b/OpenAI.SDK/ObjectModels/Models.cs @@ -30,6 +30,27 @@ public enum Model CurieSimilarityFast, + TextSimilarityAdaV1, + TextSimilarityBabbageV1, + TextSimilarityCurieV1, + TextSimilarityDavinciV1, + + TextSearchAdaDocV1, + TextSearchBabbageDocV1, + TextSearchCurieDocV1, + TextSearchDavinciDocV1, + + TextSearchAdaQueryV1, + TextSearchBabbageQueryV1, + TextSearchCurieQueryV1, + TextSearchDavinciQueryV1, + + CodeSearchAdaCodeV1, + CodeSearchBabbageCodeV1, + + CodeSearchAdaTextV1, + CodeSearchBabbageTextV1, + CodeDavinciV1, CodeCushmanV1, @@ -41,6 +62,11 @@ public enum Subject Text, InstructBeta, SimilarityFast, + TextSimilarity, + TextSearchDocument, + TextSearchQuery, + CodeSearchCode, + CodeSearchText, Code } @@ -64,6 +90,26 @@ public enum Subject public static string CodeCushmanV1 => ModelNameBuilder(BaseEngine.Cushman, Subject.Code, "001"); public static string CodeDavinciV2 => ModelNameBuilder(BaseEngine.Davinci, Subject.Code, "002"); + public static string TextSimilarityAdaV1 => ModelNameBuilder(BaseEngine.Ada, Subject.TextSimilarity, "001"); + public static string TextSimilarityBabbageV1 => ModelNameBuilder(BaseEngine.Babbage, Subject.TextSimilarity, "001"); + public static string TextSimilarityCurieV1 => ModelNameBuilder(BaseEngine.Curie, Subject.TextSimilarity, "001"); + public static string TextSimilarityDavinciV1 => ModelNameBuilder(BaseEngine.Davinci, Subject.TextSimilarity, "001"); + + public static string TextSearchAdaDocV1 => ModelNameBuilder(BaseEngine.Ada, Subject.TextSearchDocument, "001"); + public static string TextSearchBabbageDocV1 => ModelNameBuilder(BaseEngine.Babbage, Subject.TextSearchDocument, "001"); + public static string TextSearchCurieDocV1 => ModelNameBuilder(BaseEngine.Curie, Subject.TextSearchDocument, "001"); + public static string TextSearchDavinciDocV1 => ModelNameBuilder(BaseEngine.Davinci, Subject.TextSearchDocument, "001"); + public static string TextSearchAdaQueryV1 => ModelNameBuilder(BaseEngine.Ada, Subject.TextSearchQuery, "001"); + public static string TextSearchBabbageQueryV1 => ModelNameBuilder(BaseEngine.Babbage, Subject.TextSearchQuery, "001"); + public static string TextSearchCurieQueryV1 => ModelNameBuilder(BaseEngine.Curie, Subject.TextSearchQuery, "001"); + public static string TextSearchDavinciQueryV1 => ModelNameBuilder(BaseEngine.Davinci, Subject.TextSearchQuery, "001"); + + public static string CodeSearchAdaCodeV1 => ModelNameBuilder(BaseEngine.Ada, Subject.CodeSearchCode, "001"); + public static string CodeSearchBabbageCodeV1 => ModelNameBuilder(BaseEngine.Babbage, Subject.CodeSearchCode, "001"); + public static string CodeSearchAdaTextV1 => ModelNameBuilder(BaseEngine.Ada, Subject.CodeSearchText, "001"); + public static string CodeSearchBabbageTextV1 => ModelNameBuilder(BaseEngine.Babbage, Subject.CodeSearchText, "001"); + + /// /// This method does not guarantee returned model exists. /// @@ -73,23 +119,12 @@ public enum Subject /// public static string ModelNameBuilder(this BaseEngine baseEngine, Subject? subject = null, string? version = null) { - return ModelNameBuilder(baseEngine.EnumToString(), subject?.EnumToString(), version); + return ModelNameBuilder(baseEngine.EnumToString(), subject?.EnumToString(baseEngine.EnumToString()), version); } public static string ModelNameBuilder(string baseEngine, string? subject, string? version) { - var response = baseEngine; - if (!string.IsNullOrEmpty(subject)) - { - if (subject == Subject.InstructBeta.EnumToString() || subject == Subject.SimilarityFast.EnumToString()) - { - response = $"{baseEngine}-{subject}"; - } - else - { - response = $"{subject}-{baseEngine}"; - } - } + var response = subject ?? $"{baseEngine}"; if (!string.IsNullOrEmpty(version)) { @@ -119,6 +154,22 @@ public static string EnumToString(this Model engine) Model.CodeDavinciV1 => CodeDavinciV1, Model.CodeCushmanV1 => CodeCushmanV1, Model.CodeDavinciV2 => CodeDavinciV2, + Model.TextSimilarityAdaV1 => TextSimilarityAdaV1, + Model.TextSimilarityBabbageV1 => TextSimilarityBabbageV1, + Model.TextSimilarityCurieV1 => TextSimilarityCurieV1, + Model.TextSimilarityDavinciV1 => TextSimilarityDavinciV1, + Model.TextSearchAdaDocV1 => TextSearchAdaDocV1, + Model.TextSearchBabbageDocV1 => TextSearchBabbageDocV1, + Model.TextSearchCurieDocV1 => TextSearchCurieDocV1, + Model.TextSearchDavinciDocV1 => TextSearchDavinciDocV1, + Model.TextSearchAdaQueryV1 => TextSearchAdaQueryV1, + Model.TextSearchBabbageQueryV1 => TextSearchBabbageQueryV1, + Model.TextSearchCurieQueryV1 => TextSearchCurieQueryV1, + Model.TextSearchDavinciQueryV1 => TextSearchDavinciQueryV1, + Model.CodeSearchAdaCodeV1 => CodeSearchAdaCodeV1, + Model.CodeSearchBabbageCodeV1 => CodeSearchBabbageCodeV1, + Model.CodeSearchAdaTextV1 => CodeSearchAdaTextV1, + Model.CodeSearchBabbageTextV1 => CodeSearchBabbageTextV1, _ => throw new ArgumentOutOfRangeException(nameof(engine), engine, null) }; } @@ -136,16 +187,22 @@ private static string EnumToString(this BaseEngine baseEngine) }; } - private static string EnumToString(this Subject subject) + public static string EnumToString(this Subject subject, string baseEngine) { - return subject switch + return string.Format(subject switch { - Subject.Text => "text", - Subject.InstructBeta => "instruct-beta", - Subject.SimilarityFast => "similarity-fast", - Subject.Code => "code", + //{0}-{1} + Subject.Text => "text-{0}", + Subject.InstructBeta => "{0}-instruct-beta", + Subject.SimilarityFast => "{0}-similarity-fast", + Subject.TextSimilarity => "text-similarity-{0}", + Subject.TextSearchDocument => "text-search-{0}-doc", + Subject.TextSearchQuery => "text-search-{0}-query", + Subject.CodeSearchCode => "code-search-{0}-code", + Subject.CodeSearchText => "code-search-{0}-text", + Subject.Code => "code-{0}", _ => throw new ArgumentOutOfRangeException(nameof(subject), subject, null) - }; + }, baseEngine); } } } \ No newline at end of file diff --git a/OpenAI.SDK/ObjectModels/RequestModels/EmbeddingCreateRequest.cs b/OpenAI.SDK/ObjectModels/RequestModels/EmbeddingCreateRequest.cs new file mode 100644 index 00000000..b4c53452 --- /dev/null +++ b/OpenAI.SDK/ObjectModels/RequestModels/EmbeddingCreateRequest.cs @@ -0,0 +1,37 @@ +using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; +using OpenAI.GPT3.Interfaces; + +namespace OpenAI.GPT3.ObjectModels.RequestModels +{ + //TODO Update Usage of link (see cref) + //TODO add model validation + //TODO check what is string or array for prompt,.. + public record EmbeddingCreateRequest : IModelValidate + { + /// + /// Input text to get embeddings for, encoded as a string or array of tokens. To get embeddings for multiple inputs + /// in a single request, pass an array of strings or array of token arrays. Each input must not exceed 2048 tokens in + /// length. + /// Unless your are embedding code, we suggest replacing newlines (`\n`) in your input with a single space, as we have + /// observed inferior results when newlines are present. + /// + /// + [JsonPropertyName("input")] + public List? Input { get; set; } + + + /// + /// ID of the model to use. You can use the [List models](/docs/api-reference/models/list) API to see all of your + /// available models, or see our [Model overview](/docs/models/overview) for descriptions of them. + /// + /// + [JsonPropertyName("model")] + public string? Model { get; set; } + + public IEnumerable Validate() + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/OpenAI.SDK/ObjectModels/ResponseModels/EmbeddingCreateResponse.cs b/OpenAI.SDK/ObjectModels/ResponseModels/EmbeddingCreateResponse.cs new file mode 100644 index 00000000..f8b0e3a6 --- /dev/null +++ b/OpenAI.SDK/ObjectModels/ResponseModels/EmbeddingCreateResponse.cs @@ -0,0 +1,18 @@ +using System.Text.Json.Serialization; + +namespace OpenAI.GPT3.ObjectModels.ResponseModels +{ + public record EmbeddingCreateResponse : BaseResponse + { + [JsonPropertyName("model")] public string Model { get; set; } + + [JsonPropertyName("data")] public List Data { get; set; } + } + + public record EmbeddingResponse + { + [JsonPropertyName("index")] public int? Index { get; set; } + + [JsonPropertyName("embedding")] public List Embedding { get; set; } + } +} \ No newline at end of file diff --git a/OpenAI.SDK/OpenAI.GPT3.csproj b/OpenAI.SDK/OpenAI.GPT3.csproj index fff0a6c5..f0adefb9 100644 --- a/OpenAI.SDK/OpenAI.GPT3.csproj +++ b/OpenAI.SDK/OpenAI.GPT3.csproj @@ -9,7 +9,7 @@ https://openai.com/ OpenAI-Logo.png true - 6.2.0 + 6.3.0 Tolga Kayhan, Betalgo Betalgo Up Ltd. OpenAI GPT-3 dotnet SDK diff --git a/OpenAI.SDK/OpenAiEndpointProvider.cs b/OpenAI.SDK/OpenAiEndpointProvider.cs index ae0e0e58..2a685e11 100644 --- a/OpenAI.SDK/OpenAiEndpointProvider.cs +++ b/OpenAI.SDK/OpenAiEndpointProvider.cs @@ -16,6 +16,7 @@ internal interface IOpenAiEndpointProvider string FineTuneListEvents(string fineTuneId); string FineTuneDelete(string fineTuneId); string FineTuneCompletions(); + string CreateEmbedding(); } internal class OpenAiEndpointProvider : IOpenAiEndpointProvider @@ -97,6 +98,11 @@ public string FineTuneCompletions() return $"/{_apiVersion}/completions"; } + public string CreateEmbedding() + { + return $"/{_apiVersion}/embeddings"; + } + private string Files() { return $"/{_apiVersion}/files"; diff --git a/Readme.md b/Readme.md index cf943230..42d785d5 100644 --- a/Readme.md +++ b/Readme.md @@ -5,18 +5,29 @@ ``` Install-Package Betalgo.OpenAI.GPT3 ``` -For changelogs please go end of the document. +For changelogs please go to end of the document. Unofficial - Dotnet SDK for OpenAI GTP-3. GPT-3 doesn't have any official .Net SDK. +## Features +- [x] Models +- [x] Completions +- [ ] Edit +- [x] Mars +- [x] Embeddings +- [x] Files +- [x] Fine-tunes +- [ ] Moderation + + *I was building an SDK for myself then I decide to share it, I hope it will be useful for you. I haven't maintained any open source projects before. Any help would be much appreciated. I am open to suggestions If you would like to contribute somehow.* I will be using the latest libraries all the time. Also, next releasing will include breaking changes frequently *(as I mentioned before I was building the SDK for myself. Unfortunately I do not have time to plan these changes and support lower version apps)*. So please be aware of that before starting to use the library. As you can guess I do not accept any damage caused by use of the library. You are always free to use other libraries or OpenAI Web-API. -Visit https://openai.com/ to get your API key. +Visit https://openai.com/ to get your API key. Also documentation with more detail is avaliable there. ## Sample Usages The repository includes one sample project already **"OpenAI.Playground"** You can check playground project to see how I was testing it while I was developing the library. Be carefull while playing with it. Some test methods will delete your files or fine tunings. **I would suggest to use different account than your main account while you use playground.** @@ -94,4 +105,9 @@ I couldn't find enough time to test all the methods or improve the documentation * To support fast engine name changing I have created a new Method, `Models.ModelNameBuilder()` you may consider using it. ### 6.2.0 * Removed deprecated Answers, Classifications, and Search endpoints https://community.openai.com/t/answers-classification-search-endpoint-deprecation/18532. They will be still avaliable until December at web-API. If you still need them please do not update to this version. +* Code clean-up +### 6.3.0 +* Thanks to @c-d and @sarilouis for contributions on this version. +* Now we support Embedding endpoint. Thanks to @sarilouis +* Bug fixes and updates for Models * Code clean-up \ No newline at end of file