openai/openai-dotnet

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
OpenAI_2.2.0-beta.1

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/Custom/Chat/ChatClient.cs

259lines · modecode

1using OpenAI.Telemetry;
2using System;
3using System.ClientModel;
4using System.ClientModel.Primitives;
5using System.Collections.Generic;
6using System.Linq;
7using System.Threading;
8using System.Threading.Tasks;
9
10namespace OpenAI.Chat;
11
12// CUSTOM:
13// - Renamed.
14// - Suppressed constructor that takes endpoint parameter; endpoint is now a property in the options class.
15// - Suppressed methods that only take the options parameter.
16/// <summary> The service client for OpenAI chat operations. </summary>
17[CodeGenClient("Chat")]
18[CodeGenSuppress("ChatClient", typeof(ClientPipeline), typeof(ApiKeyCredential), typeof(Uri))]
19[CodeGenSuppress("CreateChatCompletionAsync", typeof(ChatCompletionOptions), typeof(CancellationToken))]
20[CodeGenSuppress("CreateChatCompletion", typeof(ChatCompletionOptions), typeof(CancellationToken))]
21public partial class ChatClient
22{
23 private readonly string _model;
24 private readonly OpenTelemetrySource _telemetry;
25
26 // CUSTOM: Added as a convenience.
27 /// <summary> Initializes a new instance of <see cref="ChatClient"/>. </summary>
28 /// <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>
29 /// <param name="apiKey"> The API key to authenticate with the service. </param>
30 /// <exception cref="ArgumentNullException"> <paramref name="model"/> or <paramref name="apiKey"/> is null. </exception>
31 /// <exception cref="ArgumentException"> <paramref name="model"/> is an empty string, and was expected to be non-empty. </exception>
32 public ChatClient(string model, string apiKey) : this(model, new ApiKeyCredential(apiKey), new OpenAIClientOptions())
33 {
34 }
35
36 // CUSTOM:
37 // - Added `model` parameter.
38 // - Used a custom pipeline.
39 // - Demoted the endpoint parameter to be a property in the options class.
40 /// <summary> Initializes a new instance of <see cref="ChatClient"/>. </summary>
41 /// <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>
42 /// <param name="credential"> The API key to authenticate with the service. </param>
43 /// <exception cref="ArgumentNullException"> <paramref name="model"/> or <paramref name="credential"/> is null. </exception>
44 /// <exception cref="ArgumentException"> <paramref name="model"/> is an empty string, and was expected to be non-empty. </exception>
45 public ChatClient(string model, ApiKeyCredential credential) : this(model, credential, new OpenAIClientOptions())
46 {
47 }
48
49 // CUSTOM:
50 // - Added `model` parameter.
51 // - Used a custom pipeline.
52 // - Demoted the endpoint parameter to be a property in the options class.
53 // - Added telemetry support.
54 /// <summary> Initializes a new instance of <see cref="ChatClient"/>. </summary>
55 /// <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>
56 /// <param name="credential"> The API key to authenticate with the service. </param>
57 /// <param name="options"> The options to configure the client. </param>
58 /// <exception cref="ArgumentNullException"> <paramref name="model"/> or <paramref name="credential"/> is null. </exception>
59 /// <exception cref="ArgumentException"> <paramref name="model"/> is an empty string, and was expected to be non-empty. </exception>
60 public ChatClient(string model, ApiKeyCredential credential, OpenAIClientOptions options)
61 {
62 Argument.AssertNotNullOrEmpty(model, nameof(model));
63 Argument.AssertNotNull(credential, nameof(credential));
64 options ??= new OpenAIClientOptions();
65
66 _model = model;
67 Pipeline = OpenAIClient.CreatePipeline(credential, options);
68 _endpoint = OpenAIClient.GetEndpoint(options);
69 _telemetry = new OpenTelemetrySource(model, _endpoint);
70 }
71
72 // CUSTOM:
73 // - Added `model` parameter.
74 // - Used a custom pipeline.
75 // - Demoted the endpoint parameter to be a property in the options class.
76 // - Added telemetry support.
77 // - Made protected.
78 /// <summary> Initializes a new instance of <see cref="ChatClient"/>. </summary>
79 /// <param name="pipeline"> The HTTP pipeline to send and receive REST requests and responses. </param>
80 /// <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>
81 /// <param name="options"> The options to configure the client. </param>
82 /// <exception cref="ArgumentNullException"> <paramref name="pipeline"/> or <paramref name="model"/> is null. </exception>
83 /// <exception cref="ArgumentException"> <paramref name="model"/> is an empty string, and was expected to be non-empty. </exception>
84 protected internal ChatClient(ClientPipeline pipeline, string model, OpenAIClientOptions options)
85 {
86 Argument.AssertNotNull(pipeline, nameof(pipeline));
87 Argument.AssertNotNullOrEmpty(model, nameof(model));
88 options ??= new OpenAIClientOptions();
89
90 _model = model;
91 Pipeline = pipeline;
92 _endpoint = OpenAIClient.GetEndpoint(options);
93 _telemetry = new OpenTelemetrySource(model, _endpoint);
94 }
95
96 /// <summary> Generates a completion for the given chat. </summary>
97 /// <param name="messages"> The messages comprising the chat so far. </param>
98 /// <param name="options"> The options to configure the chat completion. </param>
99 /// <param name="cancellationToken"> A token that can be used to cancel this method call. </param>
100 /// <exception cref="ArgumentNullException"> <paramref name="messages"/> is null. </exception>
101 /// <exception cref="ArgumentException"> <paramref name="messages"/> is an empty collection, and was expected to be non-empty. </exception>
102 public virtual async Task<ClientResult<ChatCompletion>> CompleteChatAsync(IEnumerable<ChatMessage> messages, ChatCompletionOptions options = null, CancellationToken cancellationToken = default)
103 {
104 Argument.AssertNotNullOrEmpty(messages, nameof(messages));
105
106 options ??= new();
107 CreateChatCompletionOptions(messages, ref options);
108 using OpenTelemetryScope scope = _telemetry.StartChatScope(options);
109
110 try
111 {
112 using BinaryContent content = options;
113
114 ClientResult result = await CompleteChatAsync(content, cancellationToken.ToRequestOptions()).ConfigureAwait(false);
115 ChatCompletion chatCompletion = (ChatCompletion)result;
116 scope?.RecordChatCompletion(chatCompletion);
117 return ClientResult.FromValue(chatCompletion, result.GetRawResponse());
118 }
119 catch (Exception ex)
120 {
121 scope?.RecordException(ex);
122 throw;
123 }
124 }
125
126 /// <summary> Generates a completion for the given chat. </summary>
127 /// <param name="messages"> The messages comprising the chat so far. </param>
128 /// <param name="options"> The options to configure the chat completion. </param>
129 /// <param name="cancellationToken"> A token that can be used to cancel this method call. </param>
130 /// <exception cref="ArgumentNullException"> <paramref name="messages"/> is null. </exception>
131 /// <exception cref="ArgumentException"> <paramref name="messages"/> is an empty collection, and was expected to be non-empty. </exception>
132 public virtual ClientResult<ChatCompletion> CompleteChat(IEnumerable<ChatMessage> messages, ChatCompletionOptions options = null, CancellationToken cancellationToken = default)
133 {
134 Argument.AssertNotNullOrEmpty(messages, nameof(messages));
135
136 options ??= new();
137 CreateChatCompletionOptions(messages, ref options);
138 using OpenTelemetryScope scope = _telemetry.StartChatScope(options);
139
140 try
141 {
142 using BinaryContent content = options;
143 ClientResult result = CompleteChat(content, cancellationToken.ToRequestOptions());
144 ChatCompletion chatCompletion = (ChatCompletion)result;
145
146 scope?.RecordChatCompletion(chatCompletion);
147 return ClientResult.FromValue(chatCompletion, result.GetRawResponse());
148 }
149 catch (Exception ex)
150 {
151 scope?.RecordException(ex);
152 throw;
153 }
154 }
155
156 /// <summary> Generates a completion for the given chat. </summary>
157 /// <param name="messages"> The messages comprising the chat so far. </param>
158 /// <exception cref="ArgumentNullException"> <paramref name="messages"/> is null. </exception>
159 /// <exception cref="ArgumentException"> <paramref name="messages"/> is an empty collection, and was expected to be non-empty. </exception>
160 public virtual async Task<ClientResult<ChatCompletion>> CompleteChatAsync(params ChatMessage[] messages)
161 => await CompleteChatAsync(messages, default(ChatCompletionOptions)).ConfigureAwait(false);
162
163 /// <summary> Generates a completion for the given chat. </summary>
164 /// <param name="messages"> The messages comprising the chat so far. </param>
165 /// <exception cref="ArgumentNullException"> <paramref name="messages"/> is null. </exception>
166 /// <exception cref="ArgumentException"> <paramref name="messages"/> is an empty collection, and was expected to be non-empty. </exception>
167 public virtual ClientResult<ChatCompletion> CompleteChat(params ChatMessage[] messages)
168 => CompleteChat(messages, default(ChatCompletionOptions));
169
170 /// <summary>
171 /// Generates a completion for the given chat. The completion is streamed back token by token as it is being
172 /// generated by the model instead of waiting for it to be finished first.
173 /// </summary>
174 /// <remarks>
175 /// <see cref="AsyncCollectionResult{T}"/> implements the <see cref="IAsyncEnumerable{T}"/> interface and can be
176 /// enumerated over using the <c>await foreach</c> pattern.
177 /// </remarks>
178 /// <param name="messages"> The messages comprising the chat so far. </param>
179 /// <param name="options"> The options to configure the chat completion. </param>
180 /// <param name="cancellationToken"> A token that can be used to cancel this method call. </param>
181 /// <exception cref="ArgumentNullException"> <paramref name="messages"/> is null. </exception>
182 /// <exception cref="ArgumentException"> <paramref name="messages"/> is an empty collection, and was expected to be non-empty. </exception>
183 public virtual AsyncCollectionResult<StreamingChatCompletionUpdate> CompleteChatStreamingAsync(IEnumerable<ChatMessage> messages, ChatCompletionOptions options = null, CancellationToken cancellationToken = default)
184 {
185 Argument.AssertNotNull(messages, nameof(messages));
186
187 options ??= new();
188 CreateChatCompletionOptions(messages, ref options, stream: true);
189
190 using BinaryContent content = options;
191
192 async Task<ClientResult> sendRequestAsync() =>
193 await CompleteChatAsync(content, cancellationToken.ToRequestOptions(streaming: true)).ConfigureAwait(false);
194 return new InternalAsyncStreamingChatCompletionUpdateCollection(sendRequestAsync, cancellationToken);
195 }
196
197 /// <summary>
198 /// Generates a completion for the given chat. The completion is streamed back token by token as it is being
199 /// generated by the model instead of waiting for it to be finished first.
200 /// </summary>
201 /// <remarks>
202 /// <see cref="CollectionResult{T}"/> implements the <see cref="IEnumerable{T}"/> interface and can be
203 /// enumerated over using the <c>await foreach</c> pattern.
204 /// </remarks>
205 /// <param name="messages"> The messages comprising the chat so far. </param>
206 /// <param name="options"> The options to configure the chat completion. </param>
207 /// <param name="cancellationToken"> A token that can be used to cancel this method call. </param>
208 /// <exception cref="ArgumentNullException"> <paramref name="messages"/> is null. </exception>
209 /// <exception cref="ArgumentException"> <paramref name="messages"/> is an empty collection, and was expected to be non-empty. </exception>
210 public virtual CollectionResult<StreamingChatCompletionUpdate> CompleteChatStreaming(IEnumerable<ChatMessage> messages, ChatCompletionOptions options = null, CancellationToken cancellationToken = default)
211 {
212 Argument.AssertNotNull(messages, nameof(messages));
213
214 options ??= new();
215 CreateChatCompletionOptions(messages, ref options, stream: true);
216
217 using BinaryContent content = options;
218 ClientResult sendRequest() => CompleteChat(content, cancellationToken.ToRequestOptions(streaming: true));
219 return new InternalStreamingChatCompletionUpdateCollection(sendRequest, cancellationToken);
220 }
221
222 /// <summary>
223 /// Generates a completion for the given chat. The completion is streamed back token by token as it is being
224 /// generated by the model instead of waiting for it to be finished first.
225 /// </summary>
226 /// <remarks>
227 /// <see cref="AsyncCollectionResult{T}"/> implements the <see cref="IAsyncEnumerable{T}"/> interface and can be
228 /// enumerated over using the <c>await foreach</c> pattern.
229 /// </remarks>
230 /// <param name="messages"> The messages comprising the chat so far. </param>
231 /// <exception cref="ArgumentNullException"> <paramref name="messages"/> is null. </exception>
232 /// <exception cref="ArgumentException"> <paramref name="messages"/> is an empty collection, and was expected to be non-empty. </exception>
233 public virtual AsyncCollectionResult<StreamingChatCompletionUpdate> CompleteChatStreamingAsync(params ChatMessage[] messages)
234 => CompleteChatStreamingAsync(messages, default(ChatCompletionOptions));
235
236 /// <summary>
237 /// Generates a completion for the given chat. The completion is streamed back token by token as it is being
238 /// generated by the model instead of waiting for it to be finished first.
239 /// </summary>
240 /// <remarks>
241 /// <see cref="CollectionResult{T}"/> implements the <see cref="IEnumerable{T}"/> interface and can be
242 /// enumerated over using the <c>await foreach</c> pattern.
243 /// </remarks>
244 /// <param name="messages"> The messages comprising the chat so far. </param>
245 /// <exception cref="ArgumentNullException"> <paramref name="messages"/> is null. </exception>
246 /// <exception cref="ArgumentException"> <paramref name="messages"/> is an empty collection, and was expected to be non-empty. </exception>
247 public virtual CollectionResult<StreamingChatCompletionUpdate> CompleteChatStreaming(params ChatMessage[] messages)
248 => CompleteChatStreaming(messages, default(ChatCompletionOptions));
249
250 private void CreateChatCompletionOptions(IEnumerable<ChatMessage> messages, ref ChatCompletionOptions options, bool stream = false)
251 {
252 options.Messages = messages.ToList();
253 options.Model = _model;
254 options.Stream = stream
255 ? true
256 : null;
257 options.StreamOptions = stream ? options.StreamOptions : null;
258 }
259}