openai/openai-dotnet

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
mailinhphan/clientOptions

Branches

Tags

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

Clone

HTTPS

Download ZIP

OpenAI/src/Custom/Audio/AudioClient.cs

702lines · modecode

1using Microsoft.TypeSpec.Generator.Customizations;
2using System;
3using System.ClientModel;
4using System.ClientModel.Primitives;
5using System.Diagnostics.CodeAnalysis;
6using System.IO;
7using System.Text.Json;
8using System.Threading;
9using System.Threading.Tasks;
10
11namespace OpenAI.Audio;
12
13// CUSTOM:
14// - Renamed.
15// - Suppressed constructor that takes endpoint parameter; endpoint is now a property in the options class.
16// - Suppressed methods that only take the options parameter.
17/// <summary> The service client for OpenAI audio operations. </summary>
18[CodeGenType("Audio")]
19[CodeGenSuppress("AudioClient", typeof(ClientPipeline), typeof(Uri))]
20[CodeGenSuppress("GenerateSpeechAsync", typeof(SpeechGenerationOptions), typeof(CancellationToken))]
21[CodeGenSuppress("GenerateSpeech", typeof(SpeechGenerationOptions), typeof(CancellationToken))]
22public partial class AudioClient
23{
24 private readonly string _model;
25
26 // CUSTOM: Added as a convenience.
27 /// <summary> Initializes a new instance of <see cref="AudioClient"/>. </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 AudioClient(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="AudioClient"/>. </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 <see cref="ApiKeyCredential"/> 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 AudioClient(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 /// <summary> Initializes a new instance of <see cref="AudioClient"/>. </summary>
54 /// <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>
55 /// <param name="credential"> The <see cref="ApiKeyCredential"/> to authenticate with the service. </param>
56 /// <param name="options"> The options to configure the client. </param>
57 /// <exception cref="ArgumentNullException"> <paramref name="model"/> or <paramref name="credential"/> is null. </exception>
58 /// <exception cref="ArgumentException"> <paramref name="model"/> is an empty string, and was expected to be non-empty. </exception>
59 public AudioClient(string model, ApiKeyCredential credential, OpenAIClientOptions options) : this(model, OpenAIClient.CreateApiKeyAuthenticationPolicy(credential), options)
60 {
61 }
62
63 // CUSTOM: Added as a convenience.
64 /// <summary> Initializes a new instance of <see cref="AudioClient"/>. </summary>
65 /// <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>
66 /// <param name="authenticationPolicy"> The authentication policy used to authenticate with the service. </param>
67 /// <exception cref="ArgumentNullException"> <paramref name="model"/> or <paramref name="authenticationPolicy"/> is null. </exception>
68 /// <exception cref="ArgumentException"> <paramref name="model"/> is an empty string, and was expected to be non-empty. </exception>
69 [Experimental("OPENAI001")]
70 public AudioClient(string model, AuthenticationPolicy authenticationPolicy) : this(model, authenticationPolicy, new OpenAIClientOptions())
71 {
72 }
73
74 // CUSTOM: Added as a convenience.
75 /// <summary> Initializes a new instance of <see cref="AudioClient"/>. </summary>
76 /// <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>
77 /// <param name="authenticationPolicy"> The authentication policy used to authenticate with the service. </param>
78 /// <param name="options"> The options to configure the client. </param>
79 /// <exception cref="ArgumentNullException"> <paramref name="model"/> or <paramref name="authenticationPolicy"/> is null. </exception>
80 /// <exception cref="ArgumentException"> <paramref name="model"/> is an empty string, and was expected to be non-empty. </exception>
81 [Experimental("OPENAI001")]
82 public AudioClient(string model, AuthenticationPolicy authenticationPolicy, OpenAIClientOptions options)
83 {
84 Argument.AssertNotNullOrEmpty(model, nameof(model));
85 Argument.AssertNotNull(authenticationPolicy, nameof(authenticationPolicy));
86 options ??= new OpenAIClientOptions();
87
88 _model = model;
89 Pipeline = OpenAIClient.CreatePipeline(authenticationPolicy, options);
90 _endpoint = OpenAIClient.GetEndpoint(options);
91 }
92
93 // CUSTOM:
94 // - Added `model` parameter.
95 // - Used a custom pipeline.
96 // - Demoted the endpoint parameter to be a property in the options class.
97 // - Made protected.
98 /// <summary> Initializes a new instance of <see cref="AudioClient"/>. </summary>
99 /// <param name="pipeline"> The HTTP pipeline to send and receive REST requests and responses. </param>
100 /// <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>
101 /// <param name="options"> The options to configure the client. </param>
102 /// <exception cref="ArgumentNullException"> <paramref name="pipeline"/> or <paramref name="model"/> is null. </exception>
103 /// <exception cref="ArgumentException"> <paramref name="model"/> is an empty string, and was expected to be non-empty. </exception>
104 protected internal AudioClient(ClientPipeline pipeline, string model, OpenAIClientOptions options)
105 {
106 Argument.AssertNotNull(pipeline, nameof(pipeline));
107 Argument.AssertNotNullOrEmpty(model, nameof(model));
108 options ??= new OpenAIClientOptions();
109
110 _model = model;
111 Pipeline = pipeline;
112 _endpoint = OpenAIClient.GetEndpoint(options);
113 }
114
115 /// <summary> Initializes a new instance of <see cref="AudioClient"/>. </summary>
116 /// <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>
117 /// <param name="credential"> The <see cref="ApiKeyCredential"/> to authenticate with the service. </param>
118 /// <param name="options"> The options to configure the client. </param>
119 /// <exception cref="ArgumentNullException"> <paramref name="model"/> or <paramref name="credential"/> is null. </exception>
120 /// <exception cref="ArgumentException"> <paramref name="model"/> is an empty string, and was expected to be non-empty. </exception>
121 [Experimental("OPENAI001")]
122 public AudioClient(string model, ApiKeyCredential credential, AudioClientOptions options) : this(model, OpenAIClient.CreateApiKeyAuthenticationPolicy(credential), options)
123 {
124 }
125
126 /// <summary> Initializes a new instance of <see cref="AudioClient"/>. </summary>
127 /// <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>
128 /// <param name="authenticationPolicy"> The authentication policy used to authenticate with the service. </param>
129 /// <param name="options"> The options to configure the client. </param>
130 /// <exception cref="ArgumentNullException"> <paramref name="model"/> or <paramref name="authenticationPolicy"/> is null. </exception>
131 /// <exception cref="ArgumentException"> <paramref name="model"/> is an empty string, and was expected to be non-empty. </exception>
132 [Experimental("OPENAI001")]
133 public AudioClient(string model, AuthenticationPolicy authenticationPolicy, AudioClientOptions options)
134 {
135 Argument.AssertNotNullOrEmpty(model, nameof(model));
136 Argument.AssertNotNull(authenticationPolicy, nameof(authenticationPolicy));
137 options ??= new AudioClientOptions();
138
139 _model = model;
140 Pipeline = OpenAIClientUtilities.CreatePipeline(authenticationPolicy, options, options.UserAgentApplicationId, options.OrganizationId, options.ProjectId);
141 _endpoint = OpenAIClientUtilities.GetEndpoint(options.Endpoint);
142 }
143
144 /// <summary> Initializes a new instance of <see cref="AudioClient"/>. </summary>
145 /// <param name="pipeline"> The HTTP pipeline to send and receive REST requests and responses. </param>
146 /// <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>
147 /// <param name="options"> The options to configure the client. </param>
148 /// <exception cref="ArgumentNullException"> <paramref name="pipeline"/> or <paramref name="model"/> is null. </exception>
149 /// <exception cref="ArgumentException"> <paramref name="model"/> is an empty string, and was expected to be non-empty. </exception>
150 [Experimental("OPENAI001")]
151 protected internal AudioClient(ClientPipeline pipeline, string model, AudioClientOptions options)
152 {
153 Argument.AssertNotNull(pipeline, nameof(pipeline));
154 Argument.AssertNotNullOrEmpty(model, nameof(model));
155 options ??= new AudioClientOptions();
156
157 _model = model;
158 Pipeline = pipeline;
159 _endpoint = OpenAIClientUtilities.GetEndpoint(options.Endpoint);
160 }
161
162 [Experimental("SCME0002")]
163 public AudioClient(AudioClientSettings settings)
164 : this(settings?.Model, AuthenticationPolicy.Create(settings), settings?.Options)
165 {
166 }
167
168 /// <summary>
169 /// Gets the name of the model used in requests sent to the service.
170 /// </summary>
171 [Experimental("OPENAI001")]
172 public string Model => _model;
173
174 /// <summary>
175 /// Gets the endpoint URI for the service.
176 /// </summary>
177 [Experimental("OPENAI001")]
178 public Uri Endpoint => _endpoint;
179
180 #region GenerateSpeech
181
182 /// <summary> Generates a life-like, spoken audio recording of the input text. </summary>
183 /// <remarks>
184 /// The default format of the generated audio is <see cref="GeneratedSpeechFormat.Mp3"/> unless otherwise specified
185 /// via <see cref="SpeechGenerationOptions.ResponseFormat"/>.
186 /// </remarks>
187 /// <param name="text"> The text to generate audio for. </param>
188 /// <param name="voice"> The voice to use in the generated audio. </param>
189 /// <param name="options"> The options to configure the audio generation. </param>
190 /// <param name="cancellationToken"> A token that can be used to cancel this method call. </param>
191 /// <exception cref="ArgumentNullException"> <paramref name="text"/> is null. </exception>
192 /// <returns> The generated audio in the specified output format. </returns>
193 public virtual async Task<ClientResult<BinaryData>> GenerateSpeechAsync(string text, GeneratedSpeechVoice voice, SpeechGenerationOptions options = null, CancellationToken cancellationToken = default)
194 {
195 Argument.AssertNotNull(text, nameof(text));
196
197 options ??= new();
198 CreateSpeechGenerationOptions(text, voice, ref options);
199
200 using BinaryContent content = options.ToBinaryContent();
201 ClientResult result = await GenerateSpeechAsync(content, cancellationToken.ToRequestOptions()).ConfigureAwait(false);
202 return ClientResult.FromValue(result.GetRawResponse().Content, result.GetRawResponse());
203 }
204
205 /// <summary> Generates a life-like, spoken audio recording of the input text. </summary>
206 /// <remarks>
207 /// The default format of the generated audio is <see cref="GeneratedSpeechFormat.Mp3"/> unless otherwise specified
208 /// via <see cref="SpeechGenerationOptions.ResponseFormat"/>.
209 /// </remarks>
210 /// <param name="text"> The text to generate audio for. </param>
211 /// <param name="voice"> The voice to use in the generated audio. </param>
212 /// <param name="options"> The options to configure the audio generation. </param>
213 /// <param name="cancellationToken"> A token that can be used to cancel this method call. </param>
214 /// <exception cref="ArgumentNullException"> <paramref name="text"/> is null. </exception>
215 /// <returns> The generated audio in the specified output format. </returns>
216 public virtual ClientResult<BinaryData> GenerateSpeech(string text, GeneratedSpeechVoice voice, SpeechGenerationOptions options = null, CancellationToken cancellationToken = default)
217 {
218 Argument.AssertNotNull(text, nameof(text));
219
220 options ??= new();
221 CreateSpeechGenerationOptions(text, voice, ref options);
222
223 using BinaryContent content = options.ToBinaryContent();
224 ClientResult result = GenerateSpeech(content, cancellationToken.ToRequestOptions()); ;
225 return ClientResult.FromValue(result.GetRawResponse().Content, result.GetRawResponse());
226 }
227
228 /// <summary> Generates a life-like, spoken audio recording of the input text as a streaming SSE event collection. </summary>
229 /// <param name="text"> The text to generate audio for. </param>
230 /// <param name="voice"> The voice to use in the generated audio. </param>
231 /// <param name="options"> The options to configure the audio generation. </param>
232 /// <param name="cancellationToken"> A token that can be used to cancel this method call. </param>
233 /// <exception cref="ArgumentNullException"> <paramref name="text"/> is null. </exception>
234 /// <returns> A streaming collection of speech generation updates. </returns>
235 [Experimental("OPENAI001")]
236 public virtual AsyncCollectionResult<StreamingSpeechUpdate> GenerateSpeechStreamingAsync(string text, GeneratedSpeechVoice voice, SpeechGenerationOptions options = null, CancellationToken cancellationToken = default)
237 {
238 Argument.AssertNotNull(text, nameof(text));
239 EnsureModelSupportsSpeechStreaming();
240
241 options ??= new();
242 options.StreamFormat = InternalCreateSpeechRequestStreamFormat.Sse;
243 CreateSpeechGenerationOptions(text, voice, ref options);
244
245 using BinaryContent content = options.ToBinaryContent();
246 return new AsyncSseUpdateCollection<StreamingSpeechUpdate>(
247 async () => await GenerateSpeechAsync(content, cancellationToken.ToRequestOptions(streaming: true)).ConfigureAwait(false),
248 StreamingSpeechUpdate.DeserializeStreamingSpeechUpdate,
249 cancellationToken);
250 }
251
252 /// <summary> Generates a life-like, spoken audio recording of the input text as a streaming SSE event collection. </summary>
253 /// <param name="text"> The text to generate audio for. </param>
254 /// <param name="voice"> The voice to use in the generated audio. </param>
255 /// <param name="options"> The options to configure the audio generation. </param>
256 /// <param name="cancellationToken"> A token that can be used to cancel this method call. </param>
257 /// <exception cref="ArgumentNullException"> <paramref name="text"/> is null. </exception>
258 /// <returns> A streaming collection of speech generation updates. </returns>
259 [Experimental("OPENAI001")]
260 public virtual CollectionResult<StreamingSpeechUpdate> GenerateSpeechStreaming(string text, GeneratedSpeechVoice voice, SpeechGenerationOptions options = null, CancellationToken cancellationToken = default)
261 {
262 Argument.AssertNotNull(text, nameof(text));
263 EnsureModelSupportsSpeechStreaming();
264
265 options ??= new();
266 options.StreamFormat = InternalCreateSpeechRequestStreamFormat.Sse;
267 CreateSpeechGenerationOptions(text, voice, ref options);
268
269 using BinaryContent content = options.ToBinaryContent();
270 return new SseUpdateCollection<StreamingSpeechUpdate>(
271 () => GenerateSpeech(content, cancellationToken.ToRequestOptions(streaming: true)),
272 StreamingSpeechUpdate.DeserializeStreamingSpeechUpdate,
273 cancellationToken);
274 }
275
276 #endregion
277
278 #region TranscribeAudio
279
280 /// <summary> Transcribes the input audio. </summary>
281 /// <param name="audio"> The audio stream to transcribe. </param>
282 /// <param name="audioFilename">
283 /// The filename associated with the audio stream. The filename's extension (for example: .mp3) will be used to
284 /// validate the format of the input audio. The request may fail if the filename's extension and the actual
285 /// format of the input audio do not match.
286 /// </param>
287 /// <param name="options"> The options to configure the audio transcription. </param>
288 /// <param name="cancellationToken"> A token that can be used to cancel this method call. </param>
289 /// <exception cref="ArgumentNullException"> <paramref name="audio"/> or <paramref name="audioFilename"/> is null. </exception>
290 /// <exception cref="ArgumentException"> <paramref name="audioFilename"/> is an empty string, and was expected to be non-empty. </exception>
291 public virtual async Task<ClientResult<AudioTranscription>> TranscribeAudioAsync(Stream audio, string audioFilename, AudioTranscriptionOptions options = null, CancellationToken cancellationToken = default)
292 {
293 Argument.AssertNotNull(audio, nameof(audio));
294 Argument.AssertNotNullOrEmpty(audioFilename, nameof(audioFilename));
295
296 if (options?.ResponseFormat == AudioTranscriptionFormat.Diarized)
297 {
298 throw new InvalidOperationException(
299 $"{nameof(AudioTranscriptionOptions.ResponseFormat)} must not be set to {nameof(AudioTranscriptionFormat.Diarized)} when calling {nameof(TranscribeAudio)}. "
300 + $"For diarized transcription, call {nameof(TranscribeAudioDiarizedAsync)} instead.");
301 }
302
303 using MultiPartFormDataBinaryContent content
304 = CreatePerCallTranscriptionOptions(options)
305 .ToMultipartContent(audio, audioFilename);
306
307 ClientResult result = await TranscribeAudioAsync(content, content.ContentType, cancellationToken.ToRequestOptions()).ConfigureAwait(false);
308 return ClientResult.FromValue(AudioTranscription.FromResponse(result.GetRawResponse()), result.GetRawResponse());
309 }
310
311 /// <summary> Transcribes the input audio. </summary>
312 /// <param name="audio"> The audio stream to transcribe. </param>
313 /// <param name="audioFilename">
314 /// The filename associated with the audio stream. The filename's extension (for example: .mp3) will be used to
315 /// validate the format of the input audio. The request may fail if the filename's extension and the actual
316 /// format of the input audio do not match.
317 /// </param>
318 /// <param name="options"> The options to configure the audio transcription. </param>
319 /// <param name="cancellationToken"> A token that can be used to cancel this method call. </param>
320 /// <exception cref="ArgumentNullException"> <paramref name="audio"/> or <paramref name="audioFilename"/> is null. </exception>
321 /// <exception cref="ArgumentException"> <paramref name="audioFilename"/> is an empty string, and was expected to be non-empty. </exception>
322 public virtual ClientResult<AudioTranscription> TranscribeAudio(Stream audio, string audioFilename, AudioTranscriptionOptions options = null, CancellationToken cancellationToken = default)
323 {
324 Argument.AssertNotNull(audio, nameof(audio));
325 Argument.AssertNotNullOrEmpty(audioFilename, nameof(audioFilename));
326
327 if (options?.ResponseFormat == AudioTranscriptionFormat.Diarized)
328 {
329 throw new InvalidOperationException(
330 $"{nameof(AudioTranscriptionOptions.ResponseFormat)} must not be set to {nameof(AudioTranscriptionFormat.Diarized)} when calling {nameof(TranscribeAudio)}. "
331 + $"For diarized transcription, call {nameof(TranscribeAudioDiarized)} instead.");
332 }
333
334 using MultiPartFormDataBinaryContent content
335 = CreatePerCallTranscriptionOptions(options)
336 .ToMultipartContent(audio, audioFilename);
337
338 ClientResult result = TranscribeAudio(content, content.ContentType, cancellationToken.ToRequestOptions());
339 return ClientResult.FromValue(AudioTranscription.FromResponse(result.GetRawResponse()), result.GetRawResponse());
340 }
341
342 /// <summary> Transcribes the input audio. </summary>
343 /// <param name="audioFilePath">
344 /// The path of the audio file to transcribe. The provided file path's extension (for example: .mp3) will be
345 /// used to validate the format of the input audio. The request may fail if the file path's extension and the
346 /// actual format of the input audio do not match.
347 /// </param>
348 /// <param name="options"> The options to configure the audio transcription. </param>
349 /// <exception cref="ArgumentNullException"> <paramref name="audioFilePath"/> is null. </exception>
350 /// <exception cref="ArgumentException"> <paramref name="audioFilePath"/> is an empty string, and was expected to be non-empty. </exception>
351 public virtual async Task<ClientResult<AudioTranscription>> TranscribeAudioAsync(string audioFilePath, AudioTranscriptionOptions options = null)
352 {
353 Argument.AssertNotNullOrEmpty(audioFilePath, nameof(audioFilePath));
354
355 using FileStream audioStream = File.OpenRead(audioFilePath);
356 return await TranscribeAudioAsync(audioStream, audioFilePath, options).ConfigureAwait(false);
357 }
358
359 /// <summary> Transcribes the input audio. </summary>
360 /// <param name="audioFilePath">
361 /// The path of the audio file to transcribe. The provided file path's extension (for example: .mp3) will be
362 /// used to validate the format of the input audio. The request may fail if the file path's extension and the
363 /// actual format of the input audio do not match.
364 /// </param>
365 /// <param name="options"> The options to configure the audio transcription. </param>
366 /// <exception cref="ArgumentNullException"> <paramref name="audioFilePath"/> is null. </exception>
367 /// <exception cref="ArgumentException"> <paramref name="audioFilePath"/> is an empty string, and was expected to be non-empty. </exception>
368 public virtual ClientResult<AudioTranscription> TranscribeAudio(string audioFilePath, AudioTranscriptionOptions options = null)
369 {
370 Argument.AssertNotNullOrEmpty(audioFilePath, nameof(audioFilePath));
371
372 using FileStream audioStream = File.OpenRead(audioFilePath);
373 return TranscribeAudio(audioStream, audioFilePath, options);
374 }
375
376 /// <summary> Transcribes the input audio with diarization. </summary>
377 /// <param name="audio"> The audio stream to transcribe. </param>
378 /// <param name="audioFilename">
379 /// The filename associated with the audio stream. The filename's extension (for example: .mp3) will be used to
380 /// validate the format of the input audio. The request may fail if the filename's extension and the actual
381 /// format of the input audio do not match.
382 /// </param>
383 /// <param name="options"> The options to configure the audio transcription. </param>
384 /// <param name="cancellationToken"> A token that can be used to cancel this method call. </param>
385 /// <exception cref="ArgumentNullException"> <paramref name="audio"/> or <paramref name="audioFilename"/> is null. </exception>
386 /// <exception cref="ArgumentException"> <paramref name="audioFilename"/> is an empty string, and was expected to be non-empty. </exception>
387 [Experimental("OPENAI001")]
388 public virtual async Task<ClientResult<DiarizedAudioTranscription>> TranscribeAudioDiarizedAsync(Stream audio, string audioFilename, AudioTranscriptionOptions options = null, CancellationToken cancellationToken = default)
389 {
390 Argument.AssertNotNull(audio, nameof(audio));
391 Argument.AssertNotNullOrEmpty(audioFilename, nameof(audioFilename));
392
393 if (options?.ResponseFormat is not null && options.ResponseFormat != AudioTranscriptionFormat.Diarized)
394 {
395 throw new InvalidOperationException(
396 $"{nameof(AudioTranscriptionOptions.ResponseFormat)} must be {nameof(AudioTranscriptionFormat.Diarized)} when calling {nameof(TranscribeAudioDiarized)}. "
397 + $"For non-diarized transcription, call {nameof(TranscribeAudioAsync)} instead.");
398 }
399
400 using MultiPartFormDataBinaryContent content
401 = CreatePerCallTranscriptionOptions(options)
402 .ToMultipartContent(audio, audioFilename);
403
404 ClientResult result = await TranscribeAudioAsync(content, content.ContentType, cancellationToken.ToRequestOptions()).ConfigureAwait(false);
405 using var document = JsonDocument.Parse(result.GetRawResponse().Content);
406 return ClientResult.FromValue(DiarizedAudioTranscription.DeserializeDiarizedAudioTranscription(document.RootElement, null), result.GetRawResponse());
407 }
408
409 /// <summary> Transcribes the input audio with diarization. </summary>
410 /// <param name="audio"> The audio stream to transcribe. </param>
411 /// <param name="audioFilename">
412 /// The filename associated with the audio stream. The filename's extension (for example: .mp3) will be used to
413 /// validate the format of the input audio. The request may fail if the filename's extension and the actual
414 /// format of the input audio do not match.
415 /// </param>
416 /// <param name="options"> The options to configure the audio transcription. </param>
417 /// <param name="cancellationToken"> A token that can be used to cancel this method call. </param>
418 /// <exception cref="ArgumentNullException"> <paramref name="audio"/> or <paramref name="audioFilename"/> is null. </exception>
419 /// <exception cref="ArgumentException"> <paramref name="audioFilename"/> is an empty string, and was expected to be non-empty. </exception>
420 [Experimental("OPENAI001")]
421 public virtual ClientResult<DiarizedAudioTranscription> TranscribeAudioDiarized(Stream audio, string audioFilename, AudioTranscriptionOptions options = null, CancellationToken cancellationToken = default)
422 {
423 Argument.AssertNotNull(audio, nameof(audio));
424 Argument.AssertNotNullOrEmpty(audioFilename, nameof(audioFilename));
425
426 if (options?.ResponseFormat is not null && options.ResponseFormat != AudioTranscriptionFormat.Diarized)
427 {
428 throw new InvalidOperationException(
429 $"{nameof(AudioTranscriptionOptions.ResponseFormat)} must be {nameof(AudioTranscriptionFormat.Diarized)} when calling {nameof(TranscribeAudioDiarized)}. "
430 + $"For non-diarized transcription, call {nameof(TranscribeAudio)} instead.");
431 }
432
433 using MultiPartFormDataBinaryContent content
434 = CreatePerCallTranscriptionOptions(options)
435 .ToMultipartContent(audio, audioFilename);
436
437 ClientResult result = TranscribeAudio(content, content.ContentType, cancellationToken.ToRequestOptions());
438 using var document = JsonDocument.Parse(result.GetRawResponse().Content);
439 return ClientResult.FromValue(DiarizedAudioTranscription.DeserializeDiarizedAudioTranscription(document.RootElement, null), result.GetRawResponse());
440 }
441
442 /// <summary> Transcribes the input audio with diarization. </summary>
443 /// <param name="audioFilePath">
444 /// The path of the audio file to transcribe. The provided file path's extension (for example: .mp3) will be
445 /// used to validate the format of the input audio. The request may fail if the file path's extension and the
446 /// actual format of the input audio do not match.
447 /// </param>
448 /// <param name="options"> The options to configure the audio transcription. </param>
449 /// <exception cref="ArgumentNullException"> <paramref name="audioFilePath"/> is null. </exception>
450 /// <exception cref="ArgumentException"> <paramref name="audioFilePath"/> is an empty string, and was expected to be non-empty. </exception>
451 [Experimental("OPENAI001")]
452 public virtual async Task<ClientResult<DiarizedAudioTranscription>> TranscribeAudioDiarizedAsync(string audioFilePath, AudioTranscriptionOptions options = null)
453 {
454 Argument.AssertNotNullOrEmpty(audioFilePath, nameof(audioFilePath));
455
456 using FileStream audioStream = File.OpenRead(audioFilePath);
457 return await TranscribeAudioDiarizedAsync(audioStream, audioFilePath, options).ConfigureAwait(false);
458 }
459
460 /// <summary> Transcribes the input audio with diarization. </summary>
461 /// <param name="audioFilePath">
462 /// The path of the audio file to transcribe. The provided file path's extension (for example: .mp3) will be
463 /// used to validate the format of the input audio. The request may fail if the file path's extension and the
464 /// actual format of the input audio do not match.
465 /// </param>
466 /// <param name="options"> The options to configure the audio transcription. </param>
467 /// <exception cref="ArgumentNullException"> <paramref name="audioFilePath"/> is null. </exception>
468 /// <exception cref="ArgumentException"> <paramref name="audioFilePath"/> is an empty string, and was expected to be non-empty. </exception>
469 [Experimental("OPENAI001")]
470 public virtual ClientResult<DiarizedAudioTranscription> TranscribeAudioDiarized(string audioFilePath, AudioTranscriptionOptions options = null)
471 {
472 Argument.AssertNotNullOrEmpty(audioFilePath, nameof(audioFilePath));
473
474 using FileStream audioStream = File.OpenRead(audioFilePath);
475 return TranscribeAudioDiarized(audioStream, audioFilePath, options);
476 }
477
478 // CUSTOM: Added Experimental attribute.
479 [Experimental("OPENAI001")]
480 public virtual AsyncCollectionResult<StreamingAudioTranscriptionUpdate> TranscribeAudioStreamingAsync(Stream audio, string audioFilename, AudioTranscriptionOptions options = null, CancellationToken cancellationToken = default)
481 {
482 Argument.AssertNotNull(audio, nameof(audio));
483 Argument.AssertNotNullOrEmpty(audioFilename, nameof(audioFilename));
484
485 EnsureModelSupportsStreaming();
486
487 MultiPartFormDataBinaryContent content
488 = CreatePerCallTranscriptionOptions(options, stream: true)
489 .ToMultipartContent(audio, audioFilename);
490
491 return new AsyncSseUpdateCollection<StreamingAudioTranscriptionUpdate>(
492 async () => await TranscribeAudioAsync(content, content.ContentType, cancellationToken.ToRequestOptions(streaming: true)).ConfigureAwait(false),
493 StreamingAudioTranscriptionUpdate.DeserializeStreamingAudioTranscriptionUpdate,
494 cancellationToken);
495 }
496
497 // CUSTOM: Added Experimental attribute.
498 [Experimental("OPENAI001")]
499 public virtual AsyncCollectionResult<StreamingAudioTranscriptionUpdate> TranscribeAudioStreamingAsync(string audioFilePath, AudioTranscriptionOptions options = null, CancellationToken cancellationToken = default)
500 {
501 Argument.AssertNotNullOrEmpty(audioFilePath, nameof(audioFilePath));
502
503 EnsureModelSupportsStreaming();
504
505 FileStream inputStream = File.OpenRead(audioFilePath);
506
507 MultiPartFormDataBinaryContent content
508 = CreatePerCallTranscriptionOptions(options, stream: true)
509 .ToMultipartContent(inputStream, audioFilePath);
510
511 AsyncSseUpdateCollection<StreamingAudioTranscriptionUpdate> result = new(
512 async () => await TranscribeAudioAsync(content, content.ContentType, cancellationToken.ToRequestOptions(streaming: true)).ConfigureAwait(false),
513 StreamingAudioTranscriptionUpdate.DeserializeStreamingAudioTranscriptionUpdate,
514 cancellationToken);
515 result.AdditionalDisposalActions.Add(() => inputStream?.Dispose());
516 return result;
517 }
518
519 // CUSTOM: Added Experimental attribute.
520 [Experimental("OPENAI001")]
521 public virtual CollectionResult<StreamingAudioTranscriptionUpdate> TranscribeAudioStreaming(Stream audio, string audioFilename, AudioTranscriptionOptions options = null, CancellationToken cancellationToken = default)
522 {
523 Argument.AssertNotNull(audio, nameof(audio));
524 Argument.AssertNotNullOrEmpty(audioFilename, nameof(audioFilename));
525
526 EnsureModelSupportsStreaming();
527
528 MultiPartFormDataBinaryContent content
529 = CreatePerCallTranscriptionOptions(options, stream: true)
530 .ToMultipartContent(audio, audioFilename);
531
532 return new SseUpdateCollection<StreamingAudioTranscriptionUpdate>(
533 () => TranscribeAudio(content, content.ContentType, cancellationToken.ToRequestOptions(streaming: true)),
534 StreamingAudioTranscriptionUpdate.DeserializeStreamingAudioTranscriptionUpdate,
535 cancellationToken);
536 }
537
538 // CUSTOM: Added Experimental attribute.
539 [Experimental("OPENAI001")]
540 public virtual CollectionResult<StreamingAudioTranscriptionUpdate> TranscribeAudioStreaming(string audioFilePath, AudioTranscriptionOptions options = null, CancellationToken cancellationToken = default)
541 {
542 Argument.AssertNotNullOrEmpty(audioFilePath, nameof(audioFilePath));
543
544 EnsureModelSupportsStreaming();
545
546 FileStream inputStream = File.OpenRead(audioFilePath);
547
548 MultiPartFormDataBinaryContent content
549 = CreatePerCallTranscriptionOptions(options, stream: true)
550 .ToMultipartContent(inputStream, audioFilePath);
551
552 SseUpdateCollection<StreamingAudioTranscriptionUpdate> result = new(
553 () => TranscribeAudio(content, content.ContentType, cancellationToken.ToRequestOptions(streaming: true)),
554 StreamingAudioTranscriptionUpdate.DeserializeStreamingAudioTranscriptionUpdate,
555 cancellationToken);
556 result.AdditionalDisposalActions.Add(() => inputStream?.Dispose());
557 return result;
558 }
559
560 private void EnsureModelSupportsStreaming()
561 {
562 if (string.Equals(_model, "whisper-1", StringComparison.OrdinalIgnoreCase))
563 {
564 string isEnabled = Environment.GetEnvironmentVariable("OPENAI_ENABLE_TRANSCRIPTION_SSE_STREAMING");
565 if (!string.Equals(isEnabled, "true", StringComparison.OrdinalIgnoreCase))
566 {
567 throw new NotSupportedException(
568 "The selected model 'whisper-1' does not support SSE streaming transcription. " +
569 "Please use a compatible model or set the environment variable 'OPENAI_ENABLE_TRANSCRIPTION_SSE_STREAMING=true' to bypass this check.");
570 }
571 }
572 }
573
574 private void EnsureModelSupportsSpeechStreaming()
575 {
576 if (string.Equals(_model, "tts-1", StringComparison.OrdinalIgnoreCase)
577 || string.Equals(_model, "tts-1-hd", StringComparison.OrdinalIgnoreCase))
578 {
579 string isEnabled = Environment.GetEnvironmentVariable("OPENAI_ENABLE_TTS_SSE_STREAMING");
580 if (!string.Equals(isEnabled, "true", StringComparison.OrdinalIgnoreCase))
581 {
582 throw new NotSupportedException(
583 $"The selected model '{_model}' does not support SSE streaming for speech generation. "
584 + "Please use a compatible model or set the environment variable 'OPENAI_ENABLE_TTS_SSE_STREAMING=true' to bypass this check.");
585 }
586 }
587 }
588
589 #endregion
590
591 #region TranslateAudio
592
593 /// <summary> Translates the input audio into English. </summary>
594 /// <param name="audio"> The audio stream to translate. </param>
595 /// <param name="audioFilename">
596 /// The filename associated with the audio stream. The filename's extension (for example: .mp3) will be used to
597 /// validate the format of the input audio. The request may fail if the filename's extension and the actual
598 /// format of the input audio do not match.
599 /// </param>
600 /// <param name="options"> The options to configure the audio translation. </param>
601 /// <param name="cancellationToken"> A token that can be used to cancel this method call. </param>
602 /// <exception cref="ArgumentNullException"> <paramref name="audio"/> or <paramref name="audioFilename"/> is null. </exception>
603 /// <exception cref="ArgumentException"> <paramref name="audioFilename"/> is an empty string, and was expected to be non-empty. </exception>
604 public virtual async Task<ClientResult<AudioTranslation>> TranslateAudioAsync(Stream audio, string audioFilename, AudioTranslationOptions options = null, CancellationToken cancellationToken = default)
605 {
606 Argument.AssertNotNull(audio, nameof(audio));
607 Argument.AssertNotNullOrEmpty(audioFilename, nameof(audioFilename));
608
609 options ??= new();
610 CreateAudioTranslationOptions(audio, audioFilename, ref options);
611
612 using MultiPartFormDataBinaryContent content = options.ToMultipartContent(audio, audioFilename);
613 ClientResult result = await TranslateAudioAsync(content, content.ContentType, cancellationToken.ToRequestOptions()).ConfigureAwait(false);
614 return ClientResult.FromValue(AudioTranslation.FromResponse(result.GetRawResponse()), result.GetRawResponse());
615 }
616
617 /// <summary> Translates the input audio into English. </summary>
618 /// <param name="audio"> The audio stream to translate. </param>
619 /// <param name="audioFilename">
620 /// The filename associated with the audio stream. The filename's extension (for example: .mp3) will be used to
621 /// validate the format of the input audio. The request may fail if the filename's extension and the actual
622 /// format of the input audio do not match.
623 /// </param>
624 /// <param name="options"> The options to configure the audio translation. </param>
625 /// <param name="cancellationToken"> A token that can be used to cancel this method call. </param>
626 /// <exception cref="ArgumentNullException"> <paramref name="audio"/> or <paramref name="audioFilename"/> is null. </exception>
627 /// <exception cref="ArgumentException"> <paramref name="audioFilename"/> is an empty string, and was expected to be non-empty. </exception>
628 public virtual ClientResult<AudioTranslation> TranslateAudio(Stream audio, string audioFilename, AudioTranslationOptions options = null, CancellationToken cancellationToken = default)
629 {
630 Argument.AssertNotNull(audio, nameof(audio));
631 Argument.AssertNotNullOrEmpty(audioFilename, nameof(audioFilename));
632
633 options ??= new();
634 CreateAudioTranslationOptions(audio, audioFilename, ref options);
635
636 using MultiPartFormDataBinaryContent content = options.ToMultipartContent(audio, audioFilename);
637 ClientResult result = TranslateAudio(content, content.ContentType, cancellationToken.ToRequestOptions());
638 return ClientResult.FromValue(AudioTranslation.FromResponse(result.GetRawResponse()), result.GetRawResponse());
639 }
640
641 /// <summary> Translates the input audio into English. </summary>
642 /// <param name="audioFilePath">
643 /// The path of the audio file to translate. The provided file path's extension (for example: .mp3) will be
644 /// used to validate the format of the input audio. The request may fail if the file path's extension and the
645 /// actual format of the input audio do not match.
646 /// </param>
647 /// <param name="options"> The options to configure the audio translation. </param>
648 /// <exception cref="ArgumentNullException"> <paramref name="audioFilePath"/> was null. </exception>
649 /// <exception cref="ArgumentException"> <paramref name="audioFilePath"/> is an empty string, and was expected to be non-empty. </exception>
650 public virtual ClientResult<AudioTranslation> TranslateAudio(string audioFilePath, AudioTranslationOptions options = null)
651 {
652 Argument.AssertNotNullOrEmpty(audioFilePath, nameof(audioFilePath));
653
654 using FileStream audioStream = File.OpenRead(audioFilePath);
655 return TranslateAudio(audioStream, audioFilePath, options);
656 }
657
658 /// <summary> Translates the input audio into English. </summary>
659 /// <param name="audioFilePath">
660 /// The path of the audio file to translate. The provided file path's extension (for example: .mp3) will be
661 /// used to validate the format of the input audio. The request may fail if the file path's extension and the
662 /// actual format of the input audio do not match.
663 /// </param>
664 /// <param name="options"> The options to configure the audio translation. </param>
665 /// <exception cref="ArgumentNullException"> <paramref name="audioFilePath"/> was null. </exception>
666 /// <exception cref="ArgumentException"> <paramref name="audioFilePath"/> is an empty string, and was expected to be non-empty. </exception>
667 public virtual async Task<ClientResult<AudioTranslation>> TranslateAudioAsync(string audioFilePath, AudioTranslationOptions options = null)
668 {
669 Argument.AssertNotNull(audioFilePath, nameof(audioFilePath));
670
671 using FileStream audioStream = File.OpenRead(audioFilePath);
672 return await TranslateAudioAsync(audioStream, audioFilePath, options);
673 }
674
675 #endregion
676
677 private void CreateSpeechGenerationOptions(string text, GeneratedSpeechVoice voice, ref SpeechGenerationOptions options)
678 {
679 options.Input = text;
680 options.Voice = voice;
681 options.Model = _model;
682 }
683
684 internal virtual AudioTranscriptionOptions CreatePerCallTranscriptionOptions(AudioTranscriptionOptions userOptions, bool stream = false)
685 {
686 AudioTranscriptionOptions copiedOptions = userOptions is null ? new() : userOptions.GetClone();
687
688 copiedOptions.Model = _model;
689
690 if (stream)
691 {
692 copiedOptions.Stream = true;
693 }
694
695 return copiedOptions;
696 }
697
698 private void CreateAudioTranslationOptions(Stream audio, string audioFilename, ref AudioTranslationOptions options)
699 {
700 options.Model = _model;
701 }
702}
703