diff --git a/OpenAI_API/Embedding/EmbeddingEndpoint.cs b/OpenAI_API/Embedding/EmbeddingEndpoint.cs new file mode 100644 index 0000000..617af78 --- /dev/null +++ b/OpenAI_API/Embedding/EmbeddingEndpoint.cs @@ -0,0 +1,59 @@ +using System.Threading.Tasks; + +namespace OpenAI_API.Embedding +{ + /// + /// OpenAI’s text embeddings measure the relatedness of text strings by generating an embedding, which is a vector (list) of floating point numbers. The distance between two vectors measures their relatedness. Small distances suggest high relatedness and large distances suggest low relatedness. + /// + public class EmbeddingEndpoint : EndpointBase + { + /// + /// This allows you to send request to the recommended model without needing to specify. Every request uses the model + /// + public EmbeddingRequest DefaultEmbeddingRequestArgs { get; set; } = new EmbeddingRequest() { Model = Model.AdaTextEmbedding }; + + /// + /// The name of the endpoint, which is the final path segment in the API URL. For example, "embeddings". + /// + protected override string Endpoint { get { return "embeddings"; } } + + /// + /// Constructor of the api endpoint. Rather than instantiating this yourself, access it through an instance of as . + /// + /// + internal EmbeddingEndpoint(OpenAIAPI api) : base(api) { } + + /// + /// Ask the API to embedd text using the default embedding model + /// + /// Text to be embedded + /// Asynchronously returns the embedding result. Look in its property of to find the vector of floating point numbers + public async Task CreateEmbeddingAsync(string input) + { + EmbeddingRequest req = new EmbeddingRequest(DefaultEmbeddingRequestArgs.Model, input); + return await CreateEmbeddingAsync(req); + } + + /// + /// Ask the API to embedd text using a custom request + /// + /// Request to be send + /// Asynchronously returns the embedding result. Look in its property of to find the vector of floating point numbers + public async Task CreateEmbeddingAsync(EmbeddingRequest request) + { + return await HttpPost(postData: request); + } + + /// + /// Ask the API to embedd text using the default embedding model + /// + /// Text to be embedded + /// Asynchronously returns the first embedding result as an array of floats. + public async Task GetEmbeddingsAsync(string input) + { + EmbeddingRequest req = new EmbeddingRequest(DefaultEmbeddingRequestArgs.Model, input); + var embeddingResult = await CreateEmbeddingAsync(req); + return embeddingResult?.Data?[0]?.Embedding; + } + } +} diff --git a/OpenAI_API/Embedding/EmbeddingRequest.cs b/OpenAI_API/Embedding/EmbeddingRequest.cs new file mode 100644 index 0000000..0ffb12f --- /dev/null +++ b/OpenAI_API/Embedding/EmbeddingRequest.cs @@ -0,0 +1,51 @@ +using Newtonsoft.Json; + +namespace OpenAI_API.Embedding +{ + /// + /// Represents a request to the Completions API. Matches with the docs at the OpenAI docs + /// + public class EmbeddingRequest + { + /// + /// ID of the model to use. You can use to see all of your available models, or use a standard model like . + /// + [JsonProperty("model")] + public string Model { get; set; } + + /// + /// Main text to be embedded + /// + [JsonProperty("input")] + public string Input { get; set; } + + /// + /// Cretes a new, empty + /// + public EmbeddingRequest() + { + + } + + /// + /// Creates a new with the specified parameters + /// + /// The model to use. You can use to see all of your available models, or use a standard model like . + /// The prompt to transform + public EmbeddingRequest(Model model, string input) + { + Model = model; + this.Input = input; + } + + /// + /// Creates a new with the specified input and the model. + /// + /// The prompt to transform + public EmbeddingRequest(string input) + { + Model = OpenAI_API.Model.AdaTextEmbedding; + this.Input = input; + } + } +} diff --git a/OpenAI_API/Embedding/EmbeddingResult.cs b/OpenAI_API/Embedding/EmbeddingResult.cs new file mode 100644 index 0000000..611b172 --- /dev/null +++ b/OpenAI_API/Embedding/EmbeddingResult.cs @@ -0,0 +1,79 @@ +using Newtonsoft.Json; +using System.Collections.Generic; +using System.Linq; + +namespace OpenAI_API.Embedding +{ + /// + /// Represents an embedding result returned by the Embedding API. + /// + public class EmbeddingResult : ApiResultBase + { + /// + /// List of results of the embedding + /// + [JsonProperty("data")] + public List Data { get; set; } + + /// + /// Usage statistics of how many tokens have been used for this request + /// + [JsonProperty("usage")] + public Usage Usage { get; set; } + + /// + /// Allows an EmbeddingResult to be implicitly cast to the array of floats repsresenting the first ebmedding result + /// + /// The to cast to an array of floats. + public static implicit operator float[](EmbeddingResult embeddingResult) + { + return embeddingResult.Data.FirstOrDefault()?.Embedding; + } + } + + /// + /// Data returned from the Embedding API. + /// + public class Data + { + /// + /// Type of the response. In case of Data, this will be "embedding" + /// + [JsonProperty("object")] + + public string Object { get; set; } + + /// + /// The input text represented as a vector (list) of floating point numbers + /// + [JsonProperty("embedding")] + public float[] Embedding { get; set; } + + /// + /// Index + /// + [JsonProperty("index")] + public int Index { get; set; } + + } + + /// + /// Usage statistics of how many tokens have been used for this request. + /// + public class Usage + { + /// + /// How many tokens did the prompt consist of + /// + [JsonProperty("prompt_tokens")] + public int PromptTokens { get; set; } + + /// + /// How many tokens did the request consume total + /// + [JsonProperty("total_tokens")] + public int TotalTokens { get; set; } + + } + +} diff --git a/OpenAI_API/OpenAIAPI.cs b/OpenAI_API/OpenAIAPI.cs index e91b695..93f21e0 100644 --- a/OpenAI_API/OpenAIAPI.cs +++ b/OpenAI_API/OpenAIAPI.cs @@ -1,4 +1,6 @@ using OpenAI_API.Files; +using Newtonsoft.Json; +using OpenAI_API.Embedding; using System; namespace OpenAI_API @@ -12,7 +14,7 @@ public class OpenAIAPI /// Base url for OpenAI /// public string ApiUrlBase = "https://api.openai.com/v1/"; - + /// /// The API authentication information to use for API calls /// @@ -28,7 +30,7 @@ public OpenAIAPI(APIAuthentication apiKeys = null) Completions = new CompletionEndpoint(this); Models = new ModelsEndpoint(this); //Search = new SearchEndpoint(this); - Files = new FilesEndpoint(this); + Embeddings = new EmbeddingEndpoint(this); } /// @@ -36,6 +38,11 @@ public OpenAIAPI(APIAuthentication apiKeys = null) /// public CompletionEndpoint Completions { get; } + /// + /// The API lets you transform text into a vector (list) of floating point numbers. The distance between two vectors measures their relatedness. Small distances suggest high relatedness and large distances suggest low relatedness. + /// + public EmbeddingEndpoint Embeddings { get; } + /// /// The API endpoint for querying available Engines/models /// diff --git a/OpenAI_Tests/EmbeddingEndpointTests.cs b/OpenAI_Tests/EmbeddingEndpointTests.cs new file mode 100644 index 0000000..5ecfaa5 --- /dev/null +++ b/OpenAI_Tests/EmbeddingEndpointTests.cs @@ -0,0 +1,43 @@ +using NUnit.Framework; +using OpenAI_API; +using OpenAI_API.Embedding; +using System; +using System.Linq; + +namespace OpenAI_Tests +{ + public class EmbeddingEndpointTests + { + [SetUp] + public void Setup() + { + OpenAI_API.APIAuthentication.Default = new OpenAI_API.APIAuthentication(Environment.GetEnvironmentVariable("TEST_OPENAI_SECRET_KEY")); + } + + [Test] + public void GetBasicEmbedding() + { + var api = new OpenAI_API.OpenAIAPI(); + + Assert.IsNotNull(api.Embeddings); + + var results = api.Embeddings.CreateEmbeddingAsync(new EmbeddingRequest(Model.AdaTextEmbedding, "A test text for embedding")).Result; + Assert.IsNotNull(results); + Assert.NotNull(results.Object); + Assert.NotZero(results.Data.Count); + Assert.That(results.Data.First().Embedding.Length == 1536); + } + + [Test] + public void GetSimpleEmbedding() + { + var api = new OpenAI_API.OpenAIAPI(); + + Assert.IsNotNull(api.Embeddings); + + var results = api.Embeddings.GetEmbeddingsAsync("A test text for embedding").Result; + Assert.IsNotNull(results); + Assert.That(results.Length == 1536); + } + } +}