openai/openai-dotnet

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
OpenAI_2.2.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/Custom/Chat/ChatClient.cs

324lines · modeblame

5dce104aJose Arriaga Maldonado1 years ago1using OpenAI.Evals;
d5b5c604Liudmila Molkova2 years ago2using OpenAI.Telemetry;
9f9f2936Jose Arriaga Maldonado2 years ago3using System;
4using System.ClientModel;
5using System.ClientModel.Primitives;
6using System.Collections.Generic;
5dce104aJose Arriaga Maldonado1 years ago7using System.Diagnostics.CodeAnalysis;
9f9f2936Jose Arriaga Maldonado2 years ago8using System.Linq;
19a65a0aKrzysztof Cwalina2 years ago9using System.Threading;
9f9f2936Jose Arriaga Maldonado2 years ago10using System.Threading.Tasks;
11
12namespace OpenAI.Chat;
13
13a9c686Jose Arriaga Maldonado1 years ago14// CUSTOM:
15// - Renamed.
16// - Suppressed constructor that takes endpoint parameter; endpoint is now a property in the options class.
17// - Suppressed methods that only take the options parameter.
18/// <summary> The service client for OpenAI chat operations. </summary>
0ca4c062Jose Arriaga Maldonado1 years ago19[CodeGenType("Chat")]
20[CodeGenSuppress("ChatClient", typeof(ClientPipeline), typeof(Uri))]
e0fee603Jose Arriaga Maldonado1 years ago21[CodeGenSuppress("CreateChatCompletion", typeof(ChatCompletionOptions), typeof(CancellationToken))]
5dce104aJose Arriaga Maldonado1 years ago22[CodeGenSuppress("CreateChatCompletionAsync", typeof(ChatCompletionOptions), typeof(CancellationToken))]
23[CodeGenSuppress("GetChatCompletionMessages", typeof(string), typeof(string), typeof(int?), typeof(OpenAI.VectorStores.VectorStoreCollectionOrder?), typeof(CancellationToken))]
24[CodeGenSuppress("GetChatCompletionMessagesAsync", typeof(string), typeof(string), typeof(int?), typeof(OpenAI.VectorStores.VectorStoreCollectionOrder?), typeof(CancellationToken))]
25[CodeGenSuppress("GetChatCompletions", typeof(string), typeof(int?), typeof(OpenAI.VectorStores.VectorStoreCollectionOrder?), typeof(IDictionary<string, string>), typeof(string), typeof(CancellationToken))]
26[CodeGenSuppress("GetChatCompletionsAsync", typeof(string), typeof(int?), typeof(OpenAI.VectorStores.VectorStoreCollectionOrder?), typeof(IDictionary<string, string>), typeof(string), typeof(CancellationToken))]
27[CodeGenSuppress("UpdateChatCompletion", typeof(string), typeof(IDictionary<string, string>), typeof(CancellationToken))]
28[CodeGenSuppress("UpdateChatCompletionAsync", typeof(string), typeof(IDictionary<string, string>), typeof(CancellationToken))]
9f9f2936Jose Arriaga Maldonado2 years ago29public partial class ChatClient
30{
31private readonly string _model;
d5b5c604Liudmila Molkova2 years ago32private readonly OpenTelemetrySource _telemetry;
5dce104aJose Arriaga Maldonado1 years ago33private static readonly InternalChatCompletionStreamOptions s_includeUsageStreamOptions = new(includeUsage: true, additionalBinaryDataProperties: null);
9f9f2936Jose Arriaga Maldonado2 years ago34
2ab1a942Jose Arriaga Maldonado1 years ago35// CUSTOM: Added as a convenience.
e0fee603Jose Arriaga Maldonado1 years ago36/// <summary> Initializes a new instance of <see cref="ChatClient"/>. </summary>
2ab1a942Jose Arriaga Maldonado1 years ago37/// <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>
38/// <param name="apiKey"> The API key to authenticate with the service. </param>
39/// <exception cref="ArgumentNullException"> <paramref name="model"/> or <paramref name="apiKey"/> is null. </exception>
40/// <exception cref="ArgumentException"> <paramref name="model"/> is an empty string, and was expected to be non-empty. </exception>
41public ChatClient(string model, string apiKey) : this(model, new ApiKeyCredential(apiKey), new OpenAIClientOptions())
42{
43}
44
13a9c686Jose Arriaga Maldonado1 years ago45// CUSTOM:
46// - Added `model` parameter.
47// - Used a custom pipeline.
48// - Demoted the endpoint parameter to be a property in the options class.
e0fee603Jose Arriaga Maldonado1 years ago49/// <summary> Initializes a new instance of <see cref="ChatClient"/>. </summary>
13a9c686Jose Arriaga Maldonado1 years ago50/// <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>
51/// <param name="credential"> The API key to authenticate with the service. </param>
52/// <exception cref="ArgumentNullException"> <paramref name="model"/> or <paramref name="credential"/> is null. </exception>
53/// <exception cref="ArgumentException"> <paramref name="model"/> is an empty string, and was expected to be non-empty. </exception>
54public ChatClient(string model, ApiKeyCredential credential) : this(model, credential, new OpenAIClientOptions())
55{
56}
9f9f2936Jose Arriaga Maldonado2 years ago57
13a9c686Jose Arriaga Maldonado1 years ago58// CUSTOM:
59// - Added `model` parameter.
60// - Used a custom pipeline.
61// - Demoted the endpoint parameter to be a property in the options class.
62// - Added telemetry support.
e0fee603Jose Arriaga Maldonado1 years ago63/// <summary> Initializes a new instance of <see cref="ChatClient"/>. </summary>
13a9c686Jose Arriaga Maldonado1 years ago64/// <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>
65/// <param name="credential"> The API key to authenticate with the service. </param>
66/// <param name="options"> The options to configure the client. </param>
67/// <exception cref="ArgumentNullException"> <paramref name="model"/> or <paramref name="credential"/> is null. </exception>
68/// <exception cref="ArgumentException"> <paramref name="model"/> is an empty string, and was expected to be non-empty. </exception>
69public ChatClient(string model, ApiKeyCredential credential, OpenAIClientOptions options)
70{
71Argument.AssertNotNullOrEmpty(model, nameof(model));
72Argument.AssertNotNull(credential, nameof(credential));
73options ??= new OpenAIClientOptions();
9f9f2936Jose Arriaga Maldonado2 years ago74
13a9c686Jose Arriaga Maldonado1 years ago75_model = model;
e0fee603Jose Arriaga Maldonado1 years ago76Pipeline = OpenAIClient.CreatePipeline(credential, options);
13a9c686Jose Arriaga Maldonado1 years ago77_endpoint = OpenAIClient.GetEndpoint(options);
78_telemetry = new OpenTelemetrySource(model, _endpoint);
79}
80
81// CUSTOM:
82// - Added `model` parameter.
83// - Used a custom pipeline.
84// - Demoted the endpoint parameter to be a property in the options class.
85// - Added telemetry support.
86// - Made protected.
e0fee603Jose Arriaga Maldonado1 years ago87/// <summary> Initializes a new instance of <see cref="ChatClient"/>. </summary>
13a9c686Jose Arriaga Maldonado1 years ago88/// <param name="pipeline"> The HTTP pipeline to send and receive REST requests and responses. </param>
89/// <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>
90/// <param name="options"> The options to configure the client. </param>
91/// <exception cref="ArgumentNullException"> <paramref name="pipeline"/> or <paramref name="model"/> is null. </exception>
92/// <exception cref="ArgumentException"> <paramref name="model"/> is an empty string, and was expected to be non-empty. </exception>
93protected internal ChatClient(ClientPipeline pipeline, string model, OpenAIClientOptions options)
9f9f2936Jose Arriaga Maldonado2 years ago94{
13a9c686Jose Arriaga Maldonado1 years ago95Argument.AssertNotNull(pipeline, nameof(pipeline));
9f9f2936Jose Arriaga Maldonado2 years ago96Argument.AssertNotNullOrEmpty(model, nameof(model));
13a9c686Jose Arriaga Maldonado1 years ago97options ??= new OpenAIClientOptions();
9f9f2936Jose Arriaga Maldonado2 years ago98
99_model = model;
e0fee603Jose Arriaga Maldonado1 years ago100Pipeline = pipeline;
13a9c686Jose Arriaga Maldonado1 years ago101_endpoint = OpenAIClient.GetEndpoint(options);
102_telemetry = new OpenTelemetrySource(model, _endpoint);
9f9f2936Jose Arriaga Maldonado2 years ago103}
104
13a9c686Jose Arriaga Maldonado1 years ago105/// <summary> Generates a completion for the given chat. </summary>
106/// <param name="messages"> The messages comprising the chat so far. </param>
107/// <param name="options"> The options to configure the chat completion. </param>
108/// <param name="cancellationToken"> A token that can be used to cancel this method call. </param>
109/// <exception cref="ArgumentNullException"> <paramref name="messages"/> is null. </exception>
110/// <exception cref="ArgumentException"> <paramref name="messages"/> is an empty collection, and was expected to be non-empty. </exception>
19a65a0aKrzysztof Cwalina2 years ago111public virtual async Task<ClientResult<ChatCompletion>> CompleteChatAsync(IEnumerable<ChatMessage> messages, ChatCompletionOptions options = null, CancellationToken cancellationToken = default)
9f9f2936Jose Arriaga Maldonado2 years ago112{
113Argument.AssertNotNullOrEmpty(messages, nameof(messages));
114
115options ??= new();
116CreateChatCompletionOptions(messages, ref options);
d5b5c604Liudmila Molkova2 years ago117using OpenTelemetryScope scope = _telemetry.StartChatScope(options);
118
119try
120{
5dce104aJose Arriaga Maldonado1 years ago121using BinaryContent content = options.ToBinaryContent();
d5b5c604Liudmila Molkova2 years ago122
123ClientResult result = await CompleteChatAsync(content, cancellationToken.ToRequestOptions()).ConfigureAwait(false);
5dce104aJose Arriaga Maldonado1 years ago124ChatCompletion chatCompletion = ChatCompletion.FromClientResult(result);
d5b5c604Liudmila Molkova2 years ago125scope?.RecordChatCompletion(chatCompletion);
126return ClientResult.FromValue(chatCompletion, result.GetRawResponse());
127}
128catch (Exception ex)
129{
130scope?.RecordException(ex);
131throw;
132}
9f9f2936Jose Arriaga Maldonado2 years ago133}
134
13a9c686Jose Arriaga Maldonado1 years ago135/// <summary> Generates a completion for the given chat. </summary>
136/// <param name="messages"> The messages comprising the chat so far. </param>
137/// <param name="options"> The options to configure the chat completion. </param>
138/// <param name="cancellationToken"> A token that can be used to cancel this method call. </param>
139/// <exception cref="ArgumentNullException"> <paramref name="messages"/> is null. </exception>
140/// <exception cref="ArgumentException"> <paramref name="messages"/> is an empty collection, and was expected to be non-empty. </exception>
19a65a0aKrzysztof Cwalina2 years ago141public virtual ClientResult<ChatCompletion> CompleteChat(IEnumerable<ChatMessage> messages, ChatCompletionOptions options = null, CancellationToken cancellationToken = default)
9f9f2936Jose Arriaga Maldonado2 years ago142{
143Argument.AssertNotNullOrEmpty(messages, nameof(messages));
144
145options ??= new();
146CreateChatCompletionOptions(messages, ref options);
d5b5c604Liudmila Molkova2 years ago147using OpenTelemetryScope scope = _telemetry.StartChatScope(options);
148
149try
150{
5dce104aJose Arriaga Maldonado1 years ago151using BinaryContent content = options.ToBinaryContent();
d5b5c604Liudmila Molkova2 years ago152ClientResult result = CompleteChat(content, cancellationToken.ToRequestOptions());
5dce104aJose Arriaga Maldonado1 years ago153ChatCompletion chatCompletion = ChatCompletion.FromClientResult(result);
d5b5c604Liudmila Molkova2 years ago154
155scope?.RecordChatCompletion(chatCompletion);
156return ClientResult.FromValue(chatCompletion, result.GetRawResponse());
157}
158catch (Exception ex)
159{
160scope?.RecordException(ex);
161throw;
162}
9f9f2936Jose Arriaga Maldonado2 years ago163}
164
13a9c686Jose Arriaga Maldonado1 years ago165/// <summary> Generates a completion for the given chat. </summary>
166/// <param name="messages"> The messages comprising the chat so far. </param>
167/// <exception cref="ArgumentNullException"> <paramref name="messages"/> is null. </exception>
168/// <exception cref="ArgumentException"> <paramref name="messages"/> is an empty collection, and was expected to be non-empty. </exception>
169public virtual async Task<ClientResult<ChatCompletion>> CompleteChatAsync(params ChatMessage[] messages)
170=> await CompleteChatAsync(messages, default(ChatCompletionOptions)).ConfigureAwait(false);
171
172/// <summary> Generates a completion for the given chat. </summary>
173/// <param name="messages"> The messages comprising the chat so far. </param>
174/// <exception cref="ArgumentNullException"> <paramref name="messages"/> is null. </exception>
175/// <exception cref="ArgumentException"> <paramref name="messages"/> is an empty collection, and was expected to be non-empty. </exception>
1c40de67Krzysztof Cwalina2 years ago176public virtual ClientResult<ChatCompletion> CompleteChat(params ChatMessage[] messages)
177=> CompleteChat(messages, default(ChatCompletionOptions));
178
9f9f2936Jose Arriaga Maldonado2 years ago179/// <summary>
13a9c686Jose Arriaga Maldonado1 years ago180/// Generates a completion for the given chat. The completion is streamed back token by token as it is being
181/// generated by the model instead of waiting for it to be finished first.
9f9f2936Jose Arriaga Maldonado2 years ago182/// </summary>
183/// <remarks>
13a9c686Jose Arriaga Maldonado1 years ago184/// <see cref="AsyncCollectionResult{T}"/> implements the <see cref="IAsyncEnumerable{T}"/> interface and can be
185/// enumerated over using the <c>await foreach</c> pattern.
9f9f2936Jose Arriaga Maldonado2 years ago186/// </remarks>
13a9c686Jose Arriaga Maldonado1 years ago187/// <param name="messages"> The messages comprising the chat so far. </param>
188/// <param name="options"> The options to configure the chat completion. </param>
189/// <param name="cancellationToken"> A token that can be used to cancel this method call. </param>
190/// <exception cref="ArgumentNullException"> <paramref name="messages"/> is null. </exception>
191/// <exception cref="ArgumentException"> <paramref name="messages"/> is an empty collection, and was expected to be non-empty. </exception>
7bdecfd8Anne Thompson2 years ago192public virtual AsyncCollectionResult<StreamingChatCompletionUpdate> CompleteChatStreamingAsync(IEnumerable<ChatMessage> messages, ChatCompletionOptions options = null, CancellationToken cancellationToken = default)
9f9f2936Jose Arriaga Maldonado2 years ago193{
194Argument.AssertNotNull(messages, nameof(messages));
195
196options ??= new();
197CreateChatCompletionOptions(messages, ref options, stream: true);
198
5dce104aJose Arriaga Maldonado1 years ago199using BinaryContent content = options.ToBinaryContent();
0ca4c062Jose Arriaga Maldonado1 years ago200return new AsyncSseUpdateCollection<StreamingChatCompletionUpdate>(
201async () => await CompleteChatAsync(content, cancellationToken.ToRequestOptions(streaming: true)).ConfigureAwait(false),
202StreamingChatCompletionUpdate.DeserializeStreamingChatCompletionUpdate,
203cancellationToken);
9f9f2936Jose Arriaga Maldonado2 years ago204}
205
1c40de67Krzysztof Cwalina2 years ago206/// <summary>
13a9c686Jose Arriaga Maldonado1 years ago207/// Generates a completion for the given chat. The completion is streamed back token by token as it is being
208/// generated by the model instead of waiting for it to be finished first.
1c40de67Krzysztof Cwalina2 years ago209/// </summary>
210/// <remarks>
a330c2e7Jose Arriaga Maldonado1 years ago211/// <see cref="CollectionResult{T}"/> implements the <see cref="IEnumerable{T}"/> interface and can be
13a9c686Jose Arriaga Maldonado1 years ago212/// enumerated over using the <c>await foreach</c> pattern.
1c40de67Krzysztof Cwalina2 years ago213/// </remarks>
13a9c686Jose Arriaga Maldonado1 years ago214/// <param name="messages"> The messages comprising the chat so far. </param>
215/// <param name="options"> The options to configure the chat completion. </param>
216/// <param name="cancellationToken"> A token that can be used to cancel this method call. </param>
217/// <exception cref="ArgumentNullException"> <paramref name="messages"/> is null. </exception>
218/// <exception cref="ArgumentException"> <paramref name="messages"/> is an empty collection, and was expected to be non-empty. </exception>
7bdecfd8Anne Thompson2 years ago219public virtual CollectionResult<StreamingChatCompletionUpdate> CompleteChatStreaming(IEnumerable<ChatMessage> messages, ChatCompletionOptions options = null, CancellationToken cancellationToken = default)
9f9f2936Jose Arriaga Maldonado2 years ago220{
221Argument.AssertNotNull(messages, nameof(messages));
222
223options ??= new();
224CreateChatCompletionOptions(messages, ref options, stream: true);
225
5dce104aJose Arriaga Maldonado1 years ago226using BinaryContent content = options.ToBinaryContent();
0ca4c062Jose Arriaga Maldonado1 years ago227return new SseUpdateCollection<StreamingChatCompletionUpdate>(
228() => CompleteChat(content, cancellationToken.ToRequestOptions(streaming: true)),
229StreamingChatCompletionUpdate.DeserializeStreamingChatCompletionUpdate,
230cancellationToken);
9f9f2936Jose Arriaga Maldonado2 years ago231}
232
1c40de67Krzysztof Cwalina2 years ago233/// <summary>
13a9c686Jose Arriaga Maldonado1 years ago234/// Generates a completion for the given chat. The completion is streamed back token by token as it is being
235/// generated by the model instead of waiting for it to be finished first.
236/// </summary>
237/// <remarks>
238/// <see cref="AsyncCollectionResult{T}"/> implements the <see cref="IAsyncEnumerable{T}"/> interface and can be
239/// enumerated over using the <c>await foreach</c> pattern.
240/// </remarks>
241/// <param name="messages"> The messages comprising the chat so far. </param>
242/// <exception cref="ArgumentNullException"> <paramref name="messages"/> is null. </exception>
243/// <exception cref="ArgumentException"> <paramref name="messages"/> is an empty collection, and was expected to be non-empty. </exception>
244public virtual AsyncCollectionResult<StreamingChatCompletionUpdate> CompleteChatStreamingAsync(params ChatMessage[] messages)
245=> CompleteChatStreamingAsync(messages, default(ChatCompletionOptions));
246
247/// <summary>
248/// Generates a completion for the given chat. The completion is streamed back token by token as it is being
249/// generated by the model instead of waiting for it to be finished first.
1c40de67Krzysztof Cwalina2 years ago250/// </summary>
251/// <remarks>
a330c2e7Jose Arriaga Maldonado1 years ago252/// <see cref="CollectionResult{T}"/> implements the <see cref="IEnumerable{T}"/> interface and can be
13a9c686Jose Arriaga Maldonado1 years ago253/// enumerated over using the <c>await foreach</c> pattern.
1c40de67Krzysztof Cwalina2 years ago254/// </remarks>
13a9c686Jose Arriaga Maldonado1 years ago255/// <param name="messages"> The messages comprising the chat so far. </param>
256/// <exception cref="ArgumentNullException"> <paramref name="messages"/> is null. </exception>
257/// <exception cref="ArgumentException"> <paramref name="messages"/> is an empty collection, and was expected to be non-empty. </exception>
7bdecfd8Anne Thompson2 years ago258public virtual CollectionResult<StreamingChatCompletionUpdate> CompleteChatStreaming(params ChatMessage[] messages)
1c40de67Krzysztof Cwalina2 years ago259=> CompleteChatStreaming(messages, default(ChatCompletionOptions));
260
5dce104aJose Arriaga Maldonado1 years ago261// CUSTOM:
262// - Added Experimental attribute.
263// - Call FromClientResult.
264[Experimental("OPENAI001")]
265public virtual async Task<ClientResult<ChatCompletion>> GetChatCompletionAsync(string completionId, CancellationToken cancellationToken = default)
266{
267Argument.AssertNotNullOrEmpty(completionId, nameof(completionId));
268
269ClientResult result = await GetChatCompletionAsync(completionId, cancellationToken.CanBeCanceled ? new RequestOptions { CancellationToken = cancellationToken } : null).ConfigureAwait(false);
270return ClientResult.FromValue(ChatCompletion.FromClientResult(result), result.GetRawResponse());
271}
272
273// CUSTOM:
274// - Added Experimental attribute.
275// - Call FromClientResult.
276[Experimental("OPENAI001")]
277public virtual ClientResult<ChatCompletion> GetChatCompletion(string completionId, CancellationToken cancellationToken = default)
278{
279Argument.AssertNotNullOrEmpty(completionId, nameof(completionId));
280
281ClientResult result = GetChatCompletion(completionId, cancellationToken.CanBeCanceled ? new RequestOptions { CancellationToken = cancellationToken } : null);
282return ClientResult.FromValue(ChatCompletion.FromClientResult(result), result.GetRawResponse());
283}
284
285// CUSTOM:
286// - Added Experimental attribute.
287// - Call FromClientResult.
288[Experimental("OPENAI001")]
289public virtual async Task<ClientResult<ChatCompletionDeletionResult>> DeleteChatCompletionAsync(string completionId, CancellationToken cancellationToken = default)
290{
291Argument.AssertNotNullOrEmpty(completionId, nameof(completionId));
292
293ClientResult result = await DeleteChatCompletionAsync(completionId, cancellationToken.CanBeCanceled ? new RequestOptions { CancellationToken = cancellationToken } : null).ConfigureAwait(false);
294return ClientResult.FromValue(ChatCompletionDeletionResult.FromClientResult(result), result.GetRawResponse());
295}
296
297// CUSTOM:
298// - Added Experimental attribute.
299// - Call FromClientResult.
300[Experimental("OPENAI001")]
301public virtual ClientResult<ChatCompletionDeletionResult> DeleteChatCompletion(string completionId, CancellationToken cancellationToken = default)
302{
303Argument.AssertNotNullOrEmpty(completionId, nameof(completionId));
304
305ClientResult result = DeleteChatCompletion(completionId, cancellationToken.CanBeCanceled ? new RequestOptions { CancellationToken = cancellationToken } : null);
306return ClientResult.FromValue(ChatCompletionDeletionResult.FromClientResult(result), result.GetRawResponse());
307}
308
9f9f2936Jose Arriaga Maldonado2 years ago309private void CreateChatCompletionOptions(IEnumerable<ChatMessage> messages, ref ChatCompletionOptions options, bool stream = false)
310{
311options.Messages = messages.ToList();
312options.Model = _model;
d6615abeJose Arriaga Maldonado1 years ago313if (stream)
314{
315options.Stream = true;
316options.StreamOptions = s_includeUsageStreamOptions;
317}
318else
319{
320options.Stream = null;
321options.StreamOptions = null;
322}
9f9f2936Jose Arriaga Maldonado2 years ago323}
324}