From d4bad7510b44b27baf5c76d8cc66920bd756594a Mon Sep 17 00:00:00 2001 From: Sari Louis Date: Sat, 20 Aug 2022 02:09:09 -0400 Subject: [PATCH 1/5] Implement support for Embeddings API --- OpenAI.SDK/Interfaces/IEmbedding.cs | 18 +++ OpenAI.SDK/Interfaces/IOpenAISdk.cs | 5 + OpenAI.SDK/Managers/OpenAIEmbedding.cs | 14 +++ OpenAI.SDK/Managers/OpenAIService.cs | 1 + OpenAI.SDK/ObjectModels/Models.cs | 104 ++++++++++++----- .../RequestModels/EmbeddingCreateRequest.cs | 38 +++++++ .../ResponseModels/EmbeddingCreateResponse.cs | 18 +++ .../ObjectModels/SingleOrArrayConverter.cs | 106 ++++++++++++++++++ OpenAI.SDK/OpenAiEndpointProvider.cs | 6 + 9 files changed, 284 insertions(+), 26 deletions(-) create mode 100644 OpenAI.SDK/Interfaces/IEmbedding.cs create mode 100644 OpenAI.SDK/Managers/OpenAIEmbedding.cs create mode 100644 OpenAI.SDK/ObjectModels/RequestModels/EmbeddingCreateRequest.cs create mode 100644 OpenAI.SDK/ObjectModels/ResponseModels/EmbeddingCreateResponse.cs create mode 100644 OpenAI.SDK/ObjectModels/SingleOrArrayConverter.cs diff --git a/OpenAI.SDK/Interfaces/IEmbedding.cs b/OpenAI.SDK/Interfaces/IEmbedding.cs new file mode 100644 index 00000000..c656794e --- /dev/null +++ b/OpenAI.SDK/Interfaces/IEmbedding.cs @@ -0,0 +1,18 @@ +using OpenAI.GPT3.ObjectModels; +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/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..e39acd87 100644 --- a/OpenAI.SDK/ObjectModels/Models.cs +++ b/OpenAI.SDK/ObjectModels/Models.cs @@ -30,17 +30,43 @@ public enum Model CurieSimilarityFast, + TextSimilarityAdaV1, + TextSimilarityBabbageV1, + TextSimilarityCurieV1, + TextSimilarityDavinciV1, + + TextSearchAdaDocV1, + TextSearchBabbageDocV1, + TextSearchCurieDocV1, + TextSearchDavinciDocV1, + + TextSearchAdaQueryV1, + TextSearchBabbageQueryV1, + TextSearchCurieQueryV1, + TextSearchDavinciQueryV1, + + CodeSearchAdaCodeV1, + CodeSearchBabbageCodeV1, + + CodeSearchAdaTextV1, + CodeSearchBabbageTextV1, + CodeDavinciV1, CodeCushmanV1, CodeDavinciV2 } - public enum Subject + private 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. /// @@ -71,25 +117,9 @@ public enum Subject /// /// /// - public static string ModelNameBuilder(this BaseEngine baseEngine, Subject? subject = null, string? version = null) + private static string ModelNameBuilder(this BaseEngine baseEngine, Subject? subject = null, string? version = null) { - return ModelNameBuilder(baseEngine.EnumToString(), subject?.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?.EnumToString(baseEngine) ?? $"{baseEngine.EnumToString()}"; if (!string.IsNullOrEmpty(version)) { @@ -119,6 +149,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 +182,22 @@ private static string EnumToString(this BaseEngine baseEngine) }; } - private static string EnumToString(this Subject subject) + private static string EnumToString(this Subject subject, BaseEngine 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.EnumToString()); } } } \ 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..99415ac2 --- /dev/null +++ b/OpenAI.SDK/ObjectModels/RequestModels/EmbeddingCreateRequest.cs @@ -0,0 +1,38 @@ +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")] + [JsonConverter(typeof(SingleOrArrayConverter))] + 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/ObjectModels/SingleOrArrayConverter.cs b/OpenAI.SDK/ObjectModels/SingleOrArrayConverter.cs new file mode 100644 index 00000000..03a4954e --- /dev/null +++ b/OpenAI.SDK/ObjectModels/SingleOrArrayConverter.cs @@ -0,0 +1,106 @@ +using System.Collections; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace OpenAI.GPT3.ObjectModels +{ + public class SingleOrArrayConverter : SingleOrArrayConverter, TItem> + { + public SingleOrArrayConverter() : this(true) { } + public SingleOrArrayConverter(bool canWrite) : base(canWrite) { } + } + + public class SingleOrArrayConverterFactory : JsonConverterFactory + { + public bool CanWrite { get; } + + public SingleOrArrayConverterFactory() : this(true) { } + + public SingleOrArrayConverterFactory(bool canWrite) => CanWrite = canWrite; + + public override bool CanConvert(Type typeToConvert) + { + var itemType = GetItemType(typeToConvert); + + if (itemType == null) + return false; + if (itemType != typeof(string) && typeof(IEnumerable).IsAssignableFrom(itemType)) + return false; + + if (typeToConvert.GetConstructor(Type.EmptyTypes) == null || typeToConvert.IsValueType) + return false; + + return true; + } + + public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) + { + var itemType = GetItemType(typeToConvert); + var converterType = typeof(SingleOrArrayConverter<,>).MakeGenericType(typeToConvert, itemType); + return (JsonConverter)Activator.CreateInstance(converterType, new object[] { CanWrite }); + } + + static Type GetItemType(Type type) + { + // Quick reject for performance + if (type.IsPrimitive || type.IsArray || type == typeof(string)) + return null; + while (type != null) + { + if (type.IsGenericType) + { + var genType = type.GetGenericTypeDefinition(); + if (genType == typeof(List<>)) + return type.GetGenericArguments()[0]; + // Add here other generic collection types as required, e.g. HashSet<> or ObservableCollection<> or etc. + } + type = type.BaseType; + } + return null; + } + } + + public class SingleOrArrayConverter : JsonConverter where TCollection : class, ICollection, new() + { + public SingleOrArrayConverter() : this(true) { } + public SingleOrArrayConverter(bool canWrite) => CanWrite = canWrite; + + public bool CanWrite { get; } + + public override TCollection Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + switch (reader.TokenType) + { + case JsonTokenType.Null: + return null; + case JsonTokenType.StartArray: + var list = new TCollection(); + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndArray) + break; + list.Add(JsonSerializer.Deserialize(ref reader, options)); + } + return list; + default: + return new TCollection { JsonSerializer.Deserialize(ref reader, options) }; + } + } + + public override void Write(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options) + { + if (CanWrite && value.Count == 1) + { + JsonSerializer.Serialize(writer, value.First(), options); + } + else + { + writer.WriteStartArray(); + foreach (var item in value) + JsonSerializer.Serialize(writer, item, options); + writer.WriteEndArray(); + } + } + } +} + 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"; From a5fcdbd12a87acac0257717dee89e7d62a327396 Mon Sep 17 00:00:00 2001 From: Sari Louis Date: Sat, 20 Aug 2022 16:23:55 -0400 Subject: [PATCH 2/5] Implement support for Embeddings API and associated Playground helper --- OpenAI.Playground/Program.cs | 1 + .../TestHelpers/EmbeddingTestHelper.cs | 47 +++++++++++++++++++ OpenAI.SDK/ObjectModels/Models.cs | 15 ++++-- 3 files changed, 58 insertions(+), 5 deletions(-) create mode 100644 OpenAI.Playground/TestHelpers/EmbeddingTestHelper.cs 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/ObjectModels/Models.cs b/OpenAI.SDK/ObjectModels/Models.cs index e39acd87..8d361b30 100644 --- a/OpenAI.SDK/ObjectModels/Models.cs +++ b/OpenAI.SDK/ObjectModels/Models.cs @@ -57,7 +57,7 @@ public enum Model CodeDavinciV2 } - private enum Subject + public enum Subject { Text, InstructBeta, @@ -117,9 +117,14 @@ private enum Subject /// /// /// - private static string ModelNameBuilder(this BaseEngine baseEngine, Subject? subject = null, string? version = null) + public static string ModelNameBuilder(this BaseEngine baseEngine, Subject? subject = null, string? version = null) { - var response = subject?.EnumToString(baseEngine) ?? $"{baseEngine.EnumToString()}"; + return ModelNameBuilder(baseEngine.EnumToString(), subject?.EnumToString(baseEngine.EnumToString()), version); + } + + public static string ModelNameBuilder(string baseEngine, string? subject, string? version) + { + var response = subject ?? $"{baseEngine}"; if (!string.IsNullOrEmpty(version)) { @@ -182,7 +187,7 @@ private static string EnumToString(this BaseEngine baseEngine) }; } - private static string EnumToString(this Subject subject, BaseEngine baseEngine) + public static string EnumToString(this Subject subject, string baseEngine) { return String.Format(subject switch { @@ -197,7 +202,7 @@ private static string EnumToString(this Subject subject, BaseEngine baseEngine) Subject.CodeSearchText => "code-search-{0}-text", Subject.Code => "code-{0}", _ => throw new ArgumentOutOfRangeException(nameof(subject), subject, null) - }, baseEngine.EnumToString()); + }, baseEngine); } } } \ No newline at end of file From ec72dca50028e05db1c06f56c3ac954cb4c49dea Mon Sep 17 00:00:00 2001 From: Tolga Kayhan Date: Thu, 25 Aug 2022 16:02:53 +0100 Subject: [PATCH 3/5] Code cleanup and tried to handle possible null reference exceptions in SingleOrArrayConverter. --- OpenAI.SDK/Interfaces/IEmbedding.cs | 3 +- OpenAI.SDK/Interfaces/IFile.cs | 3 +- OpenAI.SDK/ObjectModels/Models.cs | 2 +- .../RequestModels/EmbeddingCreateRequest.cs | 4 +- .../ObjectModels/SingleOrArrayConverter.cs | 64 ++++++++++++++----- 5 files changed, 54 insertions(+), 22 deletions(-) diff --git a/OpenAI.SDK/Interfaces/IEmbedding.cs b/OpenAI.SDK/Interfaces/IEmbedding.cs index c656794e..1742abba 100644 --- a/OpenAI.SDK/Interfaces/IEmbedding.cs +++ b/OpenAI.SDK/Interfaces/IEmbedding.cs @@ -1,5 +1,4 @@ -using OpenAI.GPT3.ObjectModels; -using OpenAI.GPT3.ObjectModels.RequestModels; +using OpenAI.GPT3.ObjectModels.RequestModels; using OpenAI.GPT3.ObjectModels.ResponseModels; namespace OpenAI.GPT3.Interfaces; 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/ObjectModels/Models.cs b/OpenAI.SDK/ObjectModels/Models.cs index 8d361b30..fa42ce50 100644 --- a/OpenAI.SDK/ObjectModels/Models.cs +++ b/OpenAI.SDK/ObjectModels/Models.cs @@ -189,7 +189,7 @@ private static string EnumToString(this BaseEngine baseEngine) public static string EnumToString(this Subject subject, string baseEngine) { - return String.Format(subject switch + return string.Format(subject switch { //{0}-{1} Subject.Text => "text-{0}", diff --git a/OpenAI.SDK/ObjectModels/RequestModels/EmbeddingCreateRequest.cs b/OpenAI.SDK/ObjectModels/RequestModels/EmbeddingCreateRequest.cs index 99415ac2..db9eafe1 100644 --- a/OpenAI.SDK/ObjectModels/RequestModels/EmbeddingCreateRequest.cs +++ b/OpenAI.SDK/ObjectModels/RequestModels/EmbeddingCreateRequest.cs @@ -11,8 +11,8 @@ 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. - /// + /// 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. /// diff --git a/OpenAI.SDK/ObjectModels/SingleOrArrayConverter.cs b/OpenAI.SDK/ObjectModels/SingleOrArrayConverter.cs index 03a4954e..a83b0de7 100644 --- a/OpenAI.SDK/ObjectModels/SingleOrArrayConverter.cs +++ b/OpenAI.SDK/ObjectModels/SingleOrArrayConverter.cs @@ -4,26 +4,44 @@ namespace OpenAI.GPT3.ObjectModels { + /// public class SingleOrArrayConverter : SingleOrArrayConverter, TItem> { - public SingleOrArrayConverter() : this(true) { } - public SingleOrArrayConverter(bool canWrite) : base(canWrite) { } + /// + public SingleOrArrayConverter() : this(true) + { + } + + /// + public SingleOrArrayConverter(bool canWrite) : base(canWrite) + { + } } + /// public class SingleOrArrayConverterFactory : JsonConverterFactory { - public bool CanWrite { get; } + /// + public SingleOrArrayConverterFactory() : this(true) + { + } - public SingleOrArrayConverterFactory() : this(true) { } + /// + public SingleOrArrayConverterFactory(bool canWrite) + { + CanWrite = canWrite; + } - public SingleOrArrayConverterFactory(bool canWrite) => CanWrite = canWrite; + public bool CanWrite { get; } + /// public override bool CanConvert(Type typeToConvert) { var itemType = GetItemType(typeToConvert); if (itemType == null) return false; + if (itemType != typeof(string) && typeof(IEnumerable).IsAssignableFrom(itemType)) return false; @@ -33,17 +51,18 @@ public override bool CanConvert(Type typeToConvert) return true; } - public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) + /// + public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options) { var itemType = GetItemType(typeToConvert); var converterType = typeof(SingleOrArrayConverter<,>).MakeGenericType(typeToConvert, itemType); - return (JsonConverter)Activator.CreateInstance(converterType, new object[] { CanWrite }); + return (JsonConverter?)Activator.CreateInstance(converterType, new object[] { CanWrite }); } - static Type GetItemType(Type type) + private static Type? GetItemType(Type? type) { // Quick reject for performance - if (type.IsPrimitive || type.IsArray || type == typeof(string)) + if (type == null || type.IsPrimitive || type.IsArray || type == typeof(string)) return null; while (type != null) { @@ -54,20 +73,32 @@ static Type GetItemType(Type type) return type.GetGenericArguments()[0]; // Add here other generic collection types as required, e.g. HashSet<> or ObservableCollection<> or etc. } + type = type.BaseType; } + return null; } } + /// public class SingleOrArrayConverter : JsonConverter where TCollection : class, ICollection, new() { - public SingleOrArrayConverter() : this(true) { } - public SingleOrArrayConverter(bool canWrite) => CanWrite = canWrite; + /// + public SingleOrArrayConverter() : this(true) + { + } + + /// + public SingleOrArrayConverter(bool canWrite) + { + CanWrite = canWrite; + } public bool CanWrite { get; } - public override TCollection Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + /// + public override TCollection? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions? options) { switch (reader.TokenType) { @@ -79,14 +110,16 @@ public override TCollection Read(ref Utf8JsonReader reader, Type typeToConvert, { if (reader.TokenType == JsonTokenType.EndArray) break; - list.Add(JsonSerializer.Deserialize(ref reader, options)); + list.Add(JsonSerializer.Deserialize(ref reader, options)!); } + return list; default: - return new TCollection { JsonSerializer.Deserialize(ref reader, options) }; + return new TCollection {JsonSerializer.Deserialize(ref reader, options)!}; } } + /// public override void Write(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options) { if (CanWrite && value.Count == 1) @@ -102,5 +135,4 @@ public override void Write(Utf8JsonWriter writer, TCollection value, JsonSeriali } } } -} - +} \ No newline at end of file From ea287600e3d8b26ff13e4a0789a292fe92f24388 Mon Sep 17 00:00:00 2001 From: Sari Louis Date: Mon, 29 Aug 2022 22:01:40 -0400 Subject: [PATCH 4/5] Removed SingleOrArray from Embedding request --- .../RequestModels/EmbeddingCreateRequest.cs | 1 - .../ObjectModels/SingleOrArrayConverter.cs | 138 ------------------ 2 files changed, 139 deletions(-) delete mode 100644 OpenAI.SDK/ObjectModels/SingleOrArrayConverter.cs diff --git a/OpenAI.SDK/ObjectModels/RequestModels/EmbeddingCreateRequest.cs b/OpenAI.SDK/ObjectModels/RequestModels/EmbeddingCreateRequest.cs index db9eafe1..b4c53452 100644 --- a/OpenAI.SDK/ObjectModels/RequestModels/EmbeddingCreateRequest.cs +++ b/OpenAI.SDK/ObjectModels/RequestModels/EmbeddingCreateRequest.cs @@ -18,7 +18,6 @@ public record EmbeddingCreateRequest : IModelValidate /// /// [JsonPropertyName("input")] - [JsonConverter(typeof(SingleOrArrayConverter))] public List? Input { get; set; } diff --git a/OpenAI.SDK/ObjectModels/SingleOrArrayConverter.cs b/OpenAI.SDK/ObjectModels/SingleOrArrayConverter.cs deleted file mode 100644 index a83b0de7..00000000 --- a/OpenAI.SDK/ObjectModels/SingleOrArrayConverter.cs +++ /dev/null @@ -1,138 +0,0 @@ -using System.Collections; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace OpenAI.GPT3.ObjectModels -{ - /// - public class SingleOrArrayConverter : SingleOrArrayConverter, TItem> - { - /// - public SingleOrArrayConverter() : this(true) - { - } - - /// - public SingleOrArrayConverter(bool canWrite) : base(canWrite) - { - } - } - - /// - public class SingleOrArrayConverterFactory : JsonConverterFactory - { - /// - public SingleOrArrayConverterFactory() : this(true) - { - } - - /// - public SingleOrArrayConverterFactory(bool canWrite) - { - CanWrite = canWrite; - } - - public bool CanWrite { get; } - - /// - public override bool CanConvert(Type typeToConvert) - { - var itemType = GetItemType(typeToConvert); - - if (itemType == null) - return false; - - if (itemType != typeof(string) && typeof(IEnumerable).IsAssignableFrom(itemType)) - return false; - - if (typeToConvert.GetConstructor(Type.EmptyTypes) == null || typeToConvert.IsValueType) - return false; - - return true; - } - - /// - public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options) - { - var itemType = GetItemType(typeToConvert); - var converterType = typeof(SingleOrArrayConverter<,>).MakeGenericType(typeToConvert, itemType); - return (JsonConverter?)Activator.CreateInstance(converterType, new object[] { CanWrite }); - } - - private static Type? GetItemType(Type? type) - { - // Quick reject for performance - if (type == null || type.IsPrimitive || type.IsArray || type == typeof(string)) - return null; - while (type != null) - { - if (type.IsGenericType) - { - var genType = type.GetGenericTypeDefinition(); - if (genType == typeof(List<>)) - return type.GetGenericArguments()[0]; - // Add here other generic collection types as required, e.g. HashSet<> or ObservableCollection<> or etc. - } - - type = type.BaseType; - } - - return null; - } - } - - /// - public class SingleOrArrayConverter : JsonConverter where TCollection : class, ICollection, new() - { - /// - public SingleOrArrayConverter() : this(true) - { - } - - /// - public SingleOrArrayConverter(bool canWrite) - { - CanWrite = canWrite; - } - - public bool CanWrite { get; } - - /// - public override TCollection? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions? options) - { - switch (reader.TokenType) - { - case JsonTokenType.Null: - return null; - case JsonTokenType.StartArray: - var list = new TCollection(); - while (reader.Read()) - { - if (reader.TokenType == JsonTokenType.EndArray) - break; - list.Add(JsonSerializer.Deserialize(ref reader, options)!); - } - - return list; - default: - return new TCollection {JsonSerializer.Deserialize(ref reader, options)!}; - } - } - - /// - public override void Write(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options) - { - if (CanWrite && value.Count == 1) - { - JsonSerializer.Serialize(writer, value.First(), options); - } - else - { - writer.WriteStartArray(); - foreach (var item in value) - JsonSerializer.Serialize(writer, item, options); - writer.WriteEndArray(); - } - } - } -} \ No newline at end of file From a07844573b5df22ce39337563062c5e1bea05ccf Mon Sep 17 00:00:00 2001 From: Tolga Kayhan Date: Tue, 20 Sep 2022 12:54:18 +0100 Subject: [PATCH 5/5] Version bump for 6.3.0 --- OpenAI.SDK/OpenAI.GPT3.csproj | 2 +- Readme.md | 20 ++++++++++++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) 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/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