Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OpenAI: Update convenience layer for multipart/form-data endpoints per APIView feedback #41

Merged
97 changes: 10 additions & 87 deletions .dotnet/src/Custom/Audio/AudioClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using System.ClientModel;
using System.ClientModel.Primitives;
using System.IO;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
using System.Threading.Tasks;

Expand Down Expand Up @@ -84,8 +83,9 @@ public virtual Task<ClientResult<BinaryData>> GenerateSpeechFromTextAsync(
return Shim.CreateSpeechAsync(request);
}

// convenience method - sync; Stream overload
// TODO: add refdoc comment
public virtual ClientResult<AudioTranscription> TranscribeAudio(FileStream audio, AudioTranscriptionOptions options = null)
=> TranscribeAudio(audio, Path.GetFileName(audio.Name), options);

public virtual ClientResult<AudioTranscription> TranscribeAudio(Stream audio, string fileName, AudioTranscriptionOptions options = null)
{
Argument.AssertNotNull(audio, nameof(audio));
Expand All @@ -104,28 +104,9 @@ public virtual ClientResult<AudioTranscription> TranscribeAudio(Stream audio, st
return ClientResult.FromValue(value, response);
}

// convenience method - sync
// TODO: add refdoc comment
public virtual ClientResult<AudioTranscription> TranscribeAudio(BinaryData audio, string fileName, AudioTranscriptionOptions options = null)
{
Argument.AssertNotNull(audio, nameof(audio));
Argument.AssertNotNull(fileName, nameof(fileName));

options ??= new();

using MultipartFormDataBinaryContent content = options.ToMultipartContent(audio, fileName, _clientConnector.Model);

ClientResult result = TranscribeAudio(content, content.ContentType);

PipelineResponse response = result.GetRawResponse();

AudioTranscription value = AudioTranscription.Deserialize(response.Content!);

return ClientResult.FromValue(value, response);
}
public virtual async Task<ClientResult<AudioTranscription>> TranscribeAudioAsync(FileStream audio, AudioTranscriptionOptions options = null)
=> await TranscribeAudioAsync(audio, Path.GetFileName(audio.Name), options).ConfigureAwait(false);

// convenience method - async
// TODO: add refdoc comment
public virtual async Task<ClientResult<AudioTranscription>> TranscribeAudioAsync(Stream audio, string filename, AudioTranscriptionOptions options = null)
{
Argument.AssertNotNull(audio, nameof(audio));
Expand All @@ -144,26 +125,6 @@ public virtual async Task<ClientResult<AudioTranscription>> TranscribeAudioAsync
return ClientResult.FromValue(value, response);
}

// convenience method - async
// TODO: add refdoc comment
public virtual async Task<ClientResult<AudioTranscription>> TranscribeAudioAsync(BinaryData audio, string fileName, AudioTranscriptionOptions options = null)
{
Argument.AssertNotNull(audio, nameof(audio));
Argument.AssertNotNull(fileName, nameof(fileName));

options ??= new();

using MultipartFormDataBinaryContent content = options.ToMultipartContent(audio, fileName, _clientConnector.Model);

ClientResult result = await TranscribeAudioAsync(content, content.ContentType).ConfigureAwait(false);

PipelineResponse response = result.GetRawResponse();

AudioTranscription value = AudioTranscription.Deserialize(response.Content!);

return ClientResult.FromValue(value, response);
}

private PipelineMessage CreateCreateTranscriptionRequest(BinaryContent content, string contentType, RequestOptions options)
{
PipelineMessage message = Shim.Pipeline.CreateMessage();
Expand All @@ -189,8 +150,9 @@ private PipelineMessage CreateCreateTranscriptionRequest(BinaryContent content,
return message;
}

// convenience method - sync; Stream overload
// TODO: add refdoc comment
public virtual ClientResult<AudioTranslation> TranslateAudio(FileStream audio, AudioTranslationOptions options = null)
=> TranslateAudio(audio, Path.GetFileName(audio.Name), options);

public virtual ClientResult<AudioTranslation> TranslateAudio(Stream audio, string fileName, AudioTranslationOptions options = null)
{
Argument.AssertNotNull(audio, nameof(audio));
Expand All @@ -209,28 +171,9 @@ public virtual ClientResult<AudioTranslation> TranslateAudio(Stream audio, strin
return ClientResult.FromValue(value, response);
}

// convenience method - sync
// TODO: add refdoc comment
public virtual ClientResult<AudioTranslation> TranslateAudio(BinaryData audio, string fileName, AudioTranslationOptions options = null)
{
Argument.AssertNotNull(audio, nameof(audio));
Argument.AssertNotNull(fileName, nameof(fileName));

options ??= new();

using MultipartFormDataBinaryContent content = options.ToMultipartContent(audio, fileName, _clientConnector.Model);

ClientResult result = TranslateAudio(content, content.ContentType);

PipelineResponse response = result.GetRawResponse();

AudioTranslation value = AudioTranslation.Deserialize(response.Content!);

return ClientResult.FromValue(value, response);
}
public virtual async Task<ClientResult<AudioTranslation>> TranslateAudioAsync(FileStream audio, AudioTranslationOptions options = null)
=> await TranslateAudioAsync(audio, Path.GetFileName(audio.Name), options).ConfigureAwait(false);

// convenience method - async; Stream overload
// TODO: add refdoc comment
public virtual async Task<ClientResult<AudioTranslation>> TranslateAudioAsync(Stream audio, string fileName, AudioTranslationOptions options = null)
{
Argument.AssertNotNull(audio, nameof(audio));
Expand All @@ -249,26 +192,6 @@ public virtual async Task<ClientResult<AudioTranslation>> TranslateAudioAsync(St
return ClientResult.FromValue(value, response);
}

// convenience method - async
// TODO: add refdoc comment
public virtual async Task<ClientResult<AudioTranslation>> TranslateAudioAsync(BinaryData audio, string fileName, AudioTranslationOptions options = null)
{
Argument.AssertNotNull(audio, nameof(audio));
Argument.AssertNotNull(fileName, nameof(fileName));

options ??= new();

using MultipartFormDataBinaryContent content = options.ToMultipartContent(audio, fileName, _clientConnector.Model);

ClientResult result = await TranslateAudioAsync(content, content.ContentType).ConfigureAwait(false);

PipelineResponse response = result.GetRawResponse();

AudioTranslation value = AudioTranslation.Deserialize(response.Content!);

return ClientResult.FromValue(value, response);
}

private PipelineMessage CreateCreateTranslationRequest(BinaryContent content, string contentType, RequestOptions options)
{
PipelineMessage message = Shim.Pipeline.CreateMessage();
Expand Down
25 changes: 4 additions & 21 deletions .dotnet/src/Custom/Audio/AudioTranscriptionOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,30 +15,11 @@ public partial class AudioTranscriptionOptions
public bool? EnableWordTimestamps { get; set; }
public bool? EnableSegmentTimestamps { get; set; }

internal MultipartFormDataBinaryContent ToMultipartContent(Stream fileStream, string fileName, string model)
internal MultipartFormDataBinaryContent ToMultipartContent(Stream file, string fileName, string model)
{
MultipartFormDataBinaryContent content = new();

content.Add(fileStream, "file", fileName);

AddContent(model, content);

return content;
}

internal MultipartFormDataBinaryContent ToMultipartContent(BinaryData audioBytes, string fileName, string model)
{
MultipartFormDataBinaryContent content = new();

content.Add(audioBytes, "file", fileName);

AddContent(model, content);

return content;
}

private void AddContent(string model, MultipartFormDataBinaryContent content)
{
content.Add(file, "file", fileName);
content.Add(model, "model");

if (Language is not null)
Expand Down Expand Up @@ -85,5 +66,7 @@ private void AddContent(string model, MultipartFormDataBinaryContent content)
byte[] data = JsonSerializer.SerializeToUtf8Bytes(granularities);
content.Add(data, "timestamp_granularities");
}

return content;
}
}
25 changes: 4 additions & 21 deletions .dotnet/src/Custom/Audio/AudioTranslationOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,30 +10,11 @@ public partial class AudioTranslationOptions
public AudioTranscriptionFormat? ResponseFormat { get; set; }
public float? Temperature { get; set; }

internal MultipartFormDataBinaryContent ToMultipartContent(Stream fileStream, string fileName, string model)
internal MultipartFormDataBinaryContent ToMultipartContent(Stream file, string fileName, string model)
{
MultipartFormDataBinaryContent content = new();

content.Add(fileStream, "file", fileName);

AddContent(model, content);

return content;
}

internal MultipartFormDataBinaryContent ToMultipartContent(BinaryData audioBytes, string fileName, string model)
{
MultipartFormDataBinaryContent content = new();

content.Add(audioBytes, "file", fileName);

AddContent(model, content);

return content;
}

private void AddContent(string model, MultipartFormDataBinaryContent content)
{
content.Add(file, "file", fileName);
content.Add(model, "model");

if (Prompt is not null)
Expand All @@ -54,5 +35,7 @@ private void AddContent(string model, MultipartFormDataBinaryContent content)

content.Add(value, "response_format");
}

return content;
}
}
48 changes: 6 additions & 42 deletions .dotnet/src/Custom/Files/FileClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,28 +37,10 @@ public FileClient(ApiKeyCredential credential = default, OpenAIClientOptions opt
_clientConnector = new(model: null, credential, options);
}

// convenience method - sync; Stream overload
// TODO: add refdoc comment
public virtual ClientResult<OpenAIFileInfo> UploadFile(Stream fileStream, string fileName, OpenAIFilePurpose purpose)
{
Argument.AssertNotNull(fileStream, nameof(fileStream));
Argument.AssertNotNull(fileName, nameof(fileName));

using MultipartFormDataBinaryContent content = UploadFileOptions.ToMultipartContent(fileStream, fileName, purpose);

ClientResult result = UploadFile(content, content.ContentType);

PipelineResponse response = result.GetRawResponse();
public virtual ClientResult<OpenAIFileInfo> UploadFile(FileStream file, OpenAIFilePurpose purpose)
=> UploadFile(file, Path.GetFileName(file.Name), purpose);

Internal.Models.OpenAIFile internalFile = Internal.Models.OpenAIFile.FromResponse(response);
OpenAIFileInfo fileInfo = new(internalFile);

return ClientResult.FromValue(fileInfo, response);
}

// convenience method - sync
// TODO: add refdoc comment
public virtual ClientResult<OpenAIFileInfo> UploadFile(BinaryData file, string fileName, OpenAIFilePurpose purpose)
public virtual ClientResult<OpenAIFileInfo> UploadFile(Stream file, string fileName, OpenAIFilePurpose purpose)
{
Argument.AssertNotNull(file, nameof(file));
Argument.AssertNotNull(fileName, nameof(fileName));
Expand All @@ -75,28 +57,10 @@ public virtual ClientResult<OpenAIFileInfo> UploadFile(BinaryData file, string f
return ClientResult.FromValue(fileInfo, response);
}

// convenience method - async; Stream overload
// TODO: add refdoc comment
public virtual async Task<ClientResult<OpenAIFileInfo>> UploadFileAsync(Stream file, string fileName, OpenAIFilePurpose purpose)
{
Argument.AssertNotNull(file, nameof(file));
Argument.AssertNotNull(fileName, nameof(fileName));

using MultipartFormDataBinaryContent content = UploadFileOptions.ToMultipartContent(file, fileName, purpose);

ClientResult result = await UploadFileAsync(content, content.ContentType).ConfigureAwait(false);

PipelineResponse response = result.GetRawResponse();
public virtual async Task<ClientResult<OpenAIFileInfo>> UploadFileAsync(FileStream file, OpenAIFilePurpose purpose)
=> await UploadFileAsync(file, Path.GetFileName(file.Name), purpose).ConfigureAwait(false);

Internal.Models.OpenAIFile internalFile = Internal.Models.OpenAIFile.FromResponse(response);
OpenAIFileInfo fileInfo = new(internalFile);

return ClientResult.FromValue(fileInfo, response);
}

// convenience method - async
// TODO: add refdoc comment
public virtual async Task<ClientResult<OpenAIFileInfo>> UploadFileAsync(BinaryData file, string fileName, OpenAIFilePurpose purpose)
public virtual async Task<ClientResult<OpenAIFileInfo>> UploadFileAsync(Stream file, string fileName, OpenAIFilePurpose purpose)
{
Argument.AssertNotNull(file, nameof(file));
Argument.AssertNotNull(fileName, nameof(fileName));
Expand Down
24 changes: 4 additions & 20 deletions .dotnet/src/Custom/Files/UploadFileOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,12 @@ namespace OpenAI.Files;

internal class UploadFileOptions
{
internal static MultipartFormDataBinaryContent ToMultipartContent(Stream fileStream, string fileName, OpenAIFilePurpose purpose)
internal static MultipartFormDataBinaryContent ToMultipartContent(Stream file, string fileName, OpenAIFilePurpose purpose)
{
MultipartFormDataBinaryContent content = new();

content.Add(fileStream, "file", fileName);
content.Add(file, "file", fileName);

AddContent(purpose, content);

return content;
}

internal static MultipartFormDataBinaryContent ToMultipartContent(BinaryData fileData, string fileName, OpenAIFilePurpose purpose)
{
MultipartFormDataBinaryContent content = new();

content.Add(fileData, "file", fileName);

AddContent(purpose, content);

return content;
}

private static void AddContent(OpenAIFilePurpose purpose, MultipartFormDataBinaryContent content)
{
string purposeValue = purpose switch
{
OpenAIFilePurpose.FineTuning => "fine-tune",
Expand All @@ -38,5 +20,7 @@ private static void AddContent(OpenAIFilePurpose purpose, MultipartFormDataBinar
};

content.Add(purposeValue, "\"purpose\"");

return content;
}
}
Loading
Loading