Skip to content

Commit 864263d

Browse files
committed
Added embedding endpoint and tests
1 parent 2fd8b09 commit 864263d

File tree

5 files changed

+293
-27
lines changed

5 files changed

+293
-27
lines changed
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
using Newtonsoft.Json;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Net.Http;
5+
using System.Security.Authentication;
6+
using System.Text;
7+
using System.Threading.Tasks;
8+
9+
namespace OpenAI_API.Embedding
10+
{
11+
/// <summary>
12+
/// 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.
13+
/// </summary>
14+
public class EmbeddingEndpoint
15+
{
16+
OpenAIAPI Api;
17+
/// <summary>
18+
/// This allows you to send request to the recommended model without needing to specify. Every request uses the <see cref="Model.AdaTextEmbedding"/> model
19+
/// </summary>
20+
public EmbeddingRequest DefaultEmbeddingRequestArgs { get; set; } = new EmbeddingRequest() { Model = Model.AdaTextEmbedding };
21+
22+
/// <summary>
23+
/// Constructor of the api endpoint. Rather than instantiating this yourself, access it through an instance of <see cref="OpenAIAPI"/> as <see cref="OpenAIAPI.Embeddings"/>.
24+
/// </summary>
25+
/// <param name="api"></param>
26+
internal EmbeddingEndpoint(OpenAIAPI api)
27+
{
28+
this.Api = api;
29+
}
30+
31+
/// <summary>
32+
/// Ask the API to embedd text using the default embedding model <see cref="Model.AdaTextEmbedding"/>
33+
/// </summary>
34+
/// <param name="input">Text to be embedded</param>
35+
/// <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>
36+
public async Task<EmbeddingResult> CreateEmbeddingAsync(string input)
37+
{
38+
DefaultEmbeddingRequestArgs.Input = input;
39+
return await CreateEmbeddingAsync(DefaultEmbeddingRequestArgs);
40+
}
41+
42+
/// <summary>
43+
/// Ask the API to embedd text using a custom request
44+
/// </summary>
45+
/// <param name="request">Request to be send</param>
46+
/// <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>
47+
public async Task<EmbeddingResult> CreateEmbeddingAsync(EmbeddingRequest request)
48+
{
49+
if (Api.Auth?.ApiKey is null)
50+
{
51+
throw new AuthenticationException("You must provide API authentication. Please refer to https://github.com/OkGoDoIt/OpenAI-API-dotnet#authentication for details.");
52+
}
53+
54+
HttpClient client = new HttpClient();
55+
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", Api.Auth.ApiKey);
56+
client.DefaultRequestHeaders.Add("User-Agent", "okgodoit/dotnet_openai_api");
57+
58+
string jsonContent = JsonConvert.SerializeObject(request, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore });
59+
var stringContent = new StringContent(jsonContent, Encoding.UTF8, "application/json");
60+
61+
var response = await client.PostAsync($"https://api.openai.com/v1/embeddings", stringContent);
62+
if (response.IsSuccessStatusCode)
63+
{
64+
string resultAsString = await response.Content.ReadAsStringAsync();
65+
66+
var res = JsonConvert.DeserializeObject<EmbeddingResult>(resultAsString);
67+
68+
return res;
69+
}
70+
else
71+
{
72+
throw new HttpRequestException("Error calling OpenAi API to get completion. HTTP status code: " + response.StatusCode.ToString() + ". Request body: " + jsonContent);
73+
}
74+
}
75+
76+
}
77+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
using Newtonsoft.Json;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Text;
5+
6+
namespace OpenAI_API.Embedding
7+
{
8+
/// <summary>
9+
/// 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>
10+
/// </summary>
11+
public class EmbeddingRequest
12+
{
13+
/// <summary>
14+
/// 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"/>.
15+
/// </summary>
16+
[JsonIgnore]
17+
public Model Model { get; set; }
18+
19+
/// <summary>
20+
/// The id/name of the model
21+
/// </summary>
22+
[JsonProperty("model")]
23+
public string ModelName => Model.ModelID;
24+
25+
/// <summary>
26+
/// Main text to be embedded
27+
/// </summary>
28+
[JsonProperty("input")]
29+
public string Input { get; set; }
30+
31+
/// <summary>
32+
/// Cretes a new, empty <see cref="EmbeddingRequest"/>
33+
/// </summary>
34+
public EmbeddingRequest()
35+
{
36+
37+
}
38+
39+
/// <summary>
40+
/// Creates a new <see cref="EmbeddingRequest"/> with the specified parameters
41+
/// </summary>
42+
/// <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>
43+
/// <param name="input">The prompt to transform</param>
44+
public EmbeddingRequest(Model model, string input)
45+
{
46+
Model = model;
47+
this.Input = input;
48+
}
49+
}
50+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
using Newtonsoft.Json;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Text;
5+
6+
namespace OpenAI_API.Embedding
7+
{
8+
/// <summary>
9+
/// Represents an embedding result returned by the Embedding API.
10+
/// </summary>
11+
public class EmbeddingResult
12+
{
13+
/// <summary>
14+
/// Type of the response. In case of embeddings, this will be "list"
15+
/// </summary>
16+
[JsonProperty("object")]
17+
18+
public string Object { get; set; }
19+
20+
/// <summary>
21+
/// List of results of the embedding
22+
/// </summary>
23+
[JsonProperty("data")]
24+
public Data[] Data { get; set; }
25+
26+
/// <summary>
27+
/// Name of the model used to generate this embedding
28+
/// </summary>
29+
[JsonProperty("model")]
30+
public string Model { get; set; }
31+
32+
/// <summary>
33+
/// Usage statistics of how many tokens have been used for this request
34+
/// </summary>
35+
[JsonProperty("usage")]
36+
public Usage Usage { get; set; }
37+
}
38+
39+
/// <summary>
40+
/// Data returned from the Embedding API.
41+
/// </summary>
42+
public class Data
43+
{
44+
/// <summary>
45+
/// Type of the response. In case of Data, this will be "embedding"
46+
/// </summary>
47+
[JsonProperty("object")]
48+
49+
public string Object { get; set; }
50+
51+
/// <summary>
52+
/// The input text represented as a vector (list) of floating point numbers
53+
/// </summary>
54+
[JsonProperty("embedding")]
55+
public float[] Embedding { get; set; }
56+
57+
/// <summary>
58+
/// Index
59+
/// </summary>
60+
[JsonProperty("index")]
61+
public int Index { get; set; }
62+
63+
}
64+
65+
/// <summary>
66+
/// Usage statistics of how many tokens have been used for this request.
67+
/// </summary>
68+
public class Usage
69+
{
70+
/// <summary>
71+
/// How many tokens did the prompt consist of
72+
/// </summary>
73+
[JsonProperty("prompt_tokens")]
74+
public int PromptTokens { get; set; }
75+
76+
/// <summary>
77+
/// How many tokens did the request consume total
78+
/// </summary>
79+
[JsonProperty("total_tokens")]
80+
public int TotalTokens { get; set; }
81+
82+
}
83+
84+
}

OpenAI_API/OpenAIAPI.cs

Lines changed: 34 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Newtonsoft.Json;
2+
using OpenAI_API.Embedding;
23
using System;
34
using System.Collections.Generic;
45
using System.IO;
@@ -9,32 +10,38 @@
910

1011
namespace OpenAI_API
1112
{
12-
/// <summary>
13-
/// Entry point to the OpenAPI API, handling auth and allowing access to the various API endpoints
14-
/// </summary>
15-
public class OpenAIAPI
16-
{
17-
/// <summary>
18-
/// The API authentication information to use for API calls
19-
/// </summary>
20-
public APIAuthentication Auth { get; set; }
21-
22-
/// <summary>
23-
/// Creates a new entry point to the OpenAPI API, handling auth and allowing access to the various API endpoints
24-
/// </summary>
25-
/// <param name="apiKeys">The API authentication information to use for API calls, or <see langword="null"/> to attempt to use the <see cref="APIAuthentication.Default"/>, potentially loading from environment vars or from a config file.</param>
26-
public OpenAIAPI(APIAuthentication apiKeys = null)
27-
{
28-
this.Auth = apiKeys.ThisOrDefault();
29-
Completions = new CompletionEndpoint(this);
30-
Models = new ModelsEndpoint(this);
31-
Search = new SearchEndpoint(this);
32-
}
33-
34-
/// <summary>
35-
/// 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).
36-
/// </summary>
37-
public CompletionEndpoint Completions { get; }
13+
/// <summary>
14+
/// Entry point to the OpenAPI API, handling auth and allowing access to the various API endpoints
15+
/// </summary>
16+
public class OpenAIAPI
17+
{
18+
/// <summary>
19+
/// The API authentication information to use for API calls
20+
/// </summary>
21+
public APIAuthentication Auth { get; set; }
22+
23+
/// <summary>
24+
/// Creates a new entry point to the OpenAPI API, handling auth and allowing access to the various API endpoints
25+
/// </summary>
26+
/// <param name="apiKeys">The API authentication information to use for API calls, or <see langword="null"/> to attempt to use the <see cref="APIAuthentication.Default"/>, potentially loading from environment vars or from a config file.</param>
27+
public OpenAIAPI(APIAuthentication apiKeys = null)
28+
{
29+
this.Auth = apiKeys.ThisOrDefault();
30+
Completions = new CompletionEndpoint(this);
31+
Models = new ModelsEndpoint(this);
32+
Search = new SearchEndpoint(this);
33+
Embeddings = new EmbeddingEndpoint(this);
34+
}
35+
36+
/// <summary>
37+
/// 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).
38+
/// </summary>
39+
public CompletionEndpoint Completions { get; }
40+
41+
/// <summary>
42+
/// 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.
43+
/// </summary>
44+
public EmbeddingEndpoint Embeddings { get; }
3845

3946
/// <summary>
4047
/// The API endpoint for querying available Engines/models
@@ -51,5 +58,5 @@ public OpenAIAPI(APIAuthentication apiKeys = null)
5158

5259

5360

54-
}
61+
}
5562
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
using NUnit.Framework;
2+
using OpenAI_API;
3+
using OpenAI_API.Embedding;
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
using System.Text;
8+
9+
namespace OpenAI_Tests
10+
{
11+
public class EmbeddingEndpointTests
12+
{
13+
[SetUp]
14+
public void Setup()
15+
{
16+
OpenAI_API.APIAuthentication.Default = new OpenAI_API.APIAuthentication(Environment.GetEnvironmentVariable("TEST_OPENAI_SECRET_KEY"));
17+
}
18+
19+
[Test]
20+
public void GetBasicEmbedding()
21+
{
22+
var api = new OpenAI_API.OpenAIAPI();
23+
24+
Assert.IsNotNull(api.Embeddings);
25+
26+
var results = api.Embeddings.CreateEmbeddingAsync(new EmbeddingRequest(Model.AdaTextEmbedding, "A test text for embedding")).Result;
27+
Assert.IsNotNull(results);
28+
Assert.NotNull(results.Object);
29+
Assert.NotZero(results.Data.Length);
30+
Assert.That(results.Data.First().Embedding.Length == 1536);
31+
}
32+
33+
34+
[Test]
35+
public void GetSimpleEmbedding()
36+
{
37+
var api = new OpenAI_API.OpenAIAPI();
38+
39+
Assert.IsNotNull(api.Embeddings);
40+
41+
var results = api.Embeddings.CreateEmbeddingAsync("A test text for embedding").Result;
42+
Assert.IsNotNull(results);
43+
Assert.NotNull(results.Object);
44+
Assert.NotZero(results.Data.Length);
45+
Assert.That(results.Data.First().Embedding.Length == 1536);
46+
}
47+
}
48+
}

0 commit comments

Comments
 (0)