Skip to content

Commit

Permalink
Updated embedding endpoint and result to match new composition style
Browse files Browse the repository at this point in the history
  • Loading branch information
OkGoDoIt committed Feb 3, 2023
1 parent 030891a commit 13fe061
Show file tree
Hide file tree
Showing 5 changed files with 166 additions and 189 deletions.
128 changes: 55 additions & 73 deletions OpenAI_API/Embedding/EmbeddingEndpoint.cs
Original file line number Diff line number Diff line change
@@ -1,77 +1,59 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Security.Authentication;
using System.Text;
using System.Threading.Tasks;
using System.Threading.Tasks;

namespace OpenAI_API.Embedding
{
/// <summary>
/// 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.
/// </summary>
public class EmbeddingEndpoint
{
OpenAIAPI Api;
/// <summary>
/// This allows you to send request to the recommended model without needing to specify. Every request uses the <see cref="Model.AdaTextEmbedding"/> model
/// </summary>
public EmbeddingRequest DefaultEmbeddingRequestArgs { get; set; } = new EmbeddingRequest() { Model = Model.AdaTextEmbedding };

/// <summary>
/// Constructor of the api endpoint. Rather than instantiating this yourself, access it through an instance of <see cref="OpenAIAPI"/> as <see cref="OpenAIAPI.Embeddings"/>.
/// </summary>
/// <param name="api"></param>
internal EmbeddingEndpoint(OpenAIAPI api)
{
this.Api = api;
}

/// <summary>
/// Ask the API to embedd text using the default embedding model <see cref="Model.AdaTextEmbedding"/>
/// </summary>
/// <param name="input">Text to be embedded</param>
/// <returns>Asynchronously returns the embedding result. Look in its <see cref="Data.Embedding"/> property of <see cref="EmbeddingResult.Data"/> to find the vector of floating point numbers</returns>
public async Task<EmbeddingResult> CreateEmbeddingAsync(string input)
{
DefaultEmbeddingRequestArgs.Input = input;
return await CreateEmbeddingAsync(DefaultEmbeddingRequestArgs);
}

/// <summary>
/// Ask the API to embedd text using a custom request
/// </summary>
/// <param name="request">Request to be send</param>
/// <returns>Asynchronously returns the embedding result. Look in its <see cref="Data.Embedding"/> property of <see cref="EmbeddingResult.Data"/> to find the vector of floating point numbers</returns>
public async Task<EmbeddingResult> 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<EmbeddingResult>(resultAsString);

return res;
}
else
{
throw new HttpRequestException("Error calling OpenAi API to get completion. HTTP status code: " + response.StatusCode.ToString() + ". Request body: " + jsonContent);
}
}

}
/// <summary>
/// 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.
/// </summary>
public class EmbeddingEndpoint : EndpointBase
{
/// <summary>
/// This allows you to send request to the recommended model without needing to specify. Every request uses the <see cref="Model.AdaTextEmbedding"/> model
/// </summary>
public EmbeddingRequest DefaultEmbeddingRequestArgs { get; set; } = new EmbeddingRequest() { Model = Model.AdaTextEmbedding };

/// <summary>
/// The name of the endpoint, which is the final path segment in the API URL. For example, "embeddings".
/// </summary>
protected override string Endpoint { get { return "embeddings"; } }

/// <summary>
/// Constructor of the api endpoint. Rather than instantiating this yourself, access it through an instance of <see cref="OpenAIAPI"/> as <see cref="OpenAIAPI.Embeddings"/>.
/// </summary>
/// <param name="api"></param>
internal EmbeddingEndpoint(OpenAIAPI api) : base(api) { }

/// <summary>
/// Ask the API to embedd text using the default embedding model <see cref="Model.AdaTextEmbedding"/>
/// </summary>
/// <param name="input">Text to be embedded</param>
/// <returns>Asynchronously returns the embedding result. Look in its <see cref="Data.Embedding"/> property of <see cref="EmbeddingResult.Data"/> to find the vector of floating point numbers</returns>
public async Task<EmbeddingResult> CreateEmbeddingAsync(string input)
{
EmbeddingRequest req = new EmbeddingRequest(DefaultEmbeddingRequestArgs.Model, input);
return await CreateEmbeddingAsync(req);
}

/// <summary>
/// Ask the API to embedd text using a custom request
/// </summary>
/// <param name="request">Request to be send</param>
/// <returns>Asynchronously returns the embedding result. Look in its <see cref="Data.Embedding"/> property of <see cref="EmbeddingResult.Data"/> to find the vector of floating point numbers</returns>
public async Task<EmbeddingResult> CreateEmbeddingAsync(EmbeddingRequest request)
{
return await HttpPost<EmbeddingResult>(postData: request);
}

/// <summary>
/// Ask the API to embedd text using the default embedding model <see cref="Model.AdaTextEmbedding"/>
/// </summary>
/// <param name="input">Text to be embedded</param>
/// <returns>Asynchronously returns the first embedding result as an array of floats.</returns>
public async Task<float[]> GetEmbeddingsAsync(string input)
{
EmbeddingRequest req = new EmbeddingRequest(DefaultEmbeddingRequestArgs.Model, input);
var embeddingResult = await CreateEmbeddingAsync(req);
return embeddingResult?.Data?[0]?.Embedding;
}
}
}
81 changes: 41 additions & 40 deletions OpenAI_API/Embedding/EmbeddingRequest.cs
Original file line number Diff line number Diff line change
@@ -1,50 +1,51 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Text;

