diff --git a/OpenAI_API/Embedding/EmbeddingEndpoint.cs b/OpenAI_API/Embedding/EmbeddingEndpoint.cs new file mode 100644 index 0000000..6796b07 --- /dev/null +++ b/OpenAI_API/Embedding/EmbeddingEndpoint.cs @@ -0,0 +1,77 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Security.Authentication; +using System.Text; +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 + { + OpenAIAPI Api; + /// + /// 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 }; + + /// + /// Constructor of the api endpoint. Rather than instantiating this yourself, access it through an instance of as . + /// + /// + internal EmbeddingEndpoint(OpenAIAPI api) + { + this.Api = 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) + { + DefaultEmbeddingRequestArgs.Input = input; + return await CreateEmbeddingAsync(DefaultEmbeddingRequestArgs); + } + + /// + /// 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) + { + if (Api.Auth?.ApiKey is null) + { + throw new AuthenticationException("You must provide API authentication. Please refer to https://github.com/OkGoDoIt/OpenAI-API-dotnet#authentication for details."); + } + + HttpClient client = new HttpClient(); + client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", Api.Auth.ApiKey); + client.DefaultRequestHeaders.Add("User-Agent", "okgodoit/dotnet_openai_api"); + + string jsonContent = JsonConvert.SerializeObject(request, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore }); + var stringContent = new StringContent(jsonContent, Encoding.UTF8, "application/json"); + + var response = await client.PostAsync($"https://api.openai.com/v1/embeddings", stringContent); + if (response.IsSuccessStatusCode) + { + string resultAsString = await response.Content.ReadAsStringAsync(); + + var res = JsonConvert.DeserializeObject(resultAsString); + + return res; + } + else + { + throw new HttpRequestException("Error calling OpenAi API to get completion. HTTP status code: " + response.StatusCode.ToString() + ". Request body: " + jsonContent); + } + } + + } +} diff --git a/OpenAI_API/Embedding/EmbeddingRequest.cs b/OpenAI_API/Embedding/EmbeddingRequest.cs new file mode 100644 index 0000000..a08b738 --- /dev/null +++ b/OpenAI_API/Embedding/EmbeddingRequest.cs @@ -0,0 +1,50 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; + +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 . + /// + [JsonIgnore] + public Model Model { get; set; } + + /// + /// The id/name of the model + /// + [JsonProperty("model")] + public string ModelName => Model.ModelID; + + /// + /// 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; + } + } +} diff --git a/OpenAI_API/Embedding/EmbeddingResult.cs b/OpenAI_API/Embedding/EmbeddingResult.cs new file mode 100644 index 0000000..7dcceb6 --- /dev/null +++ b/OpenAI_API/Embedding/EmbeddingResult.cs @@ -0,0 +1,84 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; + +namespace OpenAI_API.Embedding +{ + /// + /// Represents an embedding result returned by the Embedding API. + /// + public class EmbeddingResult + { + /// + /// Type of the response. In case of embeddings, this will be "list" + /// + [JsonProperty("object")] + + public string Object { get; set; } + + /// + /// List of results of the embedding + /// + [JsonProperty("data")] + public Data[] Data { get; set; } + + /// + /// Name of the model used to generate this embedding + /// + [JsonProperty("model")] + public string Model { get; set; } + + /// + /// Usage statistics of how many tokens have been used for this request + /// + [JsonProperty("usage")] + public Usage Usage { get; set; } + } + + /// + /// 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 74c5f55..3bc3ad8 100644 --- a/OpenAI_API/OpenAIAPI.cs +++ b/OpenAI_API/OpenAIAPI.cs @@ -1,4 +1,5 @@ using Newtonsoft.Json; +using OpenAI_API.Embedding; using System; using System.Collections.Generic; using System.IO; @@ -9,32 +10,38 @@ namespace OpenAI_API { - /// - /// Entry point to the OpenAPI API, handling auth and allowing access to the various API endpoints - /// - public class OpenAIAPI - { - /// - /// The API authentication information to use for API calls - /// - public APIAuthentication Auth { get; set; } - - /// - /// Creates a new entry point to the OpenAPI API, handling auth and allowing access to the various API endpoints - /// - /// The API authentication information to use for API calls, or to attempt to use the , potentially loading from environment vars or from a config file. - public OpenAIAPI(APIAuthentication apiKeys = null) - { - this.Auth = apiKeys.ThisOrDefault(); - Completions = new CompletionEndpoint(this); - Models = new ModelsEndpoint(this); - Search = new SearchEndpoint(this); - } - - /// - /// Text generation is the core function of the API. You give the API a prompt, and it generates a completion. The way you “program” the API to do a task is by simply describing the task in plain english or providing a few written examples. This simple approach works for a wide range of use cases, including summarization, translation, grammar correction, question answering, chatbots, composing emails, and much more (see the prompt library for inspiration). - /// - public CompletionEndpoint Completions { get; } + /// + /// Entry point to the OpenAPI API, handling auth and allowing access to the various API endpoints + /// + public class OpenAIAPI + { + /// + /// The API authentication information to use for API calls + /// + public APIAuthentication Auth { get; set; } + + /// + /// Creates a new entry point to the OpenAPI API, handling auth and allowing access to the various API endpoints + /// + /// The API authentication information to use for API calls, or to attempt to use the , potentially loading from environment vars or from a config file. + public OpenAIAPI(APIAuthentication apiKeys = null) + { + this.Auth = apiKeys.ThisOrDefault(); + Completions = new CompletionEndpoint(this); + Models = new ModelsEndpoint(this); + Search = new SearchEndpoint(this); + Embeddings = new EmbeddingEndpoint(this); + } + + /// + /// Text generation is the core function of the API. You give the API a prompt, and it generates a completion. The way you “program” the API to do a task is by simply describing the task in plain english or providing a few written examples. This simple approach works for a wide range of use cases, including summarization, translation, grammar correction, question answering, chatbots, composing emails, and much more (see the prompt library for inspiration). + /// + 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 @@ -51,5 +58,5 @@ public OpenAIAPI(APIAuthentication apiKeys = null) - } + } } diff --git a/OpenAI_Tests/EmbeddingEndpointTests.cs b/OpenAI_Tests/EmbeddingEndpointTests.cs new file mode 100644 index 0000000..93eb110 --- /dev/null +++ b/OpenAI_Tests/EmbeddingEndpointTests.cs @@ -0,0 +1,48 @@ +using NUnit.Framework; +using OpenAI_API; +using OpenAI_API.Embedding; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +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.Length); + 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.CreateEmbeddingAsync("A test text for embedding").Result; + Assert.IsNotNull(results); + Assert.NotNull(results.Object); + Assert.NotZero(results.Data.Length); + Assert.That(results.Data.First().Embedding.Length == 1536); + } + } +}