openai/openai-dotnet

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
OpenAI_2.0.0-beta.12

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/Custom/Chat/ChatClient.cs

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