namespace OpenAI_API.Embedding
{
/// <summary>
/// Represents a request to the Completions API. Matches with the docs at <see href="https://platform.openai.com/docs/api-reference/embeddings">the OpenAI docs</see>
/// </summary>
public class EmbeddingRequest
{
/// <summary>
/// ID of the model to use. You can use <see cref="ModelsEndpoint.GetModelsAsync()"/> to see all of your available models, or use a standard model like <see cref="Model.AdaTextEmbedding"/>.
/// </summary>
[JsonIgnore]
public Model Model { get; set; }
/// <summary>
/// Represents a request to the Completions API. Matches with the docs at <see href="https://platform.openai.com/docs/api-reference/embeddings">the OpenAI docs</see>
/// </summary>
public class EmbeddingRequest
{
/// <summary>
/// ID of the model to use. You can use <see cref="ModelsEndpoint.GetModelsAsync()"/> to see all of your available models, or use a standard model like <see cref="Model.AdaTextEmbedding"/>.
/// </summary>
[JsonProperty("model")]
public string Model { get; set; }

/// <summary>
/// The id/name of the model
/// </summary>
[JsonProperty("model")]
public string ModelName => Model.ModelID;
/// <summary>
/// Main text to be embedded
/// </summary>
[JsonProperty("input")]
public string Input { get; set; }

/// <summary>
/// Main text to be embedded
/// </summary>
[JsonProperty("input")]
public string Input { get; set; }
/// <summary>
/// Cretes a new, empty <see cref="EmbeddingRequest"/>
/// </summary>
public EmbeddingRequest()
{

/// <summary>
/// Cretes a new, empty <see cref="EmbeddingRequest"/>
/// </summary>
public EmbeddingRequest()
{
}

}
/// <summary>
/// Creates a new <see cref="EmbeddingRequest"/> with the specified parameters
/// </summary>
/// <param name="model">The model to use. You can use <see cref="ModelsEndpoint.GetModelsAsync()"/> to see all of your available models, or use a standard model like <see cref="Model.AdaTextEmbedding"/>.</param>
/// <param name="input">The prompt to transform</param>
public EmbeddingRequest(Model model, string input)
{
Model = model;
this.Input = input;
}

/// <summary>
/// Creates a new <see cref="EmbeddingRequest"/> with the specified parameters
/// </summary>
/// <param name="model">The model to use. You can use <see cref="ModelsEndpoint.GetModelsAsync()"/> to see all of your available models, or use a standard model like <see cref="Model.AdaTextEmbedding"/>.</param>
/// <param name="input">The prompt to transform</param>
public EmbeddingRequest(Model model, string input)
{
Model = model;
this.Input = input;
}
}
/// <summary>
/// Creates a new <see cref="EmbeddingRequest"/> with the specified input and the <see cref="Model.AdaTextEmbedding"/> model.
/// </summary>
/// <param name="input">The prompt to transform</param>
public EmbeddingRequest(string input)
{
Model = OpenAI_API.Model.AdaTextEmbedding;
this.Input = input;
}
}
}
130 changes: 62 additions & 68 deletions OpenAI_API/Embedding/EmbeddingResult.cs
Original file line number Diff line number Diff line change
@@ -1,84 +1,78 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;

namespace OpenAI_API.Embedding
{
/// <summary>
/// Represents an embedding result returned by the Embedding API.
/// </summary>
public class EmbeddingResult
{
/// <summary>
/// Type of the response. In case of embeddings, this will be "list"
/// </summary>
[JsonProperty("object")]
/// <summary>
/// Represents an embedding result returned by the Embedding API.
/// </summary>
public class EmbeddingResult : ApiResultBase
{
/// <summary>
/// List of results of the embedding
/// </summary>
[JsonProperty("data")]
public Data[] Data { get; set; }

public string Object { get; set; }
/// <summary>
/// Usage statistics of how many tokens have been used for this request
/// </summary>
[JsonProperty("usage")]
public Usage Usage { get; set; }

/// <summary>
/// List of results of the embedding
/// </summary>
[JsonProperty("data")]
public Data[] Data { get; set; }
/// <summary>
/// Allows an EmbeddingResult to be implicitly cast to the array of floats repsresenting the first ebmedding result
/// </summary>
/// <param name="embeddingResult">The <see cref="EmbeddingResult"/> to cast to an array of floats.</param>
public static implicit operator float[](EmbeddingResult embeddingResult)
{
return embeddingResult.Data.FirstOrDefault()?.Embedding;
}
}

/// <summary>
/// Name of the model used to generate this embedding
/// </summary>
[JsonProperty("model")]
public string Model { get; set; }
/// <summary>
/// Data returned from the Embedding API.
/// </summary>
public class Data
{
/// <summary>
/// Type of the response. In case of Data, this will be "embedding"
/// </summary>
[JsonProperty("object")]

/// <summary>
/// Usage statistics of how many tokens have been used for this request
/// </summary>
[JsonProperty("usage")]
public Usage Usage { get; set; }
}
public string Object { get; set; }

/// <summary>
/// Data returned from the Embedding API.
/// </summary>
public class Data
{
/// <summary>
/// Type of the response. In case of Data, this will be "embedding"
/// </summary>
[JsonProperty("object")]
/// <summary>
/// The input text represented as a vector (list) of floating point numbers
/// </summary>
[JsonProperty("embedding")]
public float[] Embedding { get; set; }

public string Object { get; set; }
/// <summary>
/// Index
/// </summary>
[JsonProperty("index")]
public int Index { get; set; }

/// <summary>
/// The input text represented as a vector (list) of floating point numbers
/// </summary>
[JsonProperty("embedding")]
public float[] Embedding { get; set; }
}

/// <summary>
/// Index
/// </summary>
[JsonProperty("index")]
public int Index { get; set; }
/// <summary>
/// Usage statistics of how many tokens have been used for this request.
/// </summary>
public class Usage
{
/// <summary>
/// How many tokens did the prompt consist of
/// </summary>
[JsonProperty("prompt_tokens")]
public int PromptTokens { get; set; }

}
/// <summary>
/// How many tokens did the request consume total
/// </summary>
[JsonProperty("total_tokens")]
public int TotalTokens { get; set; }

/// <summary>
/// Usage statistics of how many tokens have been used for this request.
/// </summary>
public class Usage
{
/// <summary>
/// How many tokens did the prompt consist of
/// </summary>
[JsonProperty("prompt_tokens")]
public int PromptTokens { get; set; }

/// <summary>
/// How many tokens did the request consume total
/// </summary>
[JsonProperty("total_tokens")]
public int TotalTokens { get; set; }

}
}

}
7 changes: 6 additions & 1 deletion OpenAI_API/OpenAIAPI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ namespace OpenAI_API
/// </summary>
public class OpenAIAPI
{
/// <summary>
/// Base url for OpenAI
/// </summary>
public string ApiUrlBase = "https://api.openai.com/v1/";

/// <summary>
/// The API authentication information to use for API calls
/// </summary>
Expand All @@ -24,7 +29,7 @@ public OpenAIAPI(APIAuthentication apiKeys = null)
this.Auth = apiKeys.ThisOrDefault();
Completions = new CompletionEndpoint(this);
Models = new ModelsEndpoint(this);
Search = new SearchEndpoint(this);
//Search = new SearchEndpoint(this);
Embeddings = new EmbeddingEndpoint(this);
}

Expand Down
Loading

0 comments on commit 13fe061

Please sign in to comment.