openai/openai-dotnet

Public

mirrored from https://github.com/openai/openai-dotnetAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
fe4d6cf1ccfea48fa5c2baf67103495ac9b459dd

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

OpenAI/src/Custom/Embeddings/EmbeddingClient.cs

314lines · modecode

1using Microsoft.TypeSpec.Generator.Customizations;
2using System;
3using System.ClientModel;
4using System.ClientModel.Primitives;
5using System.Collections.Generic;
6using System.Diagnostics.CodeAnalysis;
7using System.IO;
8using System.Linq;
9using System.Text.Json;
10using System.Threading;
11using System.Threading.Tasks;
12
13namespace OpenAI.Embeddings;
14
15// CUSTOM:
16// - Renamed.
17// - Suppressed constructor that takes endpoint parameter; endpoint is now a property in the options class.
18// - Suppressed methods that only take the options parameter.
19/// <summary> The service client for OpenAI embedding operations. </summary>
20[CodeGenType("Embeddings")]
21[CodeGenSuppress("EmbeddingClient", typeof(ClientPipeline), typeof(Uri))]
22[CodeGenSuppress("GenerateEmbeddingsAsync", typeof(EmbeddingGenerationOptions), typeof(CancellationToken))]
23[CodeGenSuppress("GenerateEmbeddings", typeof(EmbeddingGenerationOptions), typeof(CancellationToken))]
24public partial class EmbeddingClient
25{
26 private readonly string _model;
27
28 // CUSTOM: Added as a convenience.
29 /// <summary> Initializes a new instance of <see cref="EmbeddingClient"/>. </summary>
30 /// <param name="model"> The name of the model to use in requests sent to the service. To learn more about the available models, see <see href="https://platform.openai.com/docs/models"/>. </param>
31 /// <param name="apiKey"> The API key to authenticate with the service. </param>
32 /// <exception cref="ArgumentNullException"> <paramref name="model"/> or <paramref name="apiKey"/> is null. </exception>
33 /// <exception cref="ArgumentException"> <paramref name="model"/> is an empty string, and was expected to be non-empty. </exception>
34 public EmbeddingClient(string model, string apiKey) : this(model, new ApiKeyCredential(apiKey), new OpenAIClientOptions())
35 {
36 }
37
38 // CUSTOM:
39 // - Added `model` parameter.
40 // - Used a custom pipeline.
41 // - Demoted the endpoint parameter to be a property in the options class.
42 /// <summary> Initializes a new instance of <see cref="EmbeddingClient"/>. </summary>
43 /// <param name="model"> The name of the model to use in requests sent to the service. To learn more about the available models, see <see href="https://platform.openai.com/docs/models"/>. </param>
44 /// <param name="credential"> The <see cref="ApiKeyCredential"/> to authenticate with the service. </param>
45 /// <exception cref="ArgumentNullException"> <paramref name="model"/> or <paramref name="credential"/> is null. </exception>
46 /// <exception cref="ArgumentException"> <paramref name="model"/> is an empty string, and was expected to be non-empty. </exception>
47 public EmbeddingClient(string model, ApiKeyCredential credential) : this(model, credential, new OpenAIClientOptions())
48 {
49 }
50
51 // CUSTOM:
52 // - Added `model` parameter.
53 // - Used a custom pipeline.
54 // - Demoted the endpoint parameter to be a property in the options class.
55 /// <summary> Initializes a new instance of <see cref="EmbeddingClient"/>. </summary>
56 /// <param name="model"> The name of the model to use in requests sent to the service. To learn more about the available models, see <see href="https://platform.openai.com/docs/models"/>. </param>
57 /// <param name="credential"> The <see cref="ApiKeyCredential"/> to authenticate with the service. </param>
58 /// <param name="options"> The options to configure the client. </param>
59 /// <exception cref="ArgumentNullException"> <paramref name="model"/> or <paramref name="credential"/> is null. </exception>
60 /// <exception cref="ArgumentException"> <paramref name="model"/> is an empty string, and was expected to be non-empty. </exception>
61 public EmbeddingClient(string model, ApiKeyCredential credential, OpenAIClientOptions options) : this(model, OpenAIClient.CreateApiKeyAuthenticationPolicy(credential), options)
62 {
63 }
64
65 // CUSTOM: Added as a convenience.
66 /// <summary> Initializes a new instance of <see cref="EmbeddingClient"/>. </summary>
67 /// <param name="model"> The name of the model to use in requests sent to the service. To learn more about the available models, see <see href="https://platform.openai.com/docs/models"/>. </param>
68 /// <param name="authenticationPolicy"> The authentication policy used to authenticate with the service. </param>
69 /// <exception cref="ArgumentNullException"> <paramref name="model"/> or <paramref name="authenticationPolicy"/> is null. </exception>
70 /// <exception cref="ArgumentException"> <paramref name="model"/> is an empty string, and was expected to be non-empty. </exception>
71 [Experimental("OPENAI001")]
72 public EmbeddingClient(string model, AuthenticationPolicy authenticationPolicy) : this(model, authenticationPolicy, new OpenAIClientOptions())
73 {
74 }
75
76 // CUSTOM: Added as a convenience.
77 /// <summary> Initializes a new instance of <see cref="EmbeddingClient"/>. </summary>
78 /// <param name="model"> The name of the model to use in requests sent to the service. To learn more about the available models, see <see href="https://platform.openai.com/docs/models"/>. </param>
79 /// <param name="authenticationPolicy"> The authentication policy used to authenticate with the service. </param>
80 /// <param name="options"> The options to configure the client. </param>
81 /// <exception cref="ArgumentNullException"> <paramref name="model"/> or <paramref name="authenticationPolicy"/> is null. </exception>
82 /// <exception cref="ArgumentException"> <paramref name="model"/> is an empty string, and was expected to be non-empty. </exception>
83 [Experimental("OPENAI001")]
84 public EmbeddingClient(string model, AuthenticationPolicy authenticationPolicy, OpenAIClientOptions options)
85 {
86 Argument.AssertNotNullOrEmpty(model, nameof(model));
87 Argument.AssertNotNull(authenticationPolicy, nameof(authenticationPolicy));
88 options ??= new OpenAIClientOptions();
89
90 _model = model;
91 Pipeline = OpenAIClient.CreatePipeline(authenticationPolicy, options);
92 _endpoint = OpenAIClient.GetEndpoint(options);
93 }
94
95 [Experimental("SCME0002")]
96 public EmbeddingClient(EmbeddingClientSettings settings)
97 : this(settings?.Model, AuthenticationPolicy.Create(settings), settings?.Options)
98 {
99 }
100
101 // CUSTOM:
102 // - Added `model` parameter.
103 // - Used a custom pipeline.
104 // - Demoted the endpoint parameter to be a property in the options class.
105 // - Made protected.
106 /// <summary> Initializes a new instance of <see cref="EmbeddingClient"/>. </summary>
107 /// <param name="pipeline"> The HTTP pipeline to send and receive REST requests and responses. </param>
108 /// <param name="model"> The name of the model to use in requests sent to the service. To learn more about the available models, see <see href="https://platform.openai.com/docs/models"/>. </param>
109 /// <param name="options"> The options to configure the client. </param>
110 /// <exception cref="ArgumentNullException"> <paramref name="pipeline"/> or <paramref name="model"/> is null. </exception>
111 /// <exception cref="ArgumentException"> <paramref name="model"/> is an empty string, and was expected to be non-empty. </exception>
112 protected internal EmbeddingClient(ClientPipeline pipeline, string model, OpenAIClientOptions options)
113 {
114 Argument.AssertNotNull(pipeline, nameof(pipeline));
115 Argument.AssertNotNullOrEmpty(model, nameof(model));
116 options ??= new OpenAIClientOptions();
117
118 _model = model;
119 Pipeline = pipeline;
120 _endpoint = OpenAIClient.GetEndpoint(options);
121 }
122
123 /// <summary>
124 /// Gets the name of the model used in requests sent to the service.
125 /// </summary>
126 [Experimental("OPENAI001")]
127 public string Model => _model;
128
129 /// <summary>
130 /// Gets the endpoint URI for the service.
131 /// </summary>
132 [Experimental("OPENAI001")]
133 public Uri Endpoint => _endpoint;
134
135 // CUSTOM: Added to simplify generating a single embedding from a string input.
136 /// <summary> Generates an embedding representing the text input. </summary>
137 /// <param name="input"> The text input to generate an embedding for. </param>
138 /// <param name="options"> The options to configure the embedding generation. </param>
139 /// <param name="cancellationToken"> A token that can be used to cancel this method call. </param>
140 /// <exception cref="ArgumentNullException"> <paramref name="input"/> is null. </exception>
141 /// <exception cref="ArgumentException"> <paramref name="input"/> is an empty string, and was expected to be non-empty. </exception>
142 public virtual async Task<ClientResult<OpenAIEmbedding>> GenerateEmbeddingAsync(string input, EmbeddingGenerationOptions options = null, CancellationToken cancellationToken = default)
143 {
144 Argument.AssertNotNullOrEmpty(input, nameof(input));
145
146 options ??= new();
147 CreateEmbeddingGenerationOptions(input, ref options);
148
149 using BinaryContent content = options.ToBinaryContent();
150 ClientResult result = await GenerateEmbeddingsAsync(content, cancellationToken.ToRequestOptions()).ConfigureAwait(false);
151 return ClientResult.FromValue(((OpenAIEmbeddingCollection)result).FirstOrDefault(), result.GetRawResponse());
152 }
153
154 // CUSTOM: Added to simplify generating a single embedding from a string input.
155 /// <summary> Generates an embedding representing the text input. </summary>
156 /// <param name="input"> The text input to generate an embedding for. </param>
157 /// <param name="options"> The options to configure the embedding generation. </param>
158 /// <param name="cancellationToken"> A token that can be used to cancel this method call. </param>
159 /// <exception cref="ArgumentNullException"> <paramref name="input"/> is null. </exception>
160 /// <exception cref="ArgumentException"> <paramref name="input"/> is an empty string, and was expected to be non-empty. </exception>
161 public virtual ClientResult<OpenAIEmbedding> GenerateEmbedding(string input, EmbeddingGenerationOptions options = null, CancellationToken cancellationToken = default)
162 {
163 Argument.AssertNotNullOrEmpty(input, nameof(input));
164
165 options ??= new();
166 CreateEmbeddingGenerationOptions(input, ref options);
167
168 using BinaryContent content = options.ToBinaryContent();
169 ClientResult result = GenerateEmbeddings(content, cancellationToken.ToRequestOptions());
170 return ClientResult.FromValue(((OpenAIEmbeddingCollection)result).FirstOrDefault(), result.GetRawResponse());
171 }
172
173 // CUSTOM: Added to simplify passing the input as a collection of strings instead of BinaryData.
174 /// <summary> Generates embeddings representing the text inputs. </summary>
175 /// <param name="inputs"> The text inputs to generate embeddings for. </param>
176 /// <param name="options"> The options to configure the embedding generation. </param>
177 /// <param name="cancellationToken"> A token that can be used to cancel this method call. </param>
178 /// <exception cref="ArgumentNullException"> <paramref name="inputs"/> is null. </exception>
179 /// <exception cref="ArgumentException"> <paramref name="inputs"/> is an empty collection, and was expected to be non-empty. </exception>
180 public virtual Task<ClientResult<OpenAIEmbeddingCollection>> GenerateEmbeddingsAsync(IEnumerable<string> inputs, EmbeddingGenerationOptions options = null, CancellationToken cancellationToken = default)
181 {
182 return GenerateEmbeddingsAsync(inputs, options, cancellationToken.ToRequestOptions());
183 }
184
185 internal async Task<ClientResult<OpenAIEmbeddingCollection>> GenerateEmbeddingsAsync(IEnumerable<string> inputs, EmbeddingGenerationOptions options, RequestOptions requestOptions)
186 {
187 Argument.AssertNotNullOrEmpty(inputs, nameof(inputs));
188
189 options ??= new();
190 CreateEmbeddingGenerationOptions(inputs, ref options);
191
192 using BinaryContent content = options.ToBinaryContent();
193 ClientResult result = await GenerateEmbeddingsAsync(content, requestOptions).ConfigureAwait(false);
194 return ClientResult.FromValue((OpenAIEmbeddingCollection)result, result.GetRawResponse());
195
196 }
197
198 // CUSTOM: Added to simplify passing the input as a collection of strings instead of BinaryData.
199 /// <summary> Generates embeddings representing the text inputs. </summary>
200 /// <param name="inputs"> The text inputs to generate embeddings for. </param>
201 /// <param name="options"> The options to configure the embedding generation. </param>
202 /// <param name="cancellationToken"> A token that can be used to cancel this method call. </param>
203 /// <exception cref="ArgumentNullException"> <paramref name="inputs"/> is null. </exception>
204 /// <exception cref="ArgumentException"> <paramref name="inputs"/> is an empty collection, and was expected to be non-empty. </exception>
205 public virtual ClientResult<OpenAIEmbeddingCollection> GenerateEmbeddings(IEnumerable<string> inputs, EmbeddingGenerationOptions options = null, CancellationToken cancellationToken = default)
206 {
207 Argument.AssertNotNullOrEmpty(inputs, nameof(inputs));
208
209 options ??= new();
210 CreateEmbeddingGenerationOptions(inputs, ref options);
211
212 using BinaryContent content = options.ToBinaryContent();
213 ClientResult result = GenerateEmbeddings(content, cancellationToken.ToRequestOptions());
214 return ClientResult.FromValue((OpenAIEmbeddingCollection)result, result.GetRawResponse());
215 }
216
217 // CUSTOM: Added to simplify passing the input as a collection of ReadOnlyMemory tokens instead of BinaryData.
218 /// <summary> Generates embeddings representing the tokenized text inputs. </summary>
219 /// <param name="inputs"> The tokenized text inputs to generate embeddings for. </param>
220 /// <param name="options"> The options to configure the embedding generation. </param>
221 /// <param name="cancellationToken"> A token that can be used to cancel this method call. </param>
222 /// <exception cref="ArgumentNullException"> <paramref name="inputs"/> is null. </exception>
223 /// <exception cref="ArgumentException"> <paramref name="inputs"/> is an empty collection, and was expected to be non-empty. </exception>
224 public virtual async Task<ClientResult<OpenAIEmbeddingCollection>> GenerateEmbeddingsAsync(IEnumerable<ReadOnlyMemory<int>> inputs, EmbeddingGenerationOptions options = null, CancellationToken cancellationToken = default)
225 {
226 Argument.AssertNotNullOrEmpty(inputs, nameof(inputs));
227
228 options ??= new();
229 CreateEmbeddingGenerationOptions(inputs, ref options);
230
231 using BinaryContent content = options.ToBinaryContent();
232 ClientResult result = await GenerateEmbeddingsAsync(content, cancellationToken.ToRequestOptions()).ConfigureAwait(false);
233 return ClientResult.FromValue((OpenAIEmbeddingCollection)result, result.GetRawResponse());
234 }
235
236 // CUSTOM: Added to simplify passing the input as a collection of ReadOnlyMemory of tokens instead of BinaryData.
237 /// <summary> Generates embeddings representing the tokenized text inputs. </summary>
238 /// <param name="inputs"> The tokenized text inputs to generate embeddings for. </param>
239 /// <param name="options"> The options to configure the embedding generation. </param>
240 /// <param name="cancellationToken"> A token that can be used to cancel this method call. </param>
241 /// <exception cref="ArgumentNullException"> <paramref name="inputs"/> is null. </exception>
242 /// <exception cref="ArgumentException"> <paramref name="inputs"/> is an empty collection, and was expected to be non-empty. </exception>
243 public virtual ClientResult<OpenAIEmbeddingCollection> GenerateEmbeddings(IEnumerable<ReadOnlyMemory<int>> inputs, EmbeddingGenerationOptions options = null, CancellationToken cancellationToken = default)
244 {
245 Argument.AssertNotNullOrEmpty(inputs, nameof(inputs));
246
247 options ??= new();
248 CreateEmbeddingGenerationOptions(inputs, ref options);
249
250 using BinaryContent content = options.ToBinaryContent();
251 ClientResult result = GenerateEmbeddings(content, cancellationToken.ToRequestOptions());
252 return ClientResult.FromValue((OpenAIEmbeddingCollection)result, result.GetRawResponse());
253 }
254
255 private void CreateEmbeddingGenerationOptions(string input, ref EmbeddingGenerationOptions options)
256 {
257 using MemoryStream stream = new();
258 using Utf8JsonWriter writer = new(stream);
259
260 writer.WriteStringValue(input);
261 writer.Flush();
262
263 options.Input = BinaryData.FromBytes(stream.GetBuffer().AsMemory(0, (int)stream.Length));
264 options.Model = _model;
265 options.EncodingFormat = InternalCreateEmbeddingRequestEncodingFormat.Base64;
266 }
267
268 private void CreateEmbeddingGenerationOptions(IEnumerable<string> inputs, ref EmbeddingGenerationOptions options)
269 {
270 using MemoryStream stream = new();
271 using Utf8JsonWriter writer = new(stream);
272
273 writer.WriteStartArray();
274
275 foreach (string input in inputs)
276 {
277 writer.WriteStringValue(input);
278 }
279
280 writer.WriteEndArray();
281 writer.Flush();
282
283 options.Input = BinaryData.FromBytes(stream.GetBuffer().AsMemory(0, (int)stream.Length));
284 options.Model = _model;
285 options.EncodingFormat = InternalCreateEmbeddingRequestEncodingFormat.Base64;
286 }
287
288 private void CreateEmbeddingGenerationOptions(IEnumerable<ReadOnlyMemory<int>> inputs, ref EmbeddingGenerationOptions options)
289 {
290 using MemoryStream stream = new();
291 using Utf8JsonWriter writer = new(stream);
292
293 writer.WriteStartArray();
294
295 foreach (ReadOnlyMemory<int> input in inputs)
296 {
297 writer.WriteStartArray();
298
299 foreach (int tokenId in input.ToArray())
300 {
301 writer.WriteNumberValue(tokenId);
302 }
303
304 writer.WriteEndArray();
305 }
306
307 writer.WriteEndArray();
308 writer.Flush();
309
310 options.Input = BinaryData.FromBytes(stream.GetBuffer().AsMemory(0, (int)stream.Length));
311 options.Model = _model;
312 options.EncodingFormat = InternalCreateEmbeddingRequestEncodingFormat.Base64;
313 }
314}