openai/openai-dotnet

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
OpenAI_2.1.0-beta.1

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/Custom/Chat/ChatClient.cs

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