Create diff for generator for multipart/form-data#1
Create diff for generator for multipart/form-data#1annelo-msft wants to merge 31 commits intoopenai-generate-cleanfrom
Conversation
| using RequestBody content = audio.ToRequestBody(); | ||
| Result result = await CreateTranscriptionAsync(content, context).ConfigureAwait(false); | ||
| return Result.FromValue(CreateTranscriptionResponse.FromResponse(result.GetRawResponse()), result.GetRawResponse()); | ||
| (BinaryContent content, string contentType, RequestOptions options) = await audio.ToMultipartContentAsync().ConfigureAwait(false); |
There was a problem hiding this comment.
This is where we obtain the multipart content instead of standard BinaryContent.
There was a problem hiding this comment.
| /// <param name="model"> ID of the model to use. Only `whisper-1` is currently available. </param> | ||
| /// <exception cref="ArgumentNullException"> <paramref name="file"/> is null. </exception> | ||
| public CreateTranscriptionRequest(BinaryData file, CreateTranscriptionRequestModel model) | ||
| public CreateTranscriptionRequest(Stream file, string fileName, CreateTranscriptionRequestModel model) |
There was a problem hiding this comment.
For MPFD, if there is a file-upload parameter/model property, we need the binary-holding type to be a Stream and an option for the end-user to pass a string with the filename (they can also pass null, but nullable is disabled in this file, so it's only visible here by the fact that the method doesn't throw ArgumentException if null is passed for it). So, we would generate two properties from whatever the TypeSpec is that indicates you can upload a file using the service endpoint.
There was a problem hiding this comment.
| /// <param name="speech"> The <see cref="CreateSpeechRequest"/> to use. </param> | ||
| /// <param name="cancellationToken"> The cancellation token to use. </param> | ||
| /// <exception cref="ArgumentNullException"> <paramref name="speech"/> is null. </exception> | ||
| public virtual async Task<ClientResult<BinaryData>> CreateSpeechAsync(CreateSpeechRequest speech, CancellationToken cancellationToken = default) |
There was a problem hiding this comment.
Unbranded clients don't take cancellation tokens in convenience methods.
There was a problem hiding this comment.
| /// <list type="bullet"> | ||
| /// <item> | ||
| /// <description> | ||
| /// This <see href="https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/core/Azure.Core/samples/ProtocolMethods.md">protocol method</see> allows explicit creation of the request and processing of the response for advanced scenarios. |
There was a problem hiding this comment.
Maybe we want to remove Azure in the docs here?
There was a problem hiding this comment.
| /// <exception cref="ClientResultException"> Service returned a non-success status code. </exception> | ||
| /// <returns> The response returned from the service. </returns> | ||
| public virtual async Task<ClientResult> CreateTranscriptionAsync(BinaryContent content, RequestOptions context = null) | ||
| public virtual async Task<ClientResult> CreateTranscriptionAsync(BinaryContent content, string contentType, RequestOptions options = null) |
There was a problem hiding this comment.
MPFD protocol methods take contentType -- this is the value of the Content-Type header. This allows the same protocol method to be generated in cases where the service endpoint can accept multiple different content types -- the caller can specify, and we can provide convenience methods for the version that works best for the client.
There was a problem hiding this comment.
| request.Headers.Set("Accept", "application/octet-stream"); | ||
| request.Headers.Set("Content-Type", "application/json"); | ||
| request.Content = content; | ||
| message.Apply(options); |
There was a problem hiding this comment.
The call to message.Apply(options) needs to move to the end of the request-creation helper method so that the semantics of SetHeader on RequestOptions are correct. This is because anything the user called SetHeader with should replace what the generated code does.
There was a problem hiding this comment.
| { | ||
| await pipeline.SendAsync(message).ConfigureAwait(false); | ||
|
|
||
| if (message.Response == null) |
There was a problem hiding this comment.
We don't need to validate this in unbranded clients.
There was a problem hiding this comment.
| await pipeline.SendAsync(message).ConfigureAwait(false); | ||
|
|
||
| if (message.Response == null) | ||
| if (message.Response!.IsError && options.ErrorOptions == ClientErrorBehaviors.Default) |
There was a problem hiding this comment.
We should clarify the positive case where we throw, instead of the negative case. The method should end returning a successful result to clarify what's happening in it.
There was a problem hiding this comment.
| { | ||
| internal static class ClientPipelineExtensions | ||
| { | ||
| public static async ValueTask<PipelineResponse> ProcessMessageAsync(this ClientPipeline pipeline, PipelineMessage message, RequestOptions requestContext, CancellationToken cancellationToken = default) |
There was a problem hiding this comment.
We don't use the cancellationToken and shouldn't give the option to pass it.
| if (message.Response!.IsError && options.ErrorOptions == ClientErrorBehaviors.Default) | ||
| { | ||
| throw new InvalidOperationException("Failed to receive Result."); | ||
| throw await ClientResultException.CreateAsync(message.Response).ConfigureAwait(false); |
There was a problem hiding this comment.
In an async context, we need to call the factory method to create the exception asynchronously.
There was a problem hiding this comment.
| return message.Response; | ||
| } | ||
|
|
||
| public static async ValueTask<ClientResult<bool>> ProcessHeadAsBoolMessageAsync(this ClientPipeline pipeline, PipelineMessage message, RequestOptions requestContext) |
There was a problem hiding this comment.
I don't think this is the correct logic for unbranded clients. We don't have to revisit or block on this today for OpenAI, but we should address it soon so we don't ship an unbranded client with the wrong behavior.
One specific problem here is that we don't honor any response classifiers an end-user might pass. There may be others as well, e.g. I don't think we want to return exploding responses from unbranded client -- it is not idiomatic to .NET to throw from properties on types.
| /// <exception cref="ArgumentNullException"> <paramref name="content"/> is null. </exception> | ||
| /// <exception cref="ClientResultException"> Service returned a non-success status code. </exception> | ||
| /// <returns> The response returned from the service. </returns> | ||
| public virtual async Task<ClientResult> CreateSpeechAsync(BinaryContent content, RequestOptions context = null) |
There was a problem hiding this comment.
Since the variable name is public, we need to change context to options.
There was a problem hiding this comment.
| } | ||
|
|
||
| internal PipelineMessage CreateCreateSpeechRequest(BinaryContent content, RequestOptions context) | ||
| private PipelineMessage CreateCreateSpeechRequest(BinaryContent content, RequestOptions options) |
There was a problem hiding this comment.
It is better for these to be private if there isn't a clear need for them to be internal.
There was a problem hiding this comment.
There was a problem hiding this comment.
I do not think we need this to be "internal", because other parts of our generated libraries are not using them.
But I prefer we change this later after all the changes that affect functionalities are resolved.
There was a problem hiding this comment.
@ArcturusZhang, that's fine, I agree this is lower priority than addressing public API or functional issues. Thanks!
| // <auto-generated/> | ||
|
|
||
| #nullable disable | ||
| #nullable enable |
There was a problem hiding this comment.
This is important for a "ClientModel" type so we can do proper validation on nulls.
There was a problem hiding this comment.
well the generated code will always have "nullable disable", therefore we cannot do this unless we change all of them in generated code (including models clients) and all other dependencies (such as resourcemanager)
There was a problem hiding this comment.
@ArcturusZhang, why is generated code not able to change #nullable disable to #nullable enable for select emitted files?
There was a problem hiding this comment.
We can change. But I believe we really do not have to change it now.
The generator does not support generating nullable context yet.
There was a problem hiding this comment.
@ArcturusZhang: I think the question that needs to be answered here is why are we looking at this as "all or nothing?" I would think that it should be possible to emit nullable in the scope of the unbranded writer without impacting DPG/MGP. Is that not the case, and if not - why?
| // <auto-generated/> | ||
|
|
||
| #nullable disable | ||
| #nullable enable |
There was a problem hiding this comment.
I don't have strong opinions about this implementation -- I just copied over the one from the customized OpenAI.
There was a problem hiding this comment.
Tracking bug fixes for this with this issue: Azure/autorest.csharp#4480
|
|
||
| /// <summary> Deserializes the model from a raw response. </summary> | ||
| /// <param name="response"> The result to deserialize the model from. </param> | ||
| /// <param name="response"> The response to deserialize the model from. </param> |
There was a problem hiding this comment.
These changes are low-priority since the APIs are internal, but if they are easy, they would make the code more readable and correct.
There was a problem hiding this comment.
|
|
||
| namespace OpenAI.Models | ||
| { | ||
| public partial class CreateTranscriptionRequest : IJsonModel<CreateTranscriptionRequest> |
There was a problem hiding this comment.
I don't know offhand if we want MPFD input models to support IPersistableModel, but they are not JSON, so probably not IJsonModel. Let me know if you want to discuss what IPersistableModel would look like and on what timeline we would want to solve that.
There was a problem hiding this comment.
| /// </para> | ||
| /// </summary> | ||
| public BinaryData File { get; } | ||
| public Stream File { get; } |
There was a problem hiding this comment.
Split the information of a file to upload in Model as separate Properties works on the scenario which only have one file to upload, but it does not work on multiple files to upload. (Azure/autorest.csharp#4488 (comment) it add additional Properties to the model which may break the model. Considering the model will be used in both multipart and application/json, it may not correct to serialize to json format.
How about we define an new type for File in Multipart, as Azure/autorest.csharp#4488 (comment)
There was a problem hiding this comment.
Changes in this file is processing in this PR: Azure/autorest.csharp#4516
| { | ||
| DateTimeKind.Utc => ToString((DateTimeOffset)value, format), | ||
| _ => throw new NotSupportedException($"DateTime {value} has a Kind of {value.Kind}. Azure SDK requires it to be UTC. You can call DateTime.SpecifyKind to change Kind property value to DateTimeKind.Utc.") | ||
| _ => throw new NotSupportedException($"DateTime {value} has a Kind of {value.Kind}. Generated clients require it to be UTC. You can call DateTime.SpecifyKind to change Kind property value to DateTimeKind.Utc.") |
There was a problem hiding this comment.
This change is resolved in this PR: Azure/autorest.csharp#4516
| PathBuilder.Append(value); | ||
| UriBuilder.Path = PathBuilder.ToString(); |
There was a problem hiding this comment.
comparing with the old code, we removed the pathSeparator part in this method. This is included in this PR: Azure/autorest.csharp#4519
There was a problem hiding this comment.
I did not include other removals in this PR yet. Will those break the current generated code? Those are various of overloads of the two remaining methods.
Would those removals require generator code changes in the clients? (I assume so)
If so, I suggest we delay those parts of changes, and prioritize others.
There was a problem hiding this comment.
I'm not sure I fully understand your question or the concern here -- but please clarify, I am happy to answer any questions on this. I had filed Azure/autorest.csharp#4480 to track updates to ClientUriBuilder -- we have some problems with the current implementation around escaping that will need to be resolved, but if the OpenAI generated client is functional, we can de-prioritize those for the time being.
There was a problem hiding this comment.
Let me rephrase myself: I compared the new code and old code of this file, because here github is not properly comparing it.
Your change consists of two parts:
- The first part included in this change: Azure/autorest.csharp@
e5e175f(#4519) - The second part is that we are removing those variant of
AppendPathandAppendQuery(for instance the overload that takesDateTimeOffset). I am wondering that would this causes compile errors in the generated clients? Are you removing those because your specific case is not using those methods, or removing them is part of the issue?
In the meantime, could you have a review on this PR? Now this PR is very simple, I only included the first part of the change, which resolves an issue reported by openai
| /// <exception cref="ClientResultException"> Service returned a non-success status code. </exception> | ||
| /// <returns> The response returned from the service. </returns> | ||
| public virtual async Task<ClientResult> CreateTranslationAsync(BinaryContent content, RequestOptions context = null) | ||
| public virtual async Task<ClientResult> CreateTranslationAsync(BinaryContent content, string contentType, RequestOptions options = null) |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
There was a problem hiding this comment.
OK I saw the other comments that this change belongs to MPFD changes
| /// <param name="cancellationToken"> The cancellation token to use. </param> | ||
| /// <exception cref="ArgumentNullException"> <paramref name="audio"/> is null. </exception> | ||
| public virtual ClientResult<CreateTranslationResponse> CreateTranslation(CreateTranslationRequest audio, CancellationToken cancellationToken = default) | ||
| public virtual ClientResult<CreateTranslationResponse> CreateTranslation(CreateTranslationRequest audio) |
There was a problem hiding this comment.
this change has been included in this PR: Azure/autorest.csharp#4524
| (BinaryContent content, string contentType, RequestOptions options) = audio.ToMultipartContentAsync().GetAwaiter().GetResult(); | ||
| ClientResult result = CreateTranslation(content, contentType, options); |
There was a problem hiding this comment.
I believe this is part of the multipart change, this PR does not contain this change.
| /// <item> | ||
| /// <description> | ||
| /// This <see href="https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/core/Azure.Core/samples/ProtocolMethods.md">protocol method</see> allows explicit creation of the request and processing of the response for advanced scenarios. | ||
| /// This <see href="https://azsdk/net/protocol/quickstart">protocol method</see> allows explicit creation of the request and processing of the response for advanced scenarios. |
There was a problem hiding this comment.
is this link correct? I cannot open it.
There was a problem hiding this comment.
There was a problem hiding this comment.
It should be https://aka.ms/azsdk/net/protocol-methods
There was a problem hiding this comment.
Sure I will change the link to this new one
No description provided